记一次 vue3.0的学习 - Composition API RFC

486 阅读4分钟

记一次 vue3.0的学习

官方文档

Composition API RFC

setup

  • def:the entry point for using the Composition API inside components.

  • Invocation Timing调用时刻:在生命周期钩子beforeCreate之前被调用,在组件实例已经创建且初始化props之后。

  • Usage用法:

  • Usage with Templates:返回一个对象,该对象将被合并到渲染上下文中暴露给template且通过ref申明的状态将自动unwrapped,所以在模版中不需要通过.value的方式获取状态值

    const template = ` <div>{{ count }} {{ object.foo }}</div>`
    
    setup() {
      const count = ref(0);
      // count.value = 0 
      const object = reactive({ foo: 'bar' })
      // expose to template
      return {
        count,
        object
      }
    }
    
    // note: refs returned from setup are automatically unwrapped,so no need for .value in templates. but in js need it.
    
  • Usage with Render Functions / JSX:render函数的优先级高于template,且setup的第一个参数是props,且不能使用解构到方式获取数据,且是不可变的(immutable,保证单向数据流),且可以被watch,watchEffect观察到第二个参数是上下文对象(2.x版本中的this),用于暴露2.x版本中的api,且可以通过解构获取到数据,🌰:emit,slot,attrs等

    import { h,watchEffect} from "vue";
    export default {
    props: {
    name: String
    },
    setup(props,{attrs}) {
      // attrs 通常用来获取一些静态属性
      watchEffect(()=>{
     console.log(props.name)
     })
     console.log(props)
      return ()=>h('span',props.name)
    },
    };
    
  • Usage of thisthis is not available inside setup()。this在setup函数中是不可用到。

  • setup的类型:typing

    interface Data {
    [key: string]: unknown
    }
    
    interface SetupContext {
    attrs: Data
    slots: Slots
    emit: (event: string, ...args: unknown[]) => void
    }
    function setup(props: Data, context: SetupContext): Data
    

Reactivity APIs

  • reactive
  • 定义:接收一个对象作为参数,并且返回一个响应式的proxy对象。类似于2.x版本中的Vue.observable()
  • ⚠️:The reactive conversion is "deep": it affects all nested properties.
  • usage:
  const obj = reactive({ count: 0 })
  • Typing:

    function reactive<T extends object>(raw: T): T;
    
  • ref note ?

  • 定义:接收一个值作为初始值[primitive,reference,ref],返回一个响应式reactive且可以变mutable的ref对象,且初始值是引用值的value值都是一个proxy对象

  • usage

    1. Access in Templates: 通过setup返回的ref,automatically unwraps to the inner value
    2. Access in Reactive Objects:it automatically unwraps to the inner value so it behaves like a normal property
    const count = ref(0);
    // 这里的count用在reactive中将自动提取count中的value值
    const state = reactive({
     count //这里的count相当于 count:0
    })
    
    console.log(state.count) // 0
    state.count = 1
    console.log(count.value) // 1
    
  • typing

  interface Ref<T> {
   value: T
 }

 function ref<T>(value: T): Ref<T>
  • computed
  • 定义:Takes a getter function | an object with get and set and returns an immutable reactive ref object for the returned value from the getter.
  • usage:
  const db = computed(()=>props.count * 2);

  const db = computed({
    get:()=>props.count * 2
    ,
    set:value=>{
      console.log(value)
      // count.value = val - 1
    }
  })
  • typing:
// read-only
function computed<T>(getter: () => T): Readonly<Ref<Readonly<T>>>

 // writable
function computed<T>(options: {
get: () => T
set: (value: T) => void
}): Ref<T>
  • readonly
  • 定义:Takes an object (reactive or plain) or a ref and returns a readonly proxy to the original. A readonly proxy is deep: any nested property accessed will be readonly as well.
  • usage:

  const count = ref(0);
  const read = readonly(count);
  function add(){
    count.value ++;
  }
  watchEffect(()=>{
    // read依赖于count,所以当改变count的时候也会被tracking
    console.log(read.value)
  })

  • watchEffect

  • 定义:函数里的所有reactive state,都将在函数初次(initial run)执行的时候,作为依赖项被收集起来,一旦依赖项发生了改变,则excutor函数将再次执行。

  • Stopping the Watcher

    1. 自动移除:如果watchEffect在setup函数或者lifecycle hooks,则watchEffect会在组件unmounted的时候被移除
    2. 手动移除:called watchEffect 会返回一个 stop handle.
      const stop = watchEffect(() => {}) 
      stop()
    
  • Side Effect Invalidation ?

    1. 何时调用:the effect is about to re-run(依赖发生变化),Watcher 停止。

   const data = ref(null)
   watchEffect(async () => {
   data.value = await fetchData(props.id)
   })

  • Effect Flush Timing(Effect刷新的时间点)
    1. 类似2.0中的 $nextTick,将一次对依赖的修改的所有操作,合并到一起,最终只触发一次watchEffect的回调用
    2. watchEffect会在所有组件的update之后运行.?经测试会在updated钩子之前运行
    3. the first run is executed before the component is mounted,所有如果想要在第一次watchEffect中获取dom,或者template的ref的时候,应该将watchEffect放到mounte生命周期钩子中。
    
    onMounted(() => {
     watchEffect(() => {
      // access the DOM or template refs
       })
     })
    
  1. 如果需要 watcher effect needs to be re-run synchronously同步执行 or before component updates在组件更新之前执行,则可以给watchEffect提供第二个参数options.
 // fire synchronously
 watchEffect(
 () => {
  /* ... */
 },
 {
  flush: 'sync'//默认值是post
}
)

 // fire before component updates
 watchEffect(
  () => {
  /* ... */
  },
  {
  flush: 'pre'
  }
 )

  // demo 

    onMounted(() => {

      watchEffect(() => {
      console.log(count.value) // 1 
      const dom = document.querySelector("#title");
      const span = document.querySelector('#count');
      console.log(span.innerText); // 0视图更新但是输出0
      console.log("watchEffect");
    },{
      flush:'sync'
    });

  });
  • Watcher Debugging onTrack and onTrigger only works in development mode.
    1. onTrack : will be called when a reactive property or ref is tracked as a dependency。第一次执行excutor(前提excutor中存在依赖属性)和当依赖发生变化的时候也会执行且会在onTrigger之后运行
    2. onTrigger : will be called when the watcher callback is triggered by the mutation of a dependency。改变dependency的时候触发
watchEffect(
() => {
  /* side effect */
},
{
  onTrigger(e) {
    debugger
  },
  onTrack(e){

  }

}
)
  • typing
   function watchEffect(
  effect: (onInvalidate: InvalidateCbRegistrator) => void,
   options?: WatchEffectOptions
 ): StopHandle

  interface WatchEffectOptions {
   flush?: 'pre' | 'post' | 'sync'
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
 }

   interface DebuggerEvent {
  effect: ReactiveEffect
   target: any
  type: OperationTypes
  key: string | symbol | undefined
   }

   type InvalidateCbRegistrator = (invalidate: () => void) => void

   type StopHandle = () => void
  
  • watch
  • def:The watch API is the exact equivalent of the 2.x this.$watch (and the corresponding watch option).watch requires watching a specific data观察明确的值 source, and applies side effects in a separate callback function发生变化执行回调函数和观察函数是分离的
  • compare:
    • Perform the side effect lazily;?
    • Be more specific about what state should trigger the watcher to re-run明确被观察的对象;
    • Access both the previous and current value of the watched state.运行访问改变前和改变之后的值
  • Watching a Single Source
    • watch(getter,cb) :第一个参数是一个取值函数
    • watch(ref,cb):第一个参数是ref
  const count = ref(0);

  function add() {
    count.value++;
  }
  //  ref
  watch(count,(cur,pre)=>{
    console.log(cur,pre)
  })
  // getter
   watch(()=>count.value,(cur,pre)=>{
    console.log(cur,pre)
  })
  
 // dealing reacitve
  const count = reactive({
    count:0
  })
  function add() {
    count.count++;
  }
  watch(()=>count.count,(cur,pre)=>{
    console.log(cur,pre)
  })
  
  • Watching Multiple Sources:A watcher can also watch multiple sources at the same time using an Array:
   // 改变任何一个被监听的值,都会触发回调
   watch([count,name],([count, name], [prevcount, prevname])=>{
    console.log([count, name])
    console.log([prevcount, prevname])
  })
  • typing
// wacthing single source
function watch<T>(
source: WatcherSource<T>,
callback: (
  value: T,
  oldValue: T,
  onInvalidate: InvalidateCbRegistrator
) => void,
options?: WatchOptions
): StopHandle

// watching multiple sources
function watch<T extends WatcherSource<unknown>[]>(
sources: T
callback: (
  values: MapSources<T>,
  oldValues: MapSources<T>,
  onInvalidate: InvalidateCbRegistrator
) => void,
options? : WatchOptions
): StopHandle

type WatcherSource<T> = Ref<T> | (() => T)

type MapSources<T> = {
[K in keyof T]: T[K] extends WatcherSource<infer V> ? V : never
}

// see `watchEffect` typing for shared options
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean // default: false
deep?: boolean
}