漫谈前端模块化和构建工具

前端模块化

  • 模块化是一种思想, 是将大工程拆成小的模块分治的思想。
  • 前端采用模块化开发,使得开发体验大大增强,摆脱了很多需要人力去做且容易出错的点,使得代码管理更加清晰、规范。主要表现为以下几点:减少命名冲突,消除全局变量;一个模块一个文件,组织更清晰;依赖自动加载,按需加载。
  • 目前,比较常用的前端模块化方式有:AMD/CMD、CommonJS、ES6 Module等。

AMD/CMD

  • AMD规范主要用于解决针对浏览器环境的模块化问题,最具代表性的实现是 requirejs。
  • AMD的优点在于:1、可在不转换代码的情况下直接在浏览器中运行;2、可异步加载依赖;3、可并行加载多个依赖;4、代码可运行在浏览器环境和 Node.js 环境下。
  • AMD 的缺点在于 JavaScript 运行环境没有原生支持AMD, 需要先导入实现了 AMD 的库后才能正常使用。
  • CMD规范和AMD类似,都主要运行于浏览器端,写法上看起来也很类似。
  • 主要是区别在于模块初始化时机,AMD中只要模块作为依赖时,就会加载并初始化。而CMD中,模块作为依赖且被引用时才会初始化,否则只会加载。
1
2
3
4
5
6
7
8
9
10
11
// AMD
define(function(require, exports, module) {
var $ = require('jquery');
//...
});

// CMD
define(['jquery'], function(require, exports, module) {
var $ = require('jquery');
//...
});

CommonJS

  • CommonJS是服务器模块的规范,Node.js采用了这个规范。
  • 根据CommonJS规范,一个单独的文件就是一个模块,每一个模块都是一个单独的作用域,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的。CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。
  • CommonJS 的优点在于:1、 代码可复用于 Node.js 环境下井运行,例如做同构应用;2、 通过 Npm 发布的很多第三方模块都采用了 CommonJS 规范。
  • CommonJS 的缺点在于:这样的代码无法直接运行在浏览器环境下,必须通过工具转换成标准的 ES5 。
1
2
3
4
5
6
7
8
const x = 5;
const addX = function(value) {
return value + x;
};

// CommonJS
module.exports.x = x;
module.exports.addX = addX;

ES6 Module

  • ES6 模块化是国际标准化组织 ECMA 提出的 JavaScript 模块化规范,它在语言层面上实现了模块化。浏览器厂商和 Node.js 都宣布要原生支持该规范。 它将逐渐取代 CommonJS 和AMD 规范,成为浏览器和服务器通用的模块解决方案。
  • ES6 模块的一个缺点在于目前无法直接运行在大部分 JavaScript 运行环境下,必须通过工具转换成标准的 ES5 后才能正常运行。
1
2
3
4
5
6
// es6 modules
import { copyFile } from 'fs';

export default {
//...
}

构建工具

  • 何为构建,构建就是将源代码转换成可执行的 JavaScript 、 CSS 、 HTML 代码。具体可能包括如下内容:代码转换、文件优化、代码分割、模块合并、自动刷新、代码校验、自动发布。
  • 构建其实是工程化、自动化思想在前端开发中的体现,将一系列流程用代码去实现,让代码自动化地执行这一系列复杂的流程。
  • 经过近几年前端的快速发展,新的模块化构建工具也不断推出,从原始的Npm Script到Grunt、Gulp、Webpack、Rollup等。下面依次介绍下这些构建工具。

Npm Script

  • Npm Script 是 Npm 内置的一个功能,允许在 package.json 文件里面使用 scripts 字段定义任务。
  • 这种方式的优点是内置,无须安装其他依赖。
  • 它的缺点也很明显,功能太简单,虽然提供了 pre 和 post 两个钩子,但不能方便地管理多个任务之间的依赖。
1
2
3
4
5
{
"scripts": {
"build": "node build.js"
}
}

Grunt

  • Grunt 有大量现成的插件封装了常见的任务,也能管理任务之间的依赖关系,自动化地执行依赖的任务 ,每个任务的具体执行代码和依赖关系写在配置文件 Gruntfile.js 里。
  • Grunt 的优点是:1、灵活,它只负责执行我们定义的任务;2、大量的可复用插件封装好了常见的构建任务。
  • Grunt 的缺点是集成度不高 ,要写很多配置后才可以用,无法做到开箱即用。
1
2
3
4
5
6
7
8
grunt.initConfig({
concat: {
// ...
},
uglify: {
// ...
}
});

Gulp

  • Gulp 是一个基于流的自动化构建工具。除了可以管理和执行任务,还支持监听文件、读写文件。Gulp 的最大特点是引入了流的概念,同时提供了一系列常用的插件去处理流,流可以在插件之间传递。
  • Gulp 的优点是好用又不失灵活,既可以单独完成构建,也可以和其他工具搭配使用。
  • 缺点和 Grunt 类似,集成度不高,要写很多配置后才可以用,无法做到开箱即用。
1
2
3
4
5
6
7
8
9
10
11
12
var gulp = require("gulp");
var sass = require("gulp-sass");

// 编译scss
gulp.task('scss', function() {
// 读取文件
gulp.src('./scss/*.scss')
// 将 scss 文件编译成 css
.pipe(sass())
// 输出文件
.pipe(guilp.dest('./css'));
});

Webpack

  • Webpack是一个打包模块化 JavaScript 的工具,在 Webpack 里一切文件皆模块,通过 Loader 转换文件,通过 Plugin 注入钩子,最后输出由多个模块组合成的文件。Webpack 专注于构建模块化项目。
  • Webpack 的优点:1、专注于处理模块化的项目,能做到开箱即用、 一步到位;2、可通过 Plugin 扩展,完整好用又不失灵活 ;3、使用场景不局限于 Web 开发;4、社区庞大活跃,经常引入紧跟时代发展的新特性,能为大多数场景找到已有的开源扩展;5、良好的开发体验。
  • Webpack 的缺点是只能用于采用模块化开发的项目。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.exports = {
entry: './src/index.js',
output:{
filename:'bundle.js',
path:path.resolve(__dirname,'dist')
},
module:{
rules:[
{
test:/\.css$/,
use:[
'style-loader',
'css-loader'
]
}
]
}
};

Rollup

  • Rollup 是一个和 Webpack 很类似但专注于 ES6 的模块打包工具。Rollup 在用于打包 JavaScript 库时比 Webpack 更有优势,因为其打包出来的代码更小、更快。
  • 它的亮点在于,能针对 ES6 源码进行 Tree Shaking,以去除那些己被定义但没被使用的代码并进行 Scope Hoisting,以减小输出文件的大小和提升运行性能。
  • 但它的功能不够完善,在很多场景下都找不到现成的解决方案 。
1
2
3
4
5
6
7
module.export = {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'cjs', // amd、cjs、es、iife、umd
}
}

相关资料