<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【譯】Vue 3 Composition API: Ref vs Reactive

          共 12606字,需瀏覽 26分鐘

           ·

          2021-04-29 09:04

          前言

          Vue 3.0發(fā)布至今已經(jīng)大半年過去了,我從最初的Option API的思維轉(zhuǎn)換成Composition API花了很長時(shí)間,在使用過程中也出現(xiàn)了很多問題。我們都知道RefReactive都是定義響應(yīng)式數(shù)據(jù)的方式,而我在初學(xué)的時(shí)候從網(wǎng)上的大部分博客只得出過一個(gè)結(jié)論:Ref是定義基本類型數(shù)據(jù),Reactive是定義引用類型數(shù)據(jù)的,但隨著后面的實(shí)踐發(fā)現(xiàn),其實(shí)并不是很嚴(yán)謹(jǐn),于是我找了這么一篇文章,我覺得講得很好,便有了今天的翻譯。下面的原文翻譯采用意譯并非直譯,如有錯(cuò)誤,請諸君批評與指正。

          原文翻譯

          在寫這篇文章的時(shí)候,Vue 3的發(fā)布離我們越來越近了。我認(rèn)為我最激動的是看看其他開發(fā)者如何擁抱和使用它。在過去的幾個(gè)月中,盡管我有機(jī)會使用過Vue 3,但我知道并非每個(gè)人都如此。

          Vue 3最大的特點(diǎn)就是Composition API。這提供了一種創(chuàng)建組件的替代方法,該方法與現(xiàn)有的Option API截然不同。我毫不猶豫地承認(rèn),當(dāng)我第一次看到它時(shí),我并沒有理解它,但隨著我更多地去使用它,我發(fā)現(xiàn)它開始變得有意義。雖然您不會使用Composition API重寫整個(gè)應(yīng)用程序,但可以讓您思考如何進(jìn)一步提高創(chuàng)建組件和編寫功能的方式。我最近在Vue 3上做了幾場演講,并且不斷出現(xiàn)的一個(gè)問題是何時(shí)使用Ref vs Reactive來聲明數(shù)據(jù)的響應(yīng)式。我并沒有一個(gè)很好的答復(fù),所以在過去的幾周中,我著手去回答這個(gè)問題,而這篇文章正是該研究的結(jié)果。

          我還想指出,這是我自己的看法,請不要將其視為應(yīng)采取的“方式”。除非有人告訴我使用Ref & Reactive更好的方式,否則我目前會一直采用下面的方式去使用它。對于任何新技術(shù),我認(rèn)為需要花費(fèi)一些時(shí)間來弄清楚我們?nèi)绾问褂盟瑥亩贸鲆恍┳罴褜?shí)踐。在開始之前,我將假設(shè)您至少已經(jīng)了解了Composition API。本文將重點(diǎn)介紹Ref vs Reactive,而不是Composition API的機(jī)制,如果您對這方面的深入教程感興趣,請告訴我。

          Vue 2 中的響應(yīng)式

          為了給本文提供一些背景信息,我想快速探索如何在Vue 2應(yīng)用程序中創(chuàng)建響應(yīng)式性數(shù)據(jù)。當(dāng)您希望Vue跟蹤數(shù)據(jù)更改時(shí),需要在從data函數(shù)返回的對象內(nèi)部聲明該屬性。

          <template>
            <h1>{{ title }}</h1>
          </template>

          <script>
            export default {
              data() {
                return {
                  title: "Hello, Vue!"
                };
              }
            };
          </
          script>

          在Vue 2內(nèi)部,為了追蹤每個(gè)數(shù)據(jù)的變化,它會觀察每個(gè)屬性,并且使用Object.defineProperty()去創(chuàng)建getters和setters。這是對Vue 2響應(yīng)式數(shù)據(jù)的最基本的解釋,但我知道這并不是“魔法”。您不能只在任何地方創(chuàng)建數(shù)據(jù)并期望Vue對其進(jìn)行跟蹤,您必須遵循在data()函數(shù)中對其進(jìn)行定義的約定。

          Ref vs Reactive

          使用Options API,定義響應(yīng)式性數(shù)據(jù)時(shí)必須遵循一些規(guī)則,Composition API也不例外。您不能只聲明數(shù)據(jù)并期望Vue進(jìn)行跟蹤更改。在下面的示例中,我定義了一個(gè)title屬性,并從setup()函數(shù)返回了該title,并在模板中使用。

          <template>
            <h1>{{ title }}</h1>
          </template>

          <script>
            export default {
              setup() {
                let title = "Hello, Vue 3!";
                return { title };
              }
            };
          </
          script>

          雖然能正常運(yùn)行,但是title屬性并不是響應(yīng)式數(shù)據(jù)。這意味著,如果某些方法更改了這個(gè)title屬性后,DOM并不能更新數(shù)據(jù)。舉例來說,您想在5秒鐘后更新title,那么以下操作將無效。

          <template>
            <h1>{{ title }}</h1>
          </template>

          <script>
            export default {
              setup() {
                let title = "Hello, Vue 3!";

                setTimeout(() => {
                  title = "THIS IS A NEW TITLE";
                }, 5000);

                return { title };
              }
            };
          </
          script>

          為了解決上面的示例,我們可以使用import { ref } from 'vue'并使用ref()將其標(biāo)記為響應(yīng)式數(shù)據(jù)。在Vue 3內(nèi)部,Vue將創(chuàng)建一個(gè)Proxy代理對象。

          <template>
            <h1>{{ title }}</h1>
          </template>

          <script>
            import { ref } from "vue";

            export default {
              setup() {
                const title = ref("Hello, Vue 3!");

                setTimeout(() => {
                  /
          / you might be asking yourself, what is this .value all about...
                  /
          / more about that soon
                  title.value = "New Title";
                }, 5000);

                return { title };
              }
            };
          </
          script>

          我還想明確一點(diǎn),當(dāng)提到Ref vs Reactive時(shí),我相信有兩個(gè)場景:第一個(gè)就是當(dāng)您像我們上面那樣創(chuàng)建組件時(shí),你需要定義響應(yīng)式數(shù)據(jù)的時(shí)候,另外一個(gè)就是在創(chuàng)建組合式函數(shù)可以被復(fù)用的時(shí)候。在本文中,我將對每種情況進(jìn)行說明。

          Ref

          如果要使原始數(shù)據(jù)類型具有響應(yīng)式性,則ref()將是您的首選。同樣,這不是銀彈,但這是一個(gè)不錯(cuò)的出發(fā)點(diǎn)。如果需要復(fù)習(xí),JavaScript中的七個(gè)原始數(shù)據(jù)類型是:

          • String
          • Number
          • BigInt
          • Boolean
          • Symbol
          • Null
          • Undefined
          import { ref } from "vue";

          export default {
            setup() {
              const title = ref("");
              const one = ref(1);
              const isValid = ref(true);
              const foo = ref(null);
            }
          };

          在前面的示例中,我們有一個(gè)名為title的字符串,因此ref()是聲明響應(yīng)式性數(shù)據(jù)的不錯(cuò)選擇。如果您對我們在下面編寫的代碼有疑問,請不要擔(dān)心,我也有同樣的問題。

          import { ref } from "vue";

          export default {
            setup() {
              const title = ref("Hello, Vue 3!");

              setTimeout(() => {
                title.value = "New Title";
              }, 5000);

              return { title };
            }
          };

          當(dāng)原始值將要更改時(shí),為什么要使用const聲明?我們不應(yīng)該在這里使用let嗎?如果要使用console.log(title),則可能希望看到值Hello,Vue 3 !,而是得到一個(gè)看起來像這樣的對象:

          {_isReftrue}
          value: (...)
          _isReftrue
          get value: ? value()
          set value: ? value(newVal)
          __proto__: Object

          ref()函數(shù)接受一個(gè)內(nèi)部值,并返回一個(gè)響應(yīng)式性并且可變更的ref對象。ref對象具有指向內(nèi)部值的單個(gè)屬性.value。這意味著,如果要訪問或更改值,則需要使用title.value。并且因?yàn)檫@是一個(gè)不會改變的對象,所以我決定將其聲明為const。

          Ref拆箱

          您可能會問的下一個(gè)問題是“為什么我們不必在模板中引用.value”?

          <template>
            <h1>{{ title }}</h1>
          </template>


          當(dāng)ref作為渲染上下文(從setup()返回的對象)的屬性返回并在模板中訪問時(shí),它會自動展開為內(nèi)部值,無需在模板中附加.value,這個(gè)過程其實(shí)也叫“拆箱”的過程。

          計(jì)算屬性的工作原理相同,因此如果需要在setup()方法中使用計(jì)算屬性的值,則需要使用.value。

          Reactive

          當(dāng)您要在原始值上定義響應(yīng)式數(shù)據(jù)時(shí),我們僅查看了使用ref()的一些示例,如果要?jiǎng)?chuàng)建響應(yīng)式對象(引用類型)會怎樣?在這種情況下,您仍然可以使用ref(),但是在內(nèi)部只是調(diào)用reactive()函數(shù),所以我將堅(jiān)持使用reactive()。

          另一方面,reactive()將不適用于原始值,reactive()獲取一個(gè)對象并返回原始對象的響應(yīng)式代理。這等效于2.x的Vue.observable(),并已重命名以避免與RxJS observables混淆。

          import { reactive } from "vue";

          export default {
            setup() {
              const data = reactive({
                title"Hello, Vue 3"
              });

              return { data };
            }
          };

          這里的最大區(qū)別是,當(dāng)您要在模板中訪問reactive()定義的數(shù)據(jù)時(shí)。您將需要在模板中引用data.title,而在前面的示例中,data是一個(gè)包含名為title的屬性的對象。

          Ref vs Reactive in Components

          根據(jù)目前為止討論的所有內(nèi)容,答案很簡單,對吧?我們應(yīng)該只將ref()用于基本類型數(shù)據(jù),并將reactive()用于引用類型數(shù)據(jù)。當(dāng)我開始構(gòu)建組件時(shí),情況并非總是如此,事實(shí)上文檔說明:

          The difference between using ref and reactive can be somewhat compared to how you would write standard JavaScript logic.(ref和reactive差別有點(diǎn)就像與你如何編寫規(guī)范化的JS邏輯作對比)

          我開始思考這一點(diǎn),并得出以下結(jié)論。

          在示例中,我們看到了一個(gè)名為title的單個(gè)屬性,它是一個(gè)String,使用ref()非常有意義。但隨著我的應(yīng)用程序開始變得復(fù)雜,我定義了以下屬性:

          export default {
            setup() {
              const title = ref("Hello, World!");
              const description = ref("");
              const content = ref("Hello world");
              const wordCount = computed(() => content.value.length);

              return { title, description, content, wordCount };
            }
          };

          在這種情況下,我會將它們?nèi)糠诺揭粋€(gè)對象中,并使用reactive()方法。

          <template>
            <div class="page">
              <h1>{{ page.title }}</h1>
              <p>{{ page.wordCount }}</p>
            </div>

          </template>

          <script>
            import { ref, computed, reactive } from "vue";

            export default {
              setup() {
                const page = reactive({
                  title: "Hello, World!",
                  description: "",
                  content: "Hello world",
                  wordCount: computed(() => page.content.length)
                });

                return { page };
              }
            };
          </
          script>

          這就是我在組件中一直采用Ref vs Reactive的方式,但我希望收到您的答復(fù),你在做類似的事情嗎?這種方法是錯(cuò)誤的嗎?請?jiān)谙旅娼o我一些反饋。

          創(chuàng)建組合式邏輯(可復(fù)用)

          在組件中使用ref()reactive()都將創(chuàng)建響應(yīng)式性數(shù)據(jù),只要您了解如何在setup()方法和模板中訪問該數(shù)據(jù),就不會有任何問題。

          當(dāng)您開始編寫可組合函數(shù)時(shí),您需要了解它們之間的區(qū)別。我將使用RFC文檔中的示例,因?yàn)樗诮忉尭弊饔梅矫孀龅煤芎谩?/p>

          比如有個(gè)需求是創(chuàng)建一些邏輯,以跟蹤用戶的鼠標(biāo)位置,并且還需要具有在需要此邏輯的任何組件中重用此邏輯的能力?,F(xiàn)在您創(chuàng)建了一個(gè)組合式函數(shù),該函數(shù)跟蹤x和y坐標(biāo),然后將其返回給使用者。

          import { ref, onMounted, onUnmounted } from "vue";

          export function useMousePosition({
            const x = ref(0);
            const y = ref(0);

            function update(e{
              x.value = e.pageX;
              y.value = e.pageY;
            }

            onMounted(() => {
              window.addEventListener("mousemove", update);
            });

            onUnmounted(() => {
              window.removeEventListener("mousemove", update);
            });

            return { x, y };
          }

          如果要在組件中使用此邏輯,則可以調(diào)用這個(gè)組合式函數(shù),對返回對象進(jìn)行解構(gòu),然后將x和y坐標(biāo)返回給模板使用。

          <template>
            <h1>Use Mouse Demo</h1>
            <p>x: {{ x }} | y: {{ y }}</p>
          </template>

          <script>
            import { useMousePosition } from "./u
          se/useMousePosition";

            export default {
              setup() {
                const { x, y } = useMousePosition();
                return { x, y };
              }
            };
          </script>

          上述代碼運(yùn)行沒有任何問題,但是如果你想把x,y重構(gòu)為一個(gè)position對象里面的屬性時(shí):

          import { ref, onMounted, onUnmounted } from "vue";

          export function useMousePosition({
            const pos = {
              x0,
              y0
            };

            function update(e{
              pos.x = e.pageX;
              pos.y = e.pageY;
            }

            // ...
          }

          這種方法的問題在于,組合式函數(shù)的調(diào)用者必須始終保持對返回對象的引用,以保持響應(yīng)式。這意味著該對象不能被解構(gòu)或展開:

          // consuming component
          export default {
            setup() {
              // reactivity lost!
              const { x, y } = useMousePosition();
              return {
                x,
                y
              };

              // reactivity lost!
              return {
                ...useMousePosition()
              };

              // this is the only way to retain reactivity.
              // you must return `pos` as-is and reference x and y as `pos.x` and `pos.y`
              // in the template.
              return {
                pos: useMousePosition()
              };
            }
          };

          這并不意味著您不能使用響應(yīng)式式。有一個(gè)toRefs()方法將響應(yīng)式對象轉(zhuǎn)換為普通對象,結(jié)果就是這個(gè)對象上的每個(gè)屬性都是一個(gè)指向原始對象的響應(yīng)式引用。

          function useMousePosition({
            const pos = reactive({
              x0,
              y0
            });

            // ...
            return toRefs(pos);
          }

          // x & y are now refs!
          const { x, y } = useMousePosition();

          總結(jié)

          當(dāng)我第一次開始使用Composition API創(chuàng)建組件時(shí),我很難理解何時(shí)需要ref()和何時(shí)需要reactive()。上述所研究的案例可能會存在一些差錯(cuò),但是希望有人告訴我一些更好的方式。我希望我能幫助您解決一些問題,并希望在下面聽到您的反饋。感謝您的閱讀,我一如既往的朋友...

          譯者總結(jié)

          • 使用Composition API需要在setup函數(shù)中使用,并且返回需要給模板使用的數(shù)據(jù)(可以了解一下script setup
          • Vue 2創(chuàng)建內(nèi)部響應(yīng)式數(shù)據(jù)的方式是在data()函數(shù)所返回的對象中定義,并且調(diào)用Object.defineProperty()為每個(gè)屬性設(shè)置gettersetter來追蹤數(shù)據(jù)變更。Vue 3內(nèi)部是使用Proxy代理對象來實(shí)現(xiàn)數(shù)據(jù)的響應(yīng)式。
          • ref()定義的響應(yīng)式數(shù)據(jù)需要通過.value來訪問,而在模板中會進(jìn)行一個(gè)拆箱的操作,不需要手動通過.value來訪問。reactive()函數(shù)返回的對象需要在模板里通過.操作符訪問。
          • ref()可以為基本類型和引用類型值創(chuàng)建響應(yīng)式數(shù)據(jù),而為引用類型創(chuàng)建響應(yīng)式數(shù)據(jù)時(shí),內(nèi)部還是調(diào)用了reactive()。而reactive()只能接收一個(gè)對象,我們可以把一些相關(guān)聯(lián)的數(shù)據(jù)都放在這個(gè)對象里,可以提高代碼的可讀性。
          • 如果邏輯可以復(fù)用可以使用組合式函數(shù),這樣其他組件也可以使用這個(gè)邏輯。reactive()函數(shù)返回的對象如果被解構(gòu)的話,里面的數(shù)據(jù)將會失去響應(yīng)式,可以通過toRefs把對象里面的每個(gè)屬性轉(zhuǎn)化成ref來使用。

          原文鏈接

          • Vue 3 Composition API: Ref vs Reactive 鏈接:https://www.danvega.dev/blog/2020/02/12/vue3-ref-vs-reactive


          關(guān)于本文
          作者:不燒油的小火柴
          https://juejin.cn/post/6949432566545907742


          The End

          瀏覽 53
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产福利91极品 | 一区二区三区无码翻白眼 | 性生交大片免费看A片苹果 | 午夜神马福利片 | 日韩日屄视频 |