图片摘自官网http://webpack.github.io/

引言

webpack,简单来说就是一个打包工具,分析我们代码的依赖,然后打包为一个或多个文件,除此之外,他还可以做“翻译”的事情,比如现在浏览器对ES6支持的还不是很好,但是ES2015写起来又很舒服,没问题,写完之后,交给webpack,他会翻译为浏览器认识的语言,还有less、coffee script等。另外,还可以根据规则将我们的代码发布到对应的目录,这样开发的目录结构就和生产环境的目录可以不一致了,对于开发者来说自由度更大了。

我是个半路出家的fe,最近项目需要,去支援其他项目了,前端使用了webpack这套环境,用的比较简单,只对js做了处理,刚好名正言顺的静下心来学习学习,整理下来,以供参考,避免采坑。

现在项目使用的webpack.config.js实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const path = require('path');
const webpack = require('webpack');
module.exports = {
context: path.resolve(__dirname, './entry'),

module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'stage-3'],
plugins: [[
"transform-runtime",
{
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}
]]
}
},
],
},

resolve: {
'alias': {
'@': path.resolve(__dirname)
}
},

entry: {
test: './app.js',
},

output: {
path: path.resolve(__dirname, './release'),
filename: '[name].js',
}
};

这里面有几个配置这里稍作说明:

  1. context: path.resolve(__dirname, './entry'),这里的context设置的是entry的上下文环境,只是一个绝对路径。
  2. '@': path.resolve(__dirname)resolve配置用来自定义webpack如何解析定位依赖包,alias顾名思义,为包起个别名,这里解析报的时候碰到@替换为path.resolve(__dirname)路径。
  3. 我们这里引入了babel-loader,来将我们的代码翻译为浏览器兼容的代码,转换的时候引入了两个插件babel-preset-es2015babel-preset-stage-3

这里的presetsplugins是类似的,都是插件来处理符合规则的文件,前者是一系列插件的集合,后者是单个插件的声明。
比如babel-preset-es2015就包含了下面这么多插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
check-es2015-constants
transform-es2015-arrow-functions
transform-es2015-block-scoped-functions
transform-es2015-block-scoping
transform-es2015-classes
transform-es2015-computed-properties
transform-es2015-destructuring
transform-es2015-duplicate-keys
transform-es2015-for-of
transform-es2015-function-name
transform-es2015-literals
transform-es2015-modules-commonjs
transform-es2015-object-super
transform-es2015-parameters
transform-es2015-shorthand-properties
transform-es2015-spread
transform-es2015-sticky-regex
transform-es2015-template-literals
transform-es2015-typeof-symbol
transform-es2015-unicode-regex
transform-regenerator

babel-preset-stage-3包含了下面这么多插件

1
2
3
4
5
transform-object-rest-spread
transform-async-generator-functions
syntax-trailing-function-commas
transform-async-to-generator
transform-exponentiation-operator

plugins中引入了两个插件:

1
2
babel-plugin-transform-runtime
babel-runtime

后者是为了在避免全局污染的情况下,将babel未转换的api,比如Promise、Set、Map等转换为ES5。这在转化的时候,会生成helper函数,儿前者就是为了共享这些helper函数而生的,避免代码重写,减少包的大小。

优化扩展

使用的时候发现,之前存在一个问题,我们是多页面应用,所有页面的js,都打包到同一个文件中,这样有个问题,有些内容这个页面可能不用也会加在进去,增加打包大小。也会增加打包时间,每次更新一个文件之后,所有页面的都得打包一遍,时间可能达到好几秒钟,真的很烦,所以我就想着写个函数,自动扫描制定目录下的js文件,生成entry对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* @author 燕睿涛 <ritoyan@163.com>
* @desc 匹配并按照规则输入制定文件夹下匹配到的js文件
*/
var glob = require("glob");
var fs = require("fs");
var path = require("path");

/**
* @desc 递归扫描制定目录下的所有js文件,并发布到制定的发布子目录中
* @param codeDir String 代码目录
* @param releaseSubDir String 最终发布的子目录 不传,默认是codeDir的最后一个路径
* @return Object 发布文件路径和源文件路径映射
*/
module.exports = function(codeDir, releaseSubDir) {
if (typeof(releaseSubDir) == "undefined") {
releaseSubDir = path.basename(codeDir)
}

let realDir = fs.realpathSync(codeDir)
let regPath = path.join(realDir, "**", "*.js")
let ret = []

let files = glob.sync(regPath)

files.forEach(function(file, index) {
let releaseName = getReleaseName(realDir, releaseSubDir, file)
ret[releaseName] = file
})

return ret
}

/**
* @desc 从文件命中解析出最终发布的文件名称
* @param realDir String 匹配的文件夹绝对路径
* @param releaseSubDir String 发布之后的子目录
* @param realFilePath String 文件的真是位置
* @return String 发布的文件名
*/
function getReleaseName(realDir, releaseSubDir, realFilePath) {
let dirLen = realDir.length
let _path = realFilePath.substring(dirLen);
_path = path.join(releaseSubDir, _path)
_path = _path.substring(0, _path.length - 3)
return _path
}

然后对webpack.config.js进行改造,不影响原来的功能,在webpack配置中传入entry的时候,传入我们自定义的entry,这个enrty会扫描某个文件夹下的js文件,并发布到对应目录

1
2
3
4
5
6
7
8
9
10
11
12
const entries = require("./config/entries");

let entry = {
test: './app.js',
}

Object.assign(
// 原来已有的entry
entry,
// 2018微峰会打包文件规则
entries("./pages/2018vfenghui", "2018vfenghui")
)

这样,每次修改的时候,webpack -w会很快的打包文件,用起来爽爽的。

参考文章

  1. Babel 全家桶