JS模块化
-
什么是模块?
- 将一个复杂的程序依据一定的规则(规范)封装成几个模块(文件), 并进行组合在一起
- 块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
-
模块的组成
- 数据——>内部属性
- 操作数据的行为——>内部函数
我们,通常说的模块化,其实是指js的模块化。现在流行的模块化规范有以下几种:CommonJs、AMD、CMD、ES6模块化。本篇文章,我们来介绍下CommonJS以及ES6模块化相关的知识,其余两种大家自行了解。
CommonJS模块化
- 规范
- 服务器端:NodeJs实现了CommonJS规范,并且在服务器端模块的加载是运行时同步加载的
- 浏览器端:Browserify(CommonJS的浏览器端的打包工具)实现了CommonJS规范,在浏览器端模块需要提前编译打包处理
- 基本语法
- 暴露模块:module.exports = value 或者 exports.xxx = value。其实暴露的就是一个exports对象,在其他文件中可以直接使用exports对象。
- 引入模块:require(xxx),如果是引入第三方模块,xxx是模块名;自定义模块,xxx是模块文件路径
- 区别Node与Browserify
Node.js运行时是同步模块的,Browserify是在编译时就会加载打包合并require的模块
下面以一个案例来解释下:
首先我们创建一个文件夹来存放测试代码,然后cd到该目录里面,使用npm init命令初始化一个工程,这样就会自动生成package.json文件。之后,分别创建3个js文件来测试。
- module1.js
module.exports = {foo() {console.log('moudle1 foo()')},a:10}
//如果一个文件中有两个module.exports,下面的会覆盖上面的
// module.exports={// a:10
// }
- module2.js
module.exports = function () {console.log('module2()') }
- module3.js
exports.foo = function () {console.log('module3 foo()') }exports.bar = function () {console.log('module3 bar()') }
在app.js文件中引入模块
let module1 = require('./modules/module1')
let module2 = require('./modules/module2')
let module3 = require('./modules/module3')//等价于require('./modules/module3.js'),js可以省略module1.a
console.log(module1.a)module2()module3.bar()
最后,我们使用node命令执行app.js文件,发现原本写在module1.js、module2.js、module3.jsi里面的逻辑成功执行了,说明这3个模块确实被导入app.js中了。
接下来,有一个问题了,刚刚我们是使用node来执行js文件的,如果我们直接用浏览器执行app.js文件是会报错的,因为浏览器只认识js代码,require这些关键字浏览器是不认识的。例如,我们新建一html文件,然后用浏览器执行这个html文件
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script src="app.js"></script></body>
</html>
如果我们想要使用浏览器来执行app.js该怎么办呢?这就需要另一个工具——browserify
-
. 下载browserify
- 全局: npm install browserify -g
- 局部: npm install browserify --save-dev
-dev表示,我们只是在开发环境中需要browserify,生产环境是不需要browserify,因为生产环境已经是运行在浏览器中的。
执行了上面两句代码之后,package.json中就会有browserify的依赖
{"name": "modulejstest","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","devDependencies": {"browserify": "^17.0.0"}//如果不加-dev,那么browserify依赖在dependencies闭包下,加了就在devDependencies闭包下// "dependencies": {// "browserify": "^17.0.0"// }
}
最后,我们执行如下命令:然后引入bundle.js文件到html中,这样浏览器就不会报错了。
browserify app.js -o dist/bundle.js
ES6模块化
相比起CommonJs,es6的模块化就十分的简单了,首先我们需要安装几个工具:
-
安装babel-cli, babel-preset-es2015和browserify
- npm install babel-cli browserify -g
- npm install babel-preset-es2015 --save-dev
-
在工程根目录下定义.babelrc文件
{"presets": ["es2015"]}
- 编写测试js代码
export function foo() {console.log('module1 foo()');
}
export let bar = function () {console.log('module1 bar()');
}
export const DATA_ARR = [1, 3, 5, 1]
let data = 'module2 data'function fun1() {console.log('module2 fun1() ' + data);}function fun2() {console.log('module2 fun2() ' + data);}export {fun1, fun2}
//一个文件只能由一个export default
export default {name: 'Tom',setName: function (name) {this.name = name}}
在app.js文件中引入上述文件
import {foo,DATA_ARR}from './module1'
import { fun1,fun2 } from './module2'
import test from'./module3' //使用export default暴露的模块不用写{},任意取个名字即可foo()
fun1()
fun2()console.log("test--->"+test.name)
test.setName("brett")
console.log("test--->"+test.name)
console.log(DATA_ARR)
- 编译
- 使用Babel将ES6编译为ES5代码(但包含CommonJS语法) : babel modules -d js/lib
- 使用Browserify编译js : browserify js\lib\app.js -o dist/bundle.js
注意:如果要使用node执行js文件必须使用babel进行编译,否则会报错的,如果要在浏览器执行js代码,除了使用babel编译外,还要使用Browserify编译。
工程目录如下: