在开发vue时,发现即使使用了路由懒加载,webpack打包出来的静态文件依然还是有点大,因此,在开发途中抽空出来对webpack打包进一步探索(虽然包都是共用的,但本篇内容是基于vue-lic脚手架的框架,请自行衍生)
推荐一个非常好用的可视化分析插件 webpack-bundle-analyzer
它可以将打包后的内容束展示为方便交互的直观树状图,这样你就可以非常直观的看到构建的包中引入了那些内容,以方便你进行优化。
- 配置
// 常规配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
// vue中配置启动或不启动
if (config.build.bundleAnalyzerReport) {
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
// package.json
"analyz": "set NODE_ENV=production && set npm_config_report=true && npm run build" // window系统
或者
"analyz": "NODE_ENV=production && npm_config_report=true && npm run build"
- 效果图(网上的动态图)
为 VUE 项目添加 全局变量
-通过DefinePlugin插件配置
new webpack.DefinePlugin({
'process.env': env,
LOCAL_SERVICE: JSON.stringify('')
})
// 即可在vue中直接引用设置的参数 LOCAL_SERVICE
为 VUE 对象添加 对象变量
-在入口文件main.js中
import Vue from 'vue'
Vue.prototype.LOCAL_SERVICE = LOCAL_SERVICE ;
// 即可在vue对象中直接引用设置的参数 this.LOCAL_SERVICE
在VUE项目中引入第三方插件或js
- 方法一:直接在主入口html文件引入(利用外部扩展externals)
<script src="./static/jquery-1.12.4.js"></script>
// 在webpack中配置externals,防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)
externals: {
'jquery': 'jQuery'
}
// 引入
import $ from 'jquery';
方法二: 采用CDN方式,直接在主文件html中引入个全局变量/方法
方法三:利用插件webpack-require-http (不支持es6以上语法)(该方法回返回一个promise)
## 值得注意的是,该方法使用了promise,不会阻碍到后面方法的执行
require(['https://cdn.bootcss.com/jquery/3.1.1/jquery.min.js').then(( )=> {
console.log($, 9338)
})
Promise.all([
require('https://cdn.bootcss.com/jquery/3.1.1/jquery.min.js'),
require('https://cdn.bootcss.com/jquery/3.1.1/jquery.min.js')
]).then(() => {
console.log( $, 999998)
})
- 方法四:会在后面的打包体积方法中体现
为 VUE 项目添加 PWA 解决发布后刷新报错问题
不管是部署在 IIS,还是 nginx,每次应用部署后,再次访问因为旧的 js 已经不存在,所以页面访问的时候会整个报错,报错的结果就是一个白屏,为了解决这个问题,使用 PWA ,这样就可以将 js 缓存到本地,再次发布后,service-worker.js 会使旧的 js 失效,重新请求并缓存 js。
1.无需客户端,少量流量即可安装 2.可添加到主屏并全屏运行 3.离线功能,响应更快,及时更新 4.PUSH能力 5.数据传输必须是https
这个暂时只在应用上看过具体代码,无实操过。推荐文章 https://www.cnblogs.com/morang/p/9622394.html
移动web缓存(可忽略,查看资料顺带的)
把依赖库拆分出来,解决使用代码分割和异步加载打包后首次加载的文件过大问题
- 方法一:CommonsChunkPlugin
推荐两篇描述CommonsChunkPlugin比较好的文章 https://segmentfault.com/a/1190000012828879和https://segmentfault.com/a/1190000014221669
- 首先说下chunk文件的种类,主要有以下三种:
webpack当中配置的入口文件(entry)是chunk,可以理解为entry chunk
入口文件以及它的依赖文件通过code split(代码分割)出来的也是chunk,可以理解为children chunk
通过CommonsChunkPlugin创建出来的文件也是chunk,可以理解为commons chunk
- CommonsChunkPlugin可配置属性
name:可以是已经存在的chunk(一般指入口文件)对应的name,那么就会把公共模块代码合并到这个chunk上;否则,会创建名字为name的commons chunk进行合并
filename:指定commons chunk的文件名
chunks:指定source chunk,即指定从哪些chunk当中去找公共模块,省略该选项的时候,默认就是entry chunks
minChunks:既可以是数字,也可以是函数,还可以是Infinity
- 直接抽取第三方库放在一个文件上
plugins: [
// 查看依赖项是否来自node_modules文件夹,如果是,则将其输出到单独的文件commonChunks.js中
new webpack.optimize.CommonsChunkPlugin({
name: 'commonChunks',
minChunks: function (module,count) {
console.log(module.resource,`引用次数${count}`);
//"有正在处理文件" + "这个文件是 .js 后缀" + "这个文件是在 node_modules 中"
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(path.join(__dirname, './node_modules')) === 0
)
}
}),
// 抽离webpack运行文件
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime',
filename: '[name].js',
chunks: ['vendor']
}),
]
- 由于采用了路由懒加载,实现了代码分割和异步加载,因此需要用到children和async属性,将懒加载的路由文件公用的模块代码,抽离打包成一个单独的文件,并且该文件是按需加载的,如果某个路由没有使用到这些公用模块,是不会加载进来的,其中包括js,vue组件,从而减少每个页面生成的js体积,优化切换页面速度
#### minChunks可以设置为数字、函数和Infinity
数字:模块被多少个chunk公共引用才被抽取出来成为commons chunk
函数:接受 (module, count) 两个参数,返回一个布尔值,你可以在函数内进行你规定好的逻辑来决定某个模块是否提取成为commons chunk
Infinity:只有当入口文件(entry chunks) >= 3 才生效,用来在第三方库中分离自定义的公共模块
#### children
指定为true的时候,就代表source chunks是通过entry chunks(入口文件)进行code split出来的children chunks
children和chunks不能同时设置,因为它们都是指定source chunks的
children 可以用来把 entry chunk 创建的 children chunks 的共用模块合并到自身,但这会导致初始加载时间较长
#### async:即解决children:true时合并到entry chunks自身时初始加载时间过长的问题。async设为true时,commons chunk 将不会合并到自身,而是使用一个新的异步的commons chunk。当这个 children chunk 被下载时,自动并行下载该commons chunk
new webpack.optimize.CommonsChunkPlugin({
name: "app", // 根入口文件
minChunks: 3,
children: true,
// deepChildren: true,
async: true,
})
- 方法二:把依赖的库,先整体都不作处理地打包出来(值得注意的是,使用该方法,一定要合理跟CommonsChunkPlugin搭配,不建议把package.json中的依赖统一打包,这样会导致dll文件比较大,反而起不到想要的优化)
由于采用CommonsChunkPlugin,虽然可以拆分出很多小文件,如单独第三方库文件集合,自定义的公共模块集合等,但每次打包都会全部去重新打包一次,这样实际开发中会不友好。而实际上我们不会一直去更新我们引用的依赖库,所以dll的做法就等于是,事先先打包好依赖库,然后只对每次都修改的js做打包。
- 以vue项目为例,首先创建一个名为 webpack.dll.config.js 的文件
const path = require("path"),
UglifyJsPlugin = require('uglifyjs-webpack-plugin'),
AssetsPlugin = require('assets-webpack-plugin'),
webpack = require("webpack");
var vendors = [
'vue',
'vue-router',
'vuex'
];
module.exports = {
entry: {
vendor: ['vuex','vuex-persistedstate','vue-router']
//读取package.json里的依赖,normalize.css除外,打包会报错
//如果使用了chrome的vue-devtool,那打包的时候把vue也排除掉,因为压缩过的vue是不能使用vue-devtool的
// 我的dll通用来打第三方插件库和自身封装的不会修改的库,
},
output: {
path: path.join(__dirname, '../static'),
filename: 'dll.[name]_[hash:6].js',
library: '[name]_[hash:6]'
},
// 如果依赖库需要编译,必须带上module
module: {
rules: [
//vue文件解析
{
test: /\.vue$/,
loader: 'vue-loader'
},
//js文件解析
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src')],
}
]
},
// 如果依赖库引用别的依赖文件使用的是别名,则必须配置resolve,而且配置别名,打包出来体积相对小很多
resolve: {
//忽略后缀的文件,路径自动查找,如果在同目录下js和vue同名,则需要加后缀作为区分
extensions: ['.js', '.vue', '.json'],
//模块解析
modules: [
resolve('src'),
resolve('node_modules')
],
//路径别名
alias: {
'src': resolve('src'),
'router': resolve('src/router/index.js')
// 等等
}
},
plugins: [
// Dllplugin里的path,会输出一个vendor-manifest.json,这是用来做关联id的,打包的时候不会打包进去,所以不用放到static里
new webpack.DllPlugin({
path: path.join(__dirname, '../', '[name]-manifest.json'),
name: '[name]_[hash:6]'
}),
// 对依赖文件进行压缩混淆
new webpack.optimize.UglifyJsPlugin({ // js、css都会压缩
mangle: {
except: ['$super', '$', 'exports', 'require', 'module', '_']
},
compress: {
warnings: false
}
}),
// 生成bundle-config.json,用于把带hash的dll插入到html中
new AssetsPlugin({
filename: 'bundle-config.json',
path: './'
})
]
};
- 生成dll.vendor.js(static文件夹中)、bundle-config.json(根目录下)和vendor.manifest.json(根目录下)文件
// 添加package.json中的运行命令
"build:dll": "webpack -p --progress --config build/webpack.dll.conf.js"
// 运行命令 npm run build:dll 即可生成
- 修改build/webpack.base.conf.js,添加DllReferencePlugin的配置
const manifest = require('../vendor-manifest.json')
// 关联dll拆分出去的依赖
new webpack.DllReferencePlugin({
manifest
})
- 引入HTML中
// 修改html生成插件HtmlWebpackPlugin,添加多一个属性
// 加载dll文件
vendorJsName: bundleConfig.vendor.js
// 在html文件的底部添加
<script src="./static/<%= htmlWebpackPlugin.options.vendorJsName %>"></script>
- 结果,很明显 CommonsChunkPlugin和dll两者结合起来,一个大型项目,也只有一个js入口文件是因为highcharts而导致超过200k的,而页面js更是只有几十K。
拆分css文件,防止样式文件过大
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var CSSSplitWebpackPlugin = require('css-split-webpack-plugin').default
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
allChunks: true,
}),
// IE9会忽略你生成的CSS包中的大于4000个选择器的部分
new CSSSplitWebpackPlugin({
filename: utils.assetsPath('css/[name]-[part].[ext]'),
size: 1500,// size:单个文件中允许的最大CSS规则数,防止css样式过大,即选择器
})