webpack + vue-cli(v2.xx) 搭建前端脚手架性能优化
路由懒加载
路由懒加载语法如下
component: resolve => require(['@/views/xxx/xxx'], resolve)
例如
{
path: '/index',
name: 'index',
component: resolve => require(['@/views/index/Index'], resolve)
}
iview 按需引入
创建 iview.index.js 文件
import Vue from 'vue'
import {
Breadcrumb,
BreadcrumbItem,
Button,
ButtonGroup,
Checkbox,
CheckboxGroup,
Circle,
Collapse,
DatePicker,
Dropdown,
DropdownItem,
DropdownMenu,
Form,
FormItem,
Icon,
Input,
InputNumber,
Submenu,
Menu,
MenuGroup,
MenuItem,
Message,
Modal,
Option,
OptionGroup,
Page,
Panel,
Poptip,
Progress,
Radio,
RadioGroup,
Select,
Slider,
Spin,
Switch,
Table,
Tabs,
TabPane,
TimePicker,
Tooltip,
Upload
} from 'iview'
// iview基础模块
const components = {
Breadcrumb,
BreadcrumbItem,
Button,
ButtonGroup,
Checkbox,
CheckboxGroup,
Collapse,
DatePicker,
Dropdown,
DropdownItem,
DropdownMenu,
Form,
FormItem,
Icon,
Input,
InputNumber,
Submenu,
Menu,
MenuGroup,
MenuItem,
Message,
Modal,
Option,
OptionGroup,
Page,
Poptip,
Progress,
Radio,
RadioGroup,
Select,
Slider,
Spin,
Table,
Tabs,
TabPane,
TimePicker,
Tooltip,
Upload
}
const iviewModule = {
...components,
// 不能和html标签重复的组件,添加别名(除了Switch、Circle在使用中必须是iSwitch、iCircle,其他都可以不加"i")
iButton: Button,
iCircle: Circle,
iForm: Form,
iInput: Input,
iMenu: Menu,
iOption: Option,
iProgress: Progress,
iSelect: Select,
iSwitch: Switch,
iTable: Table
}
// 循环注册全局组件
Object.keys(iviewModule).forEach(key => {
Vue.component(key, iviewModule[key])
})
在 main.js 中引入 iview.index.js 文件即可
import "./js/iview.index"
echarts 按需引入
在 main.js 中对 echarts 按需引入
const echarts = require('echarts/lib/echarts')
// 引入折线图等组件
require('echarts/lib/chart/line')
require('echarts/lib/chart/bar')
require("echarts/lib/chart/graph")
require("echarts/lib/chart/pie")
// 引入提示框、title、图例
require("echarts/lib/component/title");
require("echarts/lib/component/tooltip");
require("echarts/lib/component/legend");
require("echarts/lib/component/dataZoom");
require("echarts/lib/component/axisPointer");
require("echarts/lib/component/graphic");
require("echarts/lib/component/grid");
Vue.config.productionTip = false;
Vue.prototype.$ajax = axios;
Vue.prototype.$echarts = echarts;
在 vue 组件中使用 this.$echarts 代替 echarts 即可
使用 happypack
开启多线程构建
HappyPack可以将原有的 webpack 对 loader 的执行过程,从单一进程的形式扩展为多进程的模式,从而加速代码构建
第一步,安装
npm install happypack --save-dev
第二步,在 webpack.base.conf.js
中使用
const HappyPack = require('happypack')
...
module:{
rules: [
// 主要针对 js 的编译速度进行优化,其他模块优化力度不大,且有些模块 happypack 会不支持
{
test: /\.js$/,
// id 为 babelJs
loader: 'happypack/loader?id=babelJs',
include: [
resolve("src"),
resolve("test"),
resolve("node_modules/webpack-dev-server/client")
]
}
]
},
plugins: [
new HappyPack({
// id 与 babel-loader 中的 id 一致
id: 'babelJs',
// 将 babel-loader 编译后的文件缓存起来,当有文件发生变化时 babel-loader 重新编译
loaders: ['babel-loader?cacheDirectory'],
// 开启 4 个线程,默认是 3
threads: 4
})
]
使用 DllPlugin 优化
在使用 webpack 进行打包的时候,对于依赖的第三方库,如 vue、vuex、vue-router、axios 等这些不会修改的依赖,可以让它和业务代码分开打包。第一次打包时将第三方库打包并生成动态链接库,然后 webpack 就只需要打包项目业务代码,需要导入的模块在动态链接库中,直接去其中获取即可。后续再打包的时候 webpack 只需打包项目业务代码即可,只要不升级依赖库版本,第三方库就不用重新打包。
DllPlugin 插件和 DllReferencePlugin 插件在 webpack 中已内置,不需安装
第一步,新建 dll 文件,作为 package 中打包第三方库命令的入口文件
'use strict'
// 实现 node.js 命令行环境的 loading 效果以及显示各种状态的图标
const ora = require('ora')
// 以包的形式包装 rm -rf 命令,删除文件和文件夹,无论文件夹是否为空,都可以删除
const rm = require('rimraf')
// 处理文件与目录的路径
const path = require('path')
// 修改控制台中字符串的样式 1.字体样式 2.字体颜色 3.背景颜色
const chalk = require('chalk')
const webpack = require('webpack')
// 引入生产配置
const webpackConfig = require('./webpack.dll.conf')
// 设置loading 文案
const spinner = ora('Dllplugin start...')
// loading 开始
spinner.start()
// rm '../static/vendor' 是将该路径下的目录清空,每次打包前都清空以前的文件打包的信息
rm(path.join(__dirname, '../static/vendor'), err => {
if (err) throw err
//开始webpack编译
webpack(webpackConfig, (err, stats) => {
//webpack编译成功后的回调函数
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({ // process.stdout.write 向屏幕输出提示信息
colors: true,
modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Dllplugin failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Dllplugin complete.\n'))
})
})
再打包第三方库时,使用 rm 命令将以前打包的第三方库文件删除,以此保证每次打包的都是最新的第三方库数据
第二步,创建 webpack.dll.conf.js 文件,配置 DllPlugin
const path = require("path");
const webpack = require("webpack");
module.exports = {
// 入口文件
entry: {
// g6、visjs 不能放入,g6 中有 es6 代码,visjs 代码太旧
vendor: ['vue/dist/vue.esm.js', 'lodash', 'vuex', 'axios', 'vue-router', 'iview', 'moment', 'echarts', 'crypto-js']
},
output: {
path: path.join(__dirname, '../static/vendor'), // 打包后文件输出的位置
filename: '[name].dll.js',
library: '[name]_library'
// vendor.dll.js 中暴露出的全局变量名。
// 主要是给 DllPlugin 中的 name 使用,
// 故这里需要和 webpack.DllPlugin 中的 `name: '[name]_library',` 保持一致。
},
plugins: [
new webpack.DllPlugin({
// path 是 manifest.json 生成的文件夹及名字,该项目让它生成在了根目录下的 static/vendor 文件夹中
path: path.join(__dirname, '../static/vendor', '[name]-manifest.json'),
// name 和 output.library 保持一致即可
name: '[name]_library',
// context 选填,manifest 文件中请求的上下文,默认为该 webpack 文件上下文
context: __dirname
}),
// 压缩打包后的第三方库文件
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
};
第三步,在 index.html 文件中引入外部链接
<!DOCTYPE html>
<html>
<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,chrome=1" />
<meta name="format-detection" content="telephone=no,email=no" />
<link rel="icon" href="static/theme/favicon.ico" type="image/x-icon" />
<title>欢迎使用xxx平台</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<script src="./static/vendor/vendor.dll.js"></script>
</body>
</html>
主要添加
<script src="./static/vendor/vendor.dll.js"></script>
第一次打包
// 运行
npm run dll
// 然后运行
npm run build
后续打包
// 运行
npm run build
若第三方库文件版本更新,或 DllPlugin 的 entry 入口第三方库增加
// 运行
npm run dll
// 然后运行
npm run build
项目打包时间由 77s 变为 35s ,节省了 54.5% 的时间
项目热更新时间由 23s 变为 19s,节省了 17.4% 的时间
性能优化有没考虑到的地方欢迎大家留言评论 ^ _ ^ ~