如果某一个状态不需要在不同函数中被响应式修改,可以通过state创建响应式对象,这个state创建的响应式对象并不是包装对象,不需要使用.value来取值。
watch & computed
watch和computed的基本概念与 Vue 2.x 的watch和computed一致,watch可以用于追踪状态变化来执行一些后续操作,computed用于计算属性,用于依赖属性发生变化进行重新计算。
computed返回一个只读的包装对象,和普通包装对象一样可以被setup函数返回,这样就可以在模板上下文中使用computed属性。可以接受两个参数,第一个参数返回当前的计算属性值,当传递第二个参数时,computed是可写的。
import { value, computed } from 'vue-function-api'; const count = value(0); const countPlusOne = computed(() => count.value + 1); console.log(countPlusOne.value); // 1 count.value++; console.log(countPlusOne.value); // 2 // 可写的计算属性值 const writableComputed = computed( // read () => count.value + 1, // write val => { count.value = val - 1; }, );
watch第一个参数和computed类似,返回被监听的包装对象属性值,不过另外需要传递两个参数:第二个参数是回调函数,当数据源发生变化时触发回调函数,第三个参数是options。其默认行为与 Vue 2.x 有所不同:
lazy:是否会在组件创建时就调用一次回调函数,与 Vue 2.x 相反,lazy默认是false,默认会在组件创建时调用一次。
deep:与 Vue 2.x 的 deep 一致
flush:有三个可选值,分别为 'post'(在渲染后,即nextTick后才调用回调函数),'pre'(在渲染前,即nextTick前调用回调函数),'sync'(同步触发)。默认值为'post'。
// double 是一个计算包装对象 const double = computed(() => count.value * 2); watch(double, value => { console.log('double the count is: ', value); }); // -> double the count is: 0 count.value++; // -> double the count is: 2
当watch多个被包装对象属性时,参数均可以通过数组的方式进行传递,同时,与 Vue 2.x 的vm.$watch一样,watch返回取消监听的函数:
const stop = watch( [valueA, () => valueB.value], ([a, b], [prevA, prevB]) => { console.log(`a is: ${a}`); console.log(`b is: ${b}`); } ); stop();
注意:在初稿中,有提到,是用于清理一些特殊情况的副作用的,目前已经在提案中被取消了。
生命周期
所有现有的生命周期都有对应的钩子函数,通过onXXX的形式创建,但有一点不同的是,destoryed钩子函数需要使用unmounted代替:
import { onMounted, onUpdated, onUnmounted } from 'vue-function-api'; const MyComponent = { setup() { onMounted(() => { console.log('mounted!'); }); onUpdated(() => { console.log('updated!'); }); // destroyed 调整为 unmounted onUnmounted(() => { console.log('unmounted!'); }); }, };
一些思考
上面的详解部分,主要抽取的是 Vue Function API 的常见部分,并非的全部,例如其中的依赖注入,TypeScript类型推导等优势,在这里,由于篇幅有限,想要了解更多的朋友,可以点开查看。个人也在Function-based component API讨论区看到了更多地一些意见:
setup() { const state = reactive({ count: 0, }); const double = computed(() => state.count * 2); function increment() { state.count++; } return { ...toBindings(state), // retains reactivity on mutations made to `state` double, increment, }; }
引入reactive API 和 binding API,其中reactive API 类似于 state API , binding API 类似于 value API。
之前使用的方法名state在 Vue 2.x 中可能被用作组件状态对象,导致变量命名空间的冲突问题,团队认为将state API 更名为 reactive 更为优雅。开发者能够写出const state = ... ,然后通过state.xxxx这种方式来获取组件状态,这样也相对而言自然一些。
value方法用于封装基本类型时,确实会出现不够优雅的.value的情况,开发者可能会在直接对包装对象取值时忘记使用.value,修正方案提出的 reactive API,其含义是创建响应式对象,初始化状态state就使用reactive创建,可保留每项属性的getter和setter,这么做既满足类型推导,也可以保留响应式引用,从而可在不同模块中共享状态值的引用。