前端工程化基础(一):Node模块化

news2024/12/25 13:47:12

Node模块化

Node.js是什么

官方定义:Node.js是一个基于V8 JavaScript引擎的JavaScript运行时的环境

  • Node.js基于V8引擎来执行 JavaScript代码,但是Node.js中不仅仅有V8
    • 我们知道,V8可以嵌入到C++应用程序中,因此无论是Chrome还是Node.js,都是嵌入了V8引擎来执行的JavaScript代码
    • 在Chrome浏览器中,除了运行JavaScript代码之外,还要解析、渲染HTML、CSS等相关渲染引擎,同时还需要提供支持浏览器操作的API、浏览器自己的事件循环等
    • 在Node.js中我们也需要进行一些额外的操作,比如 文件系统的读写、网络IO、加密、压缩解压文件等操作
    • Node.js是JS代码的运行环境,由JS/C++/C语音编写(libuv就是C语音编写

image.png

Node.js应用场景

  • 应用一:目前 前端开发的库都是以node包的形式进行管理的
  • 应用二:npm/yarn/pnpm工具成为前端开发使用的最多工具
  • 应用三:越来越多的公司使用 Node.js作为web服务器开发、中间件、代理服务器
  • 应用四:大量项目需要 借助Node.js完成前后端渲染的同构应用
  • 应用五:资深前端工程师,使用Node.js编写脚本工具
  • 应用六:使用Electron来开发桌面应用程序

Node.js的输入输出

  • 我们在用命令行执行JS文件的时候,可以在命令后面敲空格,之后输入内容 node js.js num=10
  • JS中可以通过process.argv接收
console.log(process.argv);
[
  'F:\\nodejs\\node.exe',
  'D:\\Mrzhang\\Study\\前端\\CSS\\code\\js.js',
  'num=10',
]
  • 输出 console.log()即可

Node.js中的全局对象

  • global:相当于浏览器中window
  • process:进程相关的内容:process.argv是比较常用的
  • console
  • 定时器函数
    • setTimeout
    • setInterval
    • setImmediate(function () {})
    • process.nextTick(function(){})

特殊的全局对象

这些对象实际上是 模块中的变量,只是 每个模块都有,看起来是全局变量

在命令行交互中不可以使用

  • 包括:__dirname/__filename/exports/module/require()
//显示文件所在目录(不包含文件名称)
console.log(__dirname);
//显示文件所在目录(包含文件名称)
console.log(__filename);

认识模块化开发

  • 目前的程序代码量是十分庞大的
  • 模块化的目的是将庞大程序的代码,拆分成一个个小的结构
  • 而这个结构有属于 自己的逻辑代码,有自己的作用域,定义变量的时候,不会影响到其他的结构
  • 同时 这个结构的某些变量,函数以及对象等,又希望暴露出去,让其余结构访问
  • 其余结构可以通过某种方式,导入 另外结构的变量、函数对象等内容
  • 上面所说的 结构就是 模块,按照这种 结构划分开发程序的过程,就是模块化开发的过程

模块化的提出,主要是为了应对前端页面更加复杂的局面

在ES正式提出模块化前,社区提出了模块化的规范CommonJS(依旧再用),AMD、CMD(后面的两者均不在用了)

在ES6的时候,正式提出了标准的模块化ESModule

CommonJS规范和Node关系

CommonJS是一个规范,最开始提出来的时候,主要应用于服务器的

  • Node是CommonJS在服务器端一个具有代表性的实现

  • Browserify是CommonJS在浏览器中的一种实现

  • webpack打包工具具备对CommonJS的支持和转换

  • 因为Node对CmmmonJS进行了支持和实现,因此在开发Node过程中可以使用模块化开发

    • 在Node中每一个JS文件都是单独的模块
    • exports和module.exports可以负责对模块中的内容进行导出
    • require函数可以帮助我们 导入其他模块中的内容
  • 需要导出的文件

let until_name = "until";

function foo() {
  console.log("zhangcheng");
}

function bar() {
  console.log("bar");
}

//导出相关变量
exports.until_name = until_name;
exports.foo = foo;
exports.bar = bar;
  • 需要导入的文件
//引入变量
const until = require("./until.js");

//可以通过until.的方式访问变量
console.log(until.until_name);
until.foo();
until.bar();
-----------------------------------------
//我们可以借助解构赋值的方式,简化代码
//引入变量
const { until_name, foo, bar } = require("./until.js");

console.log(until_name);
foo();
bar();

exports导出的本质

exports实际上是一个对象,通过require函数,将导入文件中的变量与exports进行了引用赋值

image.png

  • 以上可以通过代码进行验证,更改 exports.name的值,并在两个文件中打印,即可观察到

module.exports导出

CommonJS导出,实际上是通过module.exports进行导出的,而module.exports和exports是同一个对象

  • 因为module.exports和exports是同一个对象,因此可以写出以下导出代码
let until_name = "until";

function foo() {
  console.log("zhangcheng");
}

function bar() {
  console.log("bar");
}

//导出相关变量
module.exports.until_name = until_name;
module.exports.foo = foo;
module.exports.bar = bar;
  • 而在真实的开发中,我们常写出以下代码
let until_name = "until";

function foo() {
  console.log("zhangcheng");
}

function bar() {
  console.log("bar");
}

//导出相关变量
module.exports = {
  until_name,
  foo,
  bar,
};
  • 若在最后,通过exports进行更改相关变量,则在导入的文件中,不会受到影响
let until_name = "until";

function foo() {
  console.log("zhangcheng");
}

function bar() {
  console.log("bar");
}

//导出相关变量
module.exports = {
  until_name,
  foo,
  bar,
};
//在导入的时候,until_name为“until”,而不是“hhhh”
exports.until_name = "hhhh"
  • 接下来看以下内存图
    • 通过 module.exports进行导出

image.png

通过 module.exports = {}进行导出

image.png

  • 因此通过以上两幅图,可以看出,在内存中 module.exportsexports的对应关系

  • 那么我们通过维基百科中对 CommonJS规范的解析

    • CommonJS中没有module.exports的概念
    • 但是为了实现模块的导出,Node中使用的是 Module类每一个模块都是Module的一个实例,也就是 module
    • 所以在Node中真正用于导出的其实不是 exports,而是module.exports
    • 因为 module.exports = exports,所以,exports也可以进行导出

require的细节

我们知道 require是一个函数,可以帮助我们引入一个文件(模块)中导出的对象,接下来,我们就要看一下它的查找规则是什么样的

require(X)

  • 情况一:X是Node核心模块,比如path、http
//会直接返回核心模块,并停止查找
const http = require("http")
  • 情况二:X是 以./或者…/或者根目录开头的
    • 第一步:将X当作一个文件在对应的目录下查找
      • 如果有后缀名,就按照后缀名的格式查找对应的文件
      • 如果没有后缀名,会按照如下顺序
        • 直接查找文件X
        • 查找X.js文件
        • 查找X.json文件
        • 查找X.node文件
    • 第二步:若没有查找到X对应的文件,将X作为一个目录
      • 查找目录下面的index文件
        • 查找X/index.js文件
        • 查找X/index.json文件
        • 查找X/index.node文件
    • 如果都没有找到,那么就会报错
const until = require(./until)
//首先会将until当成文件,查找until.js/until.json/until.node
//若没有查到,就会将until当成目录,查找它下面的index.js/index.json/index.node文件
  • 情况三:直接是一个X,该X没有路径,且不是一个核心模块
    • 会查找本目录下,以及上级目录下中 node_module目录中的模块
const axios = require("axios")
//会在node_module目录中查找axios

模块加载过程

  • 结论一:模块在被第一次引入时,模块中的JS代码会被运行一次
const until = require(./until)
//until.js中的代码会先运行一次
  • 模块被多次引入时,会进行缓存,最终只加载一次
    • 因为每个模块对象module中有一个loaded属性
    • false表示还没有被加载,为true表示已经加载
    • 已经加载的模块,不会再次被加载
//打印------
console.log(------);
//运行until.js中的代码           
const until = require(./until)
//打印+++++
console.log(+++++);

//下面的引入不会再被加载
const until1 = require(./until)  
const until2 = require(./until)
const until3 = require(./until)
  • 如果出现循环引入的情况
    • 会按照图结构,进行深度优先算法进行加载

CommonJS的缺点

  • CommonJS加载模块是同步的
    • 这就意味着只有 等到对应的模块加载完毕,当前模块中的内容才能被运行
    • 这个再服务器中不会出现问题,因为服务器 加载的js文件都是本地文件,加载速度比较快
  • 如果应用到浏览器中
    • 浏览器 加载js文件需要先从服务器将文件下载下来,之后 再加载运行
    • 如果 引入的某个js文件运行时间过长,就会阻塞后面的js代码无法运行,即使是一些简单的DOM操作

认识ESModule

ES6提出的模块化,前提是浏览器支持

  • 与CommonJS不同之处

    • 一方面使用了import和export(对应的CommonJS的是require和module.exports
    • 另一方面采用了编译期的静态分析,并且加入了动态引用的方式
  • 采用ES Module会默认采用严格模式


  • 创建一个HTML文件
    • 引入 script标签,在标签中写入type = “module”
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
        <!--注意,在本地测试的时候,ES Module需要启用live server进行测试,单纯运行文件不行-->
    <script src="./main.js" type="module"></script>
		<!--until.js暴露变量-->
	<script src="./until.js" type="module"></script>
  </body>
</html>

  • 创建until.js暴露变量
    • 通过export进行暴露
    • 而此 export并不是对象,只是特殊语法,{标识符}
let until_name = "until";

function foo() {
  console.log("zhangcheng");
}

function bar() {
  console.log("bar");
}

//导出相关变量
export { until_name, foo, bar };
  • 创建 main.js引入 until.js暴露的变量
    • 通过 import进行引入跟的文件名一定要写后缀
import { until_name, foo, bar } from "./until.js";
console.log(until_name);
foo();
bar();

ESModule的导入导出扩展

  • 导出的三种方式
//通过export直接导出
export{name,foo}

//导出的时候取别名
export{name as unName}

//直接导出变量
export let name = "zhangcheng"
  • 导入的三种方式
//直接导入
import {name} from "./until.js"

//导入的时候取别名
import {name as unName} from "./until.js"

//导入的时候将这个module取别名
import * as foo from "./until.js"
foo.name

export和import结合使用

常见于开源的框架中

image.png

image.png

  • index.js中一般不需要写逻辑代码,仅做模块的导入导出即可

  • index.html文件
    • 该文件中通过 script标签引入了main.js文件
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script src="./main.js" type="module"></script>
  </body>
</html>

  • main.js文件
    • 该文件直接引入了index.js文件
import { name, foo } from "./until/index.js";

console.log(name);
console.log(foo());
  • index.js文件
    • 该文件引入了tool.js和tool2.js文件,同时将变量暴露
//统一引入
import { name } from "./tool.js";
import { foo } from "./tool2.js";

//统一导出
export { name, foo };

------------------还可以做出以下优化
//export和import的结合
export { name } from "./tool.js";
export { foo } from "./tool2.js";


//写成这样也可以
export * from "./tool.js";
export * from "./tool2.js";
  • tool.js文件
export let name = "zhangcheng";
  • tool2.js文件
export function foo() {
  return "foo";
}

default用法

前面用到的都是有名字的导出,default是默认导出

  • 默认导出 是不需要指定名字的
  • 导入的时候不需要加{},且名字可以自己命名
  • 注意:一个文件只有一个默认导出

  • 默认导出方式
//默认导出方式一
function foo() {
  console.log(123);
}

export default foo;

//默认导出方式二
export default function(){
    console.log(123)
}
  • 引入方式
import aaa from "./until.js"
aaa()

import函数

当我们需要动态引入文件的时候,需要用到import函数

  • 正常使用import引入文件的时候,需要写在代码最顶层
import {name} from "./index.js"
//逻辑代码
  • 但是有时候需要按需引入一些文件,这时候就可以用到import函数
    • import函数返回的是一个Promise
let flag = true
if(flag){
    import("./index.js").then(res=>{
        console.log(res)
    })
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1413195.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EventSource 长链接执行

EventSource 说明文档MDN 其他参考文档 一、利用node启服务 import fs from fs import express from express const app express() // eventSource 仅支持 get 方法 // 服务器端发送的数据必须是纯文本格式&#xff0c;不能是二进制数据。 app.get(/api, (req, res) > …

智能AI系统开发,专业软件硬件物联网开发公司,探索未来科技新纪元

在信息时代&#xff0c;人工智能&#xff08;AI&#xff09;、物联网等前沿技术日益受到人们的关注。智能AI系统、专业软件硬件物联网开发公司应运而生。今天&#xff0c;我们将向大家介绍一家位于XX城的专业公司&#xff0c;致力于智能AI系统开发和软件硬件物联网领域的创新研…

光学系统的核心--分辨率

前言 在机器视觉领域&#xff0c;可以把各个部件划分为光源&#xff0c;镜头&#xff0c;相机&#xff0c;采集卡&#xff0c;算法&#xff0c;运动平台等。各个部件都是系统的有机组合&#xff0c;均有各自的重要性。在实际应用中&#xff0c;成像镜头涉及的光学理论较多&…

Vue 响应式原理源码剖析

文章目录 1. 说明2. 初始化initState()initProps()initData()observe()ObserverdefineReactive() 3. 数据代理4. 模板解析4.1. 模板解析的基本流程4.2. 模板解析(1): 大括号表达式解析4.3. 模板解析(2): 事件指令解析4.4. 模板解析(3): 一般指令解析 5. 数据绑定5.1. 数据绑定5…

实体识别与分类方法综述

目录 前言1 实体识别简介2 基于模板和规则的方法3 基于序列标注的方法3.1 常见序列标注模型3.2 模型参数估计和学习问题3.3 常见序列预测模型 4. 基于深度学习的实体识别方法5 基于预训练语言模型的实体识别5.1 BERT、GPT等预训练语言模型5.2 解码策略 6 特殊问题与挑战6.1 标签…

Ultraleap 3Di新建项目之给所有的Joint挂载物体

工程文件 Ultraleap 3Di给所有的Joint挂载物体 前期准备 参考上一期文章&#xff0c;进行正确配置 Ultraleap 3Di配置以及在 Unity 中使用 Ultraleap 3Di手部跟踪 新建项目 初始项目如下&#xff1a; 新建Create Empty 将新建的Create Empty&#xff0c;重命名为LeapPro…

10-微服务Nacos Config的通用配置

一、解决不同环境相同配置问题-自定义Data ID配置 在实际的开发过程中&#xff0c;我们的项目所用到的配置参数有的时候并不需要根据不同的环境进行区分&#xff0c;生产、测试、开发环境所用到的参数值是相同的。那么解决同一服务在多环境中&#xff0c;引用相同的配置的问题…

HTTP中POST、GET、PUT、DELETE方式的区别

GET请求会向数据库发索取数据的请求&#xff0c;从而来获取信息&#xff0c;该请求就像数据库的select操作一样&#xff0c;只是用来查询一下数据&#xff0c;不会修改、增加数据&#xff0c;不会影响资源的内容&#xff0c;即该请求不会产生副作用。无论进行多少次操作&#x…

uni-app 微信小程序之红包雨活动

文章目录 1. 页面效果2. 页面样式代码 1. 页面效果 GIF录屏有点卡&#xff0c;实际比较丝滑 每0.5s掉落一个红包控制4s后自动移除红包点击红包消除红包&#xff08;或者自行1&#xff0c;或者弹窗需求&#xff09; 2. 页面样式代码 <!-- 红包雨活动 --> <template>…

【TCP】重传与超时机制

前言 在网络通信的世界里&#xff0c;传输控制协议&#xff08;TCP&#xff09;扮演着一个至关重要的角色。它确保了数据的可靠传输&#xff0c;就像邮差确保每一封信都能准确无误地送达收件人手中一样。但是&#xff0c;网络环境充满了不确定性&#xff0c;数据包可能会因为各…

(大众金融)SQL server面试题(3)-客户已用额度总和

今天&#xff0c;面试了一家公司&#xff0c;什么也不说先来三道面试题做做&#xff0c;第三题。 那么&#xff0c;我们就开始做题吧&#xff0c;谁叫我们是打工人呢。 题目是这样的&#xff1a; DEALER_INFO经销商授信协议号码经销商名称经销商证件号注册地址员工人数信息维…

web3d-three.js场景设计器-mesh网格添加多模型-模型描述随动

给场景中的模型加上广告牌描述&#xff0c;可以在模型的MESH里添加Sprite&#xff0c;配上相应的文字&#xff0c; 描述Sprite的位置则是在mesh中的相对位置&#xff0c;比如模型高10&#xff0c;那么我们可以给一个y等于10 来进行适配&#xff0c;这样在移动模型mesh网格时可…

重磅!讯飞星火V3.5马上发布!AI写作、AI编程、AI绘画等功能全面提升!

讯飞星火大模型相信很多友友已经不陌生了&#xff0c;可以说是国内GPT相关领域的龙头标杆&#xff0c;而对于1月30日即将在讯飞星火发布会发出的V3.5新版本来说&#xff0c;讯飞星火V3.5与之前版本相比&#xff0c;性能提升方面相当明显&#xff0c;在提示语义理解、内容生成、…

常见の算法链表问题

时间复杂度 1.链表逆序 package class04;import java.util.ArrayList; import java.util.List;public class Code01_ReverseList {public static class Node {public int value;public Node next;public Node(int data) {value data;}}public static class DoubleNode {publi…

零基础学习数学建模——(四)备战美赛

本篇博客将讲解如何备战美赛。 什么是美赛 美赛&#xff0c;全称是美国大学生数学建模竞赛&#xff08;MCM/ICM&#xff09;&#xff0c;由美国数学及其应用联合会主办&#xff0c;是最高的国际性数学建模竞赛&#xff0c;也是世界范围内最具影响力的数学建模竞赛。 赛题内容…

Unity3D学习之UI系统——NGUI

文章目录 1. 前言2 NGUI下载和导入3. NGUI三大组件3.1 Root组件3.1.1 分辨率概念3.1.2 Root的作用3.1.3 root脚本各组件3.1.4 总结 3.2 Panel 组件3.2.1 Panel的作用3.2.2 Panel的控件3.2.3 总结 3.3 EventSystem组件3.3.1 作用3.3.2 组件3.3.3 总结 4 图集制作4.1 图集的作用4…

华为和苹果手机迁移备忘录数据方法

在数字时代&#xff0c;手机已成为我们生活的重要组成部分&#xff0c;而备忘录更是我们日常不可或缺的小助手。但当我们从华为切换到苹果&#xff0c;或从苹果转向华为时&#xff0c;如何确保那些重要的备忘信息不丢失&#xff0c;顺利迁移到新手机中呢&#xff1f; 我曾亲身…

Java实现加权平均分计算程序WeightedAverageCalculator

成绩加权平均分计算程序&#xff0c;带UI界面和输入保存功能。 因为本人对成绩的加权均分有所关注&#xff0c;但学校的教务系统查分时往往又不显示个人的加权均分&#xff0c;加之每次手动敲计算器计算很麻烦就花了点时间写了一个加权均分计算程序自用&#xff0c;顺便开源。…

Vue组件化-插槽Slot和非父子通信

一、认识插槽Slot作用 1.认识插槽Slot 在开发中&#xff0c;我们会经常封装一个个可复用的组件&#xff1a; 前面我们会通过props传递给组件一些数据&#xff0c;让组件来进行展示&#xff1b;但是为了让这个组件具备更强的通用性&#xff0c;我们不能将组件中的内容限制为固…

鸿蒙开发第2篇__装饰器

在ArkTS中&#xff0c; 有装饰器语法。 装饰器用于装饰类、结构、方法、变量&#xff0c;赋予其特殊的含义。 1. Component 表示自定义组件&#xff0c; 用此装饰器的组件是可重用的&#xff0c;可以与其他组件重合 此装饰器装饰的 struct 表示该结构体具有组件化能力&#…