天天看點

Vue3 組合式API

組合式 API 基礎
(就是将一個元件中的不同邏輯分離開來,放在不同的js檔案中,再引入,使用者不需要知道怎麼實作,隻需要知道提供了哪些功能)
  • setup 元件選項
  • 新的 setup 選項在元件建立之前執行,一旦 props 被解析,就将作為組合式 API 的入口。
  • setup 中避免使用

    this

    , setup的調用 在

    data

    methods

    computed

    解析之前
  • setup 選項是一個接收

    props

    context

    的函數
  • 将setup 傳回的函數 暴露給 元件的其餘部分 (計算屬性、方法、生命周期)
  • 帶 ref 的響應式變量
    • 在 Vue 3.0 中,我們可以通過一個新的 ref 函數使任何響應式變量在任何地方起作用
    • toRefs(props)

      props

      屬性的響應式引用
import { ref } from 'vue'
   const counter = ref(0)
           
  • ref 接收參數并将其包裹在一個帶有 value property 的對象中傳回,然後可以使用該 property 通路或更改響應式變量的值:
import { ref } from 'vue'

    const counter = ref(0)

    console.log(counter) // { value: 0 }
    console.log(counter.value) // 0
           
  • 在 setup 内注冊生命周期鈎子
  • 為了使組合式 API 的功能和選項式 API 一樣完整,我們還需要一種在 setup 中注冊生命周期鈎子的方法。
  • 這些函數接受一個回調,當鈎子被元件調用時,該回調将被執行。
import {ref, onMounted} from 'vue'
    setup(){
        const count = ref(0);
        const getCount = function(){
            return count + 6;
        }
        onMounted(getCount);
        return {
            count,
            getCount
        }
    }
           
  • watch 響應式更改
  • 接收三個參數
    • 一個想要偵聽的響應式引用或 getter 函數
    • 一個回調
    • 可選的配置選項
import { ref, watch } from 'vue'
    const counter = ref(0)
    watch(counter, (newValue, oldValue) => {
        console.log('The new counter value is: ' + counter.value)
    })
           
  • 獨立的 computed 屬性
  • 與 ref 和 watch 類似,也可以使用從 Vue 導入的 computed 函數在 Vue 元件外部建立計算屬性。
import { onMounted , ref , toRefs , computed , watch } from 'vue'
    setup(props){
        // 将 name 變成 props name 屬性的引用
        const { name } = toRefs(props);
        // 将 username 變成響應式的 
        const username = ref('');
        // 方法
        const getUserName = function(){
            username.value ='ch' + name;
        }
        // 生命周期函數 
        onMounted(getUserName);
        // 監聽 username 變化 觸發 getUserName方法
        watch(username,getUserName);
        // 計算屬性
        const enName = computed(()=>'en' + name.value)

        return {
            username,
            enName,
            getUserName
        }
    }
           
Setup
  • 接收兩個參數
    • props
    • context
  • props
  • props 是響應式的

    (不要使用 ES6解構 會去掉它的響應式)

  • 如果需要解構

    prop

    ,可以在

    setup

    函數中使用

    toRefs

    函數來完成此操作:
import { toRefs } from 'vue';

    setup(props){
        // name 不再是響應式
        // let { name } = props;
        // 這裡的name是響應式的
        let { name } = toRefs(props);
    }
           
  • 對于非必填(可選)的props 不可以使用 toRefs() 結構
  • toRefs 将不會為 title(可選參數) 建立一個 ref ,需要使用 toRef 替代它
import { toRefs , toRef } from 'vue';

    setup(props){
        let { name } = toRefs(props);
        // title 非必填(可選)
        const title = toRef(props, 'title')
    }
           
  • Context
  • 對象格式,暴露元件的三個屬性
  • Context

    不是響應式的可以使用 ES6 安全解構
export default {
  setup(props, { attrs, slots, emit }) {
    ...
  }
}
           
  • attrs

    : Attribute 非響應式對象
  • slots

    : 插槽 非響應式對象
  • emit

    : 觸發事件 方法
  • attrs

    slots

    是有狀态的對象,它們總是會随元件本身的更新而更新。這意味着你應該避免對它們進行解構,并始終以 attrs.x 或 slots.x 的方式引用 property。 請注意,與 props 不同,attrs 和 slots 是非響應式的。如果你打算根據 attrs 或 slots 更改應用副作用,那麼應該在 onUpdated 生命周期鈎子中執行此操作
  • 通路元件的 property
    • setup

      的執行是在 元件建立之前,無法通路

      methonds

      data

      computed

    • setup

      可以通路

      props

      context(attrs,slots,emit)

  • 結合模闆使用
    • 如果 setup 傳回一個對象,那麼該對象的 property 以及傳遞給 setup 的 props 參數中的 property(原來就可以通路到 props) 就都可以在模闆中通路到
    • 注意,從

      setup

      傳回的

      refs

      在模闆中通路時是被自動淺解包的,是以不應在模闆中使用

      .value

  • 使用渲染函數
    • setup

      還可以傳回一個渲染函數,該函數可以直接使用在同一作用域中聲明的響應式狀态

    reactive

    傳回對象的響應式副本
import { h , reactive  } form 'vue'
    setup(){
        const img = reactive({ src:"https:xxxxxx" });
        return () => h('img', { src:img.src })
    }
           
  • 使用 this
    • setup

      this

      不是該活躍執行個體的引用,因為他是在解析其它元件選項之前被調用的
生命周期鈎子
  • 通過在生命周期鈎子前面加上 “on” 來通路元件的生命周期鈎子。
  • 接收一個回調函數,當鈎子被元件調用的時候執行
setup(){
        onMounted(()=>{})
    }
           
Provide/Inject
  • Provide/Inject

  1. 需要先引入 import { provide , inject } form ‘vue’
  2. provide 接收兩個參數
    1. name
    2. value
  3. inject 兩個參數
    1. name
    2. 預設值(可選)
import { provide , inject  } form 'vue'
    // parent.vue
    setup(){
        provide('name','xiaohei');
        provide('addr',{city:'',town:''});
    }
    //child.vue
    setup(){
        let cname = inject('name','hah');
        let caddr = inject('addr');
    }
           
  • 響應性

    • 為了增加 provide 值和 inject 值之間的響應性,我們可以在 provide 值時使用 ref 或 reactive。
import { provide , ref , reactive  } form 'vue';
    // parent.vue
    setup(){
        let name = ref('xiaohei');
        let addr = reactive({city:'',town:''});
        provide('name',name);
        provide('addr',addr);
    }
           
  • 修改響應式 property

    • 修改響應式 屬性的 時候,最好是在定義響應式的元件内進行修改
    • 有時我們需要在注入元件的内部修改

      inject

      中的值,可以通過

      provide

      一個方法來修改這個值
import { provide , ref , reactive } form 'vue';
    // parent.vue 
    setup(){
        let age = ref(1);
        let addr = reactive({city:'',town:''});
        const updateAge = function(){
            age.value +=1;
        }
        provide('name',name);
        provide('addr',addr);
        provide('updateAge',updateAge);
    }
    // child.vue
    setup(){
        let age = inject('age');
        let addr = inject('addr');
        const updateAge = inject('updateAge');
        return{
            age,
            addr,
            updateAge
        }
    },
    methods:{
        update(){
            this.updateAge();
        }
    }
           
  • 不允許 inject 的元件 修改 provide 傳遞的屬性 添加

    readonly

readonly 接受一個對象 (響應式或純對象) 或 ref 并傳回原始對象的隻讀代理。隻讀代理是深層的:任何被通路的嵌套 property 也是隻讀的。

與 reactive 一樣,如果任何 property 使用了 ref,當它通過代理通路時,則被自動解包:

import { readonly , provide , reactive } from 'vue'
    setup(){
        let age = ref(1);
        let work = reactive({money:'',addr:''}) ;
        provide('age' , readonly(age)); // 隻讀屬性
        provide('work' , readonly(work)); // 隻讀屬性
    }
           
模闆引用
  • 在使用組合式 API 時,響應式引用和模闆引用的概念是統一的。為了獲得對模闆内元素或元件執行個體的引用,我們可以像往常一樣聲明 ref 并從 setup() 傳回:
<template> 
  <div ref="root">This is a root element</div>
</template>
<script>
    import { ref, onMounted } from 'vue'
    export default {
        setup() {
            const root = ref(null)

            onMounted(() => {
                // DOM元素将在初始渲染後配置設定給ref
                console.log(root.value) // <div>這是根元素</div>
            })
      return {
        root
      }
    }
  }
</script>
           
  • 這裡我們在渲染上下文中暴露

    root

    ,并通過

    ref="root"

    ,将其綁定到 div 作為其

    ref

    。在

    虛拟 DOM

    更新檔算法中,如果

    VNode

    ref

    鍵對應于渲染上下文中的

    ref

    ,則

    VNode

    的相應元素或元件執行個體将被配置設定給該

    ref

    的值。這是在虛拟 DOM 挂載/打更新檔過程中執行的,是以模闆引用隻會在初始渲染之後獲得指派。

    大緻意思就是 模闆裡面聲明的 ref='root' 如果與下文中暴露的 ref 所指派的 變量名一樣,就會把這個模闆中的ref 與下文中的那個ref指派的變量關聯起來

  • JSX 中的用法

export default {
  setup() {
    const root = ref(null)

    return () =>
      h('div', {
        ref: root
      })

    // with JSX
    return () => <div ref={root} />
  }
}
           
  • v-for 中的用法

<template>
  <div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
    {{ item }}
  </div>
</template>

<script>
  import { ref, reactive, onBeforeUpdate } from 'vue'

  export default {
    setup() {
      const list = reactive([1, 2, 3])
      const divs = ref([])

      // 確定在每次更新之前重置ref
      onBeforeUpdate(() => {
        divs.value = []
      })

      return {
        list,
        divs
      }
    }
  }
</script>
           
  • 偵聽模闆引用

    • 但與生命周期鈎子的一個關鍵差別是,

      watch()

      watchEffect()

      在 DOM 挂載或更新之前運作副作用,是以當偵聽器運作時,模闆引用還未被更新。
    • 是以,使用模闆引用的偵聽器應該用

      flush: 'post'

      選項來定義,這将在

      DOM

      更新後運作副作用,確定模闆引用與

      DOM

      保持同步,并引用正确的元素。
<template>
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, watchEffect } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      watchEffect(() => {
        console.log(root.value) // => <div></div>
      }, 
      {
        flush: 'post'
      })

      return {
        root
      }
    }
  }
</script>

           

繼續閱讀