Node第八章(模块化二)

141 阅读4分钟

Es Module规范

1.Es Module出现

  • 之前我们说过JavaScript 没有他自己的模块化一直是他的痛点,所以才会产生一些社会规范
    • 比如用在node的CommonJS规范 用在浏览器的规范 AMD和CMD等
  • ESM模块采用export 和 import 关键字来实现模块化
    • export 负责将模块内的内容导出
    • import 负责从其他模块导入内容
  • ESM自动采用严格模式:use strict

2.在浏览器使用Es Module

  • 在浏览器中使用ESM模块 type设置为module

  • 此时你会发现你浏览器报错:

    • 这个错误MDN上面有解释: MDN

    • 你需要注意本地测试,如果你通过本地加载HTML文件比如(file://路径的文件)你将遇到cors错误

      因为JavaScript模块安全性需要

    • 你需要通过一个服务器来测试

    • 我使用的 Vscode, Vscode中有个插件 Live Server

3.在node使用ESM模块化

生成配置文件
  • 使用 npm init --y 这个命令不能包含中文名 生成package.json文件

    {
      "name": "useES6",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "type":"module", // module 是es6模块  commonjs 代表CommonJS规范
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC"
    }
    
基本使用
  • 导出有按需导出export和默认导出export default

    • bar.js
    const name = 'zs';
    const age = 18
    export { name };
    export default {
      name,
      age
    };
    
    
  • 导入有import

    • index.js
    import info, { name } from './modules/bar.js';
    console.log(info);// {name:'zs',age:18}
    console.log(name); // zs
    
其它用法
  • import导入方式有很多种:

    • 方式一: import {导出的标识} from '模块'

      import { name  } from './modules/bar.js';
      
    • 方式二:import 命名变量 from '模块'

      import info from './modules/bar.js';
      
    • 方式三:import * from '模块'

      import * from './modules/bar.js';
      
    • 方式四:导入时给标识符起别名as

      import info , { name as name1 } from './modules/bar.js';
      import * as info3 from './modules/bar.js'; 
      // [Module: null prototype]{ default:{name:'zs',age:18 }, name:'zs'} 
      console.log(info3) 
      

      注意:使用 * 导入的变量 会个export default 默认导出的对象起一个名字是default

  • export 和 import结合使用

    • math.js 文件中

      const name = 'zs';
      const age = 18
      export {
        name,
        age
      }
      
    • bar.js文件中

      export {name,age} from './math.js' // 导出方式一: export和import结合使用 按需导出
      or
      export * from './math.js' //  // 导出方式二: export和import结合使用 默认导出全部
      
    • index.js 文件中

      import {name,age} from './bar.js' // 按需引入
      or
      import * as bar from './bar.js'  // 全部引入
      
  • 为什么要这么做

    • 在开发和封装功能库时,通常我们希望将暴露的所有接口放到一个文件中
    • 这样方便统一管理,这个时候我可以使用import和export 结合
  • 注意: 默认导出export default 一个文件只能有一个, 可以和按需导出export搭配使用

import函数
  • 引入的import必须写在头部

  • 如果要引入json文件需要特殊处理 需要增加断言并且指定类型json node低版本不支持

    import data from './data.json' assert { type: "json" };
    console.log(data);
    
  • 动态导入模块

    • import静态加载不支持掺杂在逻辑中如果想动态加载请使用import函数模式

    • 动态加载 返回的Promise函数

      if(true){
          import('./test.js').then()
      }
      
  • ES11新增的特性 import meta

    • 它包含了这个模块的信息,比如这个模块的URL

      // 假如你的项目当前路径 file://home/user/my-module.mjs
      console.log(import.meta) // { url: "file:///home/user/my-module.mjs" }
      
    • vite环境中 你可以在.env文件设置环境变量,然后使用代码import meta.env读取

      // .env 文件
      VITE_APP_TITLE = Hello Vite !
      
      // main.js 文件
      console.log(import.meta.env.VITE_APP_TITLE); // 输出:Hello Vite!
      

    4.ESM的原理解析

    有以下代码

    // 在 main.js中
    import aaa from 'aaa.js'
    // 在 aaa.js中
    import bbb from 'bbb.js'
    // 在 bbb.js中
    export const name = 'zs'
    
    • 构建阶段
      • 在这个阶段浏览器首先会查找js模块文件,并去下载,将其解析成模块记录Module Record
    • 实例化阶段
      • 在这个阶段进行实例化,解析导出语句并分配内存空间即(模块环境记录)此时模块环境记录里面值是没有的
      • 解析导入语句也生成一个模块环境记录
      • 此时模块指向对应的内存地址
    • 求值阶段(运行阶段)
      • 运行代码,计算值,并且将值填入到内存地址中 image.png

Module Record 模块记录

Module Environment Record: 模块环境记录

exporting modulecan update variable value... : 导出模块可以更新变量值...

but importing modulecannot update variable value: 导入模块 无法更新变量值(不能在导入的时候去修改值)

5.ESM和CommonJS的区别

  • 相同点:
    • 同一个文件引用多次,会缓存,只会加载一次
  • 不同点
    • ES6模块是基于编译的异步加载,CommonJS是基于运行时同步加载
    • ES6顶层this是undefind(因为默认严格模式), CommonJS中的this是模块本身
    • ES6模块支持three shaking CommonJS是不支持的
    • ES6导出的值是不可以修改的(可读的,类似于const),CommonJS是支持修改的
    • 查找规则commjs会直接根据查找规则进行查找,ESM则必须显示引入
      • 比如有以下文件 index.js
      import index from 'index'// 这样写报错,因为原生ESM不支持 没有在vite或webpack环境下
      // 必须显示写出
      import index from 'index.js'