A. 解決vue2.x中數據渲染以及vuex緩存的問題
最近在學習Vue.js,把自己遇到的問題做個記錄,所以,今天添加一點小筆記。
在項目中遇到兩個問題,簡單的做個筆記來記錄自己解決的問題,可能不是很好的處理辦法,歡迎提出,自己還在不斷優化中...
第一個是vue在載入頁面的時候,會先載入靜態資源,這個時候數據還沒有請求回來,用戶會先看到靜態的內容(就是頁面固定寫死的),過一會才會有數據回來渲染,這體驗是很差的,其實解決辦法也很簡單,就是用vue里的
v-if
來判斷請求的數據是否返回...
<div
class="container"
id="app"
v-cloak>
<div
v-if='moneyInMsg.uuid'>
<in-account-msg
:money-in-msg="moneyInMsg"></in-account-msg>
</div>
</div>
這里的
v-if
=
'moneyInMsg.uuid'
就是來判斷數據有沒有請求回來,如果請求回來就讓他顯示,沒有請求到數據,就讓他loading,這樣體驗就會好很多。在這里還需要注意的是,v-if判斷的數據源,是數據返回的欄位,如果兩個欄位只能存在其一的話,可以v-if
='a
||
b'
來判斷數據是否成功的返回;還要注意的一點是,不能直接在組件里用v-if判斷,也不能直接在根標簽里判斷,直接嵌套一個div就可以解決,並不影響樣式,只做數據是否正常返回的顯示作用;
第二個就是在使用vuex時,有數據緩存;我遇到的情況是,在列表頁點擊進入詳情頁,返回到列表頁,在進入另一個詳情頁的時候,數據會顯示之前的數據,同時頁面還在loading(介面返回的數據比較慢),過一會數據返回的時候,才重新渲染頁面。可能是自己對vuex理解的不夠深入,沒有在vuex基礎上解決這個問題。雖然曲折的解決了這個問題,但是不夠zhuang,但是解決了問題,後期再做優化。
在之前解決的方案中,是進入頁面的時候,重新刷新頁面,重新請求數據,代碼如下:
export
const
refresh
=
(title)
=>
{
document.title
=
title;
let
iframe
=
document.createElement('iframe');
iframe.src
=
require('./mm.jpg');
iframe.setAttribute('style',
'display:none;');
let
loadFn
=
function
()
{
iframe.removeEventListener('load',
loadFn);
document.body.removeChild(iframe);
console.info('Page
Title
IS
'
+
title);
iframe
=
null;
loadFn
=
null;
}
document.body.appendChild(iframe)
iframe.addEventListener('load',
loadFn);
}
但是沒有達到預期的效果,依然會出現上面的情況...
丫的,抓狂了...(被別人催的感覺真的不爽...)
網路啊,google啊,都沒有遇到這種情況的?找到一個,還是提問的,沒有回答的,好吧,還是靠自己。自己動手,豐衣足食啊...
思路是,定義一個參數status為false,當數據沒有請求回來,就不顯示,也是用上面的方式來判斷,一直loading(請求失敗,去掉loading),當數據返回的時候,讓status為true;使用$nextTick來更新數據...
貼上自己部分的代碼作為參考:
<template>
<div
v-if='status
&&
order.name'>
//頁面展示的數據
</div>
</template>
<script>
export
default{
data(){
return
{
status:false
}
},
created(){
var
_this
=
this;
this.setDd({res
=>{
_this.$nextTick(function(){
_this.status=
true
});
}})
},
computed:{
...mapGetters({//getter獲取的數據})
},
methods:{
...mapActions(['setDd'])
//獲取數據的方法
}
}
</script>
處理的方式比較醜陋...,但是實現了想要的效果;這里注意一點就是v-if的判斷問題。(v-if='status
&&
order.name')這個用了並且,目的是有數據返回,才能讓他顯示,如果沒有數據,會顯示靜態的值,數據都為underfind...
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
B. vue2中computed原理
要理解這個,首先要理解vue2的數據響應式原理,因為computed這個API的實現是建立在數據響應式的基礎之上的。
這里附上vue響應式原理的地址: vue2數據響應式原理
在vue的watcher實例中配置了lazy,dirty,value屬性,就是用來配合實現computed的API。vue在初始化computed時,會給每一個computed屬性配置一個watcher,並配置lazy的值為true。在new Watcher時,還會設置dirty為true。由於lazy為true,這個時候並不會執行computed配置的get方法,也就是說不會去計算出這個計算屬性的值出來,那麼什麼時候才計算呢?就是在執行render函數時,會去取計算屬性的值,這個時候,會執行計算屬性的getter。在getter裡面,會先判斷dirty的值(為true,則表示當前計算屬性的值為臟值,已經過期了,需要重新計算出來;為false則表示當前的值是最新的,不需要重新計算,直接取緩存裡面的值就行了,也就是value欄位的值)。如果為true,則調用watcher.evaluate方法計算最新的值出來,然後將dirty設為false,把最新的值賦值給value。在計算最新的值時,也就是執行我們在computed對象中配置的相應的get函數。根據之前講的響應式原理,會先將當前的watcher掛到dep的靜態屬性target上,然後執行get,在這個過程中,會使用到data中的屬性,然後進行依賴收集,將computed的watcher存到data數據對應的dep中去。完了之後,將watcher從targetStack中彈出,這是dep的靜態屬性target的值又變成了render函數的watcher。以上步驟執行完後,會判斷當前dep的target是否還有值,有的話,會執行watcher.depend()。這一步是為了讓依賴的data對應的dep中不僅是收集到computed的wathcer,還要收集render函數的watcher,因為當我們依賴的data改變時,不僅要通知到computed的watcher,還要通知render函數的watcher,達到重渲染的目的。然後會把最新的value值返回,這時,render函數裡面終於拿到了計算屬性的最新值了。假如後面繼續用到了這個計算屬性,那麼在執行計算屬性的getter時,跟之前一樣,會先判斷dirty值,這個時候,dirty的值為false,則直接返回value值,也就是之前說的取緩存的值,不會再次運行一遍計算屬性的get函數了。
當某個時候,計算屬性依賴的data數據變了,則會先後觸發computed的watcher,還有render函數的watcher。在將vue的數據響應式原理時,我們知道,數據改變時,是會觸發watcher的update方法。在這個方法裡面,首先判斷lazy是否為true,這就是針對計算屬性設計的。我們可以看看源碼片段:
如果為true,則只做了一件非常簡單的事,就是把dirty設為了true,然後就沒了。這個時候會去執行render函數的watcher.update。也就是把render函數的執行交給了nextTick去管理,這也是之前講過的。在執行render函數時,又使用到了這個計算屬性,那麼,則會執行這個計算屬性的getter,判斷dirty是否為true,由於剛剛在執行computed的wathcer.update時,把dirty設為了true,這個時候肯定會執行watcher.evaluate重新去計算最新的值,也就是執行我們配置在computed裡面的get函數。接下來的其實跟前面所講的都是一樣的了。
這里附上關於computed初始化的源碼片段:
以上就是個人對於computed原理的理解了。
C. vue2 + Composition API 實踐
我們習慣了ES6的對象解構風格,但這在 composition- api 里可能會有陷阱。因為結構可能會讓你的響應式對象失去預期中的響應特性。
比如這里,button的click時候,不會得到預期的 count 的增加。因為setup執行返回的 count 並不是響應式的。
也就是說,雖然你的click事件確實的改變了 data.count 的值,但是這個值並沒有響應式的去改變其他引用這個值的地方。怎麼去驗證我們這個解釋呢?
我們可以開著chrome的vue插件,定位到你的組件。然後你可以點擊一下button,可以看到 count 並沒有變化, data 呢?看起來好像也沒有變化?這不是不符合邏輯嗎?甚至我們在click的回調函數里列印一下發現是有執行data.count+1 這個操作的。
事實是, data.count 確實是執行了的,但是因為不是 reactive 的,所以插件里沒有及時更新這個新數據。如果你把插件先切到別的組件上去,再切回來。你就會發現,data.count是符合預期的!
click操作前的插件看到的數據:
我做了2次click後,現在插件里把游標切到別的組件上,再切回來。就可以看到data的變化:
只是引用了 data.count 的地方沒有被更新,這就說明引用 data.count 的地方是非 reactive 的。
我們要要做的,就是改造一下引用 data.count 的地方。我們在 setup 里的返回,可以用 computed 和 toRefs 來改造一下返回值。
再看一下toRefs改造後的demo:
第二個問題來了: toRefs 一定是安全應對解構的方案么?
不是的,因為 toRefs 的結構是淺解構的,對於我們demo里的這種簡單的對象是work的。但是如果是一個嵌套很深的復雜Object,還是會有解構後響應式斷裂的問題。如果數據的層級比較復雜,建議使用 computed 。
watchEffect 很像React里的 useEffect ,是一個副作用函數。用法也基本一致。
watch 的話,接收2個參數。第一個參數是 watch 的target,看ts結構,必須是一個 ref 對象或者 computedRef 對象;第二個參數是watch的回調函數。
React的hooks出來之後,有個很好用的東西就是Context,很像一個微型Rex,可以很好的跨組件傳值,尤其是在組件粒度很細的時候,我們的組件間通信頻率也會升高。
之前vue2的時代,其實一直有 provide 和 inject 可以用。但是 provide 和 inject 的對象一般是非響應式的。官網是這么記載的:
vue2的時候,我們一般不太注重如何把一個數據變成響應式的(也不是沒有辦法,比如 Vue.observable(obj) 可以把一個對象變成響應式的。如果我們把這個對象provide出去,那麼傳遞的數據也就一直是響應式的了)。
vue3(或者vue2 + @vue/composition-api )後,我們更多的關注到了數據的 reactive 特性。比如用 ref 或者 reactive 關鍵字來構造一個響應式的對象。我們如果再用 provide 直接傳遞一個 reactive 的對象,豈不是可以模擬出類似React的 useContext 這樣的結構?
本身vue3(or vue2 + @vue/composition-api )也是支持hooks的。
這樣我們就可以按照React hooks的開發習慣去給vue抽hooks了。