带你了解ES6 Module

1.commonJS
在说 es6 模块以前,我们先来看一下后端普遍使用的打包方式,commonJS的一些特性。

  • 同步加载:也就是串行执行,后面的任务要等到前面任务执行完才能继续执行。
  • 语法:commonJS中使用 require 引入,module.exports 输出。
  • 执行顺序:commonJS输出的是一个值得拷贝。也就是说,一旦输出一个值(该值会被缓存起来),模块内部的变化就不会影响到这个值。过程大概是,先将引入模块执行一遍,再执行后面的方法,等到内容执行完,生成一个变量存储需要输出的内容。

仔细阅读以下代码

// module.js
let a = 1;
function foo(){
    a++;
}
module.exports = {foo, a};

// index.js
let {foo, a} = require('./module.js');
console.log(a); // 1
foo();
console.log(a); // 1

  • 意义:commonJS的意义在于将聚类的方法和变量等限定的私有域中,同时支持导入和导出,将上下游模块无缝衔接,每个模块具有独立的空间,互不干扰。

2.import、export
ES6以后出现的import、export很好的实现了模块功能。核心思想是尽量静态化,使得编译时就能确定依赖关系,以及输出和输入的变量。而commonJS是在运行时确定这些东西。

  • 因为这个原因,有产生了以下两个问题:
    1.import、export都必须写在模块顶层 2.引入的模块应该是可以静态分析的,所以不允许运行时改变。也就是路径中不能使用变量。即无法实现如下代码中展示的require的功能
// commonJS require
let path = './module'
let module = require(path);
  • 异步加载:也就是并发执行,模块几乎同时导入,后面模块不需要等待前面模块导入完成。
  • 语法:ES6 Module中使用import引入,export输出。
  • 执行顺序:ES6 Module 输出的是值的动态引用,不会缓存。还是相同的代码,对比commonJS
// module.js
let a = 1;
function foo(){
    a++;
}
export {foo, a};

// index.js
let {foo, a} = require('./module.js');
console.log(a); // 1
foo();
console.log(a); // 2
  • import 可以执行模块,多次import只会执行一次,import在静态解析阶段执行
import foo from './module';
import foo from './module';

以上代码中foo只会被导入一次

  • import * as module from './module’引入module.js中所有的方法并存放到变量module中。
  • 为模块指定默认输出,一个文件中只能有一个export default,且后面不能跟变量声明的语句
// 与普通输出的区别
export default function add(){}
import add from './add'

export function add(){}
import {add} from './add'

本质上,export default 就是输出一个叫 default 的变量或者方法,然后系统允许你为它重命名。所以下面写法也是等效的

function add(){}

export {add as default}; // 等同于export default add

import {default as foo} from './module'; // 等同于import foo from './module'

3.export 与 import 的复合写法

export {foo,bar} from 'module';
// 等同于
import {foo,bar} from 'module;
export {foo,bar}

上面代码中,export和import语句可以结合为一行代码。但是,写成一行以后,foo和bar实际上没有被导入当前模块,只是相当于当前对外转发了这两个接口,导致当前模块不能直接使用foo,bar。

4.跨模块常量

  • const声明的常量只能在当前代码块中有效。如果想设置跨模块的常量,可以采用以下写法:
export const db = {
    a: '1',
    b: '2',
    ...
}

import {db} from './module';
vue项目中props应用,这样子组件就可以自己修改props中的属性,而不需要通过调用父组件的方法了。
// 父组件
<Child :userInfo="userIinfo"/>
data(){
    return{
        userInfo:{
            name: '小明',
            age:18
        }
    }
}
// 子组件
props:{
    value(){
        type: Object
    }
},
created(){
    this.value.name = '小红';
}

5.import()函数

  • 上文我们说过,import的模块需要静态分析,所以不能用于动态加载。也就不能完成required同样的功能
const path = './' + fileName;
const myModule = required(path);
因此,引入了import()函数,返回一个Promise对象

import (path).then(res=>{
    console.log(res)
}).catch(err=>{
    console.log(res)
})

 - 这个函数的引入起到的很好的作用,比如我们在做多语言加载的时候,我们需要引入语言包,但是我们又不想一次性将所有语言包全部引入,我们只需要引入需要的语言包就可以了,那么就用到了import()函数,下面是一个按需加载语言包的例子
export function loadLanguageAsync(lang){
  if (i18n.locale !== lang){
    if (!loadedLanguages.includes(lang)){
      return import(/* webpackChunkName: "lang-[request]" */ '@/lang/' + lang).then(msgs=>{
        i18n.setLocaleMessage(lang, msgs.default);
        loadedLanguages.push(lang);
        return setI18nLanguage(lang);
      })
    }
    return Promise.resolve(setI18nLanguage(lang));
  }
  return Promise.resolve(lang);
}

6.浏览器加载
传统方法

  • script标签默认是同步加载的,加上defer和async就会开启异步加载。区别:
    defer要等到整个页面在内存中正常渲染结束,才会执行;async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。另外,如果有多个defer脚本,会按照他们在页面中出现的顺序加载,而多个async脚本,是不能保证按顺序加载

es6模块加载

  • 浏览器加载 es6 模块,也是用
<script type="module">
    import {add, redis} from './module';
</script>

node.js加载
node中原本存在的commonJS与es6的模块加载并不兼容。因此node中做了限制
.mjs 文件总是以es6模块加载; .cjs 文件总是以commonJS加载, .js 文件的加载取决于 package.json 中type字段,若 type=“module” 则以es6模块加载,默认commonJS

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值