webpack-前端界中的打包利器

Hello webpack

现在,前端的页面和功能越来越复杂,为了复用或者减少复杂度,开发人员经常将页面拆分成多个部分,也就是模块,每个模块负责相对独立的功能,通过组合不同的模块来实现业务需求。

但是这种方式会带来另外一个问题,就是浏览器并行加载的数量有限,如果这些模块都是单独的一个js或者css文件,就会影响到整个页面的加载效率。

因此如果能够将多个相互依赖相互关联的文件合并成一个文件,就可以无痛解决这个问题,而这也就是webpack的功能。

What can webpack do

webpack是一种模块化的解决方案。所有的文件,对于webpack来说,通过组合合适的loader,都可以当做模块来处理,包括JavaScript代码,CSS和fonts,以及多媒体资源等。

Entry

每个入口单独编译成一个js文件。entry是webpack会解析和打包的入口,没有被entry依赖的模块是不会导入,其它的模块需要通过import, require, url等导入相关位置,

entry支持两只形式,字符串和数组形式。

ruby
1
2
3
4
5
entry: {
page1: "./page1",
//支持数组形式,将加载数组中的所有模块,但以最后一个模块作为输出
page2: ["./entry1", "./entry2"]
}

使用数组形式的时候,将加载数组中的所有模块,但以最后一个模块作为输出

loader

简单来讲,就是告知webpack每一种文件应该怎么处理,即需要使用什么加载器来处理 注意所有的加载器都需要通过npm来加载

ruby
1
2
3
4
- test:一个匹配loaders所处理的文件的拓展名的正则表达式(必须)
- loader:loader的名称(必须)
- include/exclude:必须处理的文件(文件夹)或不需要处理的文件(文件夹)(可选);
- query:为loaders提供额外的设置选项(可选)

下面是几个我们经常用到的loader:

babel

编译JavaScript的平台,它的强大之处表现在可以通过编译帮你达到以下目的:

  • 下一代的JavaScript标准(ES6,ES7),这些标准目前并未被当前的浏览器完全的支持;
  • 使用基于JavaScript进行了拓展的语言,比如React的JSX

这里就不详细介绍babel了,感兴趣的可以直接到官网了解

CSS

使你能够使用类似@import 和 url(…)的方法实现 require()的功能

Style

将所有的计算后的样式加入页面中

ruby
1
2
3
4
5
url:
{
test: /\.(png|jpg|gif)$/,
loader: 'url?limit=2000'
}

图片文件的大小,如果小于2kb,则直接用uri方式,即base64,嵌在css中;大于2kb的文件,则是拷贝图片资源到目标目录,用url的方式引用。

这样的好处当然是减少网络请求的数量。对于小文件,建立链接的花费比单纯下载文件的还要多。

Plugin

  • OccurenceOrderPlugin :为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID

  • UglifyJsPlugin:顾名思义,就是用来压缩JS代码的;

  • ExtractTextPlugin:分离CSS和JS文件。webpack默认将css代码嵌在js中,如果想从js文件中抽取css,可以使用ExtractTextPlugin

生成单个文件

ruby
1
2
3
4
new ExtractTextPlugin("index.css", {
disable: false,
allChunks: true
})

生成多个文件

ruby
1
2
3
4
new ExtractTextPlugin([name.css], {
disable: false,
allChunks: true
})

调试

SourceMap

打包后的文件融合了多个文件,正常情况下很难直接定位源码错误的地方,Source Maps就是来帮我们解决这个问题的。

通过简单的配置后,webpack在打包时可以为我们生成的source maps

在生成文件的最后一行,都会有这么一句注释,//# sourceMappingURL=menuedit.js.map,描述sourcemap文件的地址。支持sourcemap的浏览器会识别这句注释,不支持的则会直接忽略。

如果需要设置断点,需要到webpack目录下的文件中设置,如下图中的businessstatistics.jsx

sourcemap

Dev Server

webpack-dev-server是一个小型的node.js Express服务器,它使用webpack-dev-middleware中间件来为通过webpack打包生成的资源文件提供Web服务。它还有一个通过Socket.IO连接着webpack-dev-server服务器的小型运行时程序。webpack-dev-server发送关于编译状态的消息到客户端,客户端根据消息作出响应。

ruby
1
2
3
4
5
6
7
8
devServer: {
contentBase: path.resolve(__dirname, "public"),
historyApiFallback: false,
hot: true,
port: 8089,
noInfo: false,
publicPath: "/assets/"
}

其中

  • contentBase: 默认会以当前目录为基本目录,如果需要修改,就可以通过这个来进行配置

  • publicPath:指定编译后的包(bundle)的访问位置(命令行模式下一定要指定)

当指定publicPath为/assets,就需要用/assets/**.js这种路径来引用html文件中对js或者css文件等外部资源:

注意:webpack-dev-server生成的文件并没有放在真实目录中,而是放在了内存中。

我们也可以直接使用node.js来开启devServer

ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for (let key in config.entry) {
let ar = config.entry[key];
ar.unshift("webpack-dev-server/client?http://localhost:8080", "webpack/hot/dev-server");
}

config.plugins = config.plugins || [];
config.plugins.push(new webpack.HotModuleReplacementPlugin());

new WebpackDevServer(webpack(config), config.devServer)
.listen(port, ip, (err) => {
if (err) {
console.log(err);
}
console.log('Listening at localhost:' + port);
});

关于devServer的配置说明可以查看官网的webpack dev server的说明,或参考这个博客详解webpack-dev-server的使用

集成

webpack即可以单独使用,也可以与其他工具相互集成,如Gulp。在Gulp中使用webpack非常简单,只要像下面这样配置就可:

ruby
1
2
3
4
5
6
gulp.task('webpack', function() {
return gulp.src(['./entries/*.jsx'])
.pipe(named())
.pipe(gulpWebpack(require('./webpack.config.json')))
.pipe(gulp.dest(DEST_PATH));
});