分享Vue项目中会用到的一些实战技巧点
在開發Vue的過程中,我們經常會遇到一些這樣那樣的問題,然后要卡好半天,等問題解決了才發現原來一些細節知識點還是沒有掌握好。今天小編就整理了幾個在項目中會用到的一些實戰技巧點,希望可以幫助到正在努力賺錢的你。江湖規矩,先贊后看,艷遇不斷。
數據不響應,可能是用法有問題
前幾天有朋友給我發了一段代碼,然后說Vue有bug,他明明寫的沒問題,為啥數據就不響應呢,一定是Vue的bug?我感覺他比尤雨溪要牛逼,高攀不起,就沒有理他了。但是確實有時候我們在開發時候會遇到數據不響應的情況,那怎么辦呢?比如下面這段代碼:
<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 = '前端有的玩' 添加之后,并沒有生效,這是為什么呢?
這是因為在Vue內部,數據響應是通過使用Object.definePrototype監聽對象的每一個鍵的getter,setter方法來實現的,但通過這種方法只能監聽到已有屬性,新增的屬性是無法監聽到的,但我就是想監聽,小編你說咋辦吧。下面小編提供了四種方式,如果有更多方式,歡迎下方評論區告訴我。
1. 將本來要新增的屬性提前在data中定義好
比如上面的公眾號,我可以提前在userInfo里面定義好,這樣就不是新增屬性了,就像下面這樣
data() {
return {
userInfo: {
name: '子君',
sex: '男',
// 我先提前定義好
officialAccount: ''
}
}
}
2. 直接替換掉userInfo
雖然無法給userInfo里面添加新的屬性,但是因為userInfo已經定義好了,所以我直接修改userInfo的值不就可以了么,所以也可以像下面這樣寫
this.userInfo = {
// 將原來的userInfo 通過擴展運算法復制到新的對象里面
...this.userInfo,
// 添加新屬性
officialAccount: '前端有的玩'
}
3. 使用Vue.set
其實上面兩種方法都有點取巧的嫌疑,其實對于新增屬性,Vue官方專門提供了一個新的方法Vue.set用來解決新增屬性無法觸發數據響應。
Vue.set 方法定義
/** * target 要修改的對象 * prpertyName 要添加的屬性名稱 * value 要添加的屬性值 */ Vue.set( target, propertyName, value )
上面的代碼使用Vue.set可以修改為
import Vue from 'vue'
// 在這里添加用戶的公眾號
handleAddOfficialAccount() {
Vue.set(this.userInfo,'officialAccount', '前端有的玩')
}
但是每次要用到set方法的時候,還要把Vue引入進來,好麻煩,所以為了簡便起見,Vue又將set方法掛載到了Vue的原型鏈上了,即Vue.prototype.$set = Vue.set,所以在Vue組件內部可以直接使用this.$set代替Vue.set
this.$set(this.userInfo,'officialAccount', '前端有的玩')
小編發現有許多同學不知道什么時候應該用Vue.set,其實只有當你要賦值的屬性還沒有定義的時候需要使用Vue,set,其他時候一般不會需要使用。
4. 使用$forceUpdate
我覺得$forceUpdate的存在,讓許多前端開發者不會再去注意數據雙向綁定的原理,因為不論什么時候,反正我修改了data之后,調用一下$forceUpdate就會讓Vue組件重新渲染,bug是不會存在的。但是實際上這個方法并不建議使用,因為它會引起許多不必要的性能消耗。
針對數組的特定方式
其實不僅僅是對象,數組也存在數據修改之后不響應的情況,比如下面這段代碼
<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>
上面的代碼希望將張三的名字修改為王五,實際上這個修改并不能生效,這是因為Vue不能檢測到以下變動的數組:
當你利用索引直接設置一個項時,例如: this.list[index] = newValue
修改數組的length屬性,例如: this.list.length = 0
所以在上例中通過this.list[0] = '王五' 是無法觸發數據響應的,那應該怎么辦呢?像上面提到的Vue.set和$forceUpdate都可以解決這個問題,比如Vue.set可以這樣寫
Vue.set(this.list,0,'王五')
除了那些方法之外,Vue還針對數組提供了變異方法
在操作數組的時候,我們一般會用到數據提供的許多方法,比如push,pop,splice等等,在Vue中調用數組上面提供的這些方法修改數組的值是可以觸發數據響應的,比如上面的代碼改為以下代碼即可觸發數據響應
this.list.splice(0,1,'王五')
實際上,如果Vue僅僅依賴getter與setter,是無法做到在數組調用push,pop等方法時候觸發數據響應的,因此Vue實際上是通過劫持這些方法,對這些方法進行包裝變異來實現的。
Vue對數組的以下方法進行的包裝變異:
pushpopshiftunshiftsplicesortreverse
所以在操作數組的時候,調用上面這些方法是可以保證數據可以正常響應,下面是Vue源碼中包裝數組方法的代碼:
var original = arrayProto[method];
def(arrayMethods, method, function mutator () {
// 將 arguments 轉換為數組
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
}
// 如果有新的數據插入,則插入的數據也要進行一個響應式
if (inserted) { ob.observeArray(inserted); }
// 通知依賴進行更新
ob.dep.notify();
return result
});
文本格式化,filter更簡單使用filter 簡化邏輯
我想把時間戳顯示成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>
像上面的寫法,針對每一個日期字段都需要調用format,然后通過計算屬性進行轉換?這時候可以考慮使用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>
通過上面的修改是不是就簡單多了
注冊全局filter
有些過濾器使用的很頻繁,比如上面提到的日期過濾器,在很多地方都要使用,這時候如果在每一個要用到的組件里面都去定義一遍,就顯得有些多余了,這時候就可以考慮Vue.filter注冊全局過濾器
對于全局過濾器,一般建議在項目里面添加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框架的時候,經常需要使用Vue.use來安裝, 比如使用element-ui時候,經常會這樣寫:
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就可以直接在組件里面使用了,好神奇哦(呸,娘炮)。接下來我們實現一個簡化版的element來看如何去安裝。
了解Vue.use的用法
Vue.use是一個全局的方法,它需要在你調用 new Vue() 啟動應用之前完成,Vue.use的參數如下
/**
* plugin: 要安裝的插件 如 ElementUI
* options: 插件的配置信息 如 {size: 'small'}
*/
Vue.use(plugin, options)
模擬element-ui的安裝邏輯
想一下,使用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 進行注冊
Vue.components(Button.name, Button)
// 注冊全局指令
Vue.directive('loading', loadingDirective)
// 將loadingMethod 掛載到 Vue原型鏈上面,方便調用
Vue.prototype.$loading = loadingMethod
}
}
通過上面的代碼,已經實現了一個丐版的element-ui插件,這時候就可以在main.js里面通過Vue.use進行插件安裝了。大家可能會有疑問,為什么我要用這種寫法,不用這種寫法我照樣可以實現功能啊。小編認為這種寫法有兩個優勢
標準化,通過提供一種統一的開發模式,無論對插件開發者還是使用者來說,都有一個規范去遵循。
插件緩存,Vue.use 在安裝插件的時候,會對插件進行緩存,即一個插件如果安裝多次,實際上只會在第一次安裝時生效。
插件的應用場景
添加全局方法或者 property。
添加全局資源:指令/過濾器/過渡等。
通過全局混入來添加一些組件選項。
添加 Vue 實例方法,通過把它們添加到 Vue.prototype 上實現。
一個庫,提供自己的 API,同時提供上面提到的一個或多個功能。如element-ui
提高Vue渲染性能,了解一下Object.freeze
當一個 Vue 實例被創建時,它將 data 對象中的所有的 property 加入到 Vue 的響應式系統中。當這些 property 的值發生改變時,視圖將會產生“響應”,即匹配更新為新的值。但是這個過程實際上是比較消耗性能的,所以對于一些有大量數據但只是展示的界面來說,并不需要將property加入到響應式系統中,這樣可以提高渲染性能,怎么做呢,你需要了解一下Object.freeze。
在Vue官網中,有這樣一段話:這里唯一的例外是使用 Object.freeze(),這會阻止修改現有的 property,也意味著響應系統無法再_追蹤_變化。這段話的意思是,如果我們的數據使用了Object.freeze,就可以讓數據脫離響應式系統,那么該如何做呢?
比如下面這個表格,因為只是渲染數據,這時候我們就可以通過Object.freeze來優化性能
<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>
有的同學可能會有疑問,如果我這個表格的數據是滾動加載的,你這樣寫我不就沒法再給tableData添加數據了嗎?是,確實沒辦法去添加數據了,但還是有辦法解決的,比如像下面這樣
export default {
data() {
return {
tableData: []
}
},
created() {
setInterval(() => {
const data = Array(1000)
.fill(1)
.map((item, index) => {
// 雖然不能凍結整個數組,但是可以凍結每一項數據
return Object.freeze({
date: '2020-07-11',
name: `子君${index}`,
address: '大西安'
})
})
this.tableData = this.tableData.concat(data)
}, 2000)
}
}
合理的使用Object.freeze,是可以節省不少渲染性能,特別對于IE瀏覽器,效果還是很明顯的,趕快去試試吧。
最后如果你現在需要開發移動端項目,可以了解一下小編整理的一個開箱即用框架 vue-vant-base,也許可以幫到你哦
結語
不要吹滅你的靈感和你的想象力; 不要成為你的模型的奴隸。 ——文森特?梵高
相關推薦:
2020年前端vue面試題大匯總(附答案)
vue教程推薦:2020最新的5個vue.js視頻教程精選
更多編程相關知識,請訪問:編程入門!!
總結
以上是生活随笔為你收集整理的分享Vue项目中会用到的一些实战技巧点的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php找回密码流程是什么
- 下一篇: SEO优化都必须经过哪些步骤