ES6+ 模块化(一)
JavaScript 在设计之初主要用来开发 Web 的交互、动画和表单验证等单一的,而且程序的体积很小,大多数都是独立执行的。随着前端的发展 JavaScript 承接的越来越多,Node 的出现让 JavaScript 可以作为一门后端开发语言,程序的复杂度瞬间提升,所以有必要提供一种将 JavaScript 程序拆分为可按需导入的单独模块的机制。Node 是 JavaScript 的先行者,它使用了 CommonJS 的规范来实现模块化的。在 ES6 没出来之前有很多模块化的实践,比较有名的有:CommonJS、AMD、CMD,每个规范都有自己独立的思考。
随着 ES6 模块的发布,AMD 和 CMD 慢慢地淡出了我们的视野。现在主要常见的场景是 Node 端还采用 CommonJS 规范,这是历史原因。前端使用的是 ES6 module 规范,但是不能直接在前端使用,需要通过打包工具进行编译如:Webpack、Babel、Rollup 等。本文中我们将使用 Webpack 进行模块化编译工具,源放在 上,仅供参考。
现在的高级浏览器还能完全地 ES6 的模块化,如何在浏览器中运行 ES6 模块呢?有两种方式:
这两种方式各有优缺点,第一种是原生的使用方式,浏览器兼容要求比较高,第二种使用的是第三方打包编译工具可以很好地浏览器兼容问题,但是会有一定的学习成本,并且不能直接在浏览器中运行,只能使用编译后的。
使用浏览器运行原生 ES6 模块的源码在 的 mjs 中,浏览器是不能直接运行 ES6 模块化的,需一些准备工作。
首先,在引入 js 时需要定义 script 的类型:type="module"
。另外,js 的后缀不能使用 .js
了,需要使用 .mjs
。这样还是不能在浏览器中运行,还需要最后一步。模块化会涉及到系统,而本地打开的 html 是没有服务的,所以我们要使用 node 服务的方式打开 html ,这里我们使用 node 的包 http-server
可以在相应的目录中启动 node 服务。安装如下:
npm install --global http-server
安装完启动服务的工具还是会有问题,依然打不开,这是需要在浏览器中打开一些配置:浏览器地址栏输入:chrome://flags/
然后 JavaScript 把 Experimental JavaScript 项选择 Enabled 启用状态。如下图。
Webpack 是模块化打包工具,它兼容现在很多模块化加载方式,本节课程也主要使用 Webpack 的方式来学习 ES6 的模块化。Webpack 需要一定的学习成本可以在 上学习,这里就不进行介绍了,下面给出 webpack.con.js 的配置如下:
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "bundle.js",
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html' // 模版
})
],
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader",
},
},
],
},
};
ES6 的模块化设计思想是尽量,使得编译时就能确定模块的依赖关系,只能在顶级作用域。模块系统中,每个都是模块,模块之间都是相互独立。在 ES6 模块中采用 ,不知道的同学可以去看看,对于学习 JavaScript 语言有一定的帮助。
export 是导出语法,import 是导入语法。看下面的实例:
// a.js
export let x = ;
export let y = ;
// main.js
import {x, y} from './a.js';
console.log(x, y)
上面中,a.js 中使用 export 导出 x 和 y 两个变量,在 mian.js 中使用 import 进行导入。a.js 中还可以使用对象的方式导出:
let a = ;
let b = ;
export {
a,
b,
}
从上面的 main.js 中可以看出,export 使用的是引用方式进行导出的,导出的是接口,所以不能直接导出值。我们如下实例:
let a = ;
export a; // 编译报错
// 正确的方式如下
Export let a = ;
虽然使用 export 不能直接导出值,但是可以使用 export default
导出特定的值:
export default ;
export 模块导出的是接口,在模块内如果数据更新,则所依赖的地方的值都是最新的。
// a.js
let a = ;
setInterval(() => {
a++
})
export {
a
}
// main.js
import { a } from './a.js';
setInterval(() => {
consolog.log(a)
})
import 有声明的特点,类似 var 的特点,可以实现变量提升,但是不能变量对应的值。
// main.js
console.log(a)
import { a } from './a.js';
a = ; // 这样赋值是的
使用 export + from 命令的方式,提供了一种便捷的方式在当前的模块导出其他模块的,不能在当前模块下使用导出的变量。
// b.js
let a = ;
let b = ;
export {
a,
b,
}
// a.js
export {a,b} from './b.js';
export c = () => {}
// 等价于使用import 先导入,然后再使用 export 导出
import { a, b } from './b';
export {
a,
b,
}
// main.js
import {a, b, c} from './a.js'
export 和 import 命令规定要处于模块顶层,一旦出现在块级作用域内,就会报错。
// a.js
{
export let a = ;
}
// main.js
{
import { a } from './a';
}
//控制台答应: 'import' and 'export' may only appear at the top level
上面的中 export 和 import 都放在块级作用域中的,执行时会报错,提升你 export 和 import 只能在顶级出现。
export default 命令用来导出模块中认变量或,上面我们也提到了使用 export 导出的是对象不能导出值类型。
// a.js
export default 'imooc';
// main.js
import name from './a.js'
console.log(name); // imooc
export default 命令声明的可以是匿名的。
export default function () {
console.log('imooc');
}
// 等价
function fn() {
console.log('imooc');
}
export default fn;
也可以是类:
// a.js
export default class {
constructor() {
console.log('imooc')
}
// ...
}
// main.js
import A from './a';
const a = new A();
console.log(a)
开可以导出的是对象:
const obj = {
name: 'imooc',
getLession: function() {
console.log('ES6 imooc');
}
}
export default obj;
as 命令是用来的,在使用 import 命令导入时可以使用 as 来更改变量名。
// a.js
let a = ;
let b = ;
export {
a,
b,
}
// main.js
import { a, b as y } from './a';
console.log(a, y); // 1,2
如果模块中同时有 export default 导出和 export 导出时,在导入时可以使用 as 对认导出进行。
// a.js
let a = ;
let b = ;
export {
a,
b,
}
export default let c = ;
// main.js
import { a, b, default as c } from './a'
// 等价于下面直接在外面进行使用
import c, { a, b } from './a'
认导出的也可以放在 export 导出的对象中,但是需要使用 as default 命令:
// a.js
let a = ;
let b = ;
let c = 'imooc';
export {
a,
b,
c as default, // 相当于 export default 'imooc',给导出的对象default
}
本节主要讲解了 ES6 Module 的使用,通过对 export、import、default、as 命令的讲解学习了 ES6 Module 的基本,基本上涵盖了日常使用的场景。