世界快消息!前端頁面加載速度優(yōu)化解決方案

2023-01-13 18:25:02 來源:51CTO博客

隨著前端各種框架的日益完善,一些基本的性能優(yōu)化和加載優(yōu)化都已經(jīng)很完善了,但是有些必要的優(yōu)化還是得開發(fā)者自己去做。vue.js是一個比較流行的前端框架,與react.js、angular.js相比來說,vue.js入手曲線更加流暢,不管掌握多少都可以快速上手。但是單頁面應(yīng)用也都有其弊病,有時候首屏加載慢的讓人捏舌。今天我們以vue cli2.x來說一說如何行之有效的緩解此問題!以項目為例,輸入網(wǎng)址以后會出現(xiàn)十幾秒的空白頁,如果是后臺管理系統(tǒng)還能接受,嵌套式的H5面對的是C端用戶,產(chǎn)品肯定是無法接受的。仔細分析了下,主要是打包后的app.js太大,以及我們引用的一些插件安裝包加載比較慢,在網(wǎng)上搜了很多解決加載慢的方案,最終優(yōu)化的時間移動端H5頁面2秒多,后臺管理系統(tǒng)5秒多。下面,將自己在平時項目上所做的優(yōu)化策略實踐分享于大家。

方案一、路由懶加載

首屏加載慢的原因無非就是單頁面應(yīng)用需要加載完整個路由表上的頁面,而路由懶加載就是來解決這個問題的。如果我們能把不同路由對應(yīng)的組件分割成不同的代碼塊,然后當(dāng)路由被訪問的時候才加載對應(yīng)組件,這樣就更加高效了。下面這個就是vue路由懶加載的一個具體例子。方法很簡單,如果您不想深入了解,只需按照這個格式引入路由就可以了。如果您對路由懶加載感興趣,請移步??vue-router??路由懶加載。

此方法會把原本打包到一個??app.js??文件分開成多個js文件打包,這樣會減小單個文件的大小,但是不會減小整個js文件夾的大小。


【資料圖】

通過這種方式可以做到按需加載,只加載單個頁面的js文件。

{    path: "/home",    name: "home",    component: r => require.ensure([], function (require) {      r(require("views/hls/Home/Home.vue"))    }, "home"),    meta: {      title: "首頁",      keepAlive: false,      hasTop: true,      hasBottom: true    }  }復(fù)制代碼

方案二、組件按需加載(代碼分割)

??webpack??將打包資源都打包在了一個??bundle.js??中,其中主要包含了開發(fā)的源代碼 和 第三方依賴??node_modules??。我們可以對??node_modules??第三方依賴 打包資源拆分細化成多個資源文件,借助瀏覽器支持??HTTP??同時發(fā)起多個請求特性,使得資源異步并行加載,從而提高資源加載速度。 ??webpack??提供了??splitChunks??來支持這一配置。

1. 首先引入按需加載工具 babel-plugin-import

babel-plugin-import是babel它會在編譯過程中將 import 的寫法自動轉(zhuǎn)換為按需引入的方式。

npm install babel-plugin-import --save-dev復(fù)制代碼

2. 在項目根目錄創(chuàng)建.babelrc文件并配置按需加載內(nèi)容:

{ "plugins": [["import", {  "libraryName": "iview",  "libraryDirectory": "src/components" }]]}復(fù)制代碼

3. webpack配置:

// webpack.config.jsmodule.exports = function ({ production = false, development = false }) {  ...  output: {    path: path.resolve(__dirname, "build"),    filename: "static/js/[name].[contenthash:8].js", // 主文件分割出的文件命名    chunkFilename: "static/js/[name].[contenthash:8].chunk.js", // splitChunks 分割出的文件命名  },    optimization: {      minimize: true,      ...      splitChunks: {        chunks: "all",        cacheGroups: {          default: false,          vendors: false,          // 多頁面應(yīng)用,或者 webpack import() 多個 chunk 文件中,有 import 其他模塊兩次或者多次時,會打包生成 common          common: {            chunks: "all",            minChunks: 2,            name: "common",            enforce: true,            priority: 5          },           // node_modules 中的公共模塊抽離          vendor: {            test: /[\\/]node_modules[\\/]/,            chunks: "initial",            enforce: true,            priority: 10,            name: "vendor"          },          // @materual-ui          material: {            name: "chunk-material",            priority: 20, // 優(yōu)先級高于 vendor            test: /[\\/]node_modules[\\/]_?@material-ui(.*)/          },        }      },      runtimeChunk: { // 運行時代碼(webpack執(zhí)行時所需的代碼)從主文件中抽離        name: entrypoint => `runtime-${entrypoint.name}`,      },    },})復(fù)制代碼

4. 在main.js配置項目需要加載的組件

下面是iview的一個例子:

Vue.component("Icon", Icon)Vue.component("Notice", Notice)Vue.component("Button", Button)復(fù)制代碼

這里需要注意全局注冊的組件需要掛在到vue原型上,例如我們需要使用Notice組件,那我就需要

Vue.prototype.$Notice = Notice;復(fù)制代碼

這樣我們就可以正常的使用iview的組件了。

方案三、第三方組件庫UI框架,使用按需引入

1. 借助 babel-plugin-component ,引入我們需要的組件,減少項目體積

npm install babel-plugin-component -D復(fù)制代碼

2. 修改 babel.config.js 的內(nèi)容

//babel.config.js 全文內(nèi)容如下module.exports = {  presets: [    "@vue/cli-plugin-babel/preset"  ],  plugins: [    [      "component",      {        libraryName: "element-ui",        styleLibraryName: "theme-chalk"      }    ]  ]}復(fù)制代碼

3. 創(chuàng)建文件 element.js(名字自定義)

// element.js 全文內(nèi)容如下,按自己需要引入就好了import Vue from "vue"import {    Button,    Form,    FormItem,    Input,    Message,    Container,    Header,    Aside,    Main,    Menu,    Submenu,    MenuItem,    Breadcrumb,    BreadcrumbItem,    Card,    Row,    Col,    Table,    TableColumn,    Switch,    Tooltip,    Pagination,    Dialog,    MessageBox,    Tag,    Tree,    Select,    Option,    Cascader,    Alert,    Tabs,    TabPane} from "element-ui"Vue.use(Button)Vue.use(Form)Vue.use(FormItem)Vue.use(Input)Vue.use(Container)Vue.use(Header)Vue.use(Aside)Vue.use(Main)Vue.use(Menu)Vue.use(Submenu)Vue.use(MenuItem)Vue.use(Breadcrumb)Vue.use(BreadcrumbItem)Vue.use(Card)Vue.use(Row)Vue.use(Col)Vue.use(Table)Vue.use(TableColumn)Vue.use(Switch)Vue.use(Tooltip)Vue.use(Pagination)Vue.use(Dialog)Vue.use(Tag)Vue.use(Tree)Vue.use(Select)Vue.use(Option)Vue.use(Cascader)Vue.use(Alert)Vue.use(Tabs)Vue.use(TabPane)Vue.prototype.$message = MessageVue.prototype.$confirm = MessageBox.confirm復(fù)制代碼

4. 最后在 main.js 中引入這個文件

//main.js 中添加下面這行代碼(路徑和文件名按自己的來)import "./plugins/element.js"復(fù)制代碼

另外在main.js中需要添加以下引入:

import ElementUI from "element-ui";Vue.use(ElementUI);復(fù)制代碼

方案四、使用CDN減小代碼體積加快請求速度

在項目開發(fā)中,我們會用到很多第三方庫,如果可以按需引入,我們可以只引入自己需要的組件,來減少所占空間,

但也會有一些不能按需引入,我們可以采用CDN外部加載,在index.html中從CDN引入組件,去掉其他頁面的組件import,修改webpack.base.config.js,在externals中加入該組件,這是為了避免編譯時找不到組件報錯。

所以使用CDN主要解決兩個問題:

??打包時間太長、打包后代碼體積太大,請求慢????服務(wù)器網(wǎng)絡(luò)不穩(wěn)帶寬不高,使用cdn可以回避服務(wù)器帶寬問題??

我們將vue,vue-router,vuex,axios,echarts,element,moment使用CDN資源引入。國內(nèi)的CDN服務(wù)推薦使用??bootCDN??

1. 在index.html里引入線上cdn,為了方便后續(xù)引用其他資源的cdn,所以我們需要把index.html里面的引入cdn配置成動態(tài)的。

  ...          <% for (var i in    htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>    <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>    <% } %>  復(fù)制代碼

2. 在vue.config.js配置中配置是為了在加載的時候,引用cdn資源 而不是node_modules里的包

....const externals = {  vue: "Vue",  "vue-router": "VueRouter",  vuex: "Vuex",  axios: "axios"}....configureWebpack: config => {    if (isProduction) { // 判斷是否是生產(chǎn)環(huán)境的包      Object.assign(config, {        externals: externals      })    }    config.module.unknownContextCritical = false  }復(fù)制代碼

同時注銷掉main.js文件當(dāng)中的import

方案五、gzip打包,nginx開啟gzip壓縮

1、gizp壓縮是一種http請求優(yōu)化方式,通過減少文件體積來提高加載速度。html、js、css文件甚至json數(shù)據(jù)都可以用它壓縮,可以減小60%以上的體積。

2、之后就是nginx配合開啟gzip模式,這個比較簡單,只要你對nginx有一點了解,我們在nginx.conf中的http中配置一些代碼。

??compression-webpack-plugin??這個依賴在??npm run build??是會生成.gz文件。之后項目訪問的文件就是這個.gz文件,正常的項目打包體積會減少一半還要多,先看下打包后的文件:

1. webpack安裝??compression-webpack-plugin???插件,新版本有問題的話下載1.1.12版本配置vue.config.js

npm i compression-webpack-plugin --save-dev復(fù)制代碼

vue.config.js配置:

const CompressionWebpackPlugin = require("compression-webpack-plugin")configureWebpack:{    plugins:[      new CompressionWebpackPlugin({        filename: "[path].gz[query]",        algorithm: "gzip",        // test: /\.js$|\.html$|\.json$|\.css/,        test: /\.js$|\.json$|\.css/,        threshold: 10240, // 只有大小大于該值的資源會被處理        minRatio: 0.8, // 只有壓縮率小于這個值的資源才會被處理        // deleteOriginalAssets: true // 刪除原文件      })    ],復(fù)制代碼

2. 需要nginx服務(wù)器,更改nginx.conf文件, 加在如圖所示位置

# 開啟gzip。gzip on# 開啟后如果能找到 .gz 文件,直接返回該文件,不會啟用服務(wù)端壓縮。gzip_static on# 文件大于指定 size 才壓縮,以 kb 為單位。gzip_min_length 1;# 用于識別http協(xié)議的版本,早期的瀏覽器不支持gzip壓縮,用戶會看到亂碼,所以為了支持前期版本加了此選項,目前此項基本可以忽略gzip_http_version 1.1;# 壓縮級別,1-9,值越大壓縮比越大,但更加占用 CPU,且壓縮效率越來越低。gzip_comp_level 9;# 壓縮的文件類型。gzip_types text/css application/javascript application/json;root /dist;復(fù)制代碼

修改配置后重新加載生效:??nginx -s reload??

3. 驗證是否配置成功

這步就很簡單了只需要查看chunk類文件的Response Headers的Content-Encoding是否是gzip即可

方案六、打包文件中去掉map文件

打包的app.js過大,另外還有一些生成的map文件。先將多余的map文件去掉,找到config文件夾下index.js文件,把這個build里面的productionSourceMap改成false即可。

/*   Source Maps   */    productionSourceMap: true,  // 把這邊的true改為false    // https://webpack.js.org/configuration/devtool/#production    devtool: "#source-map",復(fù)制代碼

方案七、壓縮代碼,移除console.log

npm install uglifyjs-webpack-plugin --save-dev復(fù)制代碼

配置vue.config.js:

const isProduction = process.env.NODE_ENV === "production";const UglifyJsPlugin = require("uglifyjs-webpack-plugin")chainWebpack(config) {    const plugins = [];    if (isProduction) {      plugins.push(        new UglifyJsPlugin({          uglifyOptions: {            output: {              comments: false, // 去掉注釋            },            warnings: false,            compress: {              drop_console: true,              drop_debugger: false,              pure_funcs: ["console.log"]//移除console            }          }        })      )    }  }復(fù)制代碼

方案八、打包通過image-webpack-loader插件對圖片壓縮優(yōu)化

vue正常打包之后一些圖片文件很大,使打包體積很大,通過??image-webpack-loader??插件可將大的圖片進行壓縮從而縮小打包體積.

1. 先安裝webpack依賴插件image-webpack-loader

npm install image-webpack-loader --save-dev復(fù)制代碼

2. 在vue.config.js中module.exports修改

module.exports = {  productionSourceMap: false,  chainWebpack: config => {    // ============壓縮圖片 start============    config.module      .rule("images")      .use("image-webpack-loader")      .loader("image-webpack-loader")      .options({ bypassOnDebug: true })      .end()    // ============壓縮圖片 end============  }}復(fù)制代碼

方案十、公共代碼的抽離

在vue.config.js module.exports configureWebpack 里面新增,直接放在gzip壓縮下邊即可

...// 公共代碼抽離configureWebpack: config => {  config.optimization = {    splitChunks: {      cacheGroups: {        vendor: {          chunks: "all",          test: /node_modules/,          name: "vendor",          minChunks: 1,          maxInitialRequests: 5,          minSize: 0,          priority: 100        },        common: {          chunks: "all",          test: /[\\/]src[\\/]js[\\/]/,          name: "common",          minChunks: 2,          maxInitialRequests: 5,          minSize: 0,          priority: 60        },        styles: {          name: "styles",          test: /\.(sa|sc|c)ss$/,          chunks: "all",          enforce: true        },        runtimeChunk: {          name: "manifest"        }      }    }  }}...復(fù)制代碼

方案十一、Vue預(yù)渲染(Prerendering)

服務(wù)器端渲染 vs 預(yù)渲染 (SSR vs Prerendering)

如果你調(diào)研服務(wù)器端渲染(SSR)只是用來改善少數(shù)營銷頁面(例如 /, /about, /contact 等)的??SEO??,那么你可能需要預(yù)渲染。無需使用??web??服務(wù)器實時動態(tài)編譯 HTML,而是使用預(yù)渲染方式,在構(gòu)建時簡單地生成針對特定路由的靜態(tài)HTML文件。優(yōu)點是設(shè)置預(yù)渲染更簡單,并可以將你的前端作為一個完全靜態(tài)的站點。

如果你使用webpack,你可以使用??prerender-spa-plugin??輕松地添加預(yù)渲染。它已經(jīng)被??Vue??應(yīng)用程序廣泛測試 - 事實上,作者是??Vue??核心團隊的成員。

三種不同渲染方式的區(qū)別:

客戶端渲染:用戶訪問url,請求html文件,前端根據(jù)路由動態(tài)渲染頁面內(nèi)容。關(guān)鍵鏈路較長,有一定的白屏?xí)r間;

服務(wù)端渲染:用戶訪問url,服務(wù)端根據(jù)訪問路徑請求所需數(shù)據(jù),拼接成 html 字符串,返回給前端。前端接收到html時已有當(dāng)前url下的完整頁面;

預(yù)渲染:構(gòu)建階段生成匹配預(yù)渲染路徑的html文件(注意:每個需要預(yù)渲染的路由都有一個對應(yīng)的 html)。構(gòu)建出來的html文件已經(jīng)有靜態(tài)數(shù)據(jù),需要ajax數(shù)據(jù)的部分未構(gòu)建。

預(yù)渲染解決的問題:

SEO:單頁應(yīng)用的網(wǎng)站內(nèi)容是根據(jù)當(dāng)前路徑動態(tài)渲染的,html文件中往往沒有內(nèi)容,網(wǎng)絡(luò)爬蟲不會等到頁面腳本執(zhí)行完再抓取;

弱網(wǎng)環(huán)境:當(dāng)用戶在一個弱環(huán)境中訪問你的站點時,你會想要盡可能快的將內(nèi)容呈現(xiàn)給他們。甚至是在 js 腳本被加載和解析前;

低版本瀏覽器:用戶的瀏覽器可能不支持你使用的js特性,預(yù)渲染或服務(wù)端渲染能夠讓用戶至少能夠看到首屏的內(nèi)容,而不是一個空白的網(wǎng)頁。

Vue預(yù)渲染實現(xiàn)流程

1. ??prerender-spa-plugin???安裝,建議使用淘寶鏡像的cnpm,因為npm安裝經(jīng)常失敗,否則會出現(xiàn)意向不到的問題,并且效率低,比較浪費時間。

cnpm install prerender-spa-plugin --save-dev復(fù)制代碼

2. vue.config.js配置文件

const path = require("path");const PrerenderSPAPlugin = require("prerender-spa-plugin");const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;在plugins下面,找到plugins對象,直接加到上面就行// 預(yù)渲染配置new PrerenderSPAPlugin({    //要求-給的WebPack-輸出應(yīng)用程序的路徑預(yù)渲染。    staticDir: path.join(__dirname, "dist"),    //必需,要渲染的路線。    routes: ["/login"],    //必須,要使用的實際渲染器,沒有則不能預(yù)編譯    renderer: new Renderer({        inject: {            foo: "bar"        },        headless: false, //渲染時顯示瀏覽器窗口。對調(diào)試很有用。          //等待渲染,直到檢測到指定元素。        //例如,在項目入口使用`document.dispatchEvent(new Event("custom-render-trigger"))`         renderAfterDocumentEvent: "render-event"    })})復(fù)制代碼

3. router.js配置文件,需要把vue的router模式設(shè)置成history模式

4. main.js文件的修改

在創(chuàng)建vue實例的mounted里面加一個事件,跟PrerenderSPAPlugin實例里面的renderAfterDocumentEvent對應(yīng)上。

mounted () {    document.dispatchEvent(new Event("render-event")) }復(fù)制代碼

這是預(yù)渲染的基本配置,npm run build 一下,如果dist文件夾多了你想預(yù)渲染的文件夾,那么恭喜你,成功了!如果項目是用nginx做的代理,nginx還需要做一些配置,具體是:

location = / {  try_files /home/index.html /index.html;}location / {  try_files $uri $uri/ /index.html;}復(fù)制代碼

5. 相關(guān)頁面情況

完整項目附件:??點此下載??

標(biāo)簽: 我們需要 文件命名 應(yīng)用程序

上一篇:全球快訊:如何制作一個羊了個羊游戲5:快速拾取
下一篇:自定義函數(shù)打印100到200間的素數(shù)