• JavaScript – ES Module


    前言

    关于 JavaScript Modular 的多种版本和历史看这篇.

    参考:

    阮一峰 – Module 的语法

    阮一峰 – Module 的加载实现

    Export 语法

    逐个 export

    在想 export 的 var, func, class 前面加上 export 关键字即可.

    export const myVar = "value";
    export function myFunction() {}
    export class MyClass {}

    底部批量 export

    const myVar = "abc";
    function myFunction() {}
    class MyClass {}
    
    export { myVar, myFunction, MyClass };

    也可以在最底部, export 一个对象, 然后把想 export 的 var, func, class 放进去即可. 推荐使用这种写法, 一目了然, 管理加分.

    别名 alias

    export { myVar as yourVar };

    通过 as 关键字, 替换原本的变量名.

    错误语法

    export 'value'; // 错误 : 直接 export 值是不可以的, 需要有 variable name
    const myVar = 'abc'; // 正确
    
    
    const myVar = 'abc';
    export myVar; // 错误 : 需要一个对象声明 variable name
    export { myVar }; // 正确
    
    
    if(true) {
      const myVar = 'abc';
      export {myVar}; // 错误 : export 一定要在顶部, 不能 func 或者 if 里面
    }

    Import 语法

    逐个 import

    使用 import 关键字, 配上一个对象, 里面声明想使用的变量, 方法等, 然后指向一个 js file.

    import { myVar, MyClass, myFunction } from "./index2.js";
    
    myFunction();
    console.log(myVar);
    const myClass = new MyClass();

    import 所有内容

    把所有内容放入 myModule 对象

    import * as myModule from "./index2.js";
    
    myModule.myFunction();
    console.log(myModule.myVar);
    const myClass = new myModule.MyClass();

    别名 alias

    import { myVar as yourVar } from "./index2.js";
    
    console.log(yourVar);

    import nothing

    import "./index2.js";

    import 有执行的效果, 所以哪怕没有要使用任何 export 的内容, 也可以使用 import, 单纯为了让 index2.js 执行.

    Import, Export 知识点

    export, import 的变量是同步

    它不是  cache, 这点跟 CommonJS 不同哦.

    index2.js

    let myVar = "value1";
    setTimeout(() => {
      myVar = "value2";
    }, 1000);
    
    export { myVar };

    index.js

    import { myVar } from "./index2.js";
    
    console.log(myVar); // value1
    setTimeout(() => {
      console.log(myVar); // value2 (注意: 这里是 value2 哦)
    }, 2000);

     export, import 的变量是 read-only

    这点也和 CommonJS 不同

    import { myVar } from "./index2.js";
    
    myVar = "value2"; // Error: Assignment to constant variable.

    使用 import * as 依然不能改

    import * as myModule from "./index2.js";
    
    myModule.myVar = "value2"; // Error: Cannot assign to read only property 'myVar' of object '[object Module]'

    如果值是一个对象, 那可以改它的属性 (但不建议这样做, 很乱)

    // index2.js 
    export const myVar = { name: "Derrick" };
    
    
    // index.js
    import { myVar } from "./index2.js";
    
    myVar.name = "new name"; // ok 的

     import 有提升效果

    就像 function 一样 (但不建议这样做, 很乱)

    console.log(myVar); // ok 的
    import { myVar } from "./index2.js";

    import multiple time

    import 多次也只会执行一次, 它类似单列的效果.

    import "./index2.js";
    import "./index2.js"; // 并不会多跑一次哦

    错误写法

    import "./ind" + "ex2.js"; // 不支持动态写法, 因为 ES Module 的概念就是静态的
    
    if (true) {
      import 'index2.js'; // import 和 export 一样不能写在 func, if 里面 (除非使用 dynamic import 语法)
    }

    Export default 语法

    基本用法

    default 的目的是让 export 的时候省略命名, 好处是 import 的时候可以不需要定义名字. 用例子说明:

    // index2.js
    const myVar = "value";
    export { myVar };
    
    // index.js 
    import { myVar } from "./index2.js";
    console.log(myVar);

    index.js 必须知道 "myVar" 这个 key 才能 import 到指定的 variable. 如果使用 default 关键字的话: 

    // index2.js
    const myVar = "value";
    export default myVar;
    
    // index.js
    import whatEverName from "./index2.js";
    console.log(whatEverName);

    export 的时候使用了 defualt, import 的时候就可以任意命名.

    注: import 的时候不需要花括弧哦

    default 也就是个别名 alias

    上面的写法和下面这个写法是完全等价的

    // index2.js
    const myVar = "value";
    export { myVar as default };
    
    // index.js
    import { default as whatEverName } from "./index2.js";
    console.log(whatEverName);
    
    import * as myModule from "./index2.js";
    console.log(myModule.default); // 这样也是可以的

    default 可以和普通 export 一起用

    // index2.js
    const myVar = "value";
    function myFunction() {}
    
    export default myVar;
    export { myFunction };
    
    
    // index.js
    import myDefaultVar, { myFunction } from "./index2.js";
    
    console.log(myDefaultVar);
    myFunction();

    注: import default 不需要花括弧哦, 其它的就需要.

    错误写法

    // variable:
    // export 'value';                          // 错误, 因为没有名字
    // export const myVar = 'value';            // 正确, myVar 就是名字
    // export default 'value';                  // 正确, 因为 default 充当了名字
    // export default const myVar = 'value';    // 错误, 因为 default 已经是一个名字了, myVar 又是一个名字. 不允许
    // const myVar = "value";
    // export default myVar;                    // 正确, 分开写就可以
    
    // function & class:
    // export function (){}                     // 错误, 因为没有名字
    // export class {}                          // 错误, 因为没有名字
    // export function myFunction () {}         // 正确, myFunction 就是名字
    // export class MyClass {}                  // 正确, MyClass 就是名字
    // export default function() {}             // 正确, 因为 default 充当了名字
    // export default class {}                  // 正确, 因为 default 充当了名字
    // export default function myFunction() {}  // 正确, 因为 myFunction 被忽视了
    // export default class MyClass {}          // 正确, 因为 MyClass 被忽视了

    variable 的部分很好理解. 比较特别的是 function 和 class 的最后 2 个. myFunction 和 myClass 名字被忽视了, 所以语法是 ok 的, 但是同样的情况 variable 的最后一行是不通过的. myVar 没有被忽视.

    re-export 语法

    有时候只是想再导出一些方法, 就会需要用到 re-export 了. 有点像 extend module 的概念.

    // index.js
    import { myFunction, myVar, yourVar } from "./index2.js";
    
    console.log([myVar, yourVar]);
    
    
    // index2.js
    const yourVar = "yourVar";
    
    export { myVar, myFunction } from "./index3.js";
    export { yourVar };
    // export * from './index3.js' // export all (注意: 确保不要和当前 module export 撞名字哦, 包括 default 也不能撞哦)
    
    
    // index3.js
    const myVar = "myVar";
    function myFunction() {}
    export { myVar, myFunction };

    re-export != import

    re-export 并不会把 variable import 到当前模块

    export { myVar } from "./index2.js";
    console.log(myVar); // 错误

    如果当前 module 依赖 variable 那么就需要 import + re-export

    import { myVar } from "./index3.js";
    const yourVar = "yourVar";
    console.log(myVar); // 可以使用
    
    export { yourVar }; // 可以这样 re-export
    export { myVar, myFunction } from "./index3.js"; // 也可以继续用这个方式

    动态 import()

    注: 这个是 ES2020 才出的哦, 虽然 ES2015 就有 module 了, 但是最好是用 ES2020 版本, 它比较完善.

    上面讲的 import 都是静态的. 这也是 ES Module 的特色之一. 在编译是就可以知道依赖.

    但有时候是需要动态 import 的, 比如有些 module 用户不常用到又很大, 那么就可以做按需加载, lazy load 之类的了.

    static import

    import * as module from "./index2.js";
    console.log(module.myVar);
    console.log(module.default);

    dynamic import

    import("./index2.js").then(module => {
      console.log(module.myVar);
      console.log(module.default);
    });

    使用 import() 关键字, 它会返回 promise

    既然它是动态的, 那就可以放到 if, function 里头了, static import 就不行

    if (true) {
      import * as module from "./index2.js"; // Error: An import declaration can only be used at the top level of a module.
      console.log(module.myVar);
      console.log(module.default);
    }
    
    if (true) {
      import("./index2.js").then((module) => { // ok 的
        console.log(module.myVar);
        console.log(module.default);
      });
    }

    HTML 引入

    普通的 script 是这样的

    <script src="./index.js"></script>

    如果 index.js 里面用到了 import / export, 那么它会报错 "Cannot use import statement outside a module".

    需要加入 type="module"

    <script type="module" src="./index.js"></script>

    注: 它同时自带了 defer 的效果哦, async 则没有.

    Node.js 使用 ES Module

    如何使用 CommonJS 看这篇

    Node.js 默认是 CommonJS. 如果直接使用 import export 的话会报错 "Cannot use import statement outside a module".

    .mjs

    解决方法是把带有 ES Module 的 js file 改成 .mjs

    // my-module.mjs
    export const myVar = "myVar";
    
    // index.mjs
    import { myVar } from "./my-module.mjs";
    console.log(myVar);
    
    // 运行 node index.mjs

    .cjs

    另一个方法是把默认的 CommonJS 换成 ES Module

    到 package.json 添加属性 "type": "module"

    把 .mjs 换回 .js 就可以了.

    但这个时候如果想使用 CommonJS 的话, 就要把 .js 替换成 .cjs. 它和 .mjs 是一个概念.

    ES Module import CommonJS

    ES Module 是异步, 预编译的, CommonJS 是 runtime 的. 所以 ES Module 想 import CommonJS 是 ok 的.

    // my-module.cjs
    exports.myVar = "myVar";
    
    // index.mjs
    import { myVar } from "./my-module.cjs"; // 注意: 这个是 cjs 哦
    console.log(myVar);

    CommonJS require ES Module

    反过来就不行了, require mjs 会报错

    const myVar = require("./my-module.mjs");

    但是可以用 dynamic import()

    const myVar = require("./my-module2.cjs");
    import("./my-module.mjs").then((m) => console.log(m.myVar));
    console.log(myVar);

    在一个 .cjs 里面用了 require 同时也用 dynamic import(). 这个 .cjs 虽然用了 import 但它任然是属于 CJS 哦.

  • 相关阅读:
    记一次网易前端实习面试
    你知道吗?Web的26项基本概念和技术
    CSS 颜色代码大全
    研究旧项目, 常用 sql 语句
    Asp.net core 学习笔记 QR code and Barcode
    Html to PDF
    Asp.net core 学习笔记 Node Service
    Asp.net core Identity + identity server + angular + odata + 权限管理
    Angular Material (Components Cdk) 学习笔记 Table
    es6 getter setter
  • 原文地址:https://www.cnblogs.com/keatkeat/p/16209483.html
Copyright © 2020-2023  润新知