Vue項目性能優(yōu)化實戰(zhàn)

前言

Vue 框架通過數(shù)據(jù)雙向綁定和虛擬 DOM 技術(shù),幫我們處理了前端開發(fā)中最臟最累的 DOM 操作部分苹祟, 我們不再需要去考慮如何操作 DOM 以及如何最高效地操作 DOM;但 Vue 項目中仍然存在項目首屏優(yōu)化闰非、Webpack 編譯配置優(yōu)化等問題,所以我們?nèi)匀恍枰リP(guān)注 Vue 項目性能方面的優(yōu)化,使項目具有更高效的性能彩匕、更好的用戶體驗。

本文是作者通過實際項目的優(yōu)化實踐進(jìn)行總結(jié)而來媒区,希望讀者讀完本文驼仪,有一定的啟發(fā)思考,從而對自己的項目進(jìn)行優(yōu)化起到幫助袜漩。本文內(nèi)容分為以下三部分組成:

  • Vue 代碼層面的優(yōu)化绪爸;
  • webpack 配置層面的優(yōu)化;
  • 基礎(chǔ)的 Web 技術(shù)層面的優(yōu)化宙攻。

一奠货、Vue 代碼層面的優(yōu)化

1.1、v-if 和 v-show 區(qū)分使用場景

v-if真正 的條件渲染座掘,因為它會確保在切換過程中條件塊內(nèi)的事件監(jiān)聽器和子組件適當(dāng)?shù)乇讳N毀和重建仇味;也是惰性的:如果在初始渲染時條件為假,則什么也不做——直到條件第一次變?yōu)檎鏁r雹顺,才會開始渲染條件塊丹墨。

v-show 就簡單得多, 不管初始條件是什么嬉愧,元素總是會被渲染贩挣,并且只是簡單地基于 CSS 的 display 屬性進(jìn)行切換。

所以没酣,v-if 適用于在運(yùn)行時很少改變條件王财,不需要頻繁切換條件的場景;v-show 則適用于需要非常頻繁切換條件的場景裕便。

1.2绒净、computed 和 watch 區(qū)分使用場景

computed: 是計算屬性,依賴其它屬性值偿衰,并且 computed 的值有緩存挂疆,只有它依賴的屬性值發(fā)生改變,下一次獲取 computed 的值時才會重新計算 computed 的值下翎;
watch: 更多的是「觀察」的作用缤言,類似于某些數(shù)據(jù)的監(jiān)聽回調(diào) ,每當(dāng)監(jiān)聽的數(shù)據(jù)變化時都會執(zhí)行回調(diào)進(jìn)行后續(xù)操作视事;

運(yùn)用場景

  • 當(dāng)我們需要進(jìn)行數(shù)值計算胆萧,并且依賴于其它數(shù)據(jù)時,應(yīng)該使用 computed俐东,因為可以利用 computed 的緩存特性跌穗,避免每次獲取值時订晌,都要重新計算;
  • 當(dāng)我們需要在數(shù)據(jù)變化時執(zhí)行異步或開銷較大的操作時蚌吸,應(yīng)該使用 watch腾仅,使用 watch 選項允許我們執(zhí)行異步操作 ( 訪問一個 API ),限制我們執(zhí)行該操作的頻率套利,并在我們得到最終結(jié)果前,設(shè)置中間狀態(tài)鹤耍。這些都是計算屬性無法做到的肉迫。
1.3、v-for 遍歷必須為 item 添加 key稿黄,且避免同時使用 v-if
(1)v-for 遍歷必須為 item 添加 key

在列表數(shù)據(jù)進(jìn)行遍歷渲染時喊衫,需要為每一項 item 設(shè)置唯一 key 值,方便 Vue.js 內(nèi)部機(jī)制精準(zhǔn)找到該條列表數(shù)據(jù)杆怕。當(dāng) state 更新時族购,新的狀態(tài)值和舊的狀態(tài)值對比,較快地定位到 diff 陵珍。

(2)v-for 遍歷避免同時使用 v-if

v-for 比 v-if 優(yōu)先級高寝杖,如果每一次都需要遍歷整個數(shù)組,將會影響速度互纯,尤其是當(dāng)之需要渲染很小一部分的時候瑟幕,必要情況下應(yīng)該替換成 computed 屬性。

推薦

<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id">
    {{ user.name }}
  </li>
</ul>
computed: {
  activeUsers: function () {
    return this.users.filter(function (user) {
     return user.isActive
    })
  }
}

不推薦

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id">
    {{ user.name }}
  </li>
</ul>
1.4留潦、長列表性能優(yōu)化

Vue 會通過 Object.defineProperty 對數(shù)據(jù)進(jìn)行劫持只盹,來實現(xiàn)視圖響應(yīng)數(shù)據(jù)的變化,然而有些時候我們的組件就是純粹的數(shù)據(jù)展示兔院,不會有任何改變殖卑,我們就不需要 Vue 來劫持我們的數(shù)據(jù),在大量數(shù)據(jù)展示的情況下坊萝,這能夠很明顯的減少組件初始化的時間孵稽,那如何禁止 Vue 劫持我們的數(shù)據(jù)呢?可以通過 Object.freeze 方法(自行百度使用方法)來凍結(jié)一個對象十偶,一旦被凍結(jié)的對象就再也不能被修改了肛冶。

export default {
  data: () => ({
    users: {}
  }),
  async mounted() {
    const users = await axios.get("/api/users");
    this.users = Object.freeze(users);
  }
};
1.5、事件的銷毀

Vue 組件銷毀時扯键,會自動清理它與其它實例的連接睦袖,解綁它的全部指令及事件監(jiān)聽器,但是僅限于組件本身的事件荣刑。如果在 js 內(nèi)使用 addEventListener 等方式是不會自動銷毀的馅笙,我們需要在組件銷毀時手動移除這些事件的監(jiān)聽伦乔,以免造成內(nèi)存泄露,如:

created() {
  addEventListener('click', this.click, false)
},
beforeDestroy() {
  removeEventListener('click', this.click, false)
}
1.6董习、圖片資源懶加載

對于圖片過多的頁面烈和,為了加速頁面加載速度,所以很多時候我們需要將頁面內(nèi)未出現(xiàn)在可視區(qū)域內(nèi)的圖片先不做加載皿淋, 等到滾動到可視區(qū)域后再去加載招刹。這樣對于頁面加載性能上會有很大的提升,也提高了用戶體驗窝趣。我們在項目中使用 Vue 的 vue-lazyload 插件:

(1)安裝插件

npm/cnpm install vue-lazyload --save-dev

(2)在入口文件main.js中引入并使用

import VueLazyload from 'vue-lazyload'

然后再 vue 中直接使用

Vue.use(VueLazyload)

或者添加自定義選項

Vue.use(VueLazyload, {
  preLoad: 1.3,
  error: 'dist/error.png',
  loading: 'dist/loading.gif',
  attempt: 1
})

(3)在 vue 文件中將 img 標(biāo)簽的 src 屬性直接改為 v-lazy 疯暑,從而將圖片顯示方式更改為懶加載顯示:

<img v-lazy="/static/img/jay.png">

奉上GitHub地址:vue-lazyload

1.7、路由懶加載

Vue 是單頁面應(yīng)用哑舒,可能會有很多的路由引入 妇拯,這樣使用 webpcak 打包后的文件很大,當(dāng)進(jìn)入首頁時洗鸵,加載的資源過多越锈,頁面會出現(xiàn)白屏的情況,不利于用戶體驗膘滨。如果我們能把不同路由對應(yīng)的組件分割成不同的代碼塊甘凭,然后當(dāng)路由被訪問的時候才加載對應(yīng)的組件,這樣就更加高效了火邓。這樣會大大提高首屏顯示的速度对蒲,但是可能其他的頁面的速度就會降下來。

const Foo = () => import('./Foo.vue')
const router = new VueRouter({
  routes: [
    { path: '/foo', component: Foo }
  ]
})

二贡翘、Webpack層面的優(yōu)化

2.1蹈矮、Webpack 對圖片進(jìn)行壓縮

在 vue 項目中除了可以在 webpack.base.conf.js 中 url-loader 中設(shè)置 limit 大小來對圖片處理,對小于 limit 的圖片轉(zhuǎn)化為 base64 格式鸣驱,其余的不做操作泛鸟。所以對有些較大的圖片資源,在請求資源的時候踊东,加載會很慢北滥,我們可以用 image-webpack-loader來壓縮圖片:

(1)首先,安裝 image-webpack-loader

npm/cnpm install image-webpack-loader --save-dev

(2)然后闸翅,在 webpack.base.conf.js 中進(jìn)行配置

{
  test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
  use:[
    {
    loader: 'url-loader',
    options: {
      limit: 10000,
      name: utils.assetsPath('img/[name].[hash:7].[ext]')
      }
    },
    {
      loader: 'image-webpack-loader',
      options: {
        bypassOnDebug: true,
      }
    }
  ]
}
2.2再芋、減少 ES6 轉(zhuǎn)為 ES5 的冗余代碼

Babel 插件會在將 ES6 代碼轉(zhuǎn)換成 ES5 代碼時會注入一些輔助函數(shù),例如下面的 ES6 代碼:

//在此藏一個彩蛋
//就在上一秒鐘 這個月的工資入賬了
//第一個發(fā)現(xiàn)這個彩蛋并評論的同學(xué)
//我給你發(fā)個紅包
//好了 繼續(xù)
class HelloWebpack extends Component{...}

這段代碼再被轉(zhuǎn)換成能正常運(yùn)行的 ES5 代碼時需要以下兩個輔助函數(shù):

babel-runtime/helpers/createClass  // 用于實現(xiàn) class 語法
babel-runtime/helpers/inherits  // 用于實現(xiàn) extends 語法    

在默認(rèn)情況下坚冀, Babel 會在每個輸出文件中內(nèi)嵌這些依賴的輔助函數(shù)代碼济赎,如果多個源代碼文件都依賴這些輔助函數(shù),那么這些輔助函數(shù)的代碼將會出現(xiàn)很多次,造成代碼冗余司训。為了不讓這些輔助函數(shù)的代碼重復(fù)出現(xiàn)构捡,可以在依賴它們時通過 require('babel-runtime/helpers/createClass') 的方式導(dǎo)入,這樣就能做到只讓它們出現(xiàn)一次壳猜。babel-plugin-transform-runtime 插件就是用來實現(xiàn)這個作用的勾徽,將相關(guān)輔助函數(shù)進(jìn)行替換成導(dǎo)入語句,從而減小 babel 編譯出來的代碼的文件大小统扳。

(1)首先喘帚,安裝 babel-plugin-transform-runtime

npm/cnpm install babel-plugin-transform-runtime --save-dev

(2)然后,修改 .babelrc 配置文件為

"plugins": [
    "transform-runtime"
]

如果要看插件的更多詳細(xì)內(nèi)容咒钟,可以查看babel-plugin-transform-runtime 的 詳細(xì)介紹吹由。

2.3、提取公共代碼

如果項目中沒有去將每個頁面的第三方庫和公共模塊提取出來盯腌,則項目會存在以下問題:

  • 相同的資源被重復(fù)加載,浪費用戶的流量和服務(wù)器的成本陨瘩。
  • 每個頁面需要加載的資源太大腕够,導(dǎo)致網(wǎng)頁首屏加載緩慢,影響用戶體驗舌劳。

所以我們需要將多個頁面的公共代碼抽離成單獨的文件帚湘,來優(yōu)化以上問題 。Webpack 內(nèi)置了專門用于提取多個Chunk 中的公共部分的插件 CommonsChunkPlugin甚淡,我們在項目中 CommonsChunkPlugin 的配置如下:

// 所有在 package.json 里面依賴的包大诸,都會被打包進(jìn) vendor.js 這個文件中。
new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: function(module, count) {
    return (
      module.resource &&
      /\.js$/.test(module.resource) &&
      module.resource.indexOf(
        path.join(__dirname, '../node_modules')
      ) === 0
    );
  }
}),
// 抽取出代碼模塊的映射關(guān)系
new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  chunks: ['vendor']
})
2.4贯卦、模板預(yù)編譯

當(dāng)使用 DOM 內(nèi)模板或 JavaScript 內(nèi)的字符串模板時资柔,模板會在運(yùn)行時被編譯為渲染函數(shù)。通常情況下這個過程已經(jīng)足夠快了撵割,但對性能敏感的應(yīng)用還是最好避免這種用法贿堰。

預(yù)編譯模板最簡單的方式就是使用單文件組件——相關(guān)的構(gòu)建設(shè)置會自動把預(yù)編譯處理好,所以構(gòu)建好的代碼已經(jīng)包含了編譯出來的渲染函數(shù)而不是原始的模板字符串啡彬。

如果你使用 webpack羹与,并且喜歡分離 JavaScript 和模板文件,你可以使用 vue-template-loader庶灿,它也可以在構(gòu)建過程中把模板文件轉(zhuǎn)換成為 JavaScript 渲染函數(shù)纵搁。

2.5、提取組件的 CSS

當(dāng)使用單文件組件時往踢,組件內(nèi)的 CSS 會以 style 標(biāo)簽的方式通過 JavaScript 動態(tài)注入腾誉。這有一些小小的運(yùn)行時開銷,如果你使用服務(wù)端渲染,這會導(dǎo)致一段 “無樣式內(nèi)容閃爍 (fouc) ” 妄辩。將所有組件的 CSS 提取到同一個文件可以避免這個問題惑灵,也會讓 CSS 更好地進(jìn)行壓縮和緩存。

查閱這個構(gòu)建工具各自的文檔來了解更多:

  • webpack + vue-loader ( vue-cli 的 webpack 模板已經(jīng)預(yù)先配置好)
  • Browserify + vueify
  • Rollup + rollup-plugin-vue
2.6眼耀、Vue 項目的編譯優(yōu)化

如果你的 Vue 項目使用 Webpack 編譯英支,需要你喝一杯咖啡的時間,那么也許你需要對項目的 Webpack 配置進(jìn)行優(yōu)化哮伟,提高 Webpack 的構(gòu)建效率干花。具體如何進(jìn)行 Vue 項目的 Webpack 構(gòu)建優(yōu)化,請自行百度Webpack 優(yōu)化實踐楞黄,本文不再敘述池凄。

三、基礎(chǔ)的 Web 技術(shù)優(yōu)化

3.1鬼廓、開啟 gzip 壓縮

gzip 是 GNUzip 的縮寫肿仑,最早用于 UNIX 系統(tǒng)的文件壓縮。HTTP 協(xié)議上的 gzip 編碼是一種用來改進(jìn) web 應(yīng)用程序性能的技術(shù)碎税,web 服務(wù)器和客戶端(瀏覽器)必須共同支持 gzip尤慰。目前主流的瀏覽器,Chrome雷蹂,firefox伟端,IE等都支持該協(xié)議。
常見的服務(wù)器如 Apache匪煌,Nginx责蝠,IIS 同樣支持,gzip 壓縮效率非常高萎庭,通乘剑可以達(dá)到 70% 的壓縮率,也就是說驳规,如果你的網(wǎng)頁有 30K支子,壓縮之后就變成了 9K 左右
以下我們以服務(wù)端使用我們熟悉的 express 為例,開啟 gzip 非常簡單达舒,相關(guān)步驟如下:

  • 安裝
npm/cnpm install compression --save
  • 添加代碼邏輯
var compression = require('compression');
var app = express();
app.use(compression())
  • 重啟服務(wù)值朋,觀察網(wǎng)絡(luò)面板里面的 response header,如果看到如下紅圈里的字段則表明 gzip 開啟成功


    image.png
3.2巩搏、瀏覽器緩存

為了提高用戶加載頁面的速度昨登,對靜態(tài)資源進(jìn)行緩存是非常必要的,根據(jù)是否需要重新向服務(wù)器發(fā)起請求來分類贯底,將 HTTP 緩存規(guī)則分為兩大類(強(qiáng)制緩存丰辣,對比緩存)撒强,如果對緩存機(jī)制還不是了解很清楚的,可以參考關(guān)于 HTTP 緩存的文章笙什,這里不再贅述飘哨。

3.3、CDN 的使用

瀏覽器從服務(wù)器上下載 CSS琐凭、js 和圖片等文件時都要和服務(wù)器連接芽隆,而大部分服務(wù)器的帶寬有限,如果超過限制统屈,網(wǎng)頁就半天反應(yīng)不過來胚吁。而 CDN 可以通過不同的域名來加載文件,從而使下載文件的并發(fā)連接數(shù)大大增加愁憔,且CDN 具有更好的可用性腕扶,更低的網(wǎng)絡(luò)延遲和丟包率。

總結(jié)

本文通過以下三部分組成:Vue 代碼層面的優(yōu)化吨掌、webpack 配置層面的優(yōu)化半抱、基礎(chǔ)的 Web 技術(shù)層面的優(yōu)化;來介紹怎么去優(yōu)化 Vue 項目的性能膜宋。希望對讀完本文的你有幫助窿侈、有啟發(fā),如果有不足之處激蹲,歡迎批評指正交流棉磨。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末江掩,一起剝皮案震驚了整個濱河市学辱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌环形,老刑警劉巖策泣,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抬吟,居然都是意外死亡萨咕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門火本,熙熙樓的掌柜王于貴愁眉苦臉地迎上來危队,“玉大人,你說我怎么就攤上這事钙畔∶B剑” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵擎析,是天一觀的道長簿盅。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么桨醋? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任棚瘟,我火速辦了婚禮,結(jié)果婚禮上喜最,老公的妹妹穿的比我還像新娘偎蘸。我一直安慰自己,他們只是感情好返顺,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布禀苦。 她就那樣靜靜地躺著,像睡著了一般遂鹊。 火紅的嫁衣襯著肌膚如雪振乏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天秉扑,我揣著相機(jī)與錄音慧邮,去河邊找鬼。 笑死舟陆,一個胖子當(dāng)著我的面吹牛误澳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秦躯,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼忆谓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了踱承?” 一聲冷哼從身側(cè)響起倡缠,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茎活,沒想到半個月后昙沦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡载荔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年盾饮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片懒熙。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡丘损,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出工扎,到底是詐尸還是另有隱情徘钥,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布定庵,位于F島的核電站吏饿,受9級特大地震影響踪危,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜猪落,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一贞远、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧笨忌,春花似錦蓝仲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至途凫,卻和暖如春垢夹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背维费。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工果元, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人犀盟。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓而晒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親阅畴。 傳聞我的和親對象是個殘疾皇子倡怎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 主要分為三個方面來優(yōu)化 Vue 代碼層面的優(yōu)化 webpack 配置層面的優(yōu)化 基礎(chǔ)的 Web 技術(shù)層面的優(yōu)化 一...
    alanwhy閱讀 791評論 0 10
  • ## 框架和庫的區(qū)別?> 框架(framework):一套完整的軟件設(shè)計架構(gòu)和**解決方案**。> > 庫(lib...
    Rui_bdad閱讀 2,906評論 1 4
  • 33贱枣、JS中的本地存儲 把一些信息存儲在當(dāng)前瀏覽器指定域下的某一個地方(存儲到物理硬盤中)1监署、不能跨瀏覽器傳輸:在...
    萌妹撒閱讀 2,080評論 0 2
  • 一、 組件component 1. 什么是組件冯事? 組件(Component)是 Vue.js 最強(qiáng)大的功能之一焦匈。組...
    饑人谷_Leonardo閱讀 1,964評論 0 18
  • 響應(yīng)式布局的理解 響應(yīng)式開發(fā)目的是一套代碼可以在多種終端運(yùn)行,適應(yīng)不同屏幕的大小,其原理是運(yùn)用媒體查詢,在不同屏幕...
    懶貓_6500閱讀 785評論 0 0