在上篇中搭建了
vue
的基本脚手架,为了加快脚手架打包项目的构建速度和减少打包代码的体积,下面在上一篇的基础上添砖加瓦,完成脚手架的优化。
脚手架目录
脚手架是在原有的基础上构建的,所以基本的目录结构相类似。不过加上了对JS
压缩,CSS
代码压缩(tree shake),引入了dllPlugin
打包不常变动的类库生成动态链接库,加入happypack
开启多进程打包,加快构建的速度,下面也会说到运用npm script
,通过sftp
把打包的文件上传到部署的目录,避免手动上传。
脚手架地址:https://github.com/Harhao/webpack/tree/dev
|-- .gitignore
|-- build
| |-- webpack.base.conf.js
| |-- webpack.dev.conf.js (开发环境打包配置)
| |-- webpack.dll.conf.js (生成dll动态链接库)
| `-- webpack.prod.conf.js(生产环境打包配置)
|-- config
| `-- index.js (打包配置参数)
|-- dist (生产环境打包目录)
| |-- index.html
| |-- precache-manifest.2df00ef5798fdac7218a2cecb8233a17.js(缓存清单)
| |-- service-worker.js(service-worker文件)
| `-- static
| |-- css
| | `-- main_de7bb505.css
| `-- js
| |-- framework_2f05dfbce1b06dd8.js
| |-- framework_2f05dfbce1b06dd8.js.gz (开启gz压缩打包结构)
| |-- main_2f05dfbce1b06dd8.js
| `-- vendors~main_2f05dfbce1b06dd8.js
|-- dll(开启dllplugin打包的链接库)
| |-- axios.dll.js
| |-- axios.manifest.json
| |-- framework.dll.js
| `-- framework.manifest.json
|-- package.json
|-- postcss.config.js
|-- public
| `-- index.html
`-- src(开发主目录)
|-- App.vue
|-- main.js
|-- router
| `-- index.js
`-- views
|-- admin
| `-- index.vue
`-- login
`-- index.vue
开发环境配置
在webpack.dev.conf.js
开发环境配置文件中,增加了happypack
、Dllplugin
动态链接。happypack
主要是开启多进程打包文件,加快打包构建速度。而Dllplugin
主要是对一些不常变动的类库提取打包,在再次编译打包过程,可以免除再次的打包。
开发环境配置文件
下面是webpack.dev.conf.js
开发环境全瞰,主要增加了DllReferencePlugin
、happypack
、add-asset-html-webpack-plugin
插件。下面对各自的作用详细说明。
const path = require("path")
const HappyPack = require("happypack")
const VueLoaderPlugin = require("vue-loader/lib/plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin")
const HotModuleReplacementPlugin = require("webpack/lib/HotModuleReplacementPlugin")
const config = require("../config/index")
module.exports = {
mode: "development",
devtool: "cheap-module-eval-source-map",
entry: path.resolve(__dirname, "../src/main.js"),
output: {
filename: "[name]_[hash:16].js",
path: path.resolve(__dirname, "../dist"),
publicPath: '/'
},
resolve: {
modules: ["node_modules"],
alias: {
"@": path.resolve(__dirname, '../src')
},
extensions: [".js", ".json", ".vue", ".scss"]
},
module: {
rules: [{
test: /\.js$/,
use: ["happypack/loader?id=babel"],
exclude: path.resolve(__dirname, '../node_modules')
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"]
}, {
test: /\.vue$/,
use: ["vue-loader"]
}, {
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: ["happypack/loader?id=url"]
}]
},
plugins: [
new DllReferencePlugin({
manifest: require('../dll/framework.manifest.json')
}),
new DllReferencePlugin({
manifest: require('../dll/axios.manifest.json')
}),
new HappyPack({
id: 'babel',
loaders: ["babel-loader?cacheDirectory"]
}),
new HappyPack({
id: 'url',
loaders: ["url-loader"]
}),
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
filename: 'index.html',
inject: 'body'
}),
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dll/framework.dll.js') // 对应的 dll 文件路径
}),
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dll/axios.dll.js') // 对应的 dll 文件路径
}),
new HotModuleReplacementPlugin()
],
devServer: {
...config.dev.server
},
/**
* 开启webpack对文件变动的监听
*/
watch: true,
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300,
poll: 1000
}
}
DllPlugin动态链接库
.dll
为后缀的文件,这些文件称为动态链接库。提供为其他模块使用的函数和数据。在一些不常变动的类库vuejs
、vue-router
、vuex
,我们可以打包成动态链接库,动态链接库只需要编译一次,在之后的构建过程中被动态链接库包含的模块将不会在重新编译。加快我们构建的速度。配置dll
动态链接库,需要另外一个配置文件webpack.dll.conf.js
。
Dllplugin
插件专门用于单独的webpack配置中,以创建仅限dll的捆绑包。它创建了一个manifest.json
文件,用于[DllReferencePlugin
]映射依赖项。下面webpack.dll.conf.js
通过配置生成dll
动态链接库。
通过配置npm script
生成动态链接库文件,在package.json
定义了npm run build:dll
打包生成dll
动态链接库。
生成下面的dll
动态链接库,在运行开发环境前,需要先打包生成dll
动态链接库,这样开发环境打包时会自动引用动态链接库。其中framework
和axios
都是我们自定义命名,menifest
是类库的映射文件。
有动态链接库,还需要在webpack.dev.conf.js
文件中对链接库的引入。DllReferencePlugin
会去 manifest.json
文件读取 name
字段的值, 把值的内容作为在从全局变量中获取动态链接库中内容时的全局变量名。而AddAssetHtmlWebpackPlugin
是将JavaScript或CSS资源添加到生成的HTML中,这里使用该插件将dll
动态链接库添加到index.html
中。
生成index.html
源码已经加入framework.dll.js
和axios.dll.js
,源码结构如下所示:
happypack多进程打包
HappyPack
通过并行
转换文件使得初始webpack
构建更快。happypack
通过开启多个子进程并行打包文件,使文件构建速度明显加快。在使用happypack
过程中,发现happypack
对scss
支持度不高。所以在webpack.dev.conf.js
没有对scss
文件进行处理。在happypack
的官网案例中,happypack
对less
的支持度比较高。happypack
使用比较简单,如下所示,下面对以js
为后缀的文件用happypack进行打包。happypack
默认开启的进程是3个,可以自定义配置,详细参数可以参照官方文档说明。
开启happypack
成功打包的构建流程图
生产环境配置
在webpack.prod.conf.js
生产配置文件中,添加了workbox-webpack-plugin
渐进行PWA
的资源缓存;optimize-css-assets-webpack-plugin
对css
代码压缩处理;compression-webpack-plugin
对大文件进行gz
压缩;UglifyJsPlugin
对js
文件压缩处理,UglifyJsPlugin
在上篇已做说明。
生产环境配置文件
webpack.prod.conf.js
生产配置环境文件,没有对不常更改的类库(vuejs
)生成动态链接库。利用webpack4
内部提取公共代码,进行了处理。总瞰如下所示:
const path = require("path")
const VueLoaderPlugin = require("vue-loader/lib/plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const UglifyJsPlugin = require("uglifyjs-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const WorkboxPlugin = require('workbox-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
module.exports = {
mode: "production",
entry: {
main: path.resolve(__dirname, "../src/main.js"),
framework: ["vue", "vue-router", "vuex"]
},
output: {
filename: "static/js/[name]_[hash:16].js",
path: path.resolve(__dirname, "../dist"),
publicPath: './'
},
resolve: {
modules: ["node_modules"],
alias: {
"@": path.resolve(__dirname, '../src')
},
extensions: [".js", ".json", ".vue", ".scss"]
},
module: {
rules: [{
test: /\.js$/,
use: [{
loader: "babel-loader",
options: {
presets: ['@babel/preset-env']
}
}],
exclude: /node_modules/
},
{
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
"postcss-loader",
"sass-loader"
],
exclude: /node_modules/
},
{
test: /\.vue$/,
use: ["vue-loader"],
exclude: /node_modules/
}]
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
title: '自搭建webpack脚手架',
template: path.resolve(__dirname, "../public/index.html"),
filename: 'index.html',
inject: 'body',
minify: {
removeComments: true,//移除注释
collapseWhitespace: true,//移除空白字符串
removeAttributeQuotes: true //移除双引号
}
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'static/css/[name]_[chunkhash:8].css',
chunkFilename: '[id].css',
}),
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
}),
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: /(\.js$|\.css$)/,
threshold: 10240,
minRatio: 0.8
})
],
optimization: {
usedExports:true,
minimizer: [
new UglifyJsPlugin({
test: /\.js(\?.*)?$/i
}),
new OptimizeCSSAssetsPlugin({
// cssProcessorOptions: true? {map: { inline: false }}:{}
})
],
splitChunks: {
chunks: "all",
minChunks: 1,
minSize: 0,
cacheGroups: {
framework: {
test: "framework",
name: "framework",
enforce: true
}
}
}
}
}
workbox-webpack-plugin
渐进式Web应用程序(或PWA
)使我们开发的应用在离线后,依然可以访问。主要依靠了Service Workers
的Web技术实现。在webpack
社区提供了workbox-webpack-plugin
方便实现PWA
功能。
在wenbpack.prod.conf.js
配置好插件workbox-webpack-plugin
后,需要在项目的入口文件里注册serviceWorker
。这里展示的vuejs
的入口文件main.js
运行生产环境打包脚本命令,有2个额外的文件正在生成; service-worker.js
和precache-manifest.18d731d6b692ffdc910ac5548db2f8c0.js
。service-worker.js
是Service Worker
文件,precache-manifest.18d731d6b692ffdc910ac5548db2f8c0.js
是一个service-worker.js
需要运行的文件。您自己生成的文件可能会有所不同; 但你应该有一个service-worker.js
文件。
接下来要离线环境进行测试,查看service-worker
离线缓存的效果。webpack
官网提供了一个http-server
对service-worker
测试。
service worker
已经生效,断开http-server
服务,刷新网页,依旧可以正确显示到需要的资源。
optimize-css-assets-webpack-plugin
optimize-css-assets-webpack-plugin
主要是对打包的css
文件进行压缩处理。在webpack4
版本中,设置mode
模式为production
,会自动对js
代码进行压缩处理,其实底下是用uglifyjs-webpack-plugin
进行压缩。但是css
文件还是需要手动进行压缩。而optimize-css-assets-webpack-plugin
起的就是压缩css
作用。
打包压缩的文件如下所示,可见css
样式是压缩状态的。
.entry[data-v-7ba5bd90]{color:red;-webkit-transform:scale(.8);-ms-transform:scale(.8);transform:scale(.8)}.admin[data-v-2ba5fe30]{color:#333}
optimize-css-assets-webpack-plugin
在webpack.prod.conf.js
配置使用:
postcss
postcss
是一个用JavaScript
工具和插件转换 CSS
代码的工具。postcss
加上autoprefixer
帮助我们在新的css3
属性自动添加厂商前缀。
在package.json
文件添加以下字段:
"browserslist": [
"last 5 version",
">1%",
"ie>=8"
]
在项目的根目录下,创建postcss.config.js
,内容如下:
module.exports = {
plugins: {
autoprefixer: {
}
}
};
JS tree shake
在webpack4
中使用tree shake
剔除没有使用的JS
代码比较简单,直接在optimization
中开启usedExports
,不过JS tree shake
只对ES6
语法有效,所以要度量使用。关于对于css
的tree shake
,有朋友建议是purecss
对没有使用的css
代码剔除,不过实验了一下,感觉没有效果,就没有做推荐。
splitChunks 提取公用代码
在webpack.dev.conf.js
开发环境中使用了dllPlugin
的动态链接库方法提取公用的类库,加快构建速度。在生产环境中,采用的是webpack4
内置功能提取公用代码。在webpack3
中采取CommonChunkPlugin
提取公共模块和第三方库。在webpack4
已经不建议使用该插件,而是使用内置的方法。
compression-webpack-plugin
在上面使用splitChunks
提取公共模块framework
,但是打包出来的模块还是比较大,在浏览器下载包模块时候时间会比较长,所以在对打包出来,体积比较大的模块使用compression-webpack-plugin
打包成gz
包。配合后端开启gz
支持,这样可以加快资源下载的速度。
总结
对常见的优化方案做了一次测试和实践,如果有其他好的优化方法,可以留言分享。