其他章节请看:
es6 快速入门 系列
模块es6 以前,每个 javascript 都共享这一个全局作用域,随着代码量的增加,容易引发一些问题,比如命名冲突。
其他语言有包这样的概念来定义作用域,es6 的一个目标是解决作用域问题,也为了使 javascript 应用程序显得有序,于是引入了模块。
Tip:模块化开发规范有amd、commonjs等,而 es6 module 属于官方出品
准备环境笔者提供了一个环境(来自”初步认识 webpack“一文),方便对下面介绍的语法进行测试、验证和学习。
项目结构如下:
es6-module - src // 项目源码 - index.html // 页面模板 - index.js // 入口 - package.json // 存放了项目依赖的包 - webpack.config.js // webpack配置文件src中的代码如下:
// index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta content="width=`, initial-scale=1.0"> <title>Document</title> </head> <body> <p>请查看控制台</p> </body> </html> // index.js console.log('我是入口')package.json:
{ "name": "webpack-example2", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack", "dev": "webpack-dev-server" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "html-webpack-plugin": "^4.5.2", "webpack": "^4.46.0", "webpack-cli": "^3.3.12", "webpack-dev-server": "^3.11.2" } }webpack.config.js:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }) ], mode: 'development', devServer: { open: true, contentBase: path.join(__dirname, 'dist'), compress: true, port: 9000, }, };在 es6-module 目录下运行项目:
// 安装项目依赖的包 > npm i // 启动服务 > npm run dev启动服务器后,浏览器会自动打开页面,如果看到”请查看控制台“,说明环境已准备就绪。
什么是模块模块是自动运行在严格模式下并且没有办法退出运行的 javascript 代码。在模块顶部创建的变量不会自动被添加到全局作用域,仅在这个模块的顶级作用域中。
模块如果需要提供一些接口给其他模块使用,则可以通过 export 关键字导出;其他模块则可以通过 import 关键字导入其他模块
模块的真正魔力是仅导出和导入你需要的绑定,而不是将所用的东西都放到一个文件。只有很好的理解导出和导入才能理解模块与脚本的区别
Tip: 脚本,就是任何不是模块的 javascript 代码;模块顶部的 this 的值是 undefined。
导出的基本语法可以使用 export 关键字将一部分代码暴露给其他模块,最简单的方法,可以将 export 放在任何变量、函数或类声明的前面,就像这样:
// 导出数据 export var name = 'ph' export let age = 18 // 导出函数 export function sum(v1, v2){ return v1 + v2 } // 导出类 export class Dog{ constructor(name, color, age){ this.name = name; this.color = color; this.age = age; } toString(){ return `name=${this.name} color=${this.color} age=${this.age}` } } // 这个函数是模块私有的 function subtract(v1, v2){ return v1 - v2; }除了 export 关键字,每一个声明与脚本中的一模一样。因为导出的函数、类等声明都需要一个名称,除非用 default 关键字,否则不能匿名导出函数或类。
// 正确。使用 default 匿名导出 export default function(v1, v2){ return v1 * v2 } // 正确 export default 1 // 错误 export 1 导入的基本语法从模块中导出的功能可以通过 import 关键字在另一个模块中访问,import 语句中的两个部分是:要导入的标识符和标识符应当从哪个模块中导入。基本形式是:
import {name, age} from './x.js'当从模块中导入一个绑定,就好像使用 const 定义的一样。结果是你无法重新定义另一个同名变量:
import moduleX from './x.js' // 报错 moduleX = 4 // 报错 let moduleX = 4导入整个模块:
// 模块(z.js) export var name = 'ph' export let age = 18 export default 'man' // 导入整个模块 import * as moduleZ from './z.js' // [["name","ph"],["age",18],["default","man"]] console.log(JSON.stringify(Object.entries(moduleZ)))不管在 import 语句中把一个模块写了多少次,该模块将只执行一次:
// 模块(z.js) export var name = 'ph' export let age = 18 console.log('module z') export default 'man' // 重复导入模块c import {name} from './z.js' import {age} from './z.js' console.log([name,age])控制台只会输出一次module z。
export 和 import 必须在其他语句和函数之外使用:
if(flag){ import {name} from './z.js' // 语法错误 }模块语法存在的一个原因是要让 javascript 引擎静态的确定哪些可以导出,因此,只能在模块顶部使用 export 和 import。