Vue 項(xiàng)目打包優(yōu)化 - CDN、配置分離、路由懶加載

前言

Vue 項(xiàng)目打包優(yōu)化 - CDN、配置分離、路由懶加載。
本例項(xiàng)目是寫的一個(gè)管理系統(tǒng),項(xiàng)目使用的庫版本:

  • vue-cli: @3.11.0
  • vue: @2.6.10
  • vue-router: @3.1.3

另外本項(xiàng)目還用到了 axios、echarts、element-ui、nprogress、vue-quill-editor等,不優(yōu)化直接打包會(huì)非常大。優(yōu)化目的:

  1. 配置分離:開發(fā)模式使用本地資源,生產(chǎn)模式打包資源使用 CDN 引入。
  2. 路由懶加載:js包過大會(huì)影響頁面加載速度,路由被訪問才加載對(duì)應(yīng)組件更加高效。
生成打包報(bào)告

有兩種方式:

  1. 命令行生成打包報(bào)告(build命令后加 --report)
  2. 利用 vue ui 工具(推薦,非常直觀)
命令行生成報(bào)告
npm vue-cli-service build --report

#或者在 package.json 的 build 命令后添加 --report,然后再
npm run build

此方法會(huì)自動(dòng)在 dist 目錄下生成 report.html,打開如下:


reprot.png
vue ui 圖形化界面生成報(bào)告

vue-cli3 中自帶了 vue ui 圖形化界面,用于創(chuàng)建和管理項(xiàng)目。

# 啟動(dòng) Vue UI
vue ui

【導(dǎo)入項(xiàng)目】-【任務(wù)】-【build】- 【運(yùn)行】,效果如下:


vue ui.png

優(yōu)化一:配置分離

從打包報(bào)告中可以看出,依賴項(xiàng)大小還是挺大的,如 element-ui、echarts等,我們可以通過配置分離,在生產(chǎn)模式打包時(shí)不要打包這些依賴以及一些 CSS 本地資源,在 html 模版中引入依賴的 CDN 資源。國內(nèi)的CDN服務(wù)推薦使用 BootCDN

使用 CDN 的好處有以下幾個(gè)方面:

  • 加快打包速度。分離公共庫以后,每次重新打包就不會(huì)再把這些打包進(jìn) vendors 文件中。
  • CDN減輕自己服務(wù)器的訪問壓力,并且能實(shí)現(xiàn)資源的并行下載。瀏覽器對(duì) src 資源的加載是并行的(執(zhí)行是按照順序的)。

需要了解的知識(shí)有:

  1. webpack-externals: 防止某些 import 的包打包到 bundle 中。
  2. vue-cli3 中的 chainWebpack鏈?zhǔn)讲僮?/a>,使用指南webpack-chain。

分離配置

vue-cli3 中需要添加 vue.config.js 配置文件。

  1. 給不同模式定義不同的入口文件
  2. 需要 CDN 引入的依賴添加到 externals 中
  3. 給 htmlWebpackPlugin 添加一個(gè)參數(shù)變量來控制 html 模版生成
// vue.config.js
module.exports = {
  chainWebpack: config => {
    // 生產(chǎn)模式
    config.when(process.env.NODE_ENV === 'production', config => {
      // 生產(chǎn)模式加載 main-prod 入口文件
      config.entry('app').clear().add('./src/main-prod.js')
      // CDN - externals
      config.set('externals', {
        vue: 'Vue',
        'vue-router': 'VueRouter',
        axios: 'axios',
        lodash: '_',
        echarts: 'echarts',
        nprogress: 'NProgress',
        'vue-quill-editor': 'VueQuillEditor'
      })
      // 首頁自定義,添加一個(gè)變量來控制html模版,是否加載cdn資源。
      config.plugin('html').tap(args => {
        args[0].isProd = true
        return args
      })
    })
    // 開發(fā)模式
    config.when(process.env.NODE_ENV === 'development', config => {
      // 開發(fā)模式加載 main-dev 入口文件
      config.entry('app').clear().add('./src/main-dev.js')
      // 首頁自定義
      config.plugin('html').tap(args => {
        args[0].isProd = false
        return args
      })
    })
  }
}

注:vue-cli 中 html 模版是由 html-webpack-plugin 處理的,所以可以通過給插件屬性添加一個(gè)變量,并且 html 模版可以通過插值的方式,根據(jù)變量來決定是否輸出 CDN 資源。

定義不同的入口文件

原入口文件 main.js 復(fù)制兩份分別命名 main-dev.js、main-prod.js。
在 main-prod.js 注釋或刪除 CDN 引入的 CSS。

// 生產(chǎn)模式入口文件 main-prod.js

// css - 生產(chǎn)模式cdn引入
// import 'quill/dist/quill.core.css'
// import 'quill/dist/quill.snow.css'
// import 'quill/dist/quill.bubble.css'
// import 'nprogress/nprogress.css'

// elementUI 按需引入,生產(chǎn)模式 cdn 引入
// import './plugins/element.js'

html 模版引入 CDN

public/index.html 引入 external 忽略的依賴和入口文件刪除的 CSS 對(duì)應(yīng)的 CDN 鏈接。

<title><%= htmlWebpackPlugin.options.isProd ? '' : 'DEV - ' %>管理系統(tǒng)</title>
<!-- 生產(chǎn)模式才加載 CDN -->
<% if(htmlWebpackPlugin.options.isProd) { %>
  <!-- nprogress進(jìn)度條 css -->
  <link rel="stylesheet" >
  <!-- vue-quill-editor富文本編輯器 css -->
  <link rel="stylesheet" >
  <link rel="stylesheet" >
  <link rel="stylesheet" >
  <!-- elementUI css -->
  <link rel="stylesheet" >
  <!-- js -->
  <script src="https://cdn.staticfile.org/vue/2.6.10/vue.min.js"></script>
  <script src="https://cdn.staticfile.org/vue-router/3.1.3/vue-router.min.js"></script>
  <script src="https://cdn.staticfile.org/axios/0.19.0/axios.min.js"></script>
  <script src="https://cdn.staticfile.org/lodash.js/4.17.13/lodash.min.js"></script>
  <script src="https://cdn.staticfile.org/echarts/4.6.0/echarts.min.js"></script>
  <script src="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.js"></script>
  <script src="https://cdn.staticfile.org/quill/1.3.7/quill.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue-quill-editor@3.0.6/dist/vue-quill-editor.min.js"></script>
  <script src="https://cdn.staticfile.org/element-ui/2.13.0/index.js"></script>
<% } %>

如上設(shè)置模版,即可通過變量 isProd 在不同模式下是否加載 CDN 資源,以及不同模式下有不同的 title。


優(yōu)化二:路由懶加載

如上操作后可以一定程度減少打包大小,但此時(shí)所有組件仍會(huì)被打包到同一個(gè) chunk-vendors.js 中,如果組件過多打包數(shù)據(jù)仍然會(huì)很大,從而影響首屏加載時(shí)間。

路由懶加載可以將代碼分割打包,且僅當(dāng)路由被訪問時(shí)才加載對(duì)應(yīng)組件。

最簡(jiǎn)單方法:

// import Login from '../views/Login.vue'
const Login = () => import('../views/Login.vue')

以上方式有個(gè)缺點(diǎn),會(huì)將每個(gè)路由組件單獨(dú)打包??梢酝ㄟ^魔法注釋將部分組件打包在同個(gè) chunk 中,相同chunkName為一個(gè)包。如下

// src/router/index.js

// import Login from '../views/Login.vue'
// import Home from '../views/Home.vue'
// import Welcome from '../components/home/Welcome.vue'
// import Users from '../components/users/Users.vue'
// import RightsList from '../components/rights/RightsList.vue'
// import RolesList from '../components/rights/RolesList.vue'
// import GoodsCategories from '../components/goods/GoodsCategories.vue'
// import GoodsList from '../components/goods/GoodsList.vue'
// import CategoriesParams from '../components/goods/CategoriesParams.vue'
// import AddGoods from '../components/goods/AddGoods.vue'
// import OrdersList from '../components/orders/OrdersList.vue'
// import StatisticsReports from '../components/statistics/StatisticsReports.vue'

// 路由懶加載
const Login = () => import(/* webpackChunkName: "Index" */ '../views/Login.vue')
const Home = () => import(/* webpackChunkName: "Index" */ '../views/Home.vue')
const Welcome = () => import(/* webpackChunkName: "Index" */ '../components/home/Welcome.vue')

const Users = () => import(/* webpackChunkName: "Users" */ '../components/users/Users.vue')

const RightsList = () => import(/* webpackChunkName: "Rights" */ '../components/rights/RightsList.vue')
const RolesList = () => import(/* webpackChunkName: "Rights" */ '../components/rights/RolesList.vue')

const GoodsCategories = () => import(/* webpackChunkName: "Goods" */ '../components/goods/GoodsCategories.vue')
const GoodsList = () => import(/* webpackChunkName: "Goods" */ '../components/goods/GoodsList.vue')
const CategoriesParams = () => import(/* webpackChunkName: "Goods" */ '../components/goods/CategoriesParams.vue')
const AddGoods = () => import(/* webpackChunkName: "Goods" */ '../components/goods/AddGoods.vue')

const OrdersList = () => import(/* webpackChunkName: "Orders" */ '../components/orders/OrdersList.vue')

const StatisticsReports = () => import(/* webpackChunkName: "Statistics" */ '../components/statistics/StatisticsReports.vue')
結(jié)果


另外:如果服務(wù)器開啟 Gzip,在 vue.config.js 配置中使用 Gzip 壓縮,能得到更好的壓縮效果,如何開啟這里就不贅述了,自行 Google 吧。

參考:https://www.cnblogs.com/slightFly/p/12485129.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容