您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Vue項(xiàng)目中的實(shí)戰(zhàn)小技巧,小編覺得挺實(shí)用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
前幾天有朋友給我發(fā)了一段代碼,然后說Vue有bug,他明明寫的沒問題,為啥數(shù)據(jù)就不響應(yīng)呢,一定是Vue的bug?我感覺他比尤雨溪要牛逼,高攀不起,就沒有理他了。但是確實(shí)有時候我們在開發(fā)時候會遇到數(shù)據(jù)不響應(yīng)的情況,那怎么辦呢?比如下面這段代碼:
<template> <div> <div> <span>用戶名: {{ userInfo.name }}</span> <span>用戶性別: {{ userInfo.sex }}</span> <span v-if="userInfo.officialAccount"> 公眾號: {{ userInfo.officialAccount }} </span> </div> <button @click="handleAddOfficialAccount">添加公眾號</button> </div> </template> <script> export default { data() { return { userInfo: { name: '子君', sex: '男' } } }, methods: { // 在這里添加用戶的公眾號 handleAddOfficialAccount() { this.userInfo.officialAccount = '前端有的玩' } } } </script>
在上面的代碼中,我們希望給用戶信息里面添加公眾號屬性,但是通過this.userInfo.officialAccount = '前端有的玩' 添加之后,并沒有生效,這是為什么呢?
這是因?yàn)樵赩ue內(nèi)部,數(shù)據(jù)響應(yīng)是通過使用Object.definePrototype監(jiān)聽對象的每一個鍵的getter,setter方法來實(shí)現(xiàn)的,但通過這種方法只能監(jiān)聽到已有屬性,新增的屬性是無法監(jiān)聽到的,但我就是想監(jiān)聽,小編你說咋辦吧。下面小編提供了四種方式,如果有更多方式,歡迎下方評論區(qū)告訴我。
比如上面的公眾號,我可以提前在userInfo里面定義好,這樣就不是新增屬性了,就像下面這樣
data() { return { userInfo: { name: '子君', sex: '男', // 我先提前定義好 officialAccount: '' } } }
雖然無法給userInfo里面添加新的屬性,但是因?yàn)閡serInfo已經(jīng)定義好了,所以我直接修改userInfo的值不就可以了么,所以也可以像下面這樣寫
this.userInfo = { // 將原來的userInfo 通過擴(kuò)展運(yùn)算法復(fù)制到新的對象里面 ...this.userInfo, // 添加新屬性 officialAccount: '前端有的玩' }
其實(shí)上面兩種方法都有點(diǎn)取巧的嫌疑,其實(shí)對于新增屬性,Vue官方專門提供了一個新的方法Vue.set用來解決新增屬性無法觸發(fā)數(shù)據(jù)響應(yīng)。
Vue.set 方法定義
/** * target 要修改的對象 * prpertyName 要添加的屬性名稱 * value 要添加的屬性值 */ Vue.set( target, propertyName, value )
上面的代碼使用Vue.set可以修改為
import Vue from 'vue' // 在這里添加用戶的公眾號 handleAddOfficialAccount() { Vue.set(this.userInfo,'officialAccount', '前端有的玩') }
但是每次要用到set方法的時候,還要把Vue引入進(jìn)來,好麻煩,所以為了簡便起見,Vue又將set方法掛載到了Vue的原型鏈上了,即Vue.prototype.$set = Vue.set
,所以在Vue組件內(nèi)部可以直接使用this.$set代替Vue.set
this.$set(this.userInfo,'officialAccount', '前端有的玩')
小編發(fā)現(xiàn)有許多同學(xué)不知道什么時候應(yīng)該用Vue.set,其實(shí)只有當(dāng)你要賦值的屬性還沒有定義的時候需要使用Vue,set,其他時候一般不會需要使用。
我覺得$forceUpdate
的存在,讓許多前端開發(fā)者不會再去注意數(shù)據(jù)雙向綁定的原理,因?yàn)椴徽撌裁磿r候,反正我修改了data之后,調(diào)用一下$forceUpdate就會讓Vue組件重新渲染,bug是不會存在的。但是實(shí)際上這個方法并不建議使用,因?yàn)樗鼤鹪S多不必要的性能消耗。
其實(shí)不僅僅是對象,數(shù)組也存在數(shù)據(jù)修改之后不響應(yīng)的情況,比如下面這段代碼
<template> <div> <ul> <li v-for="item in list" :key="item"> {{ item }} </li> </ul> <button @click="handleChangeName">修改名稱</button> </div> </template> <script> export default { data() { return { list: ['張三', '李四'] } }, methods: { // 修改用戶名稱 handleChangeName() { this.list[0] = '王五' } } } </script>
上面的代碼希望將張三的名字修改為王五,實(shí)際上這個修改并不能生效,這是因?yàn)閂ue不能檢測到以下變動的數(shù)組:
當(dāng)你利用索引直接設(shè)置一個項(xiàng)時,例如: this.list[index] = newValue
修改數(shù)組的length屬性,例如: this.list.length = 0
所以在上例中通過this.list[0] = '王五' 是無法觸發(fā)數(shù)據(jù)響應(yīng)的,那應(yīng)該怎么辦呢?像上面提到的Vue.set和$forceUpdate都可以解決這個問題,比如Vue.set可以這樣寫
Vue.set(this.list,0,'王五')
除了那些方法之外,Vue還針對數(shù)組提供了變異方法
在操作數(shù)組的時候,我們一般會用到數(shù)據(jù)提供的許多方法,比如push,pop,splice等等,在Vue中調(diào)用數(shù)組上面提供的這些方法修改數(shù)組的值是可以觸發(fā)數(shù)據(jù)響應(yīng)的,比如上面的代碼改為以下代碼即可觸發(fā)數(shù)據(jù)響應(yīng)
this.list.splice(0,1,'王五')
實(shí)際上,如果Vue僅僅依賴getter與setter,是無法做到在數(shù)組調(diào)用push,pop等方法時候觸發(fā)數(shù)據(jù)響應(yīng)的,因此Vue實(shí)際上是通過劫持這些方法,對這些方法進(jìn)行包裝變異來實(shí)現(xiàn)的。
Vue對數(shù)組的以下方法進(jìn)行的包裝變異:
push
pop
shift
unshift
splice
sort
reverse
所以在操作數(shù)組的時候,調(diào)用上面這些方法是可以保證數(shù)據(jù)可以正常響應(yīng),下面是Vue源碼中包裝數(shù)組方法的代碼:
var original = arrayProto[method]; def(arrayMethods, method, function mutator () { // 將 arguments 轉(zhuǎn)換為數(shù)組 var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; var result = original.apply(this, args); // 這兒的用法同dependArray(value),就是為了取得dep var ob = this.__ob__; var inserted; switch (method) { case 'push': case 'unshift': inserted = args; break case 'splice': inserted = args.slice(2); break } // 如果有新的數(shù)據(jù)插入,則插入的數(shù)據(jù)也要進(jìn)行一個響應(yīng)式 if (inserted) { ob.observeArray(inserted); } // 通知依賴進(jìn)行更新 ob.dep.notify(); return result });
我想把時間戳顯示成yyyy-MM-DD HH:mm:ss的格式怎么辦?是需要在代碼中先將日期格式化之后,再渲染到模板嗎?就像下面這樣
<template> <div> {{ dateStr }} <ul> <li v-for="(item, index) in getList" :key="index"> {{ item.date }} </li> </ul> </div> </template> <script> import { format } from '@/utils/date' export default { data() { return { date: Date.now(), list: [ { date: Date.now() } ] } }, computed: { dateStr() { return format(this.date, 'yyyy-MM-DD HH:mm:ss') }, getList() { return this.list.map(item => { return { ...item, date: format(item.date, 'yyyy-MM-DD HH:mm:ss') } }) } } } </script>
像上面的寫法,針對每一個日期字段都需要調(diào)用format,然后通過計算屬性進(jìn)行轉(zhuǎn)換?這時候可以考慮使用Vue提供的filter去簡化
<template> <div> <!--使用過濾器--> {{ dateStr | formatDate }} <ul> <li v-for="(item, index) in list" :key="index"> <!--在v-for中使用過濾器--> {{ item.date | formatDate }} </li> </ul> </div> </template> <script> import { format } from '@/utils/date' export default { filters: { formatDate(value) { return format(value, 'yyyy-MM-DD HH:mm:ss') } }, data() { return { date: Date.now(), list: [ { date: Date.now() } ] } } } </script>
通過上面的修改是不是就簡單多了
有些過濾器使用的很頻繁,比如上面提到的日期過濾器,在很多地方都要使用,這時候如果在每一個要用到的組件里面都去定義一遍,就顯得有些多余了,這時候就可以考慮Vue.filter注冊全局過濾器
對于全局過濾器,一般建議在項(xiàng)目里面添加filters目錄,然后在filters目錄里面添加
// filters\index.js import Vue from 'vue' import { format } from '@/utils/date' Vue.filter('formatDate', value => { return format(value, 'yyyy-MM-DD HH:mm:ss') })
然后將filters里面的文件引入到main.js里面,這時候就可以在組件里面直接用了,比如將前面的代碼可以修改為
<template> <div> <!--使用過濾器--> {{ dateStr | formatDate }} <ul> <li v-for="(item, index) in list" :key="index"> <!--在v-for中使用過濾器--> {{ item.date | formatDate }} </li> </ul> </div> </template> <script> export default { data() { return { date: Date.now(), list: [ { date: Date.now() } ] } } } </script>
是不是更簡單了
在使用一些UI框架的時候,經(jīng)常需要使用Vue.use來安裝, 比如使用element-ui時候,經(jīng)常會這樣寫:
import Vue from 'vue'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI,{size: 'small'});
使用了Vue.use之后,element-ui就可以直接在組件里面使用了,好神奇哦(呸,娘炮)。接下來我們實(shí)現(xiàn)一個簡化版的element來看如何去安裝。
Vue.use是一個全局的方法,它需要在你調(diào)用 new Vue() 啟動應(yīng)用之前完成,Vue.use的參數(shù)如下
/** * plugin: 要安裝的插件 如 ElementUI * options: 插件的配置信息 如 {size: 'small'} */ Vue.use(plugin, options)
想一下,使用Vue.use(ElementUI,{size: 'small'}) 之后我們可以用到哪些element-ui提供的東西
可以直接在組件里面用element-ui的組件,不需要再import
可以直接使用v-loading指令
通過this.$loading在組件里面顯示loading
其他...
// 這個是一個按鈕組件 import Button from '@/components/button' // loading 指令 import loadingDirective from '@/components/loading/directive' // loading 方法 import loadingMethod from '@/components/loading' export default { /** * Vue.use 需要插件提供一個install方法 * @param {*} Vue Vue * @param {*} options 插件配置信息 */ install(Vue, options) { console.log(options) // 將組件通過Vue.components 進(jìn)行注冊 Vue.components(Button.name, Button) // 注冊全局指令 Vue.directive('loading', loadingDirective) // 將loadingMethod 掛載到 Vue原型鏈上面,方便調(diào)用 Vue.prototype.$loading = loadingMethod } }
通過上面的代碼,已經(jīng)實(shí)現(xiàn)了一個丐版的element-ui插件,這時候就可以在main.js里面通過Vue.use進(jìn)行插件安裝了。大家可能會有疑問,為什么我要用這種寫法,不用這種寫法我照樣可以實(shí)現(xiàn)功能啊。小編認(rèn)為這種寫法有兩個優(yōu)勢
標(biāo)準(zhǔn)化,通過提供一種統(tǒng)一的開發(fā)模式,無論對插件開發(fā)者還是使用者來說,都有一個規(guī)范去遵循。
插件緩存,Vue.use 在安裝插件的時候,會對插件進(jìn)行緩存,即一個插件如果安裝多次,實(shí)際上只會在第一次安裝時生效。
添加全局方法或者 property。
添加全局資源:指令/過濾器/過渡等。
通過全局混入來添加一些組件選項(xiàng)。
添加 Vue 實(shí)例方法,通過把它們添加到 Vue.prototype 上實(shí)現(xiàn)。
一個庫,提供自己的 API,同時提供上面提到的一個或多個功能。如element-ui
當(dāng)一個 Vue 實(shí)例被創(chuàng)建時,它將 data 對象中的所有的 property 加入到 Vue 的響應(yīng)式系統(tǒng)中。當(dāng)這些 property 的值發(fā)生改變時,視圖將會產(chǎn)生“響應(yīng)”,即匹配更新為新的值。但是這個過程實(shí)際上是比較消耗性能的,所以對于一些有大量數(shù)據(jù)但只是展示的界面來說,并不需要將property加入到響應(yīng)式系統(tǒng)中,這樣可以提高渲染性能,怎么做呢,你需要了解一下Object.freeze。
在Vue官網(wǎng)中,有這樣一段話:這里唯一的例外是使用 Object.freeze(),這會阻止修改現(xiàn)有的 property,也意味著響應(yīng)系統(tǒng)無法再_追蹤_變化。這段話的意思是,如果我們的數(shù)據(jù)使用了Object.freeze,就可以讓數(shù)據(jù)脫離響應(yīng)式系統(tǒng),那么該如何做呢?
比如下面這個表格,因?yàn)橹皇卿秩緮?shù)據(jù),這時候我們就可以通過Object.freeze來優(yōu)化性能
<template> <el-table :data="tableData" > <el-table-column prop="date" label="日期" width="180" /> <el-table-column prop="name" label="姓名" width="180" /> <el-table-column prop="address" label="地址" /> </el-table> </template> <script> export default { data() { const data = Array(1000) .fill(1) .map((item, index) => { return { date: '2020-07-11', name: `子君${index}`, address: '大西安' } }) return { // 在這里我們用了Object.freeze tableData: Object.freeze(data) } } } </script>
有的同學(xué)可能會有疑問,如果我這個表格的數(shù)據(jù)是滾動加載的,你這樣寫我不就沒法再給tableData添加數(shù)據(jù)了嗎?是,確實(shí)沒辦法去添加數(shù)據(jù)了,但還是有辦法解決的,比如像下面這樣
export default { data() { return { tableData: [] } }, created() { setInterval(() => { const data = Array(1000) .fill(1) .map((item, index) => { // 雖然不能凍結(jié)整個數(shù)組,但是可以凍結(jié)每一項(xiàng)數(shù)據(jù) return Object.freeze({ date: '2020-07-11', name: `子君${index}`, address: '大西安' }) }) this.tableData = this.tableData.concat(data) }, 2000) } }
合理的使用Object.freeze,是可以節(jié)省不少渲染性能,特別對于IE瀏覽器,效果還是很明顯的,趕快去試試吧。
最后如果你現(xiàn)在需要開發(fā)移動端項(xiàng)目,可以了解一下小編整理的一個開箱即用框架 vue-vant-base,也許可以幫到你。
關(guān)于“Vue項(xiàng)目中的實(shí)戰(zhàn)小技巧”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。