
隨著前端各種框架的日益完善,一些基本的性能優(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的組件了。
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ù)制代碼
在項目開發(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
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即可
打包的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ù)制代碼
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ù)制代碼
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ù)制代碼
如果你調(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)頁。
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)用程序