聊聊 Webpack 使用
老早的时候就听说了 Webpack 这个工具, 当时大概的印象就是类似 Gulp 这样的东西, 并且看起来好像挺复杂的. 直到学习 React 的时候才开始接触 Webpack, 才知道 Webpack 更多的是做模块化的工作. 不过当时也是乱配置一通能用就行=.=.
现在 Vue 标配也是用 Webpack 了. Webpack 其实并没有想象中的那么复杂, 其实最核心的还是 loader 那一块. 这次就主要聊一聊 Webpack. 我用的是 Webpack 最新版本 2.1.0-beta.27.
Loader
Loader 是 Webpack 的核心, 它会自动查找项目中的我们指定的文件类型, 然后使用我们指定的 Loader 进行处理. 例如:
|
|
对于 Vue 文件, 我们要让 vue-loader
来处理, 这里可以先忽略 ExtractTextPlugin
部分, 它作用是提取 CSS 这个在后面会提. 对于 .js
文件, 我们使用 babel-loader
来处理, 我们可以在项目配置一个 .babelrc
文件来指定我们使用的 presets 和 plugins.
Webpack 我觉得一个不太好的地方就是写法很多, 而且那么多种写法大体是一样的, 但是在一些场景下它们可能又会有区别, 就不能统一一下吗? 例如, 如果我们使用了 Sass, 那常用的两种写法如下:
|
|
我们可以使用 !
来连接多个 loader, 它们会自右向左执行.
另外, -loader
可以省略不写, 但在 Webpack2 中推荐写上. 如果不加 -loader
的话在一些场景下它会出错.
devServer
这个是 webpack 另一个强大的地方了. Webpack-dev-server 是一个小型的 node.js Express 服务器, 通过 websocket 可以实现浏览器的模块热替换. 即前端代码变动的时候无需刷新整个页面, 而只是把变化的部分替换掉. 关于这个热替换, 其实也有好几种配置方法, 这里我只说我用的情况.
|
|
这里的 proxy 也是 webpack-dev-server 一个强大的地方之一, 我们可以配置一些代理来避免跨域问题和端口不一致的问题.
接着运行即可
|
|
减少打包体积
Webpack 在开发环境打包的体积非常大, 因为其包含了 source-map 等. 我们在生产环境并不需要它, 可以如下配置:
|
|
除了这点, 有时候我们还想生产环境使用 CDN, 开发环境使用本地的资源. CDN 可以通过 externals 配置
|
|
同时不要忘记了在 index.html
中把各文件的 CDN 链接导入. 区分两个环境我们可以建立两个配置文件, 或者简单的通过条件语句判断. 这样处理后生产环境和开发环境的 index.html
就有了比较大的区别, 我处理方式是建立了两个 index.html
一个用于开发环境一个用于生产环境, 再者他们刚好也位于不同的位置, 开发环境从根目录加载 index.html
, 而生产环境则有后端根据 UA 指向 public
下的 index.html
再有的优化就是进行 JavaScript 代码的压缩混淆, 当然这个也只推荐在生产环境中使用:
|
|
导入这个插件即可
另外还有 CSS 的压缩:
|
|
只要在 css-loader
后面加上 ?minimize
就好了.
提取 CSS
就上面那段代码, 用到了 ExtractTextPlugin
这个插件, 它就是用来分离 CSS 代码的, 我们需要安装这个插件, 然后在 Webpack 中导入. 使用方法就上面这样, 但还要做一个配置:
|
|
导入这个插件, 并配置文件名. 之后他就会在我们的 output 处输出这个 CSS 文件.
我这里不仅仅是要处理 CSS 文件, 还要处理 .vue
中的 CSS 样式.
|
|
注意 Webpack2 最新版本 API 想比 Webpack1 有较大变化. Webpack2 不支持在配置文件中插入其他东西, 如果你想对这个 loader 进行进一步配置, 需要在 options 中配置. 这里对 vue-loader
进行了进一步配置, 加入了 postcss-loader
和 css-loader
.
postcss-loader
也是一个比较坑的地方, 在 Webpack2 最新版本已经不支持使用 postcss.config.js
文件的配置, 你需要自己在 Webpack 中配置这个插件.
|
|
插入这个插件后后面才可以正常使用 postcss-loader
我们同时处理了 .css
和 .vue
中的 css 并最终生成了一个 CSS 文件. 这里在生产环境会生成 style.[contenthash:4].css
, contenthash
是根据文件内容生成的, 在文件名加入其哈希值后, 我们就可以大胆的最样式表进行长期缓存, 因为样式表内容一变化文件名也变了.
文件名嵌入哈希值
除了 CSS 处理外, 我们还要对 JavaScript 进行处理, 这个是在 output
中配置的:
|
|
注意这里用的是 chunkhash
, 我们在 CSS 中用的则是 contenthash
最后我们就会生成如下的文件名:
|
|
但这样做是不够的, 我们不能每次都自己手动修改 index.html
, 我们要让 index.html
中的文件哈希值也自动变化.
这个可以通过自定义插件来做, 我是直接参考了别人写的, 并没有深入去了解(仅供参考, 下面有说更好的方法)
|
|
这里的正则和路径都是根据自己项目的情况做出来的.
大致的意思就是监听插件的 done
事件, 然后传入 statsData
到这个插件的回调函数里, 如果没有出错, 那么获取得到 webpack 生成的文件名即上面说的文件名如 style.dd51.css
, 即 stats.assetsByChunkName.main
这个数组. 这个数组保存着 webpack 生成的文件名, 接着我们获取 index.html
并用正则获取所有的 script
和 style
, 我这里的处理措施是得到文件名如 style
, 然后在 stats.assetsByChunkName
中查找包含这个串的输出文件名, 将这个文件名替换原来的即可.
// 2017.1.5 更新
其实还有更简便的方法, 使用 HtmlWebpackPlugin
插件, 然后进行下面的配置:
|
|
Webpack 在运行过程提取出的 chunk, 自动输出到 public/index.html
的 head
中. 然后存储到 output
设置的 publicPath
中, 因为 index.html
通常存放在资源外面, 所以这里文件名进行了相对路劲的处理.
到这一步不得不感慨 Webpack 的强大, 上面我说的有点乱, 可以在这里(最新的配置文件已经发生了改变)查看我的详细 webpack 配置. 这里没有细说每个配置的每个选项, 这些选项有些我自己也还搞不太明白, 最近还要再好好看下里面一些选项的细微区别.
总结下, 上面的 Webpack 帮我们做了这些事情:
模块化
一切皆模块, 只要有 loader. 我们可以在我们的 JS 文件中导入 CSS, 图片等资源. Webpack 会自动帮我们做处理. 只要你想的话, 你还可以用 CSS in JS. 如果你单独分离 CSS, 那么最终生成的就是一个 JavaScript 文件.
使用 Babel 和 PostCSS
在 Webpack 中使用
babel-loader
处理.js
和.vue
文件, 我们就可以任性的写 ES6 和 ES7 了. 给.css
和vue-loader
加入post-loader
后我们就可以任性的使用 cssnext 等特性了. 原本我是用sass-loader
的, 但是我主要用的嵌套功能其实postcss-loader
也可以处理, 并且我挺喜欢postcss-loader
的丰富插件这个特性. 从此抛开 CSS 预处理器.压缩合并 JS 和 CSS
不需要使用 Gulp 了. Webpack 对 JS 和 CSS 的压缩合并处理不能再简单了.
代理服务器
反向代理了我们的 API, 避免了端口修改和跨域的问题.
文件哈希名
给 CSS 和 JS 嵌入了哈希值, 并且自动替换
index.html
中的路径文件.
恩, 不愧是前端模块化和自动化利器.