「前端」Vue首屏渲染優化的一次嘗試

文章地址:https://segmentfault.com/a/1190000019499007

前言

之前用vuecli做了個博客,是一個單頁面項目,大概有十個路由
直接npm run build打包出來,有一個1M的巨大js文件

clipboard.png

先掛載到服務器上試試
好傢伙 這加載時間 彷彿過了半個世紀

clipboard.png

首屏頁面整整加載了9s 光加載那個大文件就花了8s
這必須得做個優化了,沒有用戶能忍受9s的白屏而不關閉頁面的

過程中,我還順便把項目從vuecli 2.x遷移到了vuecli 3,所以接下來還會介紹一些它們在優化上的異同

分析

vuecli 2.x自帶了分析工具
只要運行npm run build --report

如果是vuecli 3的話,先安裝插件

cnpm intall webpack-bundle-analyzer –save-dev

然後在vue.config.js中對webpack進行配置

chainWebpack: (config) => {
    /* 添加分析工具*/
    if (process.env.NODE_ENV === 'production') {
        if (process.env.npm_config_report) {
            config
                .plugin('webpack-bundle-analyzer')
                .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
                .end();
            config.plugins.delete('prefetch')
        }
    } }

再運行npm run build --report

會在瀏覽器打開一個項目打包的情況圖,便於直觀地比較各個bundle文件的大小

clipboard.png

可以看到 項目中所有的依賴,所有的路由,都被打包進了同一個文件中

另外,在瀏覽器中,也可以通過converge來查看代碼的使用狀況

clipboard.png
紅色的是下載了但未使用的部分

路由懶加載

當打包構建應用時,JavaScript包會變得非常大,影響頁面加載。
如果我們能把不同路由對應的組件分割成不同的代碼塊,然後當路由被訪問的時候才加載對應組件,這樣就更加高效了。

在一開始就下載完所有路由對應的組件文件,這明顯是不合適的,這就像下載一個app了,所以我們就需要使用路由懶加載

clipboard.png

router.js文件中,原來的靜態引用方式

import ShowBlogs from '@/components/ShowBlogs'

routes:[ path: 'Blogs', name: 'ShowBlogs', component: ShowBlogs ]

改爲

 routes:[ path: 'Blogs',name: 'ShowBlogs',component: () => import('./components/ShowBlogs.vue')

以函數的形式動態引入,這樣就可以把各自的路由文件分別打包,只有在解析給定的路由時,纔會下載路由組件

clipboard.png

首屏需要加載的文件變成了橙色的部分,被小弟們分流出去了300k

如果是在vuecli 3中,我們還需要多做一步工作
因爲vuecli 3默認開啓prefetch(預先加載模塊),提前獲取用戶未來可能會訪問的內容
在首屏會把這十幾個路由文件,都一口氣下載了
所以我們要關閉這個功能,在vue.config.js中設置
參考官網的做法:

clipboard.png

設置完畢後,首屏就只會加載當前頁面路由的組件了

element-ui按需加載

clipboard.png

首屏需要加載的依賴包,其中element-ui整整佔了568k
原本的引進方式引進了整個包:

import ElementUI from 'element-ui'
Vue.use(ElementUI)

但實際上我用到的組件只有按鈕,分頁,表格,輸入與警告
所以我們要按需引用

import { Button, Input, Pagination, Table, TableColumn, MessageBox } from 'element-ui';
Vue.use(Button)
Vue.use(Input)
Vue.use(Pagination)
Vue.prototype.$alert = MessageBox.alert

注意MessageBox註冊方法的區別,並且我們雖然用到了alert,但並不需要引入Alert組件

.babelrc文件中添加(vue-cli 3要先安裝babel-plugin-component):

plugins: [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]

clipboard.png
element-ui小了很多,不過看到那個顯眼的table.js後想到,table組件只有後臺管理頁面用到了,不需要全局註冊,所以我們刪除main.jsTableTablColumn的引用,並在後臺組件中局部註冊

import { Table, TableColumn } from "element-ui";

components: {
“el-table”: Table,
“el-table-column”: TableColumn },

clipboard.png
table就被拆分到了路由文件中

組件重複打包

可以看到上圖,有兩個路由文件都引用了codemirror.js造成重複下載
我們可以在webpackconfig文件中,修改CommonsChunkPlugin的配置

minChunks: 3

把3改爲2,就會把使用2次及以上的包抽離出來,放進公共依賴文件,不過由於首頁也有複用的組件,所以首頁也會下載這個公共依賴文件

clipboard.png

首頁下載了黃色和灰色部分
拆了半天,又回到原點

當然,我們可以繼續折騰CommonsChunkPlugin的配置來解決這個問題
但在新版webpack中,CommonsChunkPlugin被自由度更高,更高級的SplitChunksPlugin代替

這也是爲什麼我要把項目遷移到vuecli 3(使用webpack4
默認就做了優化,首頁只會下載灰色的部分(235K

gzip

拆完包之後,我們再用gzip做一下壓縮
安裝compression-webpack-plugin

cnmp i compression-webpack-plugin -D

vue.congig.js中引入並修改webpack配置

const CompressionPlugin = require('compression-webpack-plugin')

configureWebpack: (config) => {
if (process.env.NODE_ENV === ‘production’) {
// 爲生產環境修改配置…
config.mode = ‘production’
return {
plugins: [new CompressionPlugin({
test: /.jsKaTeX parse error: Can't use function '\.' in math mode at position 2: |\̲.̲html|.css/, //匹配文件名
threshold: 10240, //對超過10k的數據進行壓縮
deleteOriginalAssets: false //是否刪除原文件
})]
}
}

clipboard.png
可以看到200k以上的文件被壓縮到了100k以內

在服務器我們也要做相應的配置
如果發送請求的瀏覽器支持gzip,就發送給它gzip格式的文件
我的服務器是用express框架搭建的
只要安裝一下compression就能使用

const compression = require('compression')
app.use(compression())

注意,後面這一句,要放在所有其他中間件註冊之前

最終效果

clipboard.png
首屏加載資源198k,加載時間1s,相比原來速度提升了90%

後記:css是否要拆分

vuecli 3vuecli2.x還有一個區別是
vuecli 3會默認開啓一個css分離插件 ExtractTextPlugin
每一個模塊的css文件都會分離出來,整整13個css文件,而我們的首頁就請求了4個,花費了不少的資源請求時間
我們可以在vue.config.js中關閉它

    css: {
    // 是否使用css分離插件 ExtractTextPlugin
    extract: false,
    // 開啓 CSS source maps?
    sourceMap: false,
    // css預設器配置項
    loaderOptions: {},
    // 啓用 CSS modules for all css / pre-processor files.
    modules: false
},

clipboard.png

clipboard.png

打包出來的文件中,直接就沒有了css文件夾
取而代之的是整合起來的一個js文件,負責在一開始就注入所有的樣式
首屏加載文件數減少,但體積變大,最終測下來速度沒有太大差異
所以,是否要css拆分就見仁見智,具體項目具體分析吧

總結

性能優化是一個非常令人愉悅的過程,同時也是個深坑,有着太多東西,本篇文章開了個頭,希望能對大家有所幫助

參考文章

Vue打包優化之code spliting
https://juejin.im/post/5ac815...
Vue 性能優化:如何實現延遲加載和代碼拆分?
https://www.infoq.cn/article/...*zN
Webpack 打包優化之體積篇
https://www.jeffjade.com/2017...
記一次vue+element+echarts項目的優化
https://juejin.im/post/5b0033...

                        </article>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章