快速打造简易高效的webpack配置
Posted 前端人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速打造简易高效的webpack配置相关的知识,希望对你有一定的参考价值。
编辑:魔卡
━━━━━
关于webpack
webpack给前端开发带来了毋庸置疑的改变,它把JS,图片,css都作为模块处理,同时具有开发便捷,自动化,兼容AMD写法等等诸多无须赘述的优点,更令人称道的是其插件社区非常强大,对于不同的业务需求和技术需求社区都有大量插件可供使用。
凡事都具有两面性,许多人说:前端开发再也不能只需新建html文件和JS文件就可以开始写代码了。webpack带来了更高级更规范的前端开发模式,由于其本身也在不断完善中,从1到2再到发布不久的webpack3,频繁的修改给新手带来了许多困惑。而且网络上各种教程鱼目混杂,经常出现别人的教程代码copy下来在自己的环境却跑不通的蛋疼问题。就拿devtool
配置项来说,官方文档提供了多达7种的配置方法,连react核心团队成员Pete Hunt都在twitter上调侃:我分不清webpack的许多配置之间的区别。所以今天我们抛开那些琳琅满目的插件和令人烦躁的配置项,笔者和大家一起5分钟从零搭建一个简易高效的webpack开发环境。
首先我们明确一下需求:
打包调试
提取公共代码
压缩
热替换
1.打包调试
第一步,我们在目标文件夹下安装webpack(假设已有package.json
)npm i webpack@ -g
cnpm i webpack@ --save-dev
(这里推荐大家安装稳定的2.x版本)
项目结构如图:
我们将编写的js代码和样式文件放置在app
文件夹内(正常项目开发需要js
文件和less
文件更规范的组织文件结构,此处仅为演示方便)。
第二步,我们在目标文件夹下新建webpack.config.js
module.exports = { entry:{ main:__dirname + '/app/main.js', }, output:{ path:__dirname + '/public', filename:'[name].[id].js',//此格式写法后续会提到为什么 publicPath:'/public/' } }
我们已经完成了webpack最基础的部分:添加了文件的输入和输出。入口是app
文件夹内的main.js
文件,出口为public
文件夹。接下来我们来处理各种文件的解析,就是大名鼎鼎的loader
的舞台了。假设我们使用es6
和less
开发,那么我们需要:npm i babel-loader babel-core babel-preset-es2015 babel-preset-stage-0 --save-dev
npm i less less-loader css-loader style-loader --save-dev
接下来我们只需要在modules
字段下把这些loader
加进去:
module.exports = { devtool:'cheap-module-eval-source-map',//多种选择,选择最适合自己的 entry:{ main:__dirname + '/app/main.js', }, output:{ path:__dirname + '/public', filename:'[name].[id].js', publicPath:'/public/' }, module:{ loaders:[ { test:/\.js$/, //解析文件类型 exclude:/node_modules/, //排除node_modules文件 loader:'babel-loader', //使用哪种loader解析 query:{ presets:['es2015','stage-0']//loader的配置项,解析es6 } }, { test:/\.less$/, exclude:/node_modules/, loader:'style-loader!css-loader!less-loader'//顺序为从右向左 } ] }, }
大功告成!
如果你在全局安装有webpack的话,可以在终端敲入webpack并回车,几秒钟后,main.js
文件已经在public
打包出来了!
之后我们在index.html
中引入main.0.js
文件,再打开index.html
就可以看到效果了。
以上步骤,我们已经实现了文件的打包调试,但是现在有个问题摆在我们面前:第三方库代码和业务代码打包到了同一个文件main.0.js
内,每次更新代码都要更新整个文件。那么接下来我们对代码进行拆分。
2.提取公共代码
引入CommonsChunkPlugin
插件,在webpack.config.js
添加如下内容:
module.exports = { devtool:'cheap-module-eval-source-map', entry:{ main:__dirname + '/app/main.js', vendor:'moment' }, output:{ path:__dirname + '/public', filename:'[name].[id].js', publicPath:'/public/' }, module:{ loaders:[ { test:/\.js$/, exclude:/node_modules/, loader:'babel-loader', query:{ presets:['es2015','stage-0'] } }, { test:/\.less$/, exclude:/node_modules/, loader:'style-loader!css-loader!less-loader' } ] }, plugins:[ new webpack.optimize.CommonsChunkPlugin({ names:['vendor','manifest'] }) ] }
我们看到向插件的构造函数传入了两个参数vendor
和manifest
,以及我们在entry
也加入了新的入口moment
。moment
是常用的时间处理的第三方库,我们可以通过npm i moment --save-dev
进行安装。而entry
处的vendor
将成为output
字段filename
中[name]
的值,也就是说将打包出main.x.js
和vendor.x.js
两个文件,main.x.js
文件将保存我们的业务代码,vendor.x.js
将保存moment
的代码,这样我们将公共代码和业务代码进行了初步分离。
在新添加的CommonmChunkPlugin
插件中,我们添加了manifest
值,这是为什么呢?如果你不添加这个值,你在打包时会发现,main.x.js
有更新,vendor.x.js
还是有更新,并未真正实现"分离"。官方文档对此的解释是:
The issue here is that on every build, webpack generates some webpack runtime code, which helps webpack do it’s job. When there is a single bundle, the runtime code resides in it. But when multiple bundles are generated, the runtime code is extracted into the common module, here the vendor file.
大致的意思就是说,webpack每次编译时运行的代码会影响到hash
值的变化,当只有一个打包文件时这部分代码会塞进去,当有多个打包文件时,这部分代码会进入公共的vendor
。所以解决办法是使用manifest
字段把这部分代码从vendor
中作为一个公共模块抽出来,从而不会影响vendor
。
将以上的配置写入webpack.config.js
,运行webpack命令,我们发现业务代码和公共库代码成功分离,改写main.1.js
文件的内容,再次打包,发现vendor
文件并没有变化,成功!
当我们再进行打包时,发现又会多出了新的main.x.js
等文件,打包三次就会出现三个main.x.js
文件,此时该怎么办呢?我们可以使用clean-webpack-plugin
插件:
npm i clean-webpack-plugin --save-dev
然后在webpack.config.js
中引入:
var CleanWebpackPlugin = require('clean-webpack-plugin'); new CleanWebpackPlugin( ['public/main.*.js','public/manifest.*.js'],//要删除的文件目录匹配 { root:__dirname, verbose:true, dry:false } ),
这样我们每次在打包新的代码时,旧文件就会删除,不会再出现同一份文件存在多份的情况。
3.压缩
在webpack中,图片,css,js等等其他资源皆可压缩,本文仅以压缩js为例。
安装插件:npm i uglifyjs-webpack-plugin --save-dev
在webpack.config.js
中引入:
var UglifyJsPlugin = require('uglifyjs-webpack-plugin'); new UglifyJsPlugin({ beautify:true, exclude:['/node_modules/'], compress:{ warnings:false }, output:{ comments:false } })
我们指定了压缩的方法,排除了不需要压缩的node_modules
部分,同时我们去除了comments
部分(comments
为@license等注释,是可观的压缩空间)。再次在终端输入打包命令,可见js打包后的体积有令人满意的减小。
4.热替换
webpack总是绕不开热替换的话题。热替换的功能配置和原理是一大话题,三天三夜也说不完,也并非本文重点,本文只提供简易高效的配置方法。
热替换存在两种使用方式,cli
和node
。cli
方式无需添加新的热替换插件,且无需在入口处添加webpack-dev-server
等入口,故本文采用cli
使用方式。
在webpack.config.js
中添加devServer
字段,加入如下代码:
devServer:{ inline:true, hot:true },
保存后运行webpack-dev-server --inline --hot --progress
,再修改下main.less
文件的样式,会发现浏览器并没有刷新,但页面已经发生了变化,我们的热替换功能也成功加入了!
tips:
在实际项目打包时,可以将filename
字段的值换为[name].[chunkhash].js
,其中[chunkhash]
为webpack每次打包后给每个模块的标识值,这个值每次打包后都会更换。为什么在此处我们使用[id]
呢,因为chunkhash
与热替换存在冲突,终端会有报错,那么使用id
可以算作一个解决方案。这就引申出另一话题,我们可以使用两套webpack配置分别用于生产环境和开发环境,通过webpack指定config来进行打包。例如我们在开发环境使用id
,在生产环境去掉热替换并使用hash
的方式。而且,一些压缩插件也没必要在开发环境过度使用,两套配置能让webpack发挥最大的威力。
另外,chunkhash
和hash
有区别,chunkhash
顾名思义是模块的标识,而hash
是webpack每次编译的标识值,不同的资源如js和css存在chunkhash
解耦的问题,此处不进行过多讨论。
关于热替换的更多细节和原理,参考文章:http://www.cnblogs.com/wonyun/p/7077296.html
5.运行
我们知道,每次打包后,都会有新的main.x.js
文件生成,其hash值每次打包后都会发生变化,难道我们的index.html
文件需要每次打包后都手动修改main.x.js
的路径吗?还好社区提供了html-webpack-plugin
插件,可以在已有html模板的条件下自动为我们生成带有最新代码的html文件:
npm i html-webpack-plugin --save-dev
在webpack.config.js
中引入:
var HtmlWebpackPlugin = require('html-webpack-plugin'); new HtmlWebpackPlugin({ title:'demo', template:'index.html' }),
在终端运行打包命令,我们看到public
文件夹下生成了新的index.html
文件:
以后我们再进行调试时,以本文为例,则需要打开localhost:8080/public/index.html
,因为每次webpack的HtmlWebpackPlugin
都会把新的js文件加入到这个html文件内。在开发全部完成后,我们可以将js路径写死,添加到原有的index.html
文件中。
以下是我们webpack.config.js
全部的配置;
var webpack = require('webpack'); var CleanWebpackPlugin = require('clean-webpack-plugin'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var UglifyJsPlugin = require('uglifyjs-webpack-plugin'); module.exports = { devtool:'cheap-module-eval-source-map', entry:{ main:__dirname + '/app/main.js', vendor:'moment' }, output:{ path:__dirname + '/public', filename:'[name].[id].js', publicPath:'/public/' }, devServer:{ inline:true, hot:true }, module:{ loaders:[ { test:/\.js$/, exclude:/node_modules/, loader:'babel-loader', query:{ presets:['es2015','stage-0'] } }, { test:/\.less$/, exclude:/node_modules/, loader:'style-loader!css-loader!less-loader' } ] }, plugins:[ new CleanWebpackPlugin( ['public/main.*.js','public/manifest.*.js'], { root:__dirname, verbose:true, dry:false } ), new webpack.optimize.CommonsChunkPlugin({ names:['vendor','manifest'] }), new HtmlWebpackPlugin({ title:'demo', template:'index.html' }), new UglifyJsPlugin({ beautify:true, exclude:['/node_modules/'], compress:{ warnings:false }, output:{ comments:false } }) ] }
整个项目,我们在app
文件下的main.js
内写业务代码,main.less
写样式,在public/index.html
下使用热替换进行调试,打包后的压缩文件在public
文件夹下,并且对业务代码,第三方代码进行了清晰地区分。
使用这份webpack配置,我们实现了:
工程的打包调试
公共代码提取,提高开发效率
资源压缩
热替换
这份配置麻雀虽小,五脏俱全。本文还有许多不完善之处,比如一些插件的使用方法,原理没有与大家讲清楚,但webpack实在太庞大了,一个插件的使用方法和原理都可以写上千字的文章了,学习不可浅尝辄止,但也不能太钻牛角尖,与大家共勉~
━━━━━
前端技术分享扫码关注下吧
以上是关于快速打造简易高效的webpack配置的主要内容,如果未能解决你的问题,请参考以下文章