前言
webpack 是一个现代 JavaScript 应用程序的静态模块打包,在前端开发中是一块非常重要的内容
前端工程化之webpack
什么是 webpack? 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
webpack 核心概念
- entry:一个可执行模块或者库的入口。
- output: webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。
// webpack 配置
const path = require('path');
module.exports = {
entry: main: './src/main.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
};
(3)loader:文件转换器。例如把 es6 转为 es5,scss 转为 css 等
// webpack 配置
module.exports = {
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader'
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
}
]
}
};
(4)plugin:扩展 webpack 功能的插件。在 webpack 构建的生命周期节点上加入扩展 hook,添加功能。
// webpack 配置
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
};
(5)模式(development | production) (webpack 4.x) |
if(process.env.NODE_ENV === 'development'){
//开发环境 do something
}else{
//生产环境 do something
}
// mode: development
module.exports = {
+ mode: 'development'
- plugins: [
- new webpack.NamedModulesPlugin(),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
- ]
}
// mode: production
module.exports = {
+ mode: 'production',
- plugins: [
- new UglifyJsPlugin(/* ... */),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
- new webpack.optimize.ModuleConcatenationPlugin(),
- new webpack.NoEmitOnErrorsPlugin()
- ]
}
(6) source map 定位代码中的错误(资源映射)
eval-source-map:构建速度:– 、重新构建速度:+ 、生产环境:no 、显示原始源代码
cheap-eval-source-map:构建速度:+ 、重新构建速度:++ 、生产环境:no 、转换过的代码(仅限行)
webpack 构建流程(原理)
从启动构建到输出结果一系列过程:
(1)初始化参数:解析 webpack 配置参数,合并 shell 传入和 webpack.config.js 文件配置的参数,形成最后的配置结果。
(2)开始编译:上一步得到的参数初始化 compiler 对象,注册所有配置的插件,插件监听 webpack 构建生命周期的事件节点,做出相应的反应,执行对象的 run 方法开始执行编译。
(3)确定入口:从配置的 entry 入口,开始解析文件构建 AST 语法树,找出依赖,递归下去。
(4)编译模块:递归中根据文件类型和 loader 配置,调用所有配置的 loader 对文件进行转换,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
(5)完成模块编译并输出:递归完事后,得到每个文件结果,包含每个模块以及他们之间的依赖关系,根据 entry 配置生成代码块 chunk。
(6)输出完成:输出所有的 chunk 到文件系统。
如何利用webpack来优化前端性能,优化代码输出质量?(提高性能和体验)
用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
(1)压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css。使用webpack4,打包项目使用production模式,会自动开启代码压缩。
(2)利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。再配合externals得到的包体积小,便于快速渲染组件
//webpackconfig
module.exports = {
entry:{},
output:{},
externals:{
'vue': 'Vue',
'element-ui': 'ElementUI'
}
}
//html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>xxx</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<div id="app">
</div>
</body>
</html>
(3)删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。除了有副作用的代码需要格外注意到不纳入剔除如[css,vue]有些特殊则无需纳入剔除选项 sideEffects:[‘.css’,’.vue’], ,我们就能为使用方提供更好的打包体验。原理是 webpack 能将标记为 side-effects-free 的包由 import {a} from xx 转换为 import {a} from ‘xx/a’,从而自动修剪掉不必要的 import.
支持的是es module import静态导入模式,不支持reuqire动态模式
(4)优化图片,对于小图可以使用 base64 的方式写入文件中采用url-loader limit限制大小,决定生成base64还是以服务端的url为主
// webpack 配置
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
}
]
}
};
(5)按照路由拆分代码,实现按需加载,提取公共代码。组件也实现异步import实现按需引入,,对js文件进行代码分割,生成单独的chunks,当用到该路由或组件才去加载,尤其对于首屏优化来说,分担了首屏加载过多过多内容导致的白屏时间过长的问题
//正常引入
const router = new Router({
routes: [
{
path: '/hyh',
component: hyh,
name: 'hyh'
}
]
})
// 按需引入 r就是resolve 使用vue的异步组件,可以实现路由的懒加载
const list = r => require.ensure([], () => r(require('../components/list/list')), 'list');
const router = new Router({
routes: [
{
path: '/list/blog',
component: list,
name: 'blog'
}
]
})
// es6提出的import(推荐使用这种方式)取代原来直接import的方式去引入组件
<script>
// import HomeHeader from "./components/Header";
// import HomeSwiper from "./components/Swiper";
// import HomeIcons from "./components/Icons";
// import HomeRecommend from "./components/Recommend";
// import HomeWeekend from "./components/Weekend";
export default {
name: "Home",
components: {
HomeHeader: () => import("./components/Header"), //异步组件加载方式
HomeSwiper: () => import("./components/Swiper"),
HomeIcons: () => import("./components/Icons"),
HomeRecommend: () => import("./components/Recommend"),
HomeWeekend: () => import("./components/Weekend")
}
}
(6)利用webpack打包分析plugin进行性能分析
webpack引入webpack-bundle-analyzer
配置该plugin再配合scripts命令 npm run build: –report 得到打包完成分析图
根据此图进行模块分析,找到优化点
// webpack.config.js
const webpack-bundle-analyzer = require('webpack-bundle-analyzer');
module.exports = {
plugins: [
new webpack-bundle-analyzer(),
],
};
//package.json
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"build": "node build/build.js",
},
(7)生产模式启用关闭sourcemap减少不必要的文件,减小包体积,提升页面渲染
(8)对输出的css进行压缩,不必要的css进行剔除
//安装
npm i cssnano optimize-css-assets-webpack-plugin -D
//webpack.config.js
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
plugins: [
// 抽离css为独立文件输出
new MiniCssExtractPlugin({
filename: "css/[name]_[contenthash:6].css"
}),
new OptimizeCSSAssetsPlugin({
cssProcessor: require("cssnano"), //引入cssnano配置压缩选项
cssProcessorOptions: {
discardComments: { removeAll: true } // 将未用到的样式全部移除
}
}),
]
(9)压缩html
new htmlwebpackplugin({
title: "首页",
template: "./src/index.html",
filename: "index.html",
minify: {
removeComments: true, // 移除HTML中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true // 压缩内联css
}
}),