[轉(zhuǎn)]Vue 全家桶宙枷、原理及優(yōu)化建議

本文轉(zhuǎn)自公眾號(hào) 藝述思維

不少互聯(lián)網(wǎng)公司都在使用vue技術(shù)棧钙畔,或稱為vue全家桶。

使用過(guò)vue的程序員一般這樣評(píng)價(jià)它仗岖,“vue.js兼具angular.js和react.js的優(yōu)點(diǎn)”逃延。Vue.js 是一個(gè)JavaScript MVVM(Model-View-ViewModel)庫(kù),用于漸近式構(gòu)建用戶界面轧拄。它以數(shù)據(jù)驅(qū)動(dòng)和組件化思想構(gòu)建揽祥,采用自底向上增量開發(fā)的設(shè)計(jì)思想。相比Angular.js檩电,Vue.js API更加簡(jiǎn)潔拄丰;相比 React + Redux 復(fù)雜的架構(gòu),Vue.js 上手更加容易俐末。

目錄

一料按、vue全家桶包括什么

vue-router路由

vuex

vue-resource

構(gòu)建工具vue-cli

調(diào)度工具Devtools

關(guān)于UI組件庫(kù)

二、vue工程目錄結(jié)構(gòu)

編輯器

三卓箫、vue使用簡(jiǎn)介

數(shù)據(jù)代理

vue實(shí)例生命周期圖解

四载矿、vue的運(yùn)行原理

雙向綁定圖解

模板是如何解析的

五、發(fā)布前優(yōu)化

UI組件按需加載

路由懶加載

使用異步組件(動(dòng)態(tài)組件)

圖片壓縮與合并

使用CDN加速vue類庫(kù)

壓縮代碼

v-for和v-if不要同時(shí)使用

使用Object.freeze凍結(jié)大數(shù)據(jù)

使用Keep-alive標(biāo)簽優(yōu)化組件創(chuàng)建

使用Set

在scope中少用元素選擇器

關(guān)于template的優(yōu)化

一烹卒、vue全家桶包括什么

vue-router路由

網(wǎng)站:http://router.vuejs.org闷盔。使用npm工具來(lái)安裝vue-router

npm install vue-router

通過(guò)import導(dǎo)入Vue模塊弯洗、vue-router模塊及其它組件。

import Vue from’vue’

importRouter from’vue-router’

在使用路由前逢勾,必須要通過(guò) Vue.use() 明確地安裝路由功能牡整。

Vue.use(Router)

通過(guò)const router= new VueRouter()定義路由,并傳入對(duì)應(yīng)的配置敏沉,包括路徑path和組件components等果正。

image

在使用newVue來(lái)創(chuàng)建和掛載vue根實(shí)例的時(shí)候,記得要通過(guò) router配置參數(shù)注入路由盟迟。使用router-link:

image

有兩種模式:

  • hash 模式

  • history 模式

vuex

網(wǎng)站:http://vuex.vuejs.org

在vue開發(fā)實(shí)戰(zhàn)中秋泳,多個(gè)組件共享數(shù)據(jù)時(shí),單向數(shù)據(jù)流的簡(jiǎn)潔性很容易被破壞攒菠。為解決多個(gè)視圖使用同一數(shù)據(jù)及多個(gè)視圖驅(qū)動(dòng)同一數(shù)據(jù)更新的問題迫皱,vuex應(yīng)運(yùn)而生。

當(dāng)網(wǎng)站足夠大時(shí)辖众,一個(gè)狀態(tài)樹下卓起,根的部分字段繁多,解決這個(gè)問題就要模塊化 vuex凹炸,官網(wǎng)提供了模塊化方案戏阅,允許我們?cè)诔跏蓟?vuex 的時(shí)候配置 modules。每一個(gè) module 里面又分別包含 state 啤它、action 等奕筐,看似是多個(gè)狀態(tài)樹,其實(shí)還是基于 rootState 的子樹变骡。細(xì)分后整個(gè) state 結(jié)構(gòu)就清晰了离赫,管理起來(lái)也方便許多。

image

Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式塌碌。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài)渊胸,并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。Vuex 的四個(gè)核心概念是:

  • The state tree:Vuex 使用單一狀態(tài)樹台妆,用一個(gè)對(duì)象就包含了全部的應(yīng)用層級(jí)狀態(tài)翎猛。至此它便作為一個(gè)唯一數(shù)據(jù)源(SSOT)而存在。這也意味著接剩,每個(gè)應(yīng)用將僅僅包含一個(gè) store 實(shí)例切厘。單狀態(tài)樹讓我們能夠直接地定位任一特定的狀態(tài)片段,在調(diào)試的過(guò)程中也能輕易地取得整個(gè)當(dāng)前應(yīng)用狀態(tài)的快照搂漠。

  • Getters:用來(lái)從 store 獲取 Vue 組件數(shù)據(jù)迂卢。

  • Mutators:事件處理器用來(lái)驅(qū)動(dòng)狀態(tài)的變化。

  • Actions:可以給組件使用的函數(shù),以此用來(lái)驅(qū)動(dòng)事件處理器 mutations而克。(注:此許或許稱之為EventHandler更為恰當(dāng)靶壮。)

Vuex和簡(jiǎn)單的全局對(duì)象是不同的。當(dāng)Vuex從store中讀取狀態(tài)值的時(shí)候员萍,若狀態(tài)發(fā)生了變化腾降,那么相應(yīng)的組件也會(huì)更新。并且改變store中狀態(tài)的唯一途徑就是提交commit mutations碎绎。只要發(fā)生了狀態(tài)的變化螃壤,一定伴隨著mutation的提交。 例如:

image

通過(guò) store.state 來(lái)獲取狀態(tài)對(duì)象筋帖,以及通過(guò) store.commit 方法觸發(fā)狀態(tài)變更:

image

由于 vuex 的靈活性奸晴,帶來(lái)了編碼不統(tǒng)一的情況,完整的閉環(huán)是 store.dispatch('action') -> action -> commit -> mutation -> getter -> computed日麸,實(shí)際上中間的環(huán)節(jié)有的可以省略寄啼,因?yàn)?API 文檔提供了以下幾個(gè)方法 mapState、mapGetters代箭、mapActions墩划、mapMutations,然后在組件里可以直接調(diào)取任何一步嗡综,還是項(xiàng)目小想怎么調(diào)用都可以乙帮,項(xiàng)目大的時(shí)候,就要考慮 vuex 使用的統(tǒng)一性极景,有人建議是不論多簡(jiǎn)單的流程都跑完整個(gè)閉環(huán)察净,形成代碼的統(tǒng)一,方便后期管理戴陡,在組件里只允許出現(xiàn) dispatch 和 mapGetters塞绿,其余的流程都在名為 store 的 vuex 文件夾里進(jìn)行沟涨。

注:mapGetters 工具函數(shù)會(huì)將 store 中的 getter 映射到局部計(jì)算屬性中恤批。它的功能和 mapState 非常類似。

vue-resource

網(wǎng)站:https://github.com/pagekit/vue-resource

使用npm來(lái)安裝Vue-resource:

$ npm install vue-resource

在安裝并引入vue-resource后裹赴,可以基于全局的Vue對(duì)象使用http喜庞,也可以基于某個(gè)Vue實(shí)例使用http。

image

在發(fā)送請(qǐng)求后棋返,使用then方法來(lái)處理響應(yīng)結(jié)果延都,then方法有兩個(gè)參數(shù),第一個(gè)參數(shù)是響應(yīng)成功時(shí)的回調(diào)函數(shù)睛竣,第二個(gè)參數(shù)是響應(yīng)失敗時(shí)的回調(diào)函數(shù)晰房。

vue-resource的請(qǐng)求API是按照REST風(fēng)格設(shè)計(jì)的,它提供了7種請(qǐng)求API:

· get(url,[options])

· head(url,[options])

· delete(url,[options])

· jsonp(url,[options])

· post(url,[body], [options])

· put(url, [body],[options])

· patch(url,[body], [options])

構(gòu)建工具vue-cli

vue-cli是vue標(biāo)準(zhǔn)的開發(fā)工具。網(wǎng)站:https://cli.vuejs.org/

安裝

npm install -g @vue/cli

最新版本為3.4.0殊者。

創(chuàng)建項(xiàng)目

vue create my-project

以上是命令行創(chuàng)建与境。也可以通過(guò) vue ui 命令以圖形化界面創(chuàng)建和管理項(xiàng)目:

vue ui

運(yùn)行

npm run serve

調(diào)度工具Devtools

vue在調(diào)試方面,可以選擇安裝chrome插件vue Devtools猖吴。打開vue項(xiàng)目摔刁,在調(diào)試vue應(yīng)用的時(shí)候,chrome開發(fā)者工具中會(huì)看一個(gè)vue的一欄海蔽,點(diǎn)擊之后就可以看見當(dāng)前頁(yè)面vue對(duì)象的一些信息共屈。

image

在Devtools工具中,可以選擇組件党窜,查看對(duì)應(yīng)組件內(nèi)的數(shù)據(jù)信息拗引。也可以選擇Vuex選項(xiàng),查看該項(xiàng)目?jī)?nèi)Vuex的狀態(tài)變量信息幌衣。

image

關(guān)于UI組件庫(kù)

可以自己寫寺擂,為提高開發(fā)效率也可以復(fù)用第三方組件庫(kù)。element(https://github.com/ElemeFE/element)是一個(gè)最好支持vue2.0的UI組件庫(kù)泼掠。

二怔软、vue工程目錄結(jié)構(gòu)

這是一個(gè)簡(jiǎn)單的vue項(xiàng)目的大概結(jié)構(gòu):

image
  • components/文件夾:用來(lái)存放Vue 組件。個(gè)人建議择镇,把每一個(gè)組件中使用到的image圖片放置到對(duì)應(yīng)的組件子文件目錄下挡逼,便于統(tǒng)一的管理

  • Node_modules/:npm安裝的該項(xiàng)目的依賴庫(kù)

  • vuex/文件夾:存放的是和 Vuex store 相關(guān)的東西(state對(duì)象,actions腻豌,mutations)

  • router/文件夾:存放的是跟vue-router相關(guān)的路由配置項(xiàng)

  • build/文件:是 webpack 的打包編譯配置文件

  • static/文件夾:存放一些靜態(tài)的家坎、較少變動(dòng)的image或者css文件

  • config/文件夾:存放的是一些配置項(xiàng),比如服務(wù)器訪問的端口配置等

  • dist/該文件夾:一開始是不存在吝梅,在我們的項(xiàng)目經(jīng)過(guò) build 之后才會(huì)產(chǎn)出

  • App.vue根組件虱疏,所有的子組件都將在這里被引用

  • index.html整個(gè)項(xiàng)目的入口文件,將會(huì)引用我們的根組件 App.vue

  • main.js入口文件的 js 邏輯苏携,在webpack 打包之后將被注入到 index.html 中

編輯器

VSCode with Vetur

image

三做瞪、vue使用簡(jiǎn)介

數(shù)據(jù)代理

每個(gè) Vue.js 應(yīng)用都是通過(guò)構(gòu)造函數(shù) Vue 創(chuàng)建一個(gè) Vue 的根實(shí)例 啟動(dòng)的。每個(gè) Vue 實(shí)例都會(huì)代理其 data 對(duì)象里所有的屬性:

var data = { a: 1 }

var vm = new Vue({

data: data

})

vm.a === data.a // -> true

設(shè)置新值也會(huì)同步影響:

vm.a = 2

data.a // -> 2

// ... 反之亦然

data.a = 3

vm.a // -> 3

實(shí)現(xiàn)數(shù)據(jù)代理的偽代碼如下:

var self = this; // this為vue實(shí)例右冻, 即vm

Object.keys(this.data).forEach(function(key) {

Object.defineProperty(this, key, {    // this.title, 即vm.title

    enumerable: false,

    configurable: true,

    get: function getter () {

        return self.data[key];   //觸發(fā)對(duì)應(yīng)data[key]的getter

    },

    set: function setter (newVal) {

        self.data[key] = newVal;  //觸發(fā)對(duì)應(yīng)data[key]的setter

    }

});

}

Vue 實(shí)例暴露了一些有用的實(shí)例屬性與方法装蓬。這些屬性與方法都有前綴 $,以便與代理的 data 屬性區(qū)分纱扭。例如:

vm.$data === data // -> true

vm.$el === document.getElementById('example') // -> true

vue實(shí)例生命周期圖解

image

四牍帚、vue的運(yùn)行原理

Vue采用簡(jiǎn)潔的模板語(yǔ)法,以聲明的方式將數(shù)據(jù)渲染進(jìn) DOM乳蛾。vue代碼是沒有辦法直接被瀏覽器解析的暗赶,必須經(jīng)過(guò)“編譯”鄙币,變?yōu)闉g覽器可以識(shí)別為html、js與css代碼蹂随。這種聲明式開發(fā)方式把方便留給了程序員爱榔,轉(zhuǎn)換工作交給了自動(dòng)化工具。

image

注:el是element的縮寫糙及,指Vue實(shí)例掛載的元素節(jié)點(diǎn)详幽。

雙向綁定圖解

一般說(shuō)的雙向綁定,指:

  • 數(shù)據(jù)變動(dòng) --> 視圖更新

  • 視圖更新 --> 數(shù)據(jù)變動(dòng)

視圖更新 --> 數(shù)據(jù)變動(dòng)浸锨,這個(gè)方向的綁定比較簡(jiǎn)單唇聘。主要通過(guò)事件監(jiān)聽來(lái)改變數(shù)據(jù),比如input控件可以監(jiān)聽input事件柱搜,一旦事件觸發(fā)迟郎,調(diào)用JS改變data。

image

模型層(model)只是普通 JavaScript 對(duì)象聪蘸,修改它宪肖,DOM本是不能更新的。當(dāng)程序員把一個(gè)普通 JavaScript 對(duì)象傳給 Vue 實(shí)例的 data 選項(xiàng)健爬,Vue 將遍歷此對(duì)象所有的屬性控乾,并使用 Object.defineProperty 把這些屬性全部轉(zhuǎn)為 getter/setter。在每個(gè)setter中娜遵,可以做許多事件蜕衡,使表面看起來(lái)數(shù)據(jù)變了,視圖就更新了。并且這種數(shù)據(jù)更新,和原來(lái)一樣解恰,只是 vm.a=123 這樣的簡(jiǎn)單更新。

image

如上所求镰吆,每個(gè)vue組件實(shí)例都有相應(yīng)的 watcher 實(shí)例對(duì)象,它會(huì)在vue組件渲染的過(guò)程中把需要用到的屬性(getter)記錄為依賴跑慕。之后万皿,當(dāng)依賴項(xiàng)的 setter 被(其它JS代碼)調(diào)用時(shí),setter 會(huì)通知 watcher 重新計(jì)算相赁,從而致使它關(guān)聯(lián)的組件得以更新相寇。

此處實(shí)現(xiàn)的是一個(gè)觀察者模式慰于。

通過(guò)object.defineProperty遍歷設(shè)置this.data里面所有屬性钮科,在每個(gè)屬性的setter里面去通知對(duì)應(yīng)的回調(diào)函數(shù),這里的回調(diào)函數(shù)包括dom視圖重新渲染的函數(shù)婆赠、使用$watch添加的回調(diào)函數(shù)等绵脯,這樣我們就通過(guò)object.defineProperty劫持了數(shù)據(jù)佳励,當(dāng)我們對(duì)數(shù)據(jù)重新賦值時(shí),如this.title = 'hello vue',就會(huì)觸發(fā)setter函數(shù)蛆挫,從而觸發(fā)dom視圖重新渲染的函數(shù)赃承,實(shí)現(xiàn)數(shù)據(jù)變動(dòng),對(duì)應(yīng)視圖更新悴侵。

那么瞧剖,如何在setter里面觸發(fā)所有綁定該數(shù)據(jù)的回調(diào)函數(shù)呢?

既然綁定該數(shù)據(jù)的回調(diào)函數(shù)不止一個(gè)可免,我們就把所有的回調(diào)函數(shù)放在一個(gè)數(shù)組里面抓于,一旦觸發(fā)該數(shù)據(jù)的setter,就遍歷數(shù)組觸發(fā)里面所有的回調(diào)函數(shù)浇借,我們把這些回調(diào)函數(shù)稱為訂閱者捉撮。數(shù)組最好就定義在setter函數(shù)的最近的上級(jí)作用域中,如下面實(shí)例代碼所示妇垢。

> Object.keys(this.data).forEach(function(key) {
> 
>     var subs = [];  // 在這里放置添加所有訂閱者的數(shù)組
> 
>     Object.defineProperty(this.data, key, {    // this.data.title
> 
>         enumerable: false,
> 
>         configurable: true,
> 
>         get: function getter () {
> 
>             console.log('訪問數(shù)據(jù)啦啦啦')
> 
>             return this.data[key];   //返回對(duì)應(yīng)數(shù)據(jù)的值
> 
>         },
> 
>         set: function setter (newVal) {
> 
>             if (newVal === this.data[key]) {   
> 
>                 return;    // 如果數(shù)據(jù)沒有變動(dòng)巾遭,函數(shù)結(jié)束,不執(zhí)行下面的代碼
> 
>             }
> 
>             this.data[key] = newVal;  //數(shù)據(jù)重新賦值
> 
>             subs.forEach(function () {
> 
>                 // 通知subs里面的所有的訂閱者
> 
>             })
> 
>         }
> 
>     });
> 
> }

那么闯估,怎么把綁定數(shù)據(jù)的所有回調(diào)函數(shù)放到一個(gè)數(shù)組里面呢灼舍?這是通過(guò)gettter內(nèi)部的代碼完成的。

我們知道只要訪問數(shù)據(jù)就會(huì)觸發(fā)對(duì)應(yīng)數(shù)據(jù)的getter涨薪,那我們可以先設(shè)置一個(gè)全局變量target片仿,如果我們要在data里面title屬性添加一個(gè)訂閱者(changeTitle函數(shù)),我們可以先設(shè)置target = changeTitle尤辱,把changeTitle函數(shù)緩存在target中砂豌,然后訪問this.title去觸發(fā)title的getter,在getter里面把target這個(gè)全局變量的值添加到subs數(shù)組里面光督,添加完成后再把全局變量target設(shè)置為null阳距,以便添加其他訂閱者。

偽代碼如下:

> target = changeTitle
> 
> ...
> 
> Object.keys(this.data).forEach(function(key) {
> 
>     var subs = [];  // 在這里放置添加所有訂閱者的數(shù)組
> 
>     Object.defineProperty(this.data, key, {    // this.data.title
> 
>         enumerable: false,
> 
>         configurable: true,
> 
>         get: function getter () {
> 
>             console.log('訪問數(shù)據(jù)啦啦啦')
> 
>             if (target) {
> 
>                 subs.push(target);                
> 
>             }
> 
>             return this.data[key];   //返回對(duì)應(yīng)數(shù)據(jù)的值
> 
>         },
> 
>         set: function setter (newVal) {
> 
>             if (newVal === this.data[key]) {   
> 
>                 return;    // 如果數(shù)據(jù)沒有變動(dòng)结借,函數(shù)結(jié)束筐摘,不執(zhí)行下面的代碼
> 
>             }
> 
>             this.data[key] = newVal;  //數(shù)據(jù)重新賦值
> 
>             subs.forEach(function () {
> 
>                 // 通知subs里面的所有的訂閱者
> 
>             })
> 
>         }
> 
>     });
> 
> }

上面代碼中提到的changeTitle,即是上面最近一張圖解中的watcher船老。vue通過(guò)getter收集watcher集合咖熟。因?yàn)関ue充許在運(yùn)行時(shí)添加代碼,所以該收集行為不能僅限制于模板“編譯”之前柳畔。(注:vue中是不存在嚴(yán)格的編譯的馍管,js是解析執(zhí)行型語(yǔ)言,像C薪韩、Go等語(yǔ)言將源碼編譯為目標(biāo)平臺(tái)的二進(jìn)制文件确沸,才是真的編譯捌锭。)

模板是如何解析的

假如說(shuō)有下面這一段代碼,我們?cè)趺窗阉馕龀蓪?duì)應(yīng)的html呢罗捎?

> <input v-model="title">
> 
> <h1>{{title}}</h1>
> 
> <button v-on:click="changeTitle">change title<button>

注:該示例實(shí)現(xiàn)的效果是观谦,在input輸入框內(nèi)輸入任何內(nèi)容,下方h1文本同步更新桨菜。

先簡(jiǎn)單介紹視圖更新函數(shù)的用途豁状,比如解析指令v-model="title",v-on:click="changeTitle",還有把{{title}}替換為對(duì)應(yīng)的數(shù)據(jù)等。

回到上面那個(gè)問題倒得,如何解析模板替蔬?我們只要去遍歷所有dom節(jié)點(diǎn)包括其子節(jié)點(diǎn):

  • 如果節(jié)點(diǎn)屬性含有v-model,視圖更新函數(shù)就為把input的value設(shè)置為title的值

  • 如果節(jié)點(diǎn)為文本節(jié)點(diǎn)屎暇,視圖更新函數(shù)就為先用正則表達(dá)式取出大括號(hào)里面的值'title'承桥,再設(shè)置文本節(jié)點(diǎn)的值為data['title']

  • 如果節(jié)點(diǎn)屬性含有v-on:xxxx,視圖更新函數(shù)就為先用正則獲取事件類型為click根悼,然后獲取該屬性的值為changeTitle凶异,則事件的回調(diào)函數(shù)為this.methods['changeTitle'],接著用addEventListener監(jiān)聽節(jié)點(diǎn)click事件挤巡。

五剩彬、發(fā)布前優(yōu)化

使用vue-cli部署生產(chǎn)包時(shí),發(fā)現(xiàn)資源包很大矿卑,打包后的vendor.js達(dá)到了1M+喉恋。

UI組件按需加載

如果使用了第三方組件/UI庫(kù),如element-ui, mint-ui,echarts等母廷,如果全部引入轻黑,項(xiàng)目體積非常大,這時(shí)可以按需引入組件琴昆。

安裝 babel-plugin-component

npm install babel-plugin-component -D

然后氓鄙,將.babelrc 修改為:

> {
> 
>   "presets": [["es2015", { "modules": false }]],
>   "plugins": [
>     [
>       "component",
>       {
>         "libraryName": "element-ui",
>         "styleLibraryName": "theme-chalk"
>       }
>     ]
>   ]
> }

然后引入部分組件,這樣一來(lái)业舍,就不需要引入樣式了抖拦,插件會(huì)幫我們處理。

> // main.js
> import Vue from 'vue'
> import { Dialog, Loading } from 'element-ui'
> Vue.use(Dialog)
> Vue.use(Loading.directive)
> Vue.prototype.$loading = Loading.service
> // 然后正常使用組件

注:Babel是一個(gè)廣泛使用的轉(zhuǎn)碼器舷暮,可以將ES6代碼轉(zhuǎn)為ES5代碼态罪。讓不支持ES6的宿主環(huán)境,支持使用一套源碼開發(fā)下面。

mint-ui是element-ui的移動(dòng)端組件复颈,所以它的使用和引入幾乎和element-ui一樣。

路由懶加載

vue-router官方推薦syntax-dynamic-import插件诸狭,不過(guò)它要求同時(shí)安裝@bable/core^7.0.0券膀,如果你安裝了babel-core6君纫,可能有版本沖突的驯遇,解決方法如下:

npm install babel-plugin-syntax-dynamic-import --save-dev(^6.18.0)

當(dāng)打包構(gòu)建應(yīng)用時(shí)芹彬,Javascript 包會(huì)變得非常大,影響頁(yè)面加載叉庐。如果我們能把不同路由對(duì)應(yīng)的組件分割成不同的代碼塊舒帮,然后當(dāng)路由被訪問的時(shí)候才加載對(duì)應(yīng)組件,這樣就更加高效了陡叠。結(jié)合 Vue 的異步組件和 Webpack 的代碼分割功能玩郊,輕松實(shí)現(xiàn)路由組件的懶加載。

> // router.js
> const login = () => import('@/components/login')
> const router = new VueRouter({
>   routes: [
>     { path: '/login', component: login }
>   ]
> })

還有一種魔法注釋用法枉阵,不推薦使用译红。

使用異步組件(動(dòng)態(tài)組件)

app bundle 文件過(guò)大,可以嘗試通過(guò)組件懶加載優(yōu)化兴溜。

動(dòng)態(tài)組件主頁(yè)面加載是不會(huì)加載侦厚,等到觸發(fā)條件時(shí)才加載該組件,并且加載一次后就有緩存拙徽。如果組件在頁(yè)面加載時(shí)不需要刨沦,只在調(diào)用時(shí)用到,這時(shí)可以使用異步組件的寫法膘怕。僅僅是引入和組件注冊(cè)寫法不同:

> // template
> <test v-if="showTest"></test>
>
> // script
>
>   components: {
>     test: () => import('./test') // 將組件異步引入想诅,告訴webpack,將該部分代碼分割打包
>   },
> 
>   methods:{
>       clickTest () {
>           this.showTest = !this.showTest
>       }
>   }

圖片壓縮與合并

無(wú)損壓縮圖片:https://tinypng.com/岛心±雌疲可以將圖片制成雪碧精靈圖。

使用CDN加速vue類庫(kù)

一般項(xiàng)目里用到的第三方j(luò)s庫(kù)主要有:vue忘古、vue-router讳癌、vuex、vue-resource存皂、axio晌坤、qiniu等。這些依賴庫(kù)的js文件被一起打包到vender那個(gè)js文件里面旦袋,導(dǎo)致vender這個(gè)文件很大骤菠,那首屏加載速度肯定會(huì)被拖慢。

類庫(kù)文件使用cdn加速

> <!-- built files will be auto injected -->
> 
> <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
> 
> <script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
> 
> <script src="https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script>
> 
> <script src="https://cdn.bootcss.com/vue-resource/1.5.1/vue-resource.min.js"></script>

修改 build/webpack.base.conf.js

> module.exports = {
> 
> context: path.resolve(__dirname, '../'),
> 
> entry: {
> 
> app: './src/main.js'
> 
> },
> 
> externals:{
> 
> 'vue': 'Vue',
> 
> 'vue-router': 'VueRouter',
> 
> 'vuex':'Vuex',
> 
> 'vue-resource': 'VueResource'
> 
> }

排除已經(jīng)手動(dòng)收入的js文件

利用webpack的externals疤孕。具體做法就是在 build/webpack.base.conf.js文件的module里面與rules同層加入externals商乎。具體做法,修改src/main.js src/router/index.js 注釋掉import引入的vue,vue-resource等:

> // import Vue from 'vue'
> 
> // import VueResource from 'vue-resource'
> 
> // Vue.use(VueResource)

上面已經(jīng)引用過(guò)祭阀。

壓縮代碼

vue-cli已經(jīng)使用UglifyJsPlugin 插件來(lái)壓縮代碼鹉戚,可以設(shè)置成如下配置:

> new webpack.optimize.UglifyJsPlugin({
> 
>   compress: {
> 
>     warnings: false,
> 
>     drop_console: true,
> 
>     pure_funcs: ['console.log']
> 
>   },
> 
>   sourceMap: false
> 
> })

其中sourceMap: false是禁用除錯(cuò)功能鲜戒。如果設(shè)為true,在部署包中會(huì)生成.map結(jié)尾的js文件抹凳。它用于在代碼混淆壓縮的情況下仍可進(jìn)行調(diào)試遏餐。這個(gè)功能雖好,但會(huì)大大增加整體資源包的體積赢底,所以將其禁用失都。

v-for和v-if不要同時(shí)使用

在vue中v-for和v-if不要放在同一個(gè)元素上使用。由于 v-for 和 v-if 放在同一個(gè)元素上使用會(huì)帶來(lái)一些性能上的影響幸冻,在計(jì)算屬性上過(guò)濾之后再進(jìn)行遍歷粹庞。反例:

image

使用Object.freeze凍結(jié)大數(shù)據(jù)

對(duì)于前端純大數(shù)據(jù)展示(純大數(shù)據(jù)指:拿到數(shù)據(jù)就是直接用于展示的,不需要做修改其中字段等處理的洽损,而且數(shù)據(jù)量比較大)的情況下庞溜,使用Object.freeze方法來(lái)包裹變量,那邊vue內(nèi)部不會(huì)使用defineproperty去監(jiān)聽數(shù)據(jù)內(nèi)部的變化碑定,只有本身變化時(shí)才會(huì)觸發(fā)流码,在大量數(shù)據(jù)的情況下,vue內(nèi)部不在去監(jiān)聽數(shù)據(jù)的變化會(huì)提高性能不傅。使用demo如下:

image

使用Keep-alive標(biāo)簽優(yōu)化組件創(chuàng)建

vue提供了keep-alive標(biāo)簽來(lái)存儲(chǔ)緩存旅掂,對(duì)于一些視頻控件object或圖表類的使用,我們經(jīng)常會(huì)使用v-if指令访娶,而v-if是會(huì)創(chuàng)建和銷毀的商虐,如果頻繁操作在ie下的內(nèi)存會(huì)持續(xù)上升,而keep-alive可以有效的緩存崖疤,抑制內(nèi)存的持續(xù)上升秘车。

見:https://cn.vuejs.org/v2/api/#keep-alive

使用Set

Es6集合Set()可優(yōu)化遍歷速度,set集合是可用于查找該集合內(nèi)是否存在某個(gè)元素劫哼。但如果使用了Bable自動(dòng)轉(zhuǎn)化叮趴,該優(yōu)化無(wú)效。

在scope中少用元素選擇器

scope中元素選擇器盡量少用权烧。在 scoped 樣式中眯亦,類選擇器比元素選擇器更好,因?yàn)榇罅渴褂迷剡x擇器是很慢的般码。

為了給樣式設(shè)置作用域妻率,Vue 會(huì)為元素添加一個(gè)獨(dú)一無(wú)二的特性,例如 data-v-f3f3eg9板祝。然后修改選擇器宫静,使得在匹配選擇器的元素中,只有帶這個(gè)特性才會(huì)真正生效 (比如 button[data-v-f3f3eg9])。問題在于大量的元素和特性組合的選擇器 (比如 button[data-v-f3f3eg9]) 會(huì)比類和特性組合的選擇器 慢孤里,所以應(yīng)該盡可能選用類選擇器伏伯。

關(guān)于template的優(yōu)化

v-show,v-if 用哪個(gè)捌袜?在我來(lái)看要分兩個(gè)維度去思考問題说搅,第一個(gè)維度是權(quán)限問題,只要涉及到權(quán)限相關(guān)的展示無(wú)疑要用 v-if琢蛤,第二個(gè)維度在沒有權(quán)限限制下根據(jù)用戶點(diǎn)擊的頻次選擇蜓堕,頻繁切換的使用 v-show抛虏,不頻繁切換的使用 v-if博其,這里要說(shuō)的優(yōu)化點(diǎn)在于減少頁(yè)面中 dom 總數(shù),我比較傾向于使用 v-if迂猴,因?yàn)闇p少了 dom 數(shù)量慕淡,加快首屏渲染,至于性能方面我感覺肉眼看不出來(lái)切換的渲染過(guò)程沸毁,也不會(huì)影響用戶的體驗(yàn)峰髓。

不要在模板里面寫過(guò)多的表達(dá)式與判斷 v-if="isShow && isAdmin && (a || b)",這種表達(dá)式雖說(shuō)可以識(shí)別息尺,但是不是長(zhǎng)久之計(jì)携兵,當(dāng)看著不舒服時(shí),適當(dāng)?shù)膶懙?methods 和 computed 里面封裝成一個(gè)方法搂誉,這樣的好處是方便我們?cè)诙嗵幣袛嘞嗤谋磉_(dá)式徐紧,其他權(quán)限相同的元素再判斷展示的時(shí)候調(diào)用同一個(gè)方法即可。

循環(huán)調(diào)用子組件時(shí)添加 key炭懊,key 可以唯一標(biāo)識(shí)一個(gè)循環(huán)個(gè)體并级,可以使用例如 item.id 作為 key,假如數(shù)組數(shù)據(jù)是這樣的 ['a' , 'b', 'c', 'a'],使用 :key="item" 顯然沒有意義侮腹,更好的辦法就是在循環(huán)的時(shí)候 (item, index) in arr嘲碧,然后 :key="index"來(lái)確保 key 的唯一性。

2019年2月14日

寫在最后:通篇文章完整讀下來(lái)可以學(xué)到蠻多知識(shí)點(diǎn)的 值得學(xué)習(xí)以下
此外 對(duì)于最后一段對(duì)于v-for的循環(huán)來(lái)說(shuō) :key的值最好不要為index 作為唯一性的key值如果變動(dòng)以后 框架會(huì)認(rèn)為替換了內(nèi)容而對(duì)改變的index項(xiàng)之后的循環(huán)都重新渲染了. 正確的做法應(yīng)該是數(shù)據(jù)中包含唯一的key來(lái)作為值來(lái)綁定. (推薦這么做 因?yàn)檫@時(shí)插入一條數(shù)據(jù) 只是在index添加一條數(shù)據(jù) 之后的數(shù)據(jù)不會(huì)重新渲染 實(shí)際情況應(yīng)項(xiàng)目而異)


參考資料:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末父阻,一起剝皮案震驚了整個(gè)濱河市愈涩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌加矛,老刑警劉巖履婉,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異荒椭,居然都是意外死亡谐鼎,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)狸棍,“玉大人身害,你說(shuō)我怎么就攤上這事〔莞辏” “怎么了塌鸯?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)唐片。 經(jīng)常有香客問我丙猬,道長(zhǎng),這世上最難降的妖魔是什么费韭? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任茧球,我火速辦了婚禮,結(jié)果婚禮上星持,老公的妹妹穿的比我還像新娘抢埋。我一直安慰自己,他們只是感情好督暂,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布揪垄。 她就那樣靜靜地躺著,像睡著了一般逻翁。 火紅的嫁衣襯著肌膚如雪饥努。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天八回,我揣著相機(jī)與錄音酷愧,去河邊找鬼。 笑死辽社,一個(gè)胖子當(dāng)著我的面吹牛伟墙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播滴铅,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼戳葵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了汉匙?” 一聲冷哼從身側(cè)響起拱烁,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎噩翠,沒想到半個(gè)月后戏自,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伤锚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年擅笔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猛们。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡弯淘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情甲锡,我是刑警寧澤缸废,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布测萎,位于F島的核電站,受9級(jí)特大地震影響届巩,放射性物質(zhì)發(fā)生泄漏硅瞧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一恕汇、第九天 我趴在偏房一處隱蔽的房頂上張望腕唧。 院中可真熱鬧,春花似錦瘾英、人聲如沸枣接。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)但惶。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間膀曾,已是汗流浹背片拍。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妓肢,地道東北人捌省。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像碉钠,于是被迫代替她去往敵國(guó)和親纲缓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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