Webpack3.0小案例躺坑资源处理、服务器启动和打包优化
webpack3.0小案例系列,抛砖引玉,希望大家多提意见,一起学习:
webpack3.0小案例webpack初体验
Webpack3.0小案例躺坑css处理与ES6编译
前两节文章我们手把手搭建了一个基于webpack的前端开发工程环境,并讲解了一些常用插件及loader的基本用法,包括代码分割、模板读取、文件清理、ES6编译及css处理等等,这篇文章我继续在前两篇的基础上,继续探讨:
- 图片处理 , 包括file-loader、url-loader、image-webpack-loader的用法,file-loader和url-loader的区别等。
- 服务端启动webpack-dev-server ,包括手动搭建测试服务端以及运用webpack建立webpackServer和一些注意事项等。
- webpack打包优化 ,包括代码压缩,公共代码提取等配置。
image图片处理
webpack对图片的处理常用的有url-loader、file-loader、image-webpack-loader,各个加载器都在打包过程中有着自己的功能职责,在探讨他们的使用方法之前,老规矩,我们先弄明白他们各自都做了什么。
- file-loader , 将项目中定义加载的图片通过webpack编译打包,并返回一个编码后的公共的url路径。
- url-loader ,url-loader作用和file-loader的作用基本是一致的,不同点是url-loader可以通过配置一个limit值来决定图片是要像file-loader一样返回一个公共的url路径,或者直接把图片进行base64编码,写入到对应的路径中去。
- image-webpack-loader ,用来对编译过后的文件进行压缩处理,在不损失图片质量的情况下减小图片的体积大小。
file-loader
1、首先,我们在src目录下新建assets文件夹用来放置所有的静态资源,在该目录下我们放入两张图片bg_webpack-sm.jpg 和 bg_webpack-lg.jpg,大小分别是6kb和12kb(之所以放两张不同大小的图片是用于体验url-loader的limit参数的作用)。
2、打开style.less文件,新增一个container的css类,并在index.html中添加一个类名为container的div
.container{ width:300px; height: 100px; background: url('../assets/bg_webpack-lg.jpg') }
<body> <div id="app"></div> <div class="container"></div> </body>
3、 命令行定位到根目录下,安装file-loader,并在webpack配置文件中新增一条处理规则:
cnpm install file-loader --save-dev
... module: { rules: [ ... { test: /\.(png|jpg|gif|svg)$/, exclude: /node_modules/, use: [{ loader: 'file-loader', options: {} }] } ] } ...
再次执行webpack命令进行编译,我们发下在dist目录下已经打包出了我们引用的图片资源
file-loader配置
现在,图片文件已经打包成功了,但是我们发现两个问题:
- 打开dist文件夹下面的index.html,发现图片并没有在界面中显示出来。
- 打包后的图片直接生成在了dist目录下,而我们想要他生成的指定文件夹下面,这显然和我们的期望是不符合的。
别着急,为了解决这两个问题,我们需要先对file-loader的部分配置项参数作用有一个大致的了解:
name ,指定打包生成后资源文件的name值,file-loader给我们指定了一些占位符来自定义name,我们可以自己任意搭配占位符来得到我们想要生成的文件名称的格式,默认情况下是:[hash].[ext]
名称 描述 [ext] 文件的后缀名,默认是原始资源的后缀名 [name] 文件名称,也就是原始资源的名称 [path] 文件存放的路径 [hash] hash值,默认是md5 [N] 数字 publicPath ,用来指定打包生成后的css或者url引用中路径的配置,也就是说pulicPath针对的是路径,并不是文件本身。
outputPath,用来配置打包生成的文件输出的位置,它针对的是文件存储的位置,和代码中的路径引用无关。
useRelativePath, 配置打包生成后的路径是否为相对位置,默认情况下是false。需要注意的是,开启useRelativePath后,outputPath的设置会以src目录中的assets文件夹为准,设置其他的名称不会生效。
emitFile ,配置对打包的资源文件是否需要进行发布,默认为true。
从这些配置项中,我们大致可以找到以上两个问题的原因:
- 1、图片生成位置不对是因为我们没指定outputPath,默认读取了出口(output)的值;
- 2、在浏览器中打开但没有显示出图片,是由于未设定publicPath的值,因此在生成的css的background的url中会自动默认到与css文件同一级里面,导致url地址不对,因此找不到图片资源。
因此针对这两个问题,我们对配置文件进行配置:
... module: { rules: [ ... { test: /\.(png|jpg|gif|svg)$/, exclude: /node_modules/, use: [{ loader: 'file-loader', options: { name: '[hash].[ext]', outputPath: '/assets/',//定义图片输出存放的文件夹位置 useRelativePath: true,//设置路径为相对位置 } }] } ] } ...
在配置项中,我们定义了文件输出的名称以hash值来命名,并指定文件输出到dist下的assets文件夹下,然后开启路径为相对路径,再次执行webpack编译,发现文件已经按照设定生成到assets目录下了。
打开dist下的index.html,图片成功加载
当然,在设置file-loader的option的时候,由于paublicPath会去读取webpack出口中的publicPath的值,因此他们之间是有这联系的,出口中设定的publicPath不一样就会导致file-loader中设定的要跟着改变,这一点要把握好。
url-loader
前面我们说过,url-loader是对file-loader的封装,因此,使用url-loader和file-loader方法基本一样,在这里,我们就探讨一下他们不同的地方,也就是limit参数。
1、首先,我们安装好url-loader,并将之前的file-loader替换成url-loader,并在index.html页面中新增类名为container-sm的div元素。
cnpm install url-loader --save-dev
// webpack.config.js ... module: { rules: [ ... { test: /\.(png|jpg|gif|svg)$/i, exclude: /node_modules/, use: [{ loader: 'url-loader', options: { limit: 10240, outputPath: 'assets/', useRelativePath: true } }] } ] } ...
配置项中,我们指定图片大于10kb的图片进行文件加载,小于10kb的文件则直接转换出base64编码直接写入到css中,这样我们刚刚放入的两张图片结果应该是大的一张以图片资源的方式加载,而小的一张则直接被编码成base64后写入到css文件中。最后效果如下:
可以发现结果和我们预计的一样,表示url-loader已经生效。url-loader对我们处理小型图片很有帮助,他可以有效减小访问服务端的次数,减小访问时间,特别是针对一些icon图标的时候很有效果。
使用url-loader的时候,我们需要对limit大小界限值进行权衡;base64编码可以减少浏览器的访问次数,但是它的不足是编码特别长,这样很容易导致代码体积变大。而通过路径的方式可以减少代码的体积量,并且浏览器在多次访问的时候也会对文件进行缓存处理,但毕竟会增加对服务器资源的访问次数。因此,如何设置limit的大小,需要根据项目的情况来定
image-webpack-loader 图片压缩
由于url-loader在对文件处理的时候会增大文件的体积,比如对文件进行base64编码,就会使文件的体积比原版体积更大,因此,image-webpack-loader就应运而生,它的作用就是在进行url-loader处理之前,首先对静态资源进行压缩处理,处理过后的图片资源再决定是否用base64编码。
1、首先我们对image-webpack-loader进行安装,然后再webpack配置中进行对配置的修改:
cnpm install image-webpack-loader --save-dev
// webpack.config.js ... module: { rules: [ ... { test: /\.(png|jpg|gif|svg)$/i, exclude: /node_modules/, use: [{ loader: 'url-loader', options: { limit: 10240, outputPath: 'assets/', useRelativePath: true } }, { loader: 'image-webpack-loader',//新增image-webpack-loader options: { mozjpeg: {//设置对jpg格式的图片压缩的程度设置 progressive: true, quality: 65 }, } }] } ] } ...
在配置项中,我们对图片进行压缩处理,并设置对jpg类型的图片压缩到65%质量值,最后运行webpack命令,发现两张图片都被压缩成4.75kb大小的图片,在css中,两张图片都已经以base64编码的方式呈现了。
image-webpack-loader对不同类型的图片都对应有不同的处理配置设定,针对不同的图片开发者可以自定义压缩比例。
- gifsicle — 压缩gif图片格式
- mozjpeg — 压缩JPEG图片格式
- optipng — 压缩PNG图片格式
- pngquant — 压缩PNG图片格式
- webp — 压缩PNG和JPEG图片成webp格式
- svgo — 压缩SVG图片格式
服务端运行设置
到目前为止,我们的小案例已经把常用的基本的loader梳理了一遍,但是我们又发现了一些问题,比如每次更改代码后都需要进行webpack重新编译,并且,我们的文件还是以文件系统的方式在浏览器中打开的,这个似乎不是webpack给我们提供的编码姿势,因此,接下来,我们将考虑如何让我们的项目在服务器上跑起来等一系列问题。
DIY一个临时服务器
既然我们指定了webpack最终打包后的文件在dist目录下,那么现在我们只要将dist内的文件在服务器端跑起来不就可以了吗,这似乎可行的,那么我就先来尝试一下
1、在根目录下,新建一个server.js的文件,并安装express
cnpm install express --save-dev
2、在server.js内,我们利用express来写一个简单的服务器:
//server.js var path = require('path'); var express = require('express'); var app = express(); var port = process.env.port || 3000; app.use(express.static(path.join(__dirname, 'dist')));//设置可访问的静态资源目录 app.get('/', function(req, res, next) { req.url = 'index.html'; next() }) app.listen(port, function() { console.log(`server is running at ${port}`) });
3、打开package.json,在script配置项下新增如下命令:
//package.json ... "scripts": { "server": "node server.js",//设置用node运行server.js "build": "webpack"//设置webpack编译命令 } ...
这样我们就可以在控制台通过cnpm来运行项目了:
cnpm run build /*执行webpack打包命令*/ cnpm run server /*让编译后的文件通过服务器加载*/
执行完以上命令后,在浏览器中输入:localhost:3000,即可访问最终打包的文件了,但是,我们要的热更新依然没有,而且还是需要每次都打包,除了用服务器跑起来似乎并没有解决我们的问题(黑人郁闷脸)。
webpack-dev-server
其实解决在开发中的这个实时编译问题,webpack早已为我们提供了简便的方法。
webpack-dev-server是一个小型的nodejs express服务器,支持两种模式来自动刷新页面,并且都提供热更新,我们的实践是以inline模式为例的
- iframe模式,页面放在iframe中,当发生改变时重载
- inline模式,将webpack-dev-sever的客户端入口添加到包(bundle)中
热更新的好处是只替换更新的部分,而不需要去页面重载。
1、安装webpack-dev-server
cnpm install webpack-dev-server --save-dev
2、安装完成后,我们打开package.json文件,在script配置项下新增:
//package.json ... "scripts": { "server": "node server.js",//设置用node运行server.js "build": "webpack",//设置webpack编译命令 "dev": "webpack-dev-server" } ...
这样,我们只要在命令行输入 “cnpm run dev ”既可以把项目运行起来。
3、 用webpack-dev-server运行的时候,默认端口是8080,因此,我们在运行webpack-dev-server后,在浏览器执行 localhost:8080,即可访问项目,这个时候因为项目是处于开发环境,文件只是存储在内存中而并没有真正打包出来,因此,项目中并不会生成dist文件夹,也正因如此,我们动态改变代码的时候,webpack就会自动更新到界面上去。
webpack在配置项中也为我们提供了配置webpack-dev-server的节点,也就是devServer配置项,在这个配置项中,我们可以更改启动的端口号,加载模式(iframe或者inline)等等,但需要注意的是webpack3.0已经默认热更新,如果在devServer中再次设定hot属性的时候,热更新反而会失效
... entry: path.resolve(__dirname, './src/main.js'), output: { path: path.resolve(__dirname, './dist'), filename: 'js/[name]-[chunkhash].js' }, devServer: { // hot: true, 不要设置hot热更新属性,设定后反而会失效 port: 3000, inline: true }, ...
webpack打包优化
webpack优化是一个很有技术性的话题,展开来说的话估计又是一个比较长的话题,我们这一节里先针对我们目前的项目结构罗列几项,抛砖引玉。
代码压缩 UglifyJsPlugin
UglifyJsPlugin是webpack默认提供的代码压缩优化器,当在配置项中使用了这个插件之后,打包后的js文件会进行代码压缩和注释删除等压缩手段,这个在项目build的时候,会减小打包后项目的体积。
... plugins: [ //启用js压缩 new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false, drop_console: false, } }), new htmlWebpackPlugin({ template: 'index.html', filename: 'index.html' }), new cleanWebpackPlugin( ['dist'], { verbose: false, dry: false, root: __dirname } ) ] ...
减小范围 include && exclude
webpack的loader加载器设置中,每一个loader都允许对处理文件进行指定,包括哪一个或者不包括哪一个文件夹内的文件,这样会很大程度上减小扫描的范围,减小打包时间。实际上,在我们的项目中我们已经设定了exclude参数,以排除loader对node_modules中文件的扫描。
{ test: /\.js$/, use: [{ loader: "babel-loader" }], exclude: /node_modules/, include: /src/ }
公共代码提取 CommonsChunkPlugin
webpack默认提供的CommonsChunkPlugin插件可以将代码中公用的模块提取出来,比如常用的类似jquery、moment这样的额第三方插件,如果在每个引用的js打包文件中都进行打包,最终的项目体积就会成倍增加,通过使用CommonsChunkPlugin可以将这些公用部分提取,有效减小项目体积。
设置 babel 的 cacheDirectory 为true
babel编译是一个比较费时间的过程,对babel处理的设置,我们不仅要设置include && exclude来尽可能准确定位编译的文件,还可以充分利用缓存来进一步提升速度,设置babel的cacheDirectory为true,是一个很好的优化方式。
{ test: /\.js$/, use: [{ loader: "babel-loader" ,cacheDirectory: true}], exclude: /node_modules/, include: /src/ }
最后
这一节我们从webpack对静态资源相关的处理以及服务器加载和webpack打包优化等做了总结,其实好多依然讲的不全,很多地方也没有做详细的解释,但千里之行始于足下,一步一步来,我相信你终究会得到你想要的。
还是不忘说一句,愿各位早日成为一名合格的前端高手,加油!