Vue3官方文档翻译之Reactivity Fundamentals
引言
突然不知道這章寫了啥, 感覺內容有點干, 顧著翻譯去了, 沒有吸收消化. 還是總結下, 主要是就是響應式的對象如何和template結合的, 響應式對象是通過JavaScript的代理對象實現的; 響應式的對象有分為深響應和淺響應; 完成響應式的對象在作為函數參數或者解構的時候會有局限性,因此提出了ref()來避免此問題,并講接了ref的拆箱問題.
原文地址: http://blog.duhbb.com/2022/02/11/translation-of-reactivity-fundamentals-in-vue-3-offiicial-doc/
歡迎訪問我的博客: http://blog.duhbb.com/
Vue3官方文檔翻譯之Reactivity Fundamentals
Reactivity Fundamentals: 我也不知道怎么翻譯, 就當作是響應式原理或者基礎吧.
P.S. 我看的這個版本是 Composition API, 所以如果有偏差的話, 應該是API風格上的不同, 請自行甄別.
聲明響應式的狀態
我們可以使用 reactive() 函數創建一個響應式的對象或者數組.
妙啊, 創建之后呢?
import { reactive } from 'vue'const state = reactive({ count: 0 })響應時的對象都是JavaScript代理對象, 學過代理模式的應該都懂吧, 不會有人覺得用common 對象就能響應式吧, 我瞎逼逼的.
但是這些代理對象看上去和普通對象并沒有什么區別. 不同的是Vue可以跟蹤代理對象的屬性訪問以及響應式對象的變更. 如果你好奇這個在Vue中的實現原理, 那么我推薦你了解一下Reactivity in Depth, 不要猴急, 你先把這篇看完了, 再去深入了解.
也可以參考: Typing Reactive
為了在組件模板中使用響應式的對象, 聲明并且從組件的setup()函數中返回這個對象.
P.S. Options API 應該是從data()返回的吧.
import { reactive } from 'vue'export default {// `setup`是一個特殊的鉤子, 專門用于composition API.setup() {const state = reactive({ count: 0 })// 將響應式狀態暴露給模板return {state}} }這樣, state就和template關聯起來了, 你瞧瞧多么牛逼的框架…
<div>{{ state.count }}</div>類似的, 我們可以在同一個域中聲明函數來操作響應式的狀態, 并將將她作為方法和state一起暴露.
import { reactive } from 'vue'export default {setup() {const state = reactive({ count: 0 })// 擦, mutation的函數是放在scope中的?function increment() {state.count++}// don't forget to expose the function as well.return {state,increment}} }被暴露的方法一般都是作為事件響應的監聽器:
<button @click="increment">{{ state.count }} </button><script setup>
通過setup手動的暴露響應式對象或者方法可能會比較繁瑣.
幸運滴是, 不需要build步驟的時候才需要這么做.
當使用Single-File Component的時候, 我們可以通過<script setup>進行極大的簡化:
<!-- 歪日, 還真是方便 --> <script setup> import { reactive } from 'vue'const state = reactive({ count: 0 })function increment() {state.count++ } </script><template><button @click="increment">{{ state.count }}</button> </template>在<script setup>中的頂層引入以及變量都會在同一個組件的模板中變得可用.
在本文檔的剩下內容中, 我們將會主要使用SFC + <script setup> 語法來編寫Composition API風格的代碼例子, 應為這種用法對于Vue開發者來說是最常用的.
我是個偽前端, 偽Vue開發者…
DOM更新Timing
不好意思, timing我不會翻譯了, 感覺應該是"時機"的意思.
當你修改了響應式的對象的時候, DOM會被自動地更新. But, 但是, 然而, 你應該注意到, DOM的更新并不是同步的.
令人震驚的消息, ??? 什么, 就這點更新, 為什么不能同步呢?
實際上Vue會將這些變更緩沖起來, 直到下一次"next click"的更新周期中來確保每個組件只被更新一次, (我擦, 這還整上了時鐘周期?), 而不管你怎么修改state.
喲, 怎么玩兒的呢?
為了在state修改后, DOM的所有的update都能被執行完畢, 你可以使用nextTick()這個API:
import { nextTick } from 'vue'function increment() {count.value++// nextTick的回調就表示DOM中的組件都被個更新了?nextTick(() => {// access updated DOM}) }更深層次的響應性
在Vue中, state默認是深度響應(這翻譯我都看不下去了, 應該是和深拷貝淺拷貝類似吧?).
這意味著, 即使你對嵌套的對象, 或者數組中的對象進行修改的時候, 這種響應也能被檢測到.
(果然是深拷貝的概念, 所以嵌套的對象和數組也是代理的? 代理模式果然牛逼, Spring框架也是靠代理模式)
import { reactive } from 'vue'const obj = reactive({nested: { count: 0 },arr: ['foo', 'bar'] })function mutateDeeply() {// these will work as expected.obj.nested.count++obj.arr.push('baz') }當然, 你可以顯式地創建淺響應對象, 那么只根節點的對象的變更才會被追蹤, 但是這種變態的用法只是在高級場所, 啊不, 高級場景中才用得到.
響應式的代理對象 VS. 原始對象
需要注意的式, 從reactive()方法中返回的對象式原始對象的代理, 和原始的對象不是等同滴.
const raw = {} const proxy = reactive(raw)// proxy is NOT equal to the original. console.log(proxy === raw) // false只有代理對象是reactive的, 修改原始對象并不會觸發更新. 隱刺, 當使用Vue響應式系統的最佳方式就是使用你的狀態的代理對象, 不要直接去操作原始對象(后面是我加的).
為了確保對代理對象訪問的一致性, 對同一個對象使用reactive()返回的總是同一個代理對象, 哇偶, 這玩意兒還是冪等的咧, 同樣對代理對方使用reactive()方法, 返回的還是它自己(應該很好理解吧).
// calling reactive() on the same object returns the same proxy console.log(reactive(raw) === proxy) // true// calling reactive() on a proxy returns itself console.log(reactive(proxy) === proxy) // true這個規則也同樣適用于嵌套對象. 由于是深層響應的, 響應式對象中的嵌套對象也是代理的:
const proxy = reactive({})const raw = {} proxy.nested = rawconsole.log(proxy.nested === raw) // falsereactive()函數的局限性
reactive()這么牛逼的函數也有她的局限性:
這也意味著當我們賦值或者解構一個響應式對象的屬性到局部變量或者當我們將屬性傳遞給函數,或者從響應式對象解構屬性,我們都將失去響應式鏈接(good! 感覺和Hibernate的狀態管理有點類似鴨, de-attach):
const state = reactive({ count: 0 })// n is a local variable that is disconnected // from state.count. let n = state.count // does not affect original state n++// count is also disconnected from state.count. let { count } = state // does not affect original state count++// the function receives a plain number and // won't be able to track changes to state.count callSomeFunction(state.count)通過ref()獲得響應式變量(大概就這?)
為了解決reactive()的不便之處, Vue也提供了一個ref()函數, 它允許我們創建任何值類型的響應式引用:
import { ref } from 'vue'const count = ref(0)ref()接受參數, 并返回一個ref對象, 屬性是.value:
const count = ref(0)console.log(count) // { value: 0 } console.log(count.value) // 0count.value++ console.log(count.value) // 1P.S.: 這他喵的是裝箱和拆箱?
參考: Typing Refs
和響應式對象的屬性類似, ref的.value屬性也是響應式的. 除此之外, 當手里拿著一個對象類型的時候, ref會自動將.value交給reactive()處理.
ref中包含的對象值可以響應式地替代整個對象(沒看懂?):
const objectRef = ref({ count: 0 })// this works reactively objectRef.value = { count: 1 }哦哦, 懂了, reactive()不是不能替換嗎, ref可以替換.
refs可以被傳遞到函數中或者從普通的對象解構而不會失去reactivity:
const obj = {foo: ref(1),bar: ref(2) }// the function receives a ref // it needs to access the value via .value but it // will retain the reactivity connection callSomeFunction(obj.foo)// still reactive const { foo, bar } = obj換句話說, ref() 允許我們創建一個對任何值得引用, 并且傳遞她的時候不會丟失reactivity.
這個特性非常重要, 在將邏輯提取到Composable Functions的時候就顯得非常重要了.
在模板中進行引用拆箱
當引用在模板中被作為頂層屬性使用的時候, 她們會自動地被拆箱, 因此就不需要我們再使用 .value 了. 下面將之前計數地例子用ref()進行了改造:
<script setup> import { ref } from 'vue'/* 嘿嘿, 這個是時候原始值也可以獲得reactivity了 */ const count = ref(0)function increment() {count.value++ } </script><template><button @click="increment">{{ count }} <!-- no .value needed --></button> </template>小心: 自動地拆箱僅適用于頂層屬性, 對于refs的嵌套訪問則不會被拆箱:
const object = { foo: ref(1) } <!-- object只是一個普通的對象, 剛才的規則也說了只有top-level的才會被自動拆箱 --> {{ object.foo }} <!-- does NOT get unwrapped -->正如設想的那樣, foo 并不會被拆箱, 呵呵, 我還沒有驗證.
響應式對象中的ref的拆箱
當一個ref作為一個響應式的對象的屬性進行訪問或者修改的時候, 她會被自動的進行拆箱, 就像正常的屬性那樣:
const count = ref(0)/* state已經是一個響應式的對象, 她有一個ref形式的屬性, 這個會被自動拆箱, 不需要是top-level了 */ /* 感覺我已經理解了這個, 但是沒有實踐 */ const state = reactive({count })console.log(state.count) // 0state.count = 1 console.log(count.value) // 1如果一個新的ref替換了原來的ref, 她將會替代原來的ref:
const otherCount = ref(2)state.count = otherCount console.log(state.count) // 2 // original ref is now disconnected from state.count console.log(count.value) // 1ref的拆箱只會發生在作為深層的響應式對象的時候, 而作為淺層響應對象的屬性時, 則不會被拆箱.
數組和集合中的ref的拆箱
和響應式對象不一樣, 當ref作為響應式數組或者集合類型的元素被訪問的時候并不會發生拆箱(這是為什么呢?):
const books = reactive([ref('Vue 3 Guide')]) // need .value here console.log(books[0].value)const map = reactive(new Map([['count', ref(0)]])) // need .value here console.log(map.get('count').value)響應式的傳遞性(實驗特性)
必須將 .value 和 refs 進行配合使用時JavaScript這個語言所施加的限制. 然而, 通過運行期的轉換在合適的位置追加 .value 則可以提高 ergonomics. Vue提供了編譯期的轉換使我們可以將之前的 “counter” 例子寫成這樣:
<script setup> let count = $ref(0)function increment() {// no need for .valuecount++ } </script><template><button @click="increment">{{ count }}</button> </template>關于Reactivity Transform可以在她的專題了解, 提醒各位這個目前還只是實驗性滴.
結束語
突然不知道這章寫了啥, 感覺內容有點干, 顧著翻譯去了, 沒有吸收消化. 還是總結下, 主要是就是響應式的對象如何和template結合的, 響應式對象是通過JavaScript的代理對象實現的; 響應式的對象有分為深響應和淺響應; 完成響應式的對象在作為函數參數或者解構的時候會有局限性,因此提出了ref()來避免此問題,并講接了ref的拆箱問題.
原文地址: http://blog.duhbb.com/2022/02/11/translation-of-reactivity-fundamentals-in-vue-3-offiicial-doc/
歡迎訪問我的博客: http://blog.duhbb.com/
總結
以上是生活随笔為你收集整理的Vue3官方文档翻译之Reactivity Fundamentals的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: You have no right to
- 下一篇: 将手机微信的图片打包成压缩包