1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        超全的Vue3文檔【Vue2遷移Vue3】

        共 42226字,需瀏覽 85分鐘

         ·

        2021-03-25 15:11

        關(guān)注 前端瓶子君,回復(fù)“交流

        加入我們一起學(xué)習(xí),天天進(jìn)步

        看到掘金的這篇很全,就整理到自己的公眾號(hào)上來(lái),自己可以隨時(shí)復(fù)習(xí)。原文鏈接如下:

        作者: 三藏會(huì)法術(shù)     
        鏈接:https://juejin.cn/post/6858558735695937544#heading-153

        Vue2與Vue3的全局配置API變化區(qū)別

        createApp

        Vue2.x創(chuàng)建實(shí)例并且掛載DOM上

        import Vue from "vue";
        import App from './App.vue'

        new Vue({
          render: (h) => h(App)
        }).$mount("#app");

        Vue3新增api===>createApp創(chuàng)建實(shí)例

        createApp 會(huì)產(chǎn)生一個(gè) app 實(shí)例,該實(shí)例擁有全局的可配置上下文

        import { createApp } from 'vue'
        import App from './App.vue'

        createApp(App).mount('#app')

        createApp 做了什么

        • ensureRenderer方法追溯過(guò)去底添加patchclass+patchStyle等跟操作DOM相關(guān)的方法

          • ensureRenderer(創(chuàng)建虛擬DOM)一直追溯到createRenderer以及baseCreateRenderer,baseCreateRenderer方法涉及了虛擬DOM的創(chuàng)建更新DIFF算法
        • 之后就是檢查時(shí)候又mount是否掛載在DOM上

        • app對(duì)象上的方法:config、use、mixin、component、directive、mount、unmount、provide/inject

        component

        Vue2.x【注冊(cè)或獲取全局組件。注冊(cè)還會(huì)自動(dòng)使用給定的 id 設(shè)置組件的名稱(chēng)】

        // 注冊(cè)組件,傳入一個(gè)選項(xiàng)對(duì)象 (自動(dòng)調(diào)用 Vue.extend) 

        Vue.component('my-component', { /* ... */ }) 

        // 獲取注冊(cè)的組件 (始終返回構(gòu)造器) 
        var MyComponent = Vue.component('my-component')

        Vue3【注冊(cè)或獲取全局組件. 注冊(cè)還會(huì)自動(dòng)使用給定的 name組件 設(shè)置組件的名稱(chēng)】

        基本vue2寫(xiě)法一致

        import { createApp } from 'vue'

        const app = createApp({})

        // 注冊(cè)組件,傳入一個(gè)選項(xiàng)對(duì)象
        app.component('my-component', {
          /* ... */
        })

        // 獲取注冊(cè)的組件 (始終返回構(gòu)造器) 
        const MyComponent = app.component('my-component', {})

        config【app=createApp(App)】

        devtools

        配置是否允許 vue-devtools 檢查代碼。開(kāi)發(fā)版本默認(rèn)為 true,生產(chǎn)版本默認(rèn)為 false。生產(chǎn)版本設(shè)為 true 可以啟用檢查。

        - Vue.config.devtools = true
        + app.config.devtools = true    

        errorHandler

        - Vue.config.errorHandler = function (err, vm, info) {
          // handle error
          // `info` 是 Vue 特定的錯(cuò)誤信息,比如錯(cuò)誤所在的生命周期鉤子
          // 只在 2.2.0+ 可用
        }
        + app.config.errorHandler = (err, vm, info) => {
          // handle error
          // `info` 是 Vue 特定的錯(cuò)誤信息,比如錯(cuò)誤所在的生命周期鉤子
          // 這里能發(fā)現(xiàn)錯(cuò)誤
        }

        指定組件的渲染和觀察期間未捕獲錯(cuò)誤的處理函數(shù)。這個(gè)處理函數(shù)被調(diào)用時(shí),可獲取錯(cuò)誤信息和 Vue 實(shí)例。

        錯(cuò)誤追蹤服務(wù) Sentry 和 Bugsnag 都通過(guò)此選項(xiàng)提供了官方支持。

        warnHandler

        - Vue.config.warnHandler = function (msg, vm, trace) {
          // `trace` 是組件的繼承關(guān)系追蹤
        }
        + app.config.warnHandler = function(msg, vm, trace) {
          // `trace` 是組件的繼承關(guān)系追蹤
        }

        為 Vue 的運(yùn)行時(shí)警告賦予一個(gè)自定義處理函數(shù)。注意這只會(huì)在開(kāi)發(fā)者環(huán)境下生效,在生產(chǎn)環(huán)境下它會(huì)被忽略。

        globalProperties 【新增屬性】

        app.config.globalProperties.foo = 'bar'

        app.component('child-component', {
          mounted() {
            console.log(this.foo) // 'bar'
          }
        })

        添加可在程序內(nèi)的任何組件實(shí)例中訪問(wèn)的全局屬性。當(dāng)存在鍵沖突時(shí),組件屬性將優(yōu)先替代掉Vue2.x的 Vue.prototype屬性放到原型上的寫(xiě)法

        isCustomElement 【新增屬性】

        替代掉Vue2.x的ignoredElements

        - Vue.config.ignoredElements = [
          // 用一個(gè) `RegExp` 忽略所有“ion-”開(kāi)頭的元素
          // 僅在 2.5+ 支持
          /^ion-/
        ]

        // 一些組件以'ion-'開(kāi)頭將會(huì)被解析為自定義組件
        + app.config.isCustomElement = tag => tag.startsWith('ion-')

        指定一個(gè)方法來(lái)識(shí)別在Vue之外定義的自定義組件(例如,使用Web Component API)。如果組件符合這個(gè)條件,它就不需要本地或全局注冊(cè),Vue也不會(huì)拋出關(guān)于Unknown custom element的警告

        注意,這個(gè)函數(shù)中不需要匹配所有原生HTML和SVG標(biāo)記—Vue解析器會(huì)自動(dòng)執(zhí)行此檢查

        optionMergeStrategies

        const app = Vue.createApp({
          mounted() {
            console.log(this.$options.hello)
          }
        })

        app.config.optionMergeStrategies.hello = (parent, child, vm) => {
          return `Hello, ${child}`
        }

        app.mixin({
          hello: 'Vue'
        })

        // 'Hello, Vue

        定義自定義選項(xiàng)的合并策略。合并策略接收在父實(shí)例options和子實(shí)例options和??子實(shí)例??options,分別作為第一個(gè)和第二個(gè)參數(shù)。上下文Vue實(shí)例作為第三個(gè)參數(shù)傳遞

        【自定義選項(xiàng)合并策略】mixin

        const app = Vue.createApp({
          custom: 'hello!'
        })

        app.config.optionMergeStrategies.custom = (toVal, fromVal) => {
          console.log(fromVal, toVal)
          // => "goodbye!", undefined
          // => "hello!""goodbye!"
          return fromVal || toVal
        }

        app.mixin({
          custom: 'goodbye!',
          created() {
            console.log(this.$options.custom) // => "hello!"
          }
        })
        • optionMergeStrategies先獲取到子實(shí)例的$options的mixin而沒(méi)有父實(shí)例【custom第一次改變從undefined到goodbye--->打印"goodbye!", undefined】

        • 父實(shí)例的options替換掉子實(shí)例的options替換掉子實(shí)例的options【custom第二次從goodbye到hello!--->打印了"hello", "goodbye!"】

        • 最后在打印app.config.optionMergeStrategies.custom返回的父實(shí)例的$options

        無(wú)論如何this.options.custom最后會(huì)返回合并策略的return的值【使用場(chǎng)景利用父子組件的options.custom最后會(huì)返回合并策略的return的值【使用場(chǎng)景利用父子組件的options.custom最后會(huì)返回合并策略的return的值【使用場(chǎng)景利用父子組件的options,然后返回計(jì)算等操作得到所需要的值】optionMergeStrategies合并$options變化

        performance

        - Vue.config.performance=true;
        + app.config.performance=true;

        設(shè)置為 true 以在瀏覽器開(kāi)發(fā)工具的性能/時(shí)間線面板中啟用對(duì)組件初始化、編譯、渲染和打補(bǔ)丁的性能追蹤。只適用于開(kāi)發(fā)模式和支持 performance.mark API 的瀏覽器上。

        directive

        注冊(cè)或獲取全局指令。

        import { createApp } from 'vue'
        const app = createApp({})

        // 注冊(cè)
        app.directive('my-directive', {
          // 指令的生命周期
          // 在綁定元素的父組件被掛載之前調(diào)用
          beforeMount(el, binding, vnode) {},
          // 在掛載綁定元素的父組件時(shí)調(diào)用
          mounted(el, binding, vnode) {},
          // 在更新包含組件的VNode之前調(diào)用
          beforeUpdate(el, binding, vnode, prevNode) {},
          // 組件的VNode及其子組件的VNode更新之后調(diào)用
          updated(el, binding, vnode, prevNode) {},
          // 在卸載綁定元素的父組件之前調(diào)用
          beforeUnmount(el, binding, vnode) {},
          // 在卸載綁定元素的父組件時(shí)調(diào)用
          unmounted(el, binding, vnode) {}
        })

        // 注冊(cè) (指令函數(shù))
        app.directive('my-directive', (el, binding, vnode, prevNode) => {
          // 這里將會(huì)被 `mounted` 和 `updated` 調(diào)用
        })

        // getter,返回已注冊(cè)的指令
        const myDirective = app.directive('my-directive')
        • el

        指令綁定到的元素。這可以用來(lái)直接操作DOM。

        • binding【包含下列屬性的對(duì)象】
          • instance:使用指令的組件的實(shí)例
          • value:指令的綁定值,例如:v-my-directive="1 + 1"中,綁定值為 2
          • oldValue:指令綁定的前一個(gè)值,僅在 beforeUpdate 和 updated 鉤子中可用。無(wú)論值是否改變都可用
          • arg:傳給指令的參數(shù),可選。例如 v-my-directive:foo 中,參數(shù)為 "foo"
          • modifiers:一個(gè)包含修飾符的對(duì)象。例如:v-my-directive.foo.bar 中,修飾符對(duì)象為 { foo: true, bar: true }
          • dir:一個(gè)對(duì)象,在注冊(cè)指令時(shí)作為參數(shù)傳遞; 舉個(gè)例子,看下面指令
        app.directive('focus', {
          mounted(el) {
            el.focus()
          }
        })

        dir就是下面的對(duì)象

        {
          mounted(el) {
            el.focus()
          }
        }
        • vnode

        編譯生成的虛擬節(jié)點(diǎn)

        • prevNode

        前一個(gè)虛擬節(jié)點(diǎn),僅在beforeUpdate和updated鉤子中可用

        tips:除了 el 之外,其它參數(shù)都應(yīng)該是只讀的,切勿進(jìn)行修改。如果需要在鉤子之間共享數(shù)據(jù),建議通過(guò)元素的 dataset 來(lái)進(jìn)行

        mixin【基本Vue2.x一致】

        全局注冊(cè)一個(gè)混入,影響注冊(cè)之后所有創(chuàng)建的每個(gè) Vue 實(shí)例。插件作者可以使用混入,向組件注入自定義的行為。不推薦在應(yīng)用代碼中使用。

        mount【類(lèi)似Vue2.x】

        在所提供的DOM元素上掛載應(yīng)用程序?qū)嵗母M件

        import { createApp } from 'vue'

        const app = createApp({})
        // 做一些準(zhǔn)備
        app.mount('#my-app')

        provide/inject【Vue2.x一致】

        該選項(xiàng)與inject一起使用,允許一個(gè)祖先組件作為其所有后代的依賴(lài)注入器,無(wú)論組件層次結(jié)構(gòu)有多深,只要它們位于同一父鏈中就可以。

        provide 選項(xiàng)應(yīng)該是一個(gè)對(duì)象或返回一個(gè)對(duì)象的函數(shù)。該對(duì)象包含可注入其子孫的 property。在該對(duì)象中你可以使用 ES2015 Symbols 作為 key,但是只在原生支持 Symbol 和 Reflect.ownKeys 的環(huán)境下可工作。

        如果在組件中兩者都只能在當(dāng)前活動(dòng)組件實(shí)例的 setup() 中調(diào)用,詳細(xì)請(qǐng)看依賴(lài)注入部分

        import { createApp } from 'vue'

        const app = createApp({
          provide: {
            user: 'John Doe'
          }
        })

        app.component('user-card', {
          inject: ['user'],
          template: `
            <div>
              {{ user }}
            </div>
          `
        })

        unmount【新增屬性】

        在所提供的DOM元素上卸載應(yīng)用程序?qū)嵗母M件

        import { createApp } from 'vue'

        const app = createApp({})
        // 做一些必要的準(zhǔn)備
        app.mount('#my-app')

        // 應(yīng)用程序?qū)⒃趻燧d后5秒被卸載
        setTimeout(() => app.unmount('#my-app'), 5000)

        use【Vue2.x一致】

        安裝 Vue.js 插件。如果插件是一個(gè)對(duì)象,必須提供 install 方法。如果插件是一個(gè)函數(shù),它會(huì)被作為 install 方法。install 方法調(diào)用時(shí),會(huì)將 Vue 作為參數(shù)傳入。

        當(dāng) install 方法被同一個(gè)插件多次調(diào)用,插件將只會(huì)被安裝一次。

        setup

        setup 函數(shù)是一個(gè)新的組件選項(xiàng)。作為在組件內(nèi)使用 Composition API 的入口點(diǎn), 注意 setup 返回的 ref 在模板中會(huì)自動(dòng)解開(kāi),不需要寫(xiě) .value【setup 內(nèi)部需要.value】

        調(diào)用時(shí)機(jī)

        • 創(chuàng)建組件實(shí)例,然后初始化 props ,緊接著就調(diào)用setup 函數(shù)。從生命周期鉤子的視角來(lái)看,它會(huì)在 beforeCreate 鉤子之前被調(diào)用

        • 如果 setup 返回一個(gè)對(duì)象,則對(duì)象的屬性將會(huì)被合并到組件模板的渲染上下文

        參數(shù)

        props 作為其第一個(gè)參數(shù)

        注意 props 對(duì)象是響應(yīng)式的,watchEffect 或 watch 會(huì)觀察和響應(yīng) props 的更新不要解構(gòu) props 對(duì)象,那樣會(huì)使其失去響應(yīng)性。

        export default {
          props: {
            name: String,
          },
          setup(props) {
            console.log(props.name)
             watchEffect(() => {
              console.log(`name is: ` + props.name)
            })
          },
        }

        第二個(gè)參數(shù)提供了一個(gè)上下文對(duì)象【從原來(lái) 2.x 中 this 選擇性地暴露了一些 property(attrs/emit/slots)】

        attrs 和 slots 都是內(nèi)部組件實(shí)例上對(duì)應(yīng)項(xiàng)的代理,可以確保在更新后仍然是最新值。所以可以解構(gòu),無(wú)需擔(dān)心后面訪問(wèn)到過(guò)期的值

        為什么props作為第一個(gè)參數(shù)?

        • 組件使用 props 的場(chǎng)景更多,有時(shí)候甚至只使用 props

        • 將 props 獨(dú)立出來(lái)作為第一個(gè)參數(shù),可以讓 TypeScript 對(duì) props 單獨(dú)做類(lèi)型推導(dǎo),不會(huì)和上下文中的其他屬性相混淆。這也使得 setup 、 render 和其他使用了 TSX 的函數(shù)式組件的簽名保持一致

        this 在 setup() 中不可用。由于 setup() 在解析 2.x 選項(xiàng)前被調(diào)用,setup() 中的 this 將與 2.x 選項(xiàng)中的 this 完全不同。同時(shí)在 setup() 和 2.x 選項(xiàng)中使用 this 時(shí)將造成混亂

        setup(props, { attrs }) {
          // 一個(gè)可能之后回調(diào)用的簽名
          function onClick() {
            console.log(attrs.foo) // 一定是最新的引用,沒(méi)有丟失響應(yīng)性
          }
        }

        響應(yīng)式系統(tǒng) API

        reactive

        接收一個(gè)普通對(duì)象然后返回該普通對(duì)象的響應(yīng)式代理【等同于 2.x 的 Vue.observable()】

        Proxy對(duì)象是目標(biāo)對(duì)象的一個(gè)代理器,任何對(duì)目標(biāo)對(duì)象的操作(實(shí)例化,添加/刪除/修改屬性等等),都必須通過(guò)該代理器。因此我們可以把來(lái)自外界的所有操作進(jìn)行攔截和過(guò)濾或者修改等操作

        響應(yīng)式轉(zhuǎn)換是“深層的”:會(huì)影響對(duì)象內(nèi)部所有嵌套的屬性?;?ES2015 的 Proxy 實(shí)現(xiàn),返回的代理對(duì)象不等于原始對(duì)象。建議僅使用代理對(duì)象而避免依賴(lài)原始對(duì)象

        reactive 類(lèi)的 api 主要提供了將復(fù)雜類(lèi)型的數(shù)據(jù)處理成響應(yīng)式數(shù)據(jù)的能力,其實(shí)這個(gè)復(fù)雜類(lèi)型是要在object array map set weakmap weakset 這五種之中【如下源碼,他會(huì)判斷是否是五類(lèi)以及是否被凍結(jié)】

        因?yàn)槭墙M合函數(shù)【對(duì)象】,所以必須始終保持對(duì)這個(gè)所返回對(duì)象的引用以保持響應(yīng)性【不能解構(gòu)該對(duì)象或者展開(kāi)】例如 const { x, y } = useMousePosition()或者return { ...useMousePosition() }

        function useMousePosition() {
            const pos = reactive({
                x: 0,
                y: 0,
              })
            return pos
        }

        toRefs API 用來(lái)提供解決此約束的辦法——它將響應(yīng)式對(duì)象的每個(gè) property 都轉(zhuǎn)成了相應(yīng)的 ref【把對(duì)象轉(zhuǎn)成了ref】。

         function useMousePosition() {
            const pos = reactive({
                x: 0,
                y: 0,
              })
            return toRefs(pos)
        }

        // x & y 現(xiàn)在是 ref 形式了!
        const { x, y } = useMousePosition()

        ref

        接受一個(gè)參數(shù)值并返回一個(gè)響應(yīng)式且可改變的 ref 對(duì)象。ref 對(duì)象擁有一個(gè)指向內(nèi)部值的單一屬性 .value

        const count = ref(0)
        console.log(count.value) // 0

        如果傳入 ref 的是一個(gè)對(duì)象,將調(diào)用 reactive 方法進(jìn)行深層響應(yīng)轉(zhuǎn)換

        陷阱

        setup 中return返回會(huì)自動(dòng)解套【在模板中不需要.value】

        ref 作為 reactive 對(duì)象的 property 被訪問(wèn)或修改時(shí),也將自動(dòng)解套 .value

        const count = ref(0)
        /*當(dāng)做reactive的對(duì)象屬性----解套*/
        const state = reactive({
          count,
        })
        /* 不需要.value*/
        console.log(state.count) // 0

        /*修改reactive的值*/
        state.count = 1
        /*修改了ref的值*/
        console.log(count.value) // 1

        注意如果將一個(gè)新的 ref 分配給現(xiàn)有的 ref, 將替換舊的 ref

        /*創(chuàng)建一個(gè)新的ref*/
        const otherCount = ref(2)

        /*賦值給reactive的舊的ref,舊的會(huì)被替換掉*/
        state.count = otherCount
        /*修改reactive會(huì)修改otherCount*/
        console.log(state.count) // 2
        /*修改reactive會(huì)count沒(méi)有被修改 */
        console.log(count.value) // 1

        嵌套在 reactive Object 中時(shí),ref 才會(huì)解套。從 Array 或者 Map 等原生集合類(lèi)中訪問(wèn) ref 時(shí),不會(huì)自動(dòng)解套【自由數(shù)據(jù)類(lèi)型是Object才會(huì)解套,array  map  set   weakmap  weakset集合類(lèi) 訪問(wèn) ref 時(shí),不會(huì)自動(dòng)解套】

        const arr = reactive([ref(0)])
        // 這里需要 .value
        console.log(arr[0].value)

        const map = reactive(new Map([['foo', ref(0)]]))
        // 這里需要 .value
        console.log(map.get('foo').value)

        心智負(fù)擔(dān)上 ref vs reactive

        • 在普通 JavaScript 中區(qū)別聲明基礎(chǔ)類(lèi)型變量與對(duì)象變量時(shí)一樣區(qū)別使用 ref 和 reactive
        • 所有的地方都用 reactive,然后記得在組合函數(shù)返回響應(yīng)式對(duì)象時(shí)使用 toRefs。這降低了一些關(guān)于 ref 的心智負(fù)擔(dān)

        readonly

        傳入一個(gè)對(duì)象(響應(yīng)式或普通)或 ref,返回一個(gè)原始對(duì)象的只讀代理。一個(gè)只讀的代理是“深層的”,對(duì)象內(nèi)部任何嵌套的屬性也都是只讀的【返回一個(gè)永遠(yuǎn)不會(huì)變的只讀代理】【場(chǎng)景可以參數(shù)比對(duì)等】

        const original = reactive({ count: 0 })

        const copy = readonly(original)

        watchEffect(() => {
          // 依賴(lài)追蹤
          console.log(copy.count)
        })

        // original 上的修改會(huì)觸發(fā) copy 上的偵聽(tīng)
        original.count++

        // 無(wú)法修改 copy 并會(huì)被警告
        copy.count++ // warning!

        reactive響應(yīng)式系統(tǒng)工具集

        isProxy

        檢查一個(gè)對(duì)象是否是由 reactive 或者 readonly 方法創(chuàng)建的代理

        isReactive

        檢查一個(gè)對(duì)象是否是由 reactive 創(chuàng)建的響應(yīng)式代理

        import { reactive, isReactive } from 'vue'
        const state = reactive({
              name: 'John'
            })
        console.log(isReactive(state)) // -> true

        如果這個(gè)代理是由 readonly 創(chuàng)建的,但是又被 reactive 創(chuàng)建的另一個(gè)代理包裹了一層,那么同樣也會(huì)返回 true

        import { reactive, isReactive, readonly } from 'vue'
        const state = reactive({
              name: 'John'
            })
        // 用readonly創(chuàng)建一個(gè)只讀響應(yīng)式對(duì)象plain
        const plain = readonly({
            name: 'Mary'
        })
        //readonly創(chuàng)建的,所以isReactive為false
        console.log(isReactive(plain)) // -> false  

        // reactive創(chuàng)建的響應(yīng)式代理對(duì)象包裹一層readonly,isReactive也是true,isReadonly也是true
        const stateCopy = readonly(state)
        console.log(isReactive(stateCopy)) // -> true

        isReadonly

        檢查一個(gè)對(duì)象是否是由 readonly 創(chuàng)建的只讀代理

        reactive高級(jí)響應(yīng)式系統(tǒng)API

        toRaw

        返回由 reactive 或 readonly 方法轉(zhuǎn)換成響應(yīng)式代理的普通對(duì)象。這是一個(gè)還原方法,可用于臨時(shí)讀取,訪問(wèn)不會(huì)被代理/跟蹤,寫(xiě)入時(shí)也不會(huì)觸發(fā)更改。不建議一直持有原始對(duì)象的引用【不建議賦值給任何變量】。請(qǐng)謹(jǐn)慎使用

        toRaw之后的對(duì)象是沒(méi)有被代理/跟蹤的的普通對(duì)象

        const foo = {}
        const reactiveFoo = reactive(foo)

        console.log(toRaw(reactiveFoo) === foo) // true
        console.log(toRaw(reactiveFoo) !== reactiveFoo) // true

        markRaw

        顯式標(biāo)記一個(gè)對(duì)象為“永遠(yuǎn)不會(huì)轉(zhuǎn)為響應(yīng)式代理”,函數(shù)返回這個(gè)對(duì)象本身。被 markRaw 標(biāo)記了,即使在響應(yīng)式對(duì)象中作屬性,也依然不是響應(yīng)式的

        const foo = markRaw({
            name: 'Mary'
        })
        console.log(isReactive(reactive(foo))) // false

        markRaw 注意點(diǎn)

        • markRaw和 shallowXXX 一族的 API允許選擇性的覆蓋reactive或者readonly 默認(rèn)創(chuàng)建的 "深層的" 特性【響應(yīng)式】/或者使用無(wú)代理的普通對(duì)象

        • 設(shè)計(jì)這種「淺層讀取」有很多原因

          • 一些值的實(shí)際上的用法非常簡(jiǎn)單,并沒(méi)有必要轉(zhuǎn)為響應(yīng)式【例如三方庫(kù)的實(shí)例/省市區(qū)json/Vue組件對(duì)象】
          • 當(dāng)渲染一個(gè)元素?cái)?shù)量龐大,但是數(shù)據(jù)是不可變的,跳過(guò) Proxy 的轉(zhuǎn)換可以帶來(lái)性能提升
        • 這些 API 被認(rèn)為是高級(jí)的,是因?yàn)檫@種特性?xún)H停留在根級(jí)別,所以如果你將一個(gè)嵌套的,沒(méi)有 markRaw 的對(duì)象設(shè)置為 reactive 對(duì)象的屬性,在重新訪問(wèn)時(shí),你又會(huì)得到一個(gè) Proxy 的版本,在使用中最終會(huì)導(dǎo)致標(biāo)識(shí)混淆的嚴(yán)重問(wèn)題:執(zhí)行某個(gè)操作同時(shí)依賴(lài)于某個(gè)對(duì)象的原始版本和代理版本(標(biāo)識(shí)混淆在一般使用當(dāng)中應(yīng)該是非常罕見(jiàn)的,但是要想完全避免這樣的問(wèn)題,必須要對(duì)整個(gè)響應(yīng)式系統(tǒng)的工作原理有一個(gè)相當(dāng)清晰的認(rèn)知)。

        const foo = markRaw({
          nested: {},
        })

        const bar = reactive({
          // 盡管 `foo` 己經(jīng)被標(biāo)記為 raw 了, 但 foo.nested 并沒(méi)有
          nested: foo.nested,
        })

        console.log(foo.nested === bar.nested) // false
        • foo.nested沒(méi)有被標(biāo)記為(永遠(yuǎn)不會(huì)轉(zhuǎn)為響應(yīng)式代理),導(dǎo)致最后的值一個(gè)reactive

        shallowReactive

        只為某個(gè)對(duì)象的私有(第一層)屬性創(chuàng)建淺層的響應(yīng)式代理,不會(huì)對(duì)“屬性的屬性”做深層次、遞歸地響應(yīng)式代理,而只是保留原樣【第一層是響應(yīng)式代理,深層次只保留原樣(不具備響應(yīng)式代理)】

        const state = shallowReactive({
          foo: 1,
          nested: {
            bar: 2,
          },
        })

        // 變更 state 的自有屬性是響應(yīng)式的【第一層次響應(yīng)式】
        state.foo++
        // ...但不會(huì)深層代理【深層次不是響應(yīng)式】(渲染性能)
        isReactive(state.nested) // false
        state.nested.bar++ // 非響應(yīng)式

        shallowReadonly

        類(lèi)似于shallowReactive,區(qū)別是:

        • 第一層將會(huì)是響應(yīng)式代理【第一層修改屬性會(huì)失敗】,屬性為響應(yīng)式
        • 深層次的對(duì)象屬性可以修改,屬性不是響應(yīng)式
        const state = shallowReadonly({
          foo: 1,
          nested: {
            bar: 2,
          },
        })

        // 變更 state 的自有屬性會(huì)失敗
        state.foo++
        // ...但是嵌套的對(duì)象是可以變更的
        isReadonly(state.nested) // false
        state.nested.bar++ // 嵌套屬性依然可修改

        ref 響應(yīng)式系統(tǒng)工具集

        customRef

        用于自定義一個(gè) ref,可以顯式地控制依賴(lài)追蹤和觸發(fā)響應(yīng),接受一個(gè)工廠函數(shù),兩個(gè)參數(shù)分別是用于追蹤的 track 與用于觸發(fā)響應(yīng)的 trigger,并返回一個(gè)一個(gè)帶有 get 和 set 屬性的對(duì)象【實(shí)際上就是手動(dòng) track追蹤 和 trigger觸發(fā)響應(yīng)】

        • 以下代碼可以使得v-model防抖
        function useDebouncedRef(value, delay = 200) {
          let timeout
          return customRef((track, trigger) => {
            return {
              get() {
                  /*初始化手動(dòng)追蹤依賴(lài)講究什么時(shí)候去觸發(fā)依賴(lài)收集*/
                track()
                return value
              },
              set(newValue) {
                  /*修改數(shù)據(jù)的時(shí)候會(huì)把上一次的定時(shí)器清除【防抖】*/
                clearTimeout(timeout)
                timeout = setTimeout(() => {
                    /*把新設(shè)置的數(shù)據(jù)給到ref數(shù)據(jù)源*/
                  value = newValue
                    /*再有依賴(lài)追蹤的前提下觸發(fā)響應(yīng)式*/
                  trigger()
                }, delay)
              },
            }
          })
        }

        setup() {
            return {
                /*暴露返回的數(shù)據(jù)加防抖*/
              text: useDebouncedRef('hello'),
            }
          }

        shallowRef

        創(chuàng)建一個(gè) ref ,將會(huì)追蹤它的 .value 更改操作,但是并不會(huì)對(duì)變更后的 .value 做響應(yīng)式代理轉(zhuǎn)換(即變更不會(huì)調(diào)用 reactive)

        前面我們說(shuō)過(guò)如果傳入 ref 的是一個(gè)對(duì)象,將調(diào)用 reactive 方法進(jìn)行深層響應(yīng)轉(zhuǎn)換,通過(guò)shallowRef創(chuàng)建的ref,將不會(huì)調(diào)用reactive【對(duì)象不會(huì)是響應(yīng)式的】

        const refOne = shallowRef({});
        refOne.value = { id: 1 };
        refOne.id == 20;
        console.log(isReactive(refOne.value),refOne.value);//false  { id: 1 }

        triggerRef 【與shallowRef配合】

        手動(dòng)執(zhí)行與shallowRef相關(guān)的任何效果

        const shallow = shallowRef({
          greet: 'Hello, world'
        })

        // 第一次運(yùn)行打印 "Hello, world" 
        watchEffect(() => {
          console.log(shallow.value.greet)
        })

        // 這不會(huì)觸發(fā)效果,因?yàn)閞ef是shallow
        shallow.value.greet = 'Hello, universe'

        // 打印 "Hello, universe"
        triggerRef(shallow)

        Computed and watch【監(jiān)控變化】

        computed

        • 傳入一個(gè) getter 函數(shù),返回一個(gè)默認(rèn)不可手動(dòng)修改的 ref 對(duì)象【默認(rèn)傳入的是get函數(shù)的對(duì)象】
        • 傳入一個(gè)擁有 get 和 set 函數(shù)的對(duì)象,創(chuàng)建一個(gè)可手動(dòng)修改的計(jì)算狀態(tài)
        const count = ref(1)
        /*不支持修改【只讀的】 */
        const plusOne = computed(() => count.value + 1)
        plusOne.value++ // 錯(cuò)誤!

        /*【可更改的】 */
        const plusOne = computed({
          get: () => count.value + 1,
          set: (val) => {
            count.value = val - 1
          },
        })

        plusOne.value = 1
        console.log(count.value) // 0

        watchEffect

        立即執(zhí)行傳入的一個(gè)函數(shù),并響應(yīng)式追蹤其依賴(lài),并在其依賴(lài)變更時(shí)重新運(yùn)行該函數(shù)

        computed與watchEffect區(qū)別:

        • computed 計(jì)算屬性可通過(guò)setup return,再模板中使用,watchEffect不能;
        • computed可以使用多個(gè),并且對(duì)多個(gè)屬性進(jìn)行不同的響應(yīng)計(jì)算,watchEffect會(huì)存在副作用
        const count = ref(0)

        watchEffect(() => console.log(count.value))
        // -> 打印出 0

        setTimeout(() => {
          count.value++
          // -> 打印出 1
        }, 100)

        當(dāng)在組件的setup()函數(shù)或生命周期鉤子期間調(diào)用watchEffect時(shí),監(jiān)視程序會(huì)鏈接到組件的生命周期,并在卸載組件時(shí)自動(dòng)停止,一般情況下watchEffect返回可以stop 操作,停止監(jiān)聽(tīng)程序

        const stop = watchEffect(() => {
          /* ... */
        })

        // 停止監(jiān)聽(tīng)程序
        stop()

        副作用(函數(shù)式編程)

        一個(gè)帶有副作用的函數(shù)不僅只是簡(jiǎn)單的返回一個(gè)值,還干了一些其他的事情,比如:

        • 修改一個(gè)變量
        • 直接修改數(shù)據(jù)結(jié)構(gòu)
        • 設(shè)置一個(gè)對(duì)象的成員
        • 拋出一個(gè)異?;蛞砸粋€(gè)錯(cuò)誤終止
        • 打印到終端或讀取用戶(hù)的輸入
        • 讀取或?qū)懭胍粋€(gè)文件
        • 在屏幕上繪畫(huà)

        如果一個(gè)函數(shù)內(nèi)外有依賴(lài)于外部變量或者環(huán)境時(shí),常常我們稱(chēng)之為其有副作用,如果我們僅通過(guò)函數(shù)簽名不打開(kāi)內(nèi)部代碼檢查并不能知道該函數(shù)在干什么,作為一個(gè)獨(dú)立函數(shù)我們期望有明確的輸入和輸出,副作用是bug的發(fā)源地,作為程序員開(kāi)發(fā)者應(yīng)盡量少的開(kāi)發(fā)有副作用的函數(shù)或方法,副作用也使得方法通用性下降不適合擴(kuò)展和可重用性

        清除副作用

        在一些時(shí)候監(jiān)聽(tīng)函數(shù)將執(zhí)行異步副作用【一個(gè)響應(yīng)式依賴(lài)被修改了,會(huì)做其他事情】,這些響應(yīng)需要在其失效時(shí)清除(例如在效果完成前狀態(tài)改變)。effect函數(shù)接收一個(gè)onInvalidate 函數(shù)作入?yún)ⅲ?用來(lái)注冊(cè)清理失效時(shí)的回調(diào)。這個(gè) invalidation函數(shù) 在什么時(shí)候會(huì)被調(diào)用:

        • 監(jiān)聽(tīng)函數(shù)重新被執(zhí)行的時(shí)候【響應(yīng)式依賴(lài)的數(shù)據(jù)被修改】
        • 監(jiān)聽(tīng)停止的時(shí)候(如果watchEffect在setup()或者生命周期函數(shù)中被使用的時(shí)候組件會(huì)被卸載)【停止觀察】
        watchEffect(onInvalidate => {
          /*這是個(gè)異步操作*/
          const token = performAsyncOperation(id.value)//id依賴(lài)
          onInvalidate(() => {
            // id被修改了或者監(jiān)聽(tīng)停止了會(huì)觸發(fā)token.cancel()事件【這塊區(qū)域的代碼】.
            // 這里是異步事件的話,前面的peding的異步操作無(wú)效【這里的異步事件只執(zhí)行一次】
             token.cancel()/*異步操作*/
            console.log('onInvalidate')
          })
        })
          
          從上面看:我們之所以是通過(guò)傳入一個(gè)函數(shù)去注冊(cè)失效回調(diào),而不是從回調(diào)返回它(如 React `useEffect` 中的方式),是因?yàn)榉祷刂祵?duì)于異步錯(cuò)誤處理很重要
          
          ````js
          const data = ref(null)
          watchEffect(async onInvalidate => {
            onInvalidate(() => {...}) // 我們?cè)赑romise的resolves之前注冊(cè)清理函數(shù)(cleanup function)
            data.value = await fetchData(props.id)
          })

        我們知道異步函數(shù)都會(huì)隱式地返回一個(gè) Promise,但是清理副作用的函數(shù)必須要在 Promise 被 resolve 之前被注冊(cè)。另外,Vue 依賴(lài)這個(gè)返回的 Promise 來(lái)自動(dòng)處理 Promise 鏈上的潛在錯(cuò)誤

        副作用刷新時(shí)機(jī)

        Vue 的響應(yīng)式系統(tǒng)會(huì)緩存副作用函數(shù),并異步地刷新它們,這樣可以避免同一個(gè) tick 中多個(gè)狀態(tài)改變導(dǎo)致的不必要的重復(fù)調(diào)用。在核心的具體實(shí)現(xiàn)中, 組件的更新函數(shù)也是一個(gè)被偵聽(tīng)的副作用。當(dāng)一個(gè)用戶(hù)定義的副作用函數(shù)進(jìn)入隊(duì)列時(shí), 會(huì)在所有的組件更新后執(zhí)行

        <template>
          <div>{{ count }}</div>
        </template>

        <script>
          export default {
            setup() {
              const count = ref(0)

              watchEffect(() => {
                console.log(count.value)
              })

              return {
                count,
              }
            },
          }
        </script>
        • count 會(huì)在初始運(yùn)行時(shí)同步打印出來(lái)
        • 更改 count 時(shí),將在組件更新后執(zhí)行副作用

        初始化運(yùn)行是在組件 mounted 之前執(zhí)行的【你希望在編寫(xiě)副作用函數(shù)時(shí)訪問(wèn) DOM(或模板 ref),請(qǐng)?jiān)?onMounted 鉤子中進(jìn)行】

        onMounted(() => {
          watchEffect(() => {
            // 在這里可以訪問(wèn)到 DOM 或者 template refs
          })
        })

        如果副作用需要同步或在組件更新之前重新運(yùn)行,我們可以傳遞一個(gè)擁有 flush 屬性的對(duì)象作為選項(xiàng)(默認(rèn)為 'post')

        // 同步運(yùn)行
        watchEffect(
          () => {
            /* ... */
          },
          {
            flush: 'sync',
          }
        )

        // 組件更新前執(zhí)行
        watchEffect(
          () => {
            /* ... */
          },
          {
            flush: 'pre',
          }
        )

        偵聽(tīng)器調(diào)試【響應(yīng)式調(diào)試用的】

        onTrack 和 onTrigger 選項(xiàng)可用于調(diào)試一個(gè)偵聽(tīng)器的行為。

        • 當(dāng)一個(gè) reactive 對(duì)象屬性或一個(gè) ref 作為依賴(lài)被追蹤時(shí),將調(diào)用 onTrack【調(diào)用次數(shù)為被追蹤的數(shù)量】
        • 依賴(lài)項(xiàng)變更會(huì)導(dǎo)致重新追蹤依賴(lài),從而onTrack被調(diào)用【調(diào)用次數(shù)為被追蹤的數(shù)量】
        • 的數(shù)量】 依賴(lài)項(xiàng)變更會(huì)導(dǎo)致重新追蹤依賴(lài),從而onTrack被調(diào)用【調(diào)用次數(shù)為被追蹤的數(shù)量】 依賴(lài)項(xiàng)變更導(dǎo)致副作用被觸發(fā)時(shí),將調(diào)用 onTrigger

        這兩個(gè)回調(diào)都將接收到一個(gè)包含有關(guān)所依賴(lài)項(xiàng)信息的調(diào)試器事件。建議在以下回調(diào)中編寫(xiě) debugger 語(yǔ)句來(lái)檢查依賴(lài)關(guān)系:【onTrack 和 onTrigger 僅在開(kāi)發(fā)模式下生效】

        watchEffect(
          () => {
            /* 副作用的內(nèi)容 */
          },
          {
            onTrigger(e) {
              /*副作用依賴(lài)修改*/
              debugger
            },
            onTrack(e) {
              /*副作用依賴(lài)修改*/
              debugger
            },
          }
        )

        watch

        watch API 完全等效于 2.x watch 中相應(yīng)的選項(xiàng)。watch 需要偵聽(tīng)特定的數(shù)據(jù)源,并在回調(diào)函數(shù)中執(zhí)行副作用【默認(rèn)情況是懶執(zhí)行的,也就是說(shuō)僅在偵聽(tīng)的源變更時(shí)才執(zhí)行回調(diào)】

        watch允許我們:

        • 懶執(zhí)行副作用
        • 更明確哪些狀態(tài)的改變會(huì)觸發(fā)偵聽(tīng)器重新運(yùn)行副作用
        • 訪問(wèn)偵聽(tīng)狀態(tài)變化前后的值

        偵聽(tīng)單個(gè)數(shù)據(jù)源

        偵聽(tīng)器的數(shù)據(jù)源可以是一個(gè)擁有返回值的 getter 函數(shù),也可以是 ref:

        // 偵聽(tīng)一個(gè) getter
        const state = reactive({ count: 0 })
        watch(
          () => state.count,
          (count, prevCount) => {
            /* ... */
          }
        )

        // 直接偵聽(tīng)一個(gè) ref
        const count = ref(0)
        watch(count, (count, prevCount) => {
          /* ... */
        })

        偵聽(tīng)多個(gè)數(shù)據(jù)源

        watcher 也可以使用數(shù)組來(lái)同時(shí)偵聽(tīng)多個(gè)源

        watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
          /* ... */
        })

        與 watchEffect 共享的行為

        watch 和 watchEffect 在停止偵聽(tīng), 清除副作用 (相應(yīng)地 onInvalidate 會(huì)作為回調(diào)的第三個(gè)參數(shù)傳入),副作用刷新時(shí)機(jī) 和 偵聽(tīng)器調(diào)試 等方面行為一致:

        watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar],onInvalidate) => {
          /* ... */
          onInvalidate(() => {...})
        },
          {
            onTrigger(e) {
              /*副作用依賴(lài)修改*/
              debugger
            },
            onTrack(e) {
              /*副作用依賴(lài)修改*/
              debugger
            },
          })

        生命周期鉤子函數(shù)

        與 2.x 版本生命周期相對(duì)應(yīng)的組合式 API

        • beforeCreate -> 使用 setup()
        • created -> 使用 setup()
        • beforeMount -> onBeforeMount
        • mounted -> onMounted
        • beforeUpdate -> onBeforeUpdate
        • updated -> onUpdated
        • beforeDestroy -> onBeforeUnmount
        • destroyed -> onUnmounted
        • errorCaptured -> onErrorCaptured
        import { onMounted, onUpdated, onUnmounted } from 'vue'
        setup() {
            onMounted(() => {
              console.log('mounted!')
            })
            onUpdated(() => {
              console.log('updated!')
            })
            onUnmounted(() => {
              console.log('unmounted!')
            })
          }

        這些生命周期鉤子注冊(cè)函數(shù)只能在 setup() 期間同步使用, 因?yàn)樗鼈円蕾?lài)于內(nèi)部的全局狀態(tài)來(lái)定位當(dāng)前組件實(shí)例(正在調(diào)用 setup() 的組件實(shí)例), 不在當(dāng)前組件下調(diào)用這些函數(shù)會(huì)拋出一個(gè)錯(cuò)誤。

        組件實(shí)例上下文也是在生命周期鉤子同步執(zhí)行期間設(shè)置的,因此,在卸載組件時(shí),在生命周期鉤子內(nèi)部同步創(chuàng)建的偵聽(tīng)器和計(jì)算狀態(tài)也將自動(dòng)刪除。

        新增的鉤子函數(shù)

        除了和 2.x 生命周期等效項(xiàng)之外,組合式 API 還提供了以下調(diào)試鉤子函數(shù):

        • onRenderTracked
        • onRenderTriggered

        兩個(gè)鉤子函數(shù)都接收一個(gè) DebuggerEvent,與 watchEffect 參數(shù)選項(xiàng)中的 onTrack 和 onTrigger 類(lèi)似:

        export default {
          onRenderTracked(e){
              debugger
            // 檢查有響應(yīng)和追蹤的依賴(lài)性
         },
          onRenderTriggered(e) {
            debugger
            // 檢查哪個(gè)依賴(lài)性導(dǎo)致組件重新渲染
          },
        }

        Vue提供的內(nèi)置組件

        component 與Vue2.x一致

        渲染一個(gè)“元組件”為動(dòng)態(tài)組件。依 is 的值,來(lái)決定哪個(gè)組件被渲染。

        <!-- 動(dòng)態(tài)組件由 vm 實(shí)例的 `componentId` property 控制 -->
        <component :is="componentId"></component>

        <!-- 也能夠渲染注冊(cè)過(guò)的組件或 prop 傳入的組件 -->
        <component :is="$options.components.child"></component>

        transition 與 Vue2.x 【基本】 一致有差異

        Props新增

        • persisted - boolean 如果為true,則表示這是一個(gè)轉(zhuǎn)換,實(shí)際上不會(huì)插入/刪除元素,而是切換顯示/隱藏狀態(tài)。transition 過(guò)渡掛鉤被注入,但會(huì)被渲染器跳過(guò)。相反,自定義指令可以通過(guò)調(diào)用注入的鉤子(例如v-show)來(lái)控制過(guò)渡

        • enter-class----->enter-from-class

        • ~~leave-class----->leave-from-class

        事件

        • before-appear

        transition-group 與 Vue2.x 一致

        **slot 與 Vue2.x 一致 **

        teleport 【新增組件】

        Props

        to - string 必填屬性,必須是一個(gè)有效的query選擇器,或者是元素(如果在瀏覽器環(huán)境中使用)。中的內(nèi)容將會(huì)被放置到指定的目標(biāo)元素中

        <!-- 正確的 -->
        <teleport to="#some-id" />
        <teleport to=".some-class" />
         /*元素*/
        <teleport to="[data-teleport]" />

        <!-- 錯(cuò)誤的 -->
        <teleport to="h1" />
        <teleport to="some-string" />

        disabled - boolean 這是一個(gè)可選項(xiàng) ,做一個(gè)是可以用來(lái)禁用的功能,這意味著它的插槽內(nèi)容不會(huì)移動(dòng)到任何地方,而是按沒(méi)有teleport組件一般來(lái)呈現(xiàn)【默認(rèn)為false】

        <teleport to="#popup" :disabled="displayVideoInline">
          <h1>999999</h1>
        </teleport>

        注意,這將移動(dòng)實(shí)際的DOM節(jié)點(diǎn),而不是銷(xiāo)毀和重新創(chuàng)建,并且還將保持任何組件實(shí)例是活動(dòng)的。所有有狀態(tài)HTML元素(比如一個(gè)正在播放的視頻)將保持它們的狀態(tài)。【控制displayVideoInline并不是銷(xiāo)毀重建,它保持實(shí)例是存在的,不會(huì)被注銷(xiāo)】

        關(guān)于Teleport 其他內(nèi)容

        Vue 鼓勵(lì)我們通過(guò)將UI和相關(guān)行為封裝到組件中來(lái)構(gòu)建UI。我們可以將它們彼此嵌套在一起,以構(gòu)建構(gòu)成應(yīng)用程序UI的樹(shù)

        但是,有時(shí)組件模板的一部分邏輯上屬于這個(gè)組件,而從技術(shù)角度來(lái)看,最好將這一部分模板移到DOM中的其他地方,放到Vue應(yīng)用程序之外

        一個(gè)常見(jiàn)的場(chǎng)景是創(chuàng)建一個(gè)包含全屏模態(tài)的組件。在大多數(shù)情況下,您希望模態(tài)的邏輯駐留在組件中,但是模態(tài)框的定位問(wèn)題很快就很難通過(guò)CSS解決,或者需要更改組件的組成

        考慮下面的HTML結(jié)構(gòu):

        <body>
          <div style="position: relative;">
            <h3>Tooltips with Vue 3 Teleport</h3>
            <div>
              <modal-button></modal-button>
            </div>
          </div>
        </body>

        讓我們看看 mode -button

        該組件將有一個(gè)button元素來(lái)觸發(fā)模態(tài)的打開(kāi),還有一個(gè)div元素,其類(lèi)為.modal,它將包含模態(tài)的內(nèi)容和一個(gè)自關(guān)閉按鈕

        const app = Vue.createApp({});

        app.component('modal-button', {
          template: `
            <button @click="modalOpen = true">
                Open full screen modal!
            </button>

            <div v-if="modalOpen" class="modal">
              <div>
                I'm a modal! 
                <button @click="modalOpen = false">
                  Close
                </button>
              </div>
            </div>
          `,
          data() {
            return { 
              modalOpen: false
            }
          }
        })

        當(dāng)在初始HTML結(jié)構(gòu)中使用這個(gè)組件時(shí),我們可以看到一個(gè)問(wèn)題——模態(tài)被呈現(xiàn)在深嵌套的div中,模態(tài)的絕對(duì)位置以父div相對(duì)位置作為參考。

        Teleport提供了一種干凈的方式,允許我們控制DOM中希望在哪個(gè)父節(jié)點(diǎn)下呈現(xiàn)HTML片段,而不必訴諸全局狀態(tài)或?qū)⑵洳鸱譃閮蓚€(gè)組件。

        讓我們修改我們的modal-button來(lái)使用并告訴Vue "teleport this HTML to the "body"標(biāo)簽"。

        app.component('modal-button', {
          template: `
            <button @click="modalOpen = true">
                Open full screen modal! (With teleport!)
            </button>

            <teleport to="body">
              <div v-if="modalOpen" class="modal">
                <div>
                  I'm a teleported modal! 
                  (My parent is "body")
                  <button @click="modalOpen = false">
                    Close
                  </button>
                </div>
              </div>
            </teleport>
          `,
          data() {
            return { 
              modalOpen: false
            }
          }
        })

        與Vue組件一起使用

        如果包含一個(gè)Vue組件,它將仍然是的父組件的邏輯子組件

        const app = Vue.createApp({
          template: `
            <h1>Root instance</h1>
            <parent-component />
          `
        })

        app.component('parent-component', {
          template: `
            <h2>This is a parent component</h2>
            <teleport to="#endofbody">
              <child-component name="John" />
            </teleport>
          `
        })

        app.component('child-component', {
          props: ['name'],
          template: `
            <div>Hello, {{ name }}</div>
          `
        })

        在這種情況下,即使在不同的地方呈現(xiàn)child-component,它仍將是parent-componen的子組件【而不是爺爺組件】,并將從其父組件接收一個(gè)name 的props

        這也意味著來(lái)自父組件的注入如預(yù)期的那樣工作,并且子組件將嵌套在Vue Devtools的父組件之下,而不是放在實(shí)際內(nèi)容移動(dòng)到的地方

        對(duì)同一目標(biāo)使用多次teleports

        一個(gè)常見(jiàn)的用例場(chǎng)景是一個(gè)可重用的組件,該組件可能同時(shí)有多個(gè)活動(dòng)實(shí)例。對(duì)于這種場(chǎng)景,多個(gè)組件可以將它們的內(nèi)容掛載到相同的目標(biāo)元素。這個(gè)順序?qū)⑹且粋€(gè)簡(jiǎn)單的附加—稍后的掛載將位于目標(biāo)元素中較早的掛載之后。

        <teleport to="#modals">
          <div>A</div>
        </teleport>
        <teleport to="#modals">
          <div>B</div>
        </teleport>

        <!-- result-->
        <div id="modals">
          <div>A</div>
          <div>B</div>
        </div>

        依賴(lài)注入Provide / Inject

        provide 和 inject 提供依賴(lài)注入,功能類(lèi)似 2.x 的 provide/inject。兩者都只能在當(dāng)前活動(dòng)組件實(shí)例的 setup() 中調(diào)用。

        例如,如果我們想在根組件上提供一個(gè)book name,并將其inject到子組件上

        import { provide, inject } from 'vue'

        const RootComponent = {
          setup() {
            provide('book''Vue 3 guide')
          }
        }

        const MyBook = {
          setup() {
            const book = inject(
              'book',
              'Eloquent Javascript' /* 選項(xiàng)的默認(rèn)值,假如父組件不提供值就返回默認(rèn) */
            )
            return {
              book
            }
          }
        }

        inject 接受一個(gè)可選的的默認(rèn)值作為第二個(gè)參數(shù)。如果未提供默認(rèn)值,并且在 provide 上下文中未找到該屬性,則 inject 返回 undefined。

        如果我們需要提供或注入多個(gè)值,我們可以通過(guò)隨后分別調(diào)用provide或inject來(lái)實(shí)現(xiàn)【多次調(diào)用】

        import { provide, inject } from 'vue'

        const RootComponent = {
          setup() {
            provide('book''Vue 3 guide')
            provide('year''2020')
          }
        }

        const MyBook = {
          setup() {
            const book = inject(
              'book',
              'Eloquent Javascript' /* 選項(xiàng)的默認(rèn)值,假如父組件不提供值就返回默認(rèn) */
            )
            const year = inject('year')
            return {
              book,
              year
            }
          }
        }

        注入的響應(yīng)性

        可以使用 ref 或 reactive 來(lái)保證 provided 和 injected 之間值的響應(yīng)

        import { ref, reactive } from 'vue'

        // 提供者
        setup() {
          const book = reactive({
            title: 'Vue 3 Guide',
            author: 'Vue Team'
          })
          const year = ref('2020')

         /*提供reactive響應(yīng)式*/
          provide('book', book)
         /*提供ref響應(yīng)式*/
          provide('year', year)
        }

        // 消費(fèi)者
        setup() {
          const book = inject('book')
          const year = inject('year')
         /*響應(yīng)式*/
          return { book, year }
        }

        現(xiàn)在,當(dāng)提供者組件上的book或year發(fā)生變化時(shí),我們可以觀察到它們?cè)谧⑷氲慕M件上的變化。

        警告:我們不建議改變一個(gè)被注入的反應(yīng)性屬性【子組件去修改數(shù)據(jù)流】,因?yàn)樗鼤?huì)破壞Vue的單向數(shù)據(jù)流。相反,嘗試在提供值【父組件去修改】的地方改變值,或者提供一個(gè)方法來(lái)改變值

        import { ref, reactive } from 'vue'

        // in provider
        setup() {
          const book = reactive({
            title: 'Vue 3 Guide',
            author: 'Vue Team'
          })

          function changeBookName() {
            book.title = 'Vue 3 Advanced Guide'
          }

          provide('book', book)
          provide('changeBookName', changeBookName)
        }

        // in consumer
        setup() {
          const book = inject('book')
          const changeBookName = inject('changeBookName')

          return { book, changeBookName }
        }

        指令

        v-text 【Vue2.x一致】

        v-html【Vue2.x一致】

        v-show【Vue2.x一致】

        v-if【Vue2.x一致】

        v-else【Vue2.x一致】

        v-else-if【Vue2.x一致】

        v-for【Vue2.x一致】

        v-on【Vue2.x一致】

        v-bind 【Vue2.x 修飾符差異】

        修飾符

        .prop 去除.sync 去除.camel 將 kebab-case attribute 名轉(zhuǎn)換為 camelCase

        v-model【Vue2.x一致】

        v-slot【Vue2.x一致】

        v-cloak【Vue2.x一致】

        v-once 【Vue2.x一致】

        v-pre【Vue2.x一致】

        v-is【新增】

        注意:本節(jié)只影響在頁(yè)面的HTML中直接編寫(xiě)Vue模板的情況

        全局API

        createApp

        返回一個(gè)應(yīng)用程序?qū)嵗?提供了一個(gè)應(yīng)用程序上下文。應(yīng)用程序?qū)嵗龗燧d的整個(gè)組件樹(shù)共享相同的上下文。

        const app = Vue.createApp({})

        參數(shù):該函數(shù)接收一個(gè)根組件選項(xiàng)對(duì)象作為第一個(gè)參數(shù)

        const app = Vue.createApp({
          data() {
            return {
              ...
            }
          },
          methods: {...},
          computed: {...}
          setup(){...}
          ...
        })

        使用第二個(gè)參數(shù),我們可以將根組件props 傳遞給應(yīng)用

        <div id="app">
          <!-- 這里將會(huì)顯示 'Evan' -->
          {{ username }}
        </div>

        const app = Vue.createApp(
          {
            props: ['username']
          },
          { username: 'Evan' }
        )

        h

        返回“虛擬節(jié)點(diǎn)”,通??s寫(xiě)為VNode:一個(gè)簡(jiǎn)單的對(duì)象,它包含描述Vue應(yīng)該在頁(yè)面上渲染何種類(lèi)型的節(jié)點(diǎn)的信息,包括對(duì)任何子節(jié)點(diǎn)的描述。你可以手動(dòng)閱讀render functions

        render() {
          return Vue.h('h1', {}, 'Some title')
        }

        參數(shù):接受三個(gè)參數(shù)tag, props and children

        tag:

        類(lèi)型:String | Object | Function | null 詳情:一個(gè)HTML標(biāo)簽名,一個(gè)組件,一個(gè)異步組件或null。使用null將渲染成注釋。此參數(shù)是必需的

        props

        類(lèi)型:Object 詳情:模板中使用的attributes、props 和events 對(duì)應(yīng)的對(duì)象??蛇x

        children

        類(lèi)型: String | Array | Object

        詳情:Children VNodes,使用h()構(gòu)建,或使用字符串來(lái)獲取“text VNodes”或帶有槽的對(duì)象??蛇x

        const aaa = {
          props: {
            someProp: String
          },
          setup(props) {
            console.log(props, "dsadasdasddasds");
          },
          render() {
            return h(
              "h2",
                // {Object}props
                //與props,attributes和events相對(duì)應(yīng)的對(duì)象
                //我們將在template中使用。
                // 可選的。
                {style: {"font-size""20px",
                  color: "#136"}},
                  [this.someProp,this.$slots.default()]);
                }
        };
        app.component("anchored-heading", {
          render() {
            return h(
                /*
          // {String | Object | Function | null}標(biāo)簽
                // HTML標(biāo)記名稱(chēng),組件,異步組件或null。
                //使用null將渲染注釋。
                //必填
                */
              "h" + this.level, // tag name
                // {Object}props
                //與props,attributes和events相對(duì)應(yīng)的對(duì)象
                //我們將在template中使用。
                // 可選的。
              {}, 
                // {String | Array | Object} children
                //使用`h()`構(gòu)建的子級(jí)VNode,
                //或使用字符串獲取“文本VNodes”或
                //具有插槽的對(duì)象。
                // 可選的。
              [
                "Some text comes first.",
                h("h1""A headline"),
                h(aaa, {
                  someProp: "foobar"
                })
              ]  );},
        });
         Vue.h(
                'a',
                {
                  name: headingId,
                  href: '#' + headingId
                },
                this.$slots.default()
              )
            ])

        defineAsyncComponent 【異步組件】

        創(chuàng)建只在必要時(shí)加載的異步組件

        參數(shù)

        對(duì)于基本用法,defineAsyncComponent可以接受返回Promise的工廠函數(shù)。當(dāng)您從serve檢索到組件定義時(shí),應(yīng)該調(diào)用Promise的解析回調(diào)。您還可以調(diào)用reject(reason)來(lái)指示加載失敗。

        import { defineAsyncComponent } from 'vue'


        const AsyncComp = defineAsyncComponent(() =>
           /*或者*/
          import('./components/AsyncComponent.vue')
           /*或者*/
          new Promise((resolve, reject) => {
          /*可以reject*/
              resolve({
                template: '<div>I am async!</div>'
              })
            })
        )

        app.component('async-component', AsyncComp)

        在使用本地注冊(cè)時(shí),還可以直接提供返回Promise的函數(shù)

        import { createApp, defineAsyncComponent } from 'vue'

        createApp({
          // ...
          components: {
            AsyncComponent: defineAsyncComponent(() =>
              import('./components/AsyncComponent.vue')
            )
          }
        })

        resolveDynamicComponent【解析活動(dòng)的組件active】

        resolveDynamicComponent只能在render或setup函數(shù)中使用。允許使用與component:is=""相同的機(jī)制來(lái)解析組件。返回解析的組件或一個(gè)新創(chuàng)建的VNode以組件名稱(chēng)作為節(jié)點(diǎn)標(biāo)記的。如果沒(méi)有找到組件,會(huì)發(fā)出警告

        resolveDirective

        resolveDirective只能在render或setup函數(shù)中使用。允許通過(guò)名稱(chēng)解析指令,如果它在當(dāng)前應(yīng)用程序?qū)嵗锌捎?。返回一個(gè)Directive或 當(dāng)沒(méi)有找到的時(shí)候,返回undefined。

        app.directive('highlight', {})
        render(){
            const highlightDirective = resolveDirective('highlight')
        }

        withDirectives

        警告withDirectives只能在render或setup函數(shù)中使用。允許應(yīng)用指令到VNode。返回一個(gè)帶有應(yīng)用指令的VNode。

        const bar = resolveDirective('bar')

        return withDirectives(h('div'), [
          [bar, this.y]
        ])

        nextTick

        將回調(diào)延遲到下一個(gè)DOM更新周期之后執(zhí)行。在更改了一些數(shù)據(jù)以等待DOM更新之后立即使用它

        setup() {
            const message = ref('Hello!')
            const changeMessage = async newMessage => {
              message.value = newMessage
              /*等待DOM更新*/
              await nextTick()
              console.log('Now DOM is updated')
            }
          }

        最后

        歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
        回復(fù)「算法」,加入前端算法源碼編程群,每日一刷(工作日),每題瓶子君都會(huì)很認(rèn)真的解答喲!
        回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
        回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
        如果這篇文章對(duì)你有幫助,在看」是最大的支持
        》》面試官也在看的算法資料《《
        “在看和轉(zhuǎn)發(fā)”就是最大的支持
        瀏覽 129
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            大鸡巴操逼的视频 | 中文天堂新在线 | 久久久久久久久久久韩国男女 | 69AV无码 | 俺来射| 国产无码福利 | 嗯灬啊灬快灬高潮了 | 三上悠亚ssni在线 | 国产一二三视频 | www.奇米色.com |