前端构建系统
用 npm 运行脚本
Node 有 npm,而 npm 能运行脚本。在项目的 package.json 文件中,有个 scripts 属性,可以在那里指定自己的 npm start 命令:
{
...
"scripts": {
"start": "node server.js"
},
...
}
我们一般都会定义 test 属性,因为可以把测试框架作为依赖项,然后用 npm test 来运行测试脚本。比如说,你选了 Mocha 来做测试,并且已经用 npm install --save-dev 装好了。如果在 package.json 中添加下面的语句,就不用全局安装 Mocha 了:
{
...
"scripts": {
"test": "./node_modules/.bin/mocha test/*.js"
},
...
}
注意看一下,这个例子里的参数是传给了 Mocha。也可以在运行 npm 脚本时用两个连字符传入参数:
npm testo -- test/*.js
创建定制的 npm 脚本
从创建新项目开始,然后安装必要的依赖项:
mkdir es2015-example
cd es2015-example
npm init -y
npm install --save-dev babel-cli babel-preset-es2015
echo '{ "presets": ["es2015"] }' > .babelrc
打开 package.json,在 scripts 下面添加 babel 属性。
{
"name": "es2015_example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"babel":"./node_modules/.bin/babel browser.js -d build/",
"uglify":"./node_modules/.bin/uglifyjs build/browser.js -o build/browser.min.js",
"build":"npm run babel && npm run uglify"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-es2015": "^6.24.1"
}
}
用 ES2015 语法写的代码,将它存为 browser.js 文件:
//es2015_example/browser.js
class Example{
render(){
return '<h1>Example</h1>'
}
}
const example = new Example();
console.log(example.render());
npm run babel
npm i --save-dev uglify-es
npm run uglify
配置前端构建工具
- 指定命令行参数。比如./node_modules/.bin/ uglify --source-map。
- 针对项目创建配置文件,将参数放在这个文件中。 Babel 和 ESLint 经常这么干。
- 将配置参数添加到 package.json 中。 Babel 也支持这种方式。
用 Gulp 实现自动化
Gulp 是基于流的构建系统。我们可以通过对这些流的引导来创建构建过程,除了转译和缩码,还能做很多事情。想象一个项目,后台管理区是用 Angular 做的,公开区域是基于 React 的。两个子项目的构建需求都是一样的。借助 Gulp,我们可以重用某些阶段的构建过程。
Gulp 之所以能实现高度重用,主要归功于两项技术:使用插件和自定义构建任务。

把 Gulp 添加到项目中
全局安装 gulp-cli,并创建一个带有 Gulp开发依赖项的新 Node 项目:
npm i --global gulp-cli
mkdir gulp-example
cd gulp-example
npm init -y
npm i -save-dev gulp
接着创建 gulpfile.js:
//Linux
touch gulpfile.js
//Windows
New-Item gulpfile.js
打开这个文件。现在用 Gulp 构建一个小型的 React 项目。这里会用到 gulp-babel、 gulp-sourcemaps和 gulp-concat:
npm i --save-dev gulp-sourcemaps gulp-babel babel-preset-es2015
npm i --save-dev gulp-concat react react-dom babel-preset-react
npm i --save-dev babel-core
Gulp 任务的创建及运行
创建 Gulp 任务需要在 gulpfile.js 中编写 Node 代码,调用 Gulp 的 API。 Gulp 的 API 可以做很多事,比如查找文件,把对文件进行某种转换的插件拼到一起等。
用 Babel 处理 ES2015 和 React 的 gulpfile
//加载Gulp插件
const gulp = requirea('gulp');
const sourcemaps = require('gulp-sourcemaps');
const babel = require('gulp-babel');
const concat = require('gulp-concat');
gulp.task('default',()=>{
//用Gulp自带文件聚集工具gulp.src查找所有的React jsx文件
return gulp.src('app/*.jsx')
.pipe(sourcemaps.init())//开始监视源文件,为调试构建源码映射
.pipe(babel({//使用ES2015和React配置gulb-babel
presets:['es2015','react']
}))
.pipe(concat('all.js'))//把所有源码拼在一个all.js中
.pipe(sourcemaps.write('.'))//单独写入源码映射文件
.pipe(gulp.dest('dist'));//将所有文件放到dist/目录下
});
再创建一个名为 app/index.jsx 的文件,就可以试验一下 Gulp 了。
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<h1>Hello,React!</h1>,
document.getElementById('example')
);
在 Gulp 中,用 JavaScript 表示构建阶段很容易。并且我们可以用 gulp.task()往这个文件里添加自己的任务。这些任务通常都遵循相同的模式。
(1) 源文件——收集输入文件。
(2) 转译——让它们依次通过一个个对它们进行转换的插件。
(3) 合并——把这些文件合到一起,创建一个整体构建文件。
(4) 输出——设定文件的目标地址或移动输出文件。


监测变化
npm i --save-dev gulp-watch
const watch = require('gulp-watch');
//添加监测任务
gulp.task('watch',()=>{
watch('app/*.jsx',()=>gulp.start('default'));
});
这段代码定义了一个名为 watch 的任务,然后用 watch()监测 React JSX 文件的变化。只要有文件发生了变化,默认的构建任务就会运行。只需稍稍修改,这个处方就可以用来构建 SASS文件、优化图片,以及做需要在前端项目上做的很多事情。
在大项目中把任务分散到不同文件中
项目规模变大后,一般会需要更多的 Gulp 任务。最终会出现一个大到难以理解的长文件,如果把代码分解成不同的模块,就可以解决这个问题。
可以按如下步骤来使用分散的文件。
(1) 创建一个名为 gulp 的文件夹以及一个名为 tasks 的子目录。
(2) 在各个文件中用 gulp.task()语法定义任务,最好是每个任务放一个文件。
(3) 创建一个名为 gulp/index.js 的文件,在其中加载所有的 Gulp 任务文件。
(4) 在 gulpfile.js 中引入这个 gulp/index.js 文件。
用 Webpack 构建 Web 程序
Webpack 是专门用来构建 Web 程序的。用 Gulp时,写 JavaScript 代码是为了驱动构建系统,所以会涉及写 gulpfile 和构建任务。而用 Webpack时,写的是配置文件,用插件和加载器添加新功能。有时候不需要额外的配置:在命令行里输入webpack,将源文件的路径作为参数,它就能构建项目。
Webpack 的优势之一是更容易快速搭建出一个支持增量式构建的构建系统。如果配置成文件发生变化时自动构建, Webpack 不会因为一个文件发生变化而重新构建整个项目。所以它的构建更快,也更好理解。
使用打包器和插件
Webpack 插件是用来改变构建过程的行为的。这些行为包括自动将静态资源上传到 Amazon S3 或去掉输出中重复的文件等。
与插件相反,加载器是用来转换资源文件的。比如将 SASS 转换为 CSS,或者将 ES2015 转换为 ES5。 加载器是函数,负责将输入的源文本转换为特定的文本输出。它们既可以是异步的,也可以是同步的。插件是可以挂接到 Webpack 更底层 API 的类的实例。
如果需要转换 React 代码、 CoffeeScript、 SASS 或其他转译语言,就用加载器。如果需要调整 JavaScript,或用某种方式处理文件,就用插件。
配置和运行 Webpack
mkdir webpack-example
npm init -y
npm install --save react react-dom
npm install --save-dev webpack babel-loader babel-core
npm install --save-dev babel-preset-es2015 babel-preset-react
//一个 webpack.config.js 文件
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry:'/app/index.jsx',//输入文件
output:{path: __dirname, filename: 'dist/bundle.js'},//输出文件
module:{
rules:[
{
test:/.jsx?$/,//匹配所有JSX文件
loader:'babel-loader',
exclude:/node_modules/,
query:{
presets:['es2015','react']//使用BabelES2015和React插件
}
}
]
},
};