<em id="09ttv"></em>
    <sup id="09ttv"><pre id="09ttv"></pre></sup>
    <dd id="09ttv"></dd>

        • Vue 3.0 前瞻,體驗(yàn) Vue Function API

          2019-9-1    seo達(dá)人

          概述

          Vue 2.x 及以前的高階組件的組織形式或多或少都會(huì)面臨一些問題,特別是在需要處理重復(fù)邏輯的項(xiàng)目中,一旦開發(fā)者組織項(xiàng)目結(jié)構(gòu)組織得不好,組件代碼極有可能被人詬病為“膠水代碼”。而在 Vue 2.x 及之前的版本,解決此類問題的辦法大致是下面的方案:



          mixin

          函數(shù)式組件

          slots

          筆者維護(hù)的項(xiàng)目也需要處理大量復(fù)用邏輯,在這之前,筆者一直嘗試使用mixin的方式來實(shí)現(xiàn)組件的復(fù)用。有些問題也一直會(huì)對(duì)開發(fā)者和維護(hù)者造成困惑,如一個(gè)組件同時(shí)mixin多個(gè)組件,很難分清對(duì)應(yīng)的屬性或方法寫在哪個(gè)mixin里。其次,mixin的命名空間沖突也可能造成問題。難以保證不同的mixin不用到同一個(gè)屬性名。為此,官方團(tuán)隊(duì)提出函數(shù)式寫法的意見征求稿,也就是RFC:Function-based component API。使用函數(shù)式的寫法,可以做到更靈活地復(fù)用組件,開發(fā)者在組織高階組件時(shí),不必在組件組織上考慮復(fù)用,可以更好地把精力集中在功能本身的開發(fā)上。



          注:本文只是筆者使用vue-function-api提前體驗(yàn) Vue Function API ,而這個(gè) API 只是 Vue 3.0 的 RFC,而并非與最終 Vue 3.x API 一致。發(fā)布后可能有不一致的地方。



          在 Vue 2.x 中使用

          要想提前在Vue 2.x中體驗(yàn) Vue Function API ,需要引入vue-function-api,基本引入方式如下:



          import Vue from 'vue';

          import { plugin as VueFunctionApiPlugin } from 'vue-function-api';

           

          Vue.use(VueFunctionApiPlugin);

          基本組件示例

          先來看一個(gè)基本的例子:



          <template>

              <div>

                  <span>count is {{ count }}</span>

                  <span>plusOne is {{ plusOne }}</span>

                  <button @click="increment">count++</button>

              </div>

          </template>

           

          <script>

          import Vue from 'vue';

          import { value, computed, watch, onMounted } from 'vue-function-api';

           

          export default {

              setup(props, context) {

                  // reactive state

                  const count = value(0);

                  // computed state

                  const plusOne = computed(() => count.value + 1);

                  // method

                  const increment = () => {

                      count.value++;

                  };

                  // watch

                  watch(

                      () => count.value 2,

                      val => {

                          console.log(`count
          2 is ${val});<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br /> &nbsp; &nbsp; &nbsp; &nbsp; );<br /> &nbsp; &nbsp; &nbsp; &nbsp; // lifecycle<br /> &nbsp; &nbsp; &nbsp; &nbsp; onMounted(() =&gt; {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log(mounted);<br /> &nbsp; &nbsp; &nbsp; &nbsp; });<br /> &nbsp; &nbsp; &nbsp; &nbsp; // expose bindings on render context<br /> &nbsp; &nbsp; &nbsp; &nbsp; return {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; plusOne,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; increment,<br /> &nbsp; &nbsp; &nbsp; &nbsp; };<br /> &nbsp; &nbsp; },<br /> };<br /> &lt;/script&gt;<br /> 詳解<br /> setup<br /> setup函數(shù)是Vue Function API 構(gòu)建的函數(shù)式寫法的主邏輯,當(dāng)組件被創(chuàng)建時(shí),就會(huì)被調(diào)用,函數(shù)接受兩個(gè)參數(shù),分別是父級(jí)組件傳入的props和當(dāng)前組件的上下文context。看下面這個(gè)例子,可以知道在context中可以獲取到下列屬性值<br /> <br /> const MyComponent = {<br /> &nbsp; &nbsp; props: {<br /> &nbsp; &nbsp; &nbsp; &nbsp; name: String<br /> &nbsp; &nbsp; },<br /> &nbsp; &nbsp; setup(props, context) {<br /> &nbsp; &nbsp; &nbsp; &nbsp; console.log(props.name);<br /> &nbsp; &nbsp; &nbsp; &nbsp; // context.attrs<br /> &nbsp; &nbsp; &nbsp; &nbsp; // context.slots<br /> &nbsp; &nbsp; &nbsp; &nbsp; // context.refs<br /> &nbsp; &nbsp; &nbsp; &nbsp; // context.emit<br /> &nbsp; &nbsp; &nbsp; &nbsp; // context.parent<br /> &nbsp; &nbsp; &nbsp; &nbsp; // context.root<br /> &nbsp; &nbsp; }<br /> }<br /> value &amp; state<br /> value函數(shù)創(chuàng)建一個(gè)包裝對(duì)象,它包含一個(gè)響應(yīng)式屬性value:<br /> <br /> <br /> <br /> 那么為何要使用value呢,因?yàn)樵贘avaScript中,基本類型并沒有引用,為了保證屬性是響應(yīng)式的,只能借助包裝對(duì)象來實(shí)現(xiàn),這樣做的好處是組件狀態(tài)會(huì)以引用的方式保存下來,從而可以被在setup中調(diào)用的不同的模塊的函數(shù)以參數(shù)的形式傳遞,既能復(fù)用邏輯,又能方便地實(shí)現(xiàn)響應(yīng)式。<br /> <br /> 直接獲取包裝對(duì)象的值必須使用.value,但是,如果包裝對(duì)象作為另一個(gè)響應(yīng)式對(duì)象的屬性,Vue內(nèi)部會(huì)通過proxy來自動(dòng)展開包裝對(duì)象。同時(shí),在模板渲染的上下文中,也會(huì)被自動(dòng)展開。<br /> <br /> import { state, value } from 'vue-function-api';<br /> const MyComponent = {<br /> &nbsp; &nbsp; setup() {<br /> &nbsp; &nbsp; &nbsp; &nbsp; const count = value(0);<br /> &nbsp; &nbsp; &nbsp; &nbsp; const obj = state({<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count,<br /> &nbsp; &nbsp; &nbsp; &nbsp; });<br /> &nbsp; &nbsp; &nbsp; &nbsp; console.log(obj.count) // 作為另一個(gè)響應(yīng)式對(duì)象的屬性,會(huì)被自動(dòng)展開<br /> &nbsp;<br /> &nbsp; &nbsp; &nbsp; &nbsp; obj.count++ // 作為另一個(gè)響應(yīng)式對(duì)象的屬性,會(huì)被自動(dòng)展開<br /> &nbsp; &nbsp; &nbsp; &nbsp; count.value++ // 直接獲取響應(yīng)式對(duì)象,必須使用.value<br /> &nbsp;<br /> &nbsp; &nbsp; &nbsp; &nbsp; return {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count,<br /> &nbsp; &nbsp; &nbsp; &nbsp; };<br /> &nbsp; &nbsp; },<br /> &nbsp; &nbsp; template:<button @click="count++">{{ count }}</button>,<br /> };<br /> 如果某一個(gè)狀態(tài)不需要在不同函數(shù)中被響應(yīng)式修改,可以通過state創(chuàng)建響應(yīng)式對(duì)象,這個(gè)state創(chuàng)建的響應(yīng)式對(duì)象并不是包裝對(duì)象,不需要使用.value來取值。<br /> <br /> watch &amp; computed<br /> watch和computed的基本概念與 Vue 2.x 的watch和computed一致,watch可以用于追蹤狀態(tài)變化來執(zhí)行一些后續(xù)操作,computed用于計(jì)算屬性,用于依賴屬性發(fā)生變化進(jìn)行重新計(jì)算。<br /> <br /> computed返回一個(gè)只讀的包裝對(duì)象,和普通包裝對(duì)象一樣可以被setup函數(shù)返回,這樣就可以在模板上下文中使用computed屬性。可以接受兩個(gè)參數(shù),第一個(gè)參數(shù)返回當(dāng)前的計(jì)算屬性值,當(dāng)傳遞第二個(gè)參數(shù)時(shí),computed是可寫的。<br /> <br /> import { value, computed } from 'vue-function-api';<br /> &nbsp;<br /> const count = value(0);<br /> const countPlusOne = computed(() =&gt; count.value + 1);<br /> &nbsp;<br /> console.log(countPlusOne.value); // 1<br /> &nbsp;<br /> count.value++;<br /> console.log(countPlusOne.value); // 2<br /> &nbsp;<br /> // 可寫的計(jì)算屬性值<br /> const writableComputed = computed(<br /> &nbsp; &nbsp; // read<br /> &nbsp; &nbsp; () =&gt; count.value + 1,<br /> &nbsp; &nbsp; // write<br /> &nbsp; &nbsp; val =&gt; {<br /> &nbsp; &nbsp; &nbsp; &nbsp; count.value = val - 1;<br /> &nbsp; &nbsp; },<br /> );<br /> watch第一個(gè)參數(shù)和computed類似,返回被監(jiān)聽的包裝對(duì)象屬性值,不過另外需要傳遞兩個(gè)參數(shù):第二個(gè)參數(shù)是回調(diào)函數(shù),當(dāng)數(shù)據(jù)源發(fā)生變化時(shí)觸發(fā)回調(diào)函數(shù),第三個(gè)參數(shù)是options。其默認(rèn)行為與 Vue 2.x 有所不同:<br /> <br /> lazy:是否會(huì)在組件創(chuàng)建時(shí)就調(diào)用一次回調(diào)函數(shù),與 Vue 2.x 相反,lazy默認(rèn)是false,默認(rèn)會(huì)在組件創(chuàng)建時(shí)調(diào)用一次。<br /> deep:與 Vue 2.x 的 deep 一致<br /> flush:有三個(gè)可選值,分別為 'post'(在渲染后,即nextTick后才調(diào)用回調(diào)函數(shù)),'pre'(在渲染前,即nextTick前調(diào)用回調(diào)函數(shù)),'sync'(同步觸發(fā))。默認(rèn)值為'post'。<br /> // double 是一個(gè)計(jì)算包裝對(duì)象<br /> const double = computed(() =&gt; count.value * 2);<br /> &nbsp;<br /> watch(double, value =&gt; {<br /> &nbsp; &nbsp; console.log('double the count is: ', value);<br /> }); // -&gt; double the count is: 0<br /> &nbsp;<br /> count.value++; // -&gt; double the count is: 2<br /> 當(dāng)watch多個(gè)被包裝對(duì)象屬性時(shí),參數(shù)均可以通過數(shù)組的方式進(jìn)行傳遞,同時(shí),與 Vue 2.x 的vm.$watch一樣,watch返回取消監(jiān)聽的函數(shù):<br /> <br /> const stop = watch(<br /> &nbsp; &nbsp; [valueA, () =&gt; valueB.value],<br /> &nbsp; &nbsp; ([a, b], [prevA, prevB]) =&gt; {<br /> &nbsp; &nbsp; &nbsp; &nbsp; console.log(a is: ${a});<br /> &nbsp; &nbsp; &nbsp; &nbsp; console.log(b is: ${b});<br /> &nbsp; &nbsp; }<br /> );<br /> &nbsp;<br /> stop();<br /> 注意:在RFC:Function-based component API初稿中,有提到effect-cleanup,是用于清理一些特殊情況的副作用的,目前已經(jīng)在提案中被取消了。<br /> <br /> 生命周期<br /> 所有現(xiàn)有的生命周期都有對(duì)應(yīng)的鉤子函數(shù),通過onXXX的形式創(chuàng)建,但有一點(diǎn)不同的是,destoryed鉤子函數(shù)需要使用unmounted代替:<br /> <br /> import { onMounted, onUpdated, onUnmounted } from 'vue-function-api';<br /> &nbsp;<br /> const MyComponent = {<br /> &nbsp; &nbsp; setup() {<br /> &nbsp; &nbsp; &nbsp; &nbsp; onMounted(() =&gt; {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log('mounted!');<br /> &nbsp; &nbsp; &nbsp; &nbsp; });<br /> &nbsp; &nbsp; &nbsp; &nbsp; onUpdated(() =&gt; {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log('updated!');<br /> &nbsp; &nbsp; &nbsp; &nbsp; });<br /> &nbsp; &nbsp; &nbsp; &nbsp; // destroyed 調(diào)整為 unmounted<br /> &nbsp; &nbsp; &nbsp; &nbsp; onUnmounted(() =&gt; {<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log('unmounted!');<br /> &nbsp; &nbsp; &nbsp; &nbsp; });<br /> &nbsp; &nbsp; },<br /> };<br /> 一些思考<br /> 上面的詳解部分,主要抽取的是 Vue Function API 的常見部分,并非RFC:Function-based component API的全部,例如其中的依賴注入,TypeScript類型推導(dǎo)等優(yōu)勢(shì),在這里,由于篇幅有限,想要了解更多的朋友,可以點(diǎn)開RFC:Function-based component API查看。個(gè)人也在Function-based component API討論區(qū)看到了更多地一些意見:<br /> <br /> 由于底層設(shè)計(jì),在setup取不到組件實(shí)例this的問題,這個(gè)問題在筆者嘗試體驗(yàn)時(shí)也遇到了,期待正式發(fā)布的 Vue 3.x 能夠改進(jìn)這個(gè)問題。<br /> <br /> 對(duì)于基本類型的值必須使用包裝對(duì)象的問題:在 RFC 討論區(qū),為了同時(shí)保證TypeScript類型推導(dǎo)、復(fù)用性和保留Vue的數(shù)據(jù)監(jiān)聽,包裝屬性必須使用.value來取值是討論最激烈的<br /> <br /> 關(guān)于包裝對(duì)象value和state方法命名不清晰可能導(dǎo)致開發(fā)者誤導(dǎo)等問題,已經(jīng)在Amendment proposal to Function-based Component API這個(gè)提議中展開了討論:<br /> <br /> setup() {<br /> &nbsp; &nbsp; const state = reactive({<br /> &nbsp; &nbsp; &nbsp; &nbsp; count: 0,<br /> &nbsp; &nbsp; });<br /> &nbsp;<br /> &nbsp; &nbsp; const double = computed(() =&gt; state.count * 2);<br /> &nbsp;<br /> &nbsp; &nbsp; function increment() {<br /> &nbsp; &nbsp; &nbsp; &nbsp; state.count++;<br /> &nbsp; &nbsp; }<br /> &nbsp;<br /> &nbsp; &nbsp; return {<br /> &nbsp; &nbsp; &nbsp; &nbsp; ...toBindings(state), // retains reactivity on mutations made tostate`

                  double,

                  increment,

              };

          }

           



          引入reactive API 和 binding API,其中reactive API 類似于 state API , binding API 類似于 value API。

          之前使用的方法名state在 Vue 2.x 中可能被用作組件狀態(tài)對(duì)象,導(dǎo)致變量命名空間的沖突問題,團(tuán)隊(duì)認(rèn)為將state API 更名為 reactive 更為優(yōu)雅。開發(fā)者能夠?qū)懗鯿onst state = ... ,然后通過state.xxxx這種方式來獲取組件狀態(tài),這樣也相對(duì)而言自然一些。

          value方法用于封裝基本類型時(shí),確實(shí)會(huì)出現(xiàn)不夠優(yōu)雅的.value的情況,開發(fā)者可能會(huì)在直接對(duì)包裝對(duì)象取值時(shí)忘記使用.value,修正方案提出的 reactive API,其含義是創(chuàng)建響應(yīng)式對(duì)象,初始化狀態(tài)state就使用reactive創(chuàng)建,可保留每項(xiàng)屬性的getter和setter,這么做既滿足類型推導(dǎo),也可以保留響應(yīng)式引用,從而可在不同模塊中共享狀態(tài)值的引用。

          但reactive可能導(dǎo)致下面的問題,需要引入binding API。 解決,如使用reactive創(chuàng)建的響應(yīng)式對(duì)象,對(duì)其使用拓展運(yùn)算符...時(shí),則會(huì)丟失對(duì)象的getter和setter,提供toBindings方法能夠保留狀態(tài)的響應(yīng)式。

          當(dāng)然,目前 Vue Function API 還處在討論階段,Vue 3.0 還處在開發(fā)階段,還是期待下半年 Vue 3.0 的初版問世吧,希望能給我們帶來更多的驚喜。


          日歷

          鏈接

          個(gè)人資料

          存檔

          久久精品国产免费观看三人同眠| 久久久久久九九99精品| 香蕉久久av一区二区三区| 无码人妻少妇久久中文字幕蜜桃 | 婷婷久久综合| 99久久国产精品免费一区二区| www.久久热| 伊人久久大香线蕉综合5g| 久久精品国产99国产电影网| 久久精品国产99久久久香蕉| 少妇精品久久久一区二区三区| 99精品国产在热久久| 理论片午午伦夜理片久久| 99久久国语露脸精品国产| 久久人人超碰精品CAOPOREN | 色欲久久久天天天综合网| 国产亚洲精品美女久久久| 国内精品伊人久久久影院| 久久无码人妻精品一区二区三区| 7777久久久国产精品消防器材| 久久99精品国产麻豆不卡| 精品久久一区二区| 欧美日韩久久中文字幕| 久久伊人中文无码| 国产99久久久国产精免费| 国产综合免费精品久久久| 精品久久久久久无码专区| 99久久国产综合精品女同图片| 色婷婷综合久久久久中文字幕| 热久久这里只有精品| 色天使久久综合网天天| 久久久久久噜噜精品免费直播| 婷婷综合久久中文字幕| 午夜精品久久影院蜜桃| 久久精品这里只有精99品| 91久久九九无码成人网站 | 欧美黑人又粗又大久久久| 国产69精品久久久久9999APGF | AV无码久久久久不卡网站下载| 亚洲国产精品高清久久久| 天天爽天天狠久久久综合麻豆|