1、Node安装及前端模块化CommonJS学习

100 阅读5分钟

一. 邂逅Node.js

1.1. 对Node的认识

1.1.1. Node的概念理解

  • Node.js是一个基于V8 JavaScript引擎的JavaScript运行时环境

V8可以嵌入到任何C ++应用程序中,无论是Chrome还是Node.js,事实上都是嵌入了V8引擎来执行JavaScript代码; 但是在Chrome浏览器中,还需要解析、渲染HTML、CSS等相关渲染引擎,另外还需要提供支持浏览器操作的API、浏览器自己的事件循环等 在Node.js中我们也需要进行一些额外的操作,比如文件系统读/写、网络IO、加密、压缩解压文件等操作

1.1.2. Node和浏览器区别

在浏览器中,全局变量都是在window上的,比如有document、setInterval、setTimeout、alert、console等等

在浏览器中执行的JavaScript代码,如果我们在顶级范围内通过var定义的一个属性,默认会被添加到window对象上

在Node中,我们也有一个global属性,并且看起来它里面有很多其他对象

在node中,我们通过var定义一个变量,它只是在当前模块中有一个变量,不会放到全局中

1.2. Node的应用场景

  1. 前端开发的库
  2. npm、yarn、pnpm工具成为前端开发使用最多的工具
  3. 使用Node.js作为web服务器开发、中间件、代理服务器
  4. Node.js完成前后端渲染的同构应用
  5. 编写脚本工具
  6. 使用Electron来开发桌面应用程序

1.3. Node的安装

1.3.1. 安装Node的LTS版本

  • LTS版本:(Long-term support, 长期支持)相对稳定一些,推荐线上环境使用该版本;
  • Current版本:最新的Node版本,包含很多新特性;

1.4. Node的输入和输出

  • 输出: console.log

  • 输入: node aaa.js 参数

    • process.argv
// 输出的内容
console.log("Hello World");
const num1 = 10;
const num2 = 20;
console.log(num1 + num2);
​
// 2.给程序输入内容
const arg1 = process.argv[2];
const arg2 = process.argv[3];
console.log(arg1, arg2);

1.5. Node的全局对象

1.5.1. 特殊的全局对象

  • __dirname:获取当前文件所在的路径
  • __filename: 获取当前文件所在的路径和文件名称
  • exports/require/module.exports
// 1.类似于window的全局对象
console.log(global);
​
// 2.特殊的全局对象
// __dirname当前的文件所在的目录结构
console.log(__dirname);
// __filename当前目录文件名称
console.log(__filename);

1.5.2. 其他的全局对象

  • process

  • console

  • 定时器

    • setTimeout
    • setInterval
    • setImmediate
    • process.next
  • global

    • 和window的区别

      在浏览器中执行的JavaScript代码,如果我们在顶级范围内通过var定义的一个属性,默认会被添加到window对象上

      在node中,我们通过var定义一个变量,它只是在当前模块中有一个变量,不会放到全局中

    • globalThis

二. 前端模块化

2.1. 认识模块化

  • 对模块化概念的理解

    目的是将程序划分成一个个小的结构

    这个结构中编写属于自己的逻辑代码,有自己的作用域,定义变量名词时不会影响到其他的结构

    这个结构可以将自己希望暴露的变量、函数、对象等导出给其结构使用

    也可以通过某种方式,导入另外结构中的变量、函数、对象等

  • 早期是没有模块化, 带来一些问题

    • 命名冲突 -> 立即执行函数 -> 自定义模块 -> 没有规范
    • 社区中模块化规范: CommonJS/AMD/CMD
    • ES6中推出模块化: ES Module

2.2. CommonJS

2.2.1. Node基础使用

  • exports导出

util.js

const UTI_NAME = "util_name";
function formatCount() {
    return "500万"
}
function formatDate() {
    return "2023-03-02"
}
exports.UTI_NAME = UTI_NAME;
exports.formatCount = formatCount;
exports.formatDate = formatDate;
  • require导入

main.js

// 1.直接获取导出的对象,从对象中获取属性
// const util = require("./util.js");
// console.log(util.UTI_NAME);
// console.log(util.formatCount());
// console.log(util.formatDate());// 2.导入对象之后,直接对其进行解构
const { UTI_NAME, formatCount, formatDate } = require("./util.js");
console.log(UTI_NAME);
console.log(formatCount);
console.log(formatDate);

2.2.2. exports的本质

  • exports和require指向是同一个对象: 引入赋值

2.2.3. module.exports

  • module.exports和exports的关系:

    • 默认指向的是同一个对象
  • require本质查找的module.exports的对象

  • module.exports赋值新的对象

    • exports就没有什么意义
foo.js
const name = "foo";
const age = 18;
function sayHello() {
    console.log("sayHello");
}
​
// 1.在开发中使用的很少
// exports.name = name;
// exports.age = age;
// exports.sayHello = sayHello;
const name = "foo";
const age = 18;
function sayHello() {
  console.log("sayHello");
}
​
// 1.在开发中使用的很少
// exports.name = name;
// exports.age = age;
// exports.sayHello = sayHello;
​
// 2.将模块中内容导出
// 结论: Node导出的本质是在导出module.exports对象
// module.exports.name = name;
// module.exports.age = age;
// module.exports.sayHello = sayHello;
​
// console.log(exports.name, "-------");
// console.log(exports.age, "-------");
// console.log(exports.sayHello, "-------");
// console.log(exports === module.exports);
​
// 3.开发中常见的写法
module.exports = {
    name,
    age,
    sayHello
}
// exports.name = "哈哈哈";
// module.exports.name = "哈哈哈";
​
mian.js
const foo = require("./foo.js");
console.log(foo.name);
console.log(foo.age);
foo.sayHello();

2.2.4. require导入解析

  • 情况一: 内置模块path、http
  • 情况二: 路径
  • 情况三: 从node_modules查找

require 查找规则

  • 情况一:X是一个Node核心模块,比如path、http

    • 直接返回核心模块,并且停止查找
  • 情况二:直接是一个X(没有路径),并且X不是一个核心模块

    • 从node_modules查找
  • 情况三:X是一个Node核心模块,比如path、http

    • 第一步:将X当做一个文件在对应的目录下查找;

      • .如果有后缀名,按照后缀名的格式查找对应的文件

      • 如果没有后缀名,会按照如下顺序:

      1. 直接查找文件X

      2. 查找X.js文件

      3. 查找X.json文件

      4. 查找X.node文件

    • 第二步:没有找到对应的文件,将X作为一个目录

      • 查找目录下面的index文件

      1. 查找X/index.js文件

      2. 查找X/index.json文件

      3. 查找X/index.node文件

      4. 如果没有找到,那么报错:not found

2.2.5. 模块的加载过程

  • 结论一: 在第一次导入模块时, 代码会被执行一次

  • 结论二: 多次导入只会执行一次

    • module对象.loaded
  • 结论三: 循环引入(图结构)

    • 深度优先算法

2.3. AMD/CMD规范

  • AMD主要是应用于浏览器的一种模块化规范

    • 它采用的是异步加载模块
    • AMD实现的比较常用的库是require.js和curl.js
  • CMD规范也是应用于浏览器的一种模块化规范

    • 它也采用的也是异步加载模块,但是它将CommonJS的优点吸收了过来

    • CMD也有自己比较优秀的实现方案

      • SeaJS