之前一直在用各种脚手架构建前端项目,而这些脚手架都已经自带了webpack,虽然自带的默认配置基本也够用,最多也是小改改,就可以进行项目的开发和最终打包部署。但是如果涉及到为了提升各方面的性能进行webpack升级,或者想用webpack打包部署很旧的项目,那么学习webpack就会变得有必要了。
【前言】
webpack有什么用?
- 基于webpack的前端开发体验非常好。
- 用webpack打包部署的前端项目性能也比较好。
和gulp的共同点和差异点
共同点:
- 都能打包部署前端项目。
- 都能提供提供WEB服务和配套的开发环境。
差异点
- 侧重和目的不同,webpack是模块打包器,最终目的是为了打包所有资源(注意不一定是打包成一个资源,可以打包成几个包然后按需加载 提高性能)。而gulp是为了执行用户的任务,目的取决于使用它的人(比如做嵌入式开发打包成bin文件)。
- 体验不同。
- webpack起的服务(webpack-dev-server)中所有前端文件运行在内存中,速度比gulp起的服务(存在磁盘上)要快。
- webpack可以实现HMR,而gulp只能实现live reload。
- 实际运行代码不同,
webpack运行的代码是经过自身处理的,而gulp 除了压缩基本还是自己写的代码。
【正文】
正文会按照如下目录总结
- 开发环境
- 入口文件
- 出口文件
- loader
- plugin
- HMR
- 生产环境
- 懒加载
- 压缩
- 缓存
- CDN
开发环境
看下的配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); //生成html文件
const CleanWebpackPlugin = require('clean-webpack-plugin'); //每次build的时候清空之前的目录
const webpack = require('webpack');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const WorkboxPlugin = require('workbox-webpack-plugin');
//path.resolve(...[args])
//从右往左添加路径,最低路径其实拼接取决右边的参数,即使左边写了,也会被忽略,..会忽略左边第一个/后面的路径值,../../会忽略2个,以此类推
//把所有路径定位到项目工程根目录下
function resolve(dir) {
return path.resolve(__dirname, '..', dir);
}
module.exports = {
//sourcemap类型
devtool: 'source-map',
mode: 'none',
//入口文件
entry: {
main: resolve('./src/main.js')
},
//出口文件
output: {
path: resolve('dist'),
// filename: '[name].[chunkhash].js'
filename: '[name].[hash].js'
},
devServer: {
contentBase: resolve('dist'), //本地服务器所加载的页面所在的目录
hot: true //HMR开关
},
//loader的使用方式
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.png|svg|jpg|gif$/,
use: [
'file-loader'
]
}
]
},
//plugin的使用方式
plugins: [
new CleanWebpackPlugin(),
new webpack.NamedModulesPlugin(),
// new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
title: 'Cache',
template: resolve('src/index.html')
}),
new webpack.ProvidePlugin({
jQuery: "jquery"
}),
new WorkboxPlugin.GenerateSW({
// 这些选项帮助 ServiceWorkers 快速启用
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true
})
// new BundleAnalyzerPlugin()
]
}
项目目录:
![](https://img.haomeiwen.com/i10754968/93b6e7a0816dac16.png)
package.json:
{
"name": "test",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"webpack": "webpack --config ./build/webpack.base.conf.js",
"server": "webpack-dev-server --config ./build/webpack.base.conf.js",
"start": "http-server dist"
},
"license": "MIT",
"dependencies": {
"clean-webpack-plugin": "^2.0.1",
"css-loader": "^2.1.1",
"file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0",
"http-server": "^0.11.1",
"jquery": "^3.4.0",
"lodash": "^4.17.11",
"style-loader": "^0.23.1",
"webpack": "^4.29.6",
"webpack-bundle-analyzer": "^3.3.2",
"webpack-cli": "^3.3.0",
"webpack-dev-server": "^3.3.1",
"workbox-webpack-plugin": "^4.3.0"
}
}
这里需要在webpack脚本中指定配置文件,也就是-- config ./build/webpack.base.conf.js
最后只要在项目工程中运行npm run webpack即可。
PS:
只要打开了HMR开关(hot: true
),每个loader会按照webpack提供HMR接口完善各自的HMR。
生产环境
为了达到更好的客户体验,生产环境必须达到最好的性能。而提升性能无非从提高网络传输速度
和减小资源体积
两方面着手。
懒加载
所谓懒加载就是真正需要资源的时候才去获取,而不是一开始就获取所有资源。
在SPA应用中,懒加载典型的应用就是路由懒加载,看下面的vue脚手架的代码(基于webpack)和动图:
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ './views/About.vue')//路由懒加载
}
]
})
![](https://img.haomeiwen.com/i10754968/c926c8a73f4f28a4.gif)
可以看到,当进到about路由的时候,才加载了about.js这个模块。这个得益于webpack的打包技术,让按需加载(懒加载)成为了可能。
压缩
- UglifyJsPlugin
- CompressionWebpackPlugin
这个两个压缩插件分别执行了去注释,合并的常规压缩
和gzip压缩
。
具体使用:
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: true,
parallel: true
}),
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
['js', 'css'].join('|') +
')$'
),
threshold: 10240,//超过这个大小开始压缩,单位字节
minRatio: 0.8
})
)
缓存
在webpack4.x之前用的是CommonChunkPlugin
,之后移除来了这个插件,用了开箱及用的SplitChunksPlugin
,
optimization: {
splitChunks: {
chunks: 'async',//只对异步模块分割
minSize: 30000,//条件1——代码块的最小尺寸, 大于30kb的模块进行分割
maxSize: 0,
minChunks: 1,// 条件2——在分割之前模块的被引用次数
maxAsyncRequests: 5,// 条件3——按需加载最大并行请求数量
maxInitialRequests: 3,// 条件4——一个入口最大并行的请求数量
automaticNameDelimiter: '~',//名称的连接符
name: true,
cacheGroups: {
vendors: {//缓存组
test: /[\\/]node_modules[\\/]/,//缓存试用的模块
priority: -10//优先级,越靠近0优先级越高
},
default: {//默认缓存的配置
minChunks: 1,
priority: -20,
reuseExistingChunk: true//是否复用模块
}
}
}
}
看下main.js
import printNode from './print.js';//包括依赖,大小有300+kb
let getComponent = () => {
var button2 = document.createElement('button');
button2.innerHTML = "get component";
button2.onclick = () => import(/* webpackChunkName: "lodash" */ 'lodash').then(module => {//异步加载的模块
console.log('lodash loaded')
})
return button2;
}
document.body.appendChild(getComponent().button2)
看下打包后的输出结果:
![](https://img.haomeiwen.com/i10754968/49d372639ba04975.png)
可见满足条件1~4的已经组成一个新块(
vendors~lodash.abdc43e003ca5d183219.js
)了。这个插件的作用就是分割,提取公共的模块,作为缓存模块。这样只要对于这种经常不变的模块,浏览器只需要缓存在本地不需要向server端频繁请求,从而达到提高性能的目的。
cdn
这玩意就厉害了,特别是对于SPA应用来说,首屏加载就是一个质的飞越!
先说下CDN的概念:
CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。
下面说下如何在webpack中使用CDN:
在index.html直接引资源
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>iwangcx</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body>
<div id="app">
</div>
<!-- built files will be auto injected -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/echarts/4.2.1-rc1/echarts.common.min.js"></script>
</body>
</html>
在webpack配置文件中通过externals
排除相应的库
//webpack.base.conf.js
externals: {
jQuery: "jquery",
$: "jquery",
echarts: "echarts",
ElementUI: "element-ui",
Vue: "vue"
},
看下面的图,是我通过cdn优化前后对比的差距:
![](https://img.haomeiwen.com/i10754968/ee33fd3cb723df14.png)
![](https://img.haomeiwen.com/i10754968/9694d96cf38a6c47.png)
![](https://img.haomeiwen.com/i10754968/3146b3fcbf127e3c.png)
![](https://img.haomeiwen.com/i10754968/36c48d63b03810f0.png)
恐怖如斯。。。。
【完】
参考文献
https://www.cnblogs.com/theblogs/p/10472900.html
https://blog.csdn.net/qq_41047322/article/details/81706061
https://segmentfault.com/a/1190000015938570
网友评论