记一次 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 this:this is not available inside setup()。this在setup函数中是不可用到。
-
setup的类型:
typinginterface 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
- Access in Templates: 通过setup返回的ref,
automatically unwraps to the inner value。 - 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 - Access in Templates: 通过setup返回的ref,
-
typing
interface Ref<T> {
value: T
}
function ref<T>(value: T): Ref<T>
- computed
- 定义:Takes a
getter function | an object with get and setand 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 refand 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
- 自动移除:如果watchEffect在
setup函数或者lifecycle hooks,则watchEffect会在组件unmounted的时候被移除 - 手动移除:called watchEffect 会返回一个
stop handle.
const stop = watchEffect(() => {}) stop() - 自动移除:如果watchEffect在
-
Side Effect Invalidation ?
- 何时调用:the effect is about to re-run(依赖发生变化),Watcher 停止。
const data = ref(null)
watchEffect(async () => {
data.value = await fetchData(props.id)
})
- Effect Flush Timing(Effect刷新的时间点)
- 类似2.0中的 $nextTick,将一次对依赖的修改的所有操作,合并到一起,最终只触发一次watchEffect的回调用
- watchEffect会在所有组件的update之后运行.?经测试会在updated钩子之前运行
- the first run is executed before the component is mounted,所有如果想要在第一次watchEffect中获取dom,或者template的ref的时候,应该将watchEffect放到mounte生命周期钩子中。
onMounted(() => { watchEffect(() => { // access the DOM or template refs }) })
- 如果需要 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.- onTrack : will be called when a reactive property or ref is tracked as a dependency。第一次执行excutor(前提excutor中存在依赖属性)和当依赖发生变化的时候也会执行且会在onTrigger之后运行
- 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
}