Vue 項目優化
之前寫了一篇解析 vue-cli 腳手架的博客 https://blog.csdn.net/u014168594/article/details/90174169,講了 vue-cli 構建項目時是如何優化的,最近在優化項目,看看我們還能在項目中做些什麼。
1.拆分項目模塊
- 按軟件工程項目拆分文件
- 拆分 css
- 抽離代碼塊,使用全局命令
2.按需加載
- 路由按需加載
- echarts
- iview
- vant
3.提取公共代碼
- 拆分公共代碼
- externals 分離
- Dllplugin 分離
4.webppack 輔助插件推薦
一、拆分模塊
按軟件工程項目拆分文件
按照軟件工程項目,建立公共組件、路由、靜態文件等文件夾;在路由按模塊拆分文件夾,把同一模塊路由寫進一個文件等。
如:
assets //靜態文件
components //公共組件
plugins //插件
router //路由
......
......
拆分 css
可把 css 模塊拆分爲全局模塊和局部模塊,這樣就無需把所有 css 文件一次性打包,導致文件過大阻塞 js 加載,而是按需加載,利於首屏加載。
在每個文件增加模塊
<style scoped>......</style>
屬性詳情可看我之前的文章 Vue style 屬性 scoped 原理詳解
但是爲每個頁面增加各自的局部樣式模塊,維護未免過於困難,所以最好的做法,我認爲還是應該把某個模塊的樣式內容集中在一個文件,然後這個模塊裏的每個頁面都引入該樣式文件。
例子:
全局樣式模塊,就是公共樣式模塊,在 main.js 引入
import style from 'ASSETS/css/style.css'
模塊 1 的每個頁面引入模塊 1 的樣式文件
<style scoped src="ASSETS/css/module1.css"></style>
模塊 2 的每個頁面引入模塊 2 的樣式文件
<style scoped src="ASSETS/css/module2.css"></style>
最好記得在 webpack 中設置相對路徑,這裏 ASSETS 就是直接指向樣式文件夾
注意:局部模塊一定要用 src 引用而不是 import ,因爲無論在什麼地方 import 樣式文件,最終的結果都會變成全局模塊。
抽離代碼塊,使用全局命令
使用全局變量、函數以及組件,而不是每個頁面都 import 一次
全局變量路徑 global_val.js:
const baseUrl = 'www.baiud.com';
const name = 'panda';
export default {
baseUrl,
name
}
全局函數路徑 api/golbal.js:
export default{
install(Vue, options) {
Vue.prototype.func = function (){
......
......
};
}
}
main.js
import global_func from '.../api/global_func.js'
import global_val from '.../api/global_val.js'
import nav from '......'
//註冊全局變量
Vue.prototype.global = global_val;
//註冊全局函數
Vue.use(global_func);
//註冊全局組件
Vue.component('nav',nav)
new Vue({......})
Vue.use() 會自動執行內部的 install 方法,因爲是添加在 vue 實例中,所以在整個 vue 項目中都可引用。
二、按需加載
1.路由按需加載
const A1 = () => import('VIEWS/a/a1')
const A2 = () => import('VIEWS/a/a2')
......
{
name: 'a1',
path: '/a1',
component: A1
},
{
name: 'a2',
path: '/a2',
component: A2
}
2.echarts
//引入基本模板
import echarts from 'echarts/lib/echarts'
//按實際使用情況引入組件
import 'echarts/lib/chart/line'
import 'echarts/lib/component/tooltip'
import 'echarts/lib/component/title'
import 'echarts/lib/component/legendScroll'
import 'echarts/lib/component/grid'
......
按實際使用情況需要什麼組件就引入相應 echarts 組件
3.iview
在 npm 內引入插件 babel-plugin-import(按需引入插件)
在 .babelrc 文件中設置
"plugins": ["transform-runtime",
["import", {
"libraryName": "iview",
"libraryDirectory": "src/components"
}]
]
在 build/webpack.base.conf.js 中設置:
module: {
rules: [
......
......
{
test: /iview.src.*?js$/,
loader: 'babel-loader'
},
]
}
這樣 import 引入 iview 的時候就會自動在 src/components 中查找組件,按需引入。
如:import {Carousel,CarouselItem} from 'iview';
4.vant
與 iview 相似
"plugins": [
["import",
{
"libraryName": "vant",
"libraryDirectory": "es",
"style": true
}
]
]
import { Button, List } from 'vant';
三、提取公共代碼
拆分公共代碼
vue-cli 腳手架之前已經做了拆分公共的基礎代碼塊 node_module
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
})
但是如果是按需加載的話,我在 webpack 3.x 裏面發現該插件並不會遍歷所有入口,導致個別按需加載頁面的 node_module 並沒有被提取,我們還需要把所有頁面的公共代碼再進行一次提取。
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'commons',
children: true,
minChunks: x,
})
把從 app.js 入口裏面的所有子頁面再提取一次,力度 minChunks 由實際情況決定。
externals 分離
main.js:
import "babel-polyfill"
import Vue from 'vue'
import router from 'router'
webpack.base.conf:
externals: { //把第三方庫的js文件從打包文件裏去掉
'vue' : 'Vue',
'vue-router' : 'VueRouter',
'babel-polyfill': 'window',
}
最後在 index.html 文件中引入插件(最好存到 cdn 網址內)。
注意:如果引入是 import xxx from ‘xxx/src/xx’ 這種有地址形式或者按需引入,則分離失效
Dllplugin 分離
把需要獨立打包的第三方庫提前打包,由於打包完一次,在無需改動的情況下,以後都不需要再次打包第三方插件。比起 externals ,不僅抽離文件,也加快了打包速度。
使用 Dllplugin 有三步:
(1)新建文件 webpack.dll.config:
const webpack = require('webpack')
const path = require('path')
// 需要獨立打包的第三方庫,自行添加
let vendors = [
'vue/dist/vue.esm.js',
'vue-router/dist/vue-router.esm.js',
'axios',
'babel-polyfill',
'vuex'
]
module.exports = {
entry:{
vendor: vendors
},
output:{
path: path.resolve(__dirname, '../static'), // 路徑
filename: '[name].dll.js', // 文件名
library: '[name]_[hash]'
},
plugins:[
new webpack.DllPlugin({
context: __dirname, // 可選參數,manifest 文件中請求的上下文(默認值爲 webpack 的上下文)
path: path.join(__dirname, '../static', '[name]-manifest.json'), // manifest.json 文件的路徑
name: '[name]_[hash]' // 暴露出來的Dll函數名,必須跟output.library保持一致
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
sourceMap: true
}),
]
}
執行此文件,會生成 vender.dll.js(打包的文件)和 vendor-manifest.json(文件引入映射)
二、新建命令
"scripts": {
......
......
"dll": "webpack -p --progress --config build/webpack.dll.config.js"
},
運行 npm run dll 執行上一步文件
三、確定已分離的第三方插件
打包時,根據 vendor-manifest.json 文件的映射關係確定已緩存的插件不被打包
在 webpack.base.conf.js 中:
plugins:[
......
new webpack.DllReferencePlugin({
context: __dirname, // manifest中請求的上下文(即依賴的相對路徑)
manifest: require('../static/vendor-manifest.json')
})
]
最後記得在 html 文件中加上對 vendor.dll.js 的引用
四、webppack 輔助插件推薦
1.webpack-bundle-analyzer:用於分析打包後文件的工具
2.happypack:開啓多進程處理打包(js是單線程的),用於優化webpack啓動和打包的速度
module: {
rules: [
......
......
{
test: /\.js$/,
//loader: 'babel-loader',
loader: ['happypack/loader?id=babel'],
include: [resolve('src'), resolve('test')]
},
]
}
......
......
new HappyPack({
// id 標識符,要和 rules 中指定的 id 對應起來
id: 'babel',
// 需要使用的 loader,用法和 rules 中 Loader 配置一樣
// 可以直接是字符串,也可以是對象形式
loaders: ['babel-loader?cacheDirectory']
})