Stay before every beautiful thoughts.
Just be nice, always think twice!
使用 gulp 搭建CommonJs & ES6 模块化环境
1. Browserify简介
“Browserify lets you require(‘modules’) in the browser by bundling up all of your dependencies.” - Browserify.org
上面的描述是摘自 browserify 官网;用通俗的话讲就是:browserify 是一个浏览器端代码模块化工具,可以处理模块之间的依赖关系,让服务器端的 CommonJS 格式的模块可以运行在浏览器端。
browserify的原理:
- 处理代码依赖,将模块打包到一起
打包为单个文件存在的问题:
暂时用不到的代码也会被打包,体积大,首次加载速度慢
只要一个模块更新,整个文件缓存失效
注:暂时用不到的代码指不同的页面有不同的 JS 文件,不需要在当前页面引用其他页面的代码即为暂时用不到的代码
Browserify的解决方案:
- entry point:入口点技术,每个入口点打包一个文件,两个入口点的相同依赖模块单独打包为common.js
安装与配置
安装 browserify
npm install -g browserify
引入 browserify
import browserify from 'browserify'
基本配置
glup.taks('browserify', function() {
browserify({
//先处理依赖,入口文件
entries: ['./foo.js','./main.js'],
//进行转化
transform: []
})
.bundle() // 多个文件打包成一个文件
.pipe(source()) // browserify的输出不能直接用做gulp输入,所以需要source进行处理
.pipe(gulp.dest('./'));
})
安装一些依赖插件
npm install --save-dev vinyl-source-stream vinyl-buffer gulp-sourcemaps
vinyl-source-stream
: browserify的输出不能直接用着gulp的输入,vinly-source-stream 主要是做一个转化
vinyl-buffer
: 用于将vinyl流转化为buffered vinyl文件(gulp-sourcemaps及大部分Gulp插件都需要这种格式)
gulp-sourcemaps
: Source map就是一个信息文件,里面储存着位置信息。也就是说,转换后的代码的每一个位置,所对应的转换前的位置,便于调试
Watchify
: 加速 browserify 编译
2. 编写 CommonJS 模块
目录结构
|-- dist/
|-----bundle.js
|-- src/
|-----foo.js
|-----main.js
|--gulpfile.babel.js
|--package.json
新建两个模块文件 foo.js, main.js
$ touch foo.js main.js
让我使用 CommonJs 规范来写一些代码
CommonJS 规范是为了解决 JavaScript的作用域问题而定义的模块形式,可以使每个模块在它自身的命名空间中执行。 该规范的主要内容是,模块必须通过module.exports 导出对外的变量或接口,通过 require() 来导入其他模块的输出到当前模块作用域中。
// foo.js
// 定义foo.js模块,通过 module.exports 导出对外的变量或接口
let variable = 8;
let sum = (a, b = 6) => (a + b);
let square = (b) => {
return b * b;
};
module.exports.variable = variable;
module.exports.sum = sum;
module.exports.square = square;
// mian.js
// 通过 require() 来导入 foo.js 模块
var bar = require('./foo')
console.log(bar); // Object
console.log(bar.variable); // 8
console.log(bar.sum(1)); // 7
console.log(bar.square(5)); // 25
上面我们使用 ES6 的语法写了两个模块,分别是 foo.js 和 main.js; 在 foo.js 中通过 module.exports 导出对外的变量或接口;在 main.js 中通过 require() 来导入 foo.js 模块,那我们就可以在 mian.js 模块中使用 foo.js 中的变量和接口了。这就是一个最基本的 CommonJs 示例了
配置 browserify
通过第一小节的学习,我们知道要在浏览器中运行 CommonJs 风格的模块代码,就需要借助 browserify 来作为转换工具,下面我们在 gulp.babel.js 中来配置 browserify:
// set browserify task
gulp.task('browserify',()=> {
browserify({
entries: ['src/js/main.js','src/js/foo.js'],
debug: true, // 告知Browserify在运行同时生成内联sourcemap用于调试
})
.transform("babelify", {presets: ["es2015"]})
.bundle()
.pipe(source('bundle.js'))
.pipe(buffer()) // 缓存文件内容
.pipe(sourcemaps.init({loadMaps: true})) // 从 browserify 文件载入 map
.pipe(sourcemaps.write('.')) // 写入 .map 文件
.pipe(gulp.dest('dist/js'))
.pipe(notify({ message: 'browserify task complete' }));
})
运行
gulp-browserify
打开浏览器,查看运行结果(见上面main.js文件的注释)
编写 ES6 Module 模块
上面的代码主要是 CommonJs 模块化的写法,我们再来看看最近火热的 ES6 提供的 Module;让我们使用 ES6 Module 来改写上面的代码:
// foo.js
// 定义foo.js模块,通过 exports 导出对外的变量或接口
let variable = 8;
let sum = (a, b = 6) => (a + b);
let square = (b) => {
return b * b;
};
export { variable, sum, square };
// main.js
// 测试一:
// 通过 import 来导入 foo.js 模块
import {variable, sum, square} from './foo';
console.log(variable); // 8
console.log(sum(1)); // 7
console.log(square(5)); // 25
// 测试二:
// 直接引用整个 foo 模块
import bar from './foo';
console.log(bar); // 输出值是undefined,后面做解释
// 测试三:
// 通过 ES6 的语法加载整个 foo模块
import * as bar from './foo'
console.log(bar); // Object
总结 CommonJs 和 ES6 Module
CommonJs
- 根据 CommonJS 规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(包括函数和类),都是私有的,对其他文件是不可见的
- 通过 module.exports 对象,定义对外接口,其他文件加载该模块,实际上就是读取 module.exports 变量
通过 require 命令加载模块文件,然后返回该模块的exports对象
ES6 Module
通过 export 命令用于规定模块的对外接口
通过 import 命令用于加载其他模块提供的功能
ES6 Module 与 CommonJs 的区别
在ES6中使用 import 取代 require
在ES6中使用 export 取代 module.exports
ES6 在编译时就能确定模块的依赖关系,以及输入和输出的变量,而 CommonJs 只能在运行时确定模块的依赖关系以及输入和输出的变量。
运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”
编译时加载: ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,输入时采用静态命令的形式。即在输入时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载” 注:上面提到 ES6 加载模块时是采用指定加载某个输出值的形式,如果你要想加载整个模块,你可以这么做:
import * as customName from './moduleFileName';