在分享 vue-next
各个子模块的实现以前,我觉的有必要比较全面的整理下 vue-next
中提出的函数式 api
,了解这些的话,不管是对于源码的阅读,仍是当正式版发布时开始学习,应该都会有起到必定的辅助做用。vue
相似的东西在网上有不少,只是会比较零碎,同时有些也相对过期了,固然当前整理的这些也有可能会过期,毕竟代码还处于 pre-alpha
的阶段,但其中的设计思想应该是不会改变了。react
往期文章git
setup
会做为编写组件业务逻辑的主战场,各类 hook
类型的方法均须要在 setup
这个做用域下调用,直接来看 RFC
中的例子:github
<template> <button @click="increment"> Count is: {{ state.count }}, double is: {{ state.double }} </button> </template> <script> import { reactive, computed } from 'vue' export default { setup() { const state = reactive({ count: 0, double: computed(() => state.count \* 2) }) function increment() { state.count++ } return { state, increment } } } </script> 复制代码
其代码结构看起来和 vue2
基本保持一致,不过有如下几点须要注意:typescript
setup
自己的调用时机,从目前的源码来看,介于 beforeCreate
和 created
这两个生命周期之间,言外之意,就是你没法在这里使用 this
指向当前组件实例state
的声明,咱们使用 reactive
这个 api
来构造,对于 computed state
的声明,使用 computed
,二者本质上均是 Proxy
对象state
的变动,直接经过闭包使用 reactive
返回的 Proxy
对象便可setup
的返回值被称做 render context
,顾名思义,就是模板中能够访问到的各类数据和方法的上下文对象,我在以前的文章中曾说起,模板在解析数据时,会优先考虑从 data
对象取值,以后就是这个 render context
了render context
,还能够返回模板渲染函数,对,就是那个 h(...)
,这种形式对应的状况是,当咱们没有声明 template
属性时在 vue-next 中,咱们直接从 vue
导入 reactive
、computed
以及其余的 api
便可,若是在 vue2
版本上,咱们还能够经过使用 composition-api 这个 plugin
来使用这些 api
。api
声明 state 主要有如下几种形式。数组
基础类型能够经过 ref
这个 api
来声明,以下:bash
import { ref } from "vue"; export default { setup() { const count = ref(0); function inc() { count.value++; } return { count, inc }; } }; 复制代码
之因此要经过 ref
,是由于 js
中的基础类型传值不是引用传值,所以 vue-next
内部会自动将它封装为一个 ref
对象,其值指向原始值。固然,ref
指向引用类型也是没有问题的,其 value
指向引用类型变量的引用。markdown
引用类型除了可使用 ref
来声明,也能够直接使用 reactive
,以下:闭包
import { reactive } from "vue"; export default { setup() { const state = reactive({ count: 0 }); function inc() { state.count++; } return { state, inc }; // 或者经过 toRefs 方法 // return { ...toRefs(state), inc }; } }; 复制代码
这里使用 `toRefs` 的缘由主要是由于,若是是 reactive 产生的对象,因为咱们要只是保存对于该 Proxy 对象的引用,咱们没法使用解构来将它 `flat`,而 `toRefs` 会将每个属性在内部包裹为一个 `ref` 对象。
复制代码
对于 prop
能够经过以下代码声明及使用:
export default { props: { count: Number }, setup(props) { watch(() => { console.log(\`count is: \` + props.count) }) } } 复制代码
这里能够发现其实和 vue2
中声明的方式基本同样,但值得注意的是,在 vue-next
中,props
的类型声明不是必须的,若是你使用 typescript
,彻底能够改写为以下的版本:
interface PropTypesI { count: number } export default { setup(props: PropTypesI) { watch(() => { console.log(\`count is: \` + props.count) }) } } 复制代码
除此以外,还能够直接经过 watch
方法来观察某个 prop
的变更,这是为何呢?答案很是简单,就是 props
自己在源码中,也是一个被 reactive
包裹后的对象,所以它具备响应性,因此在 watch
方法中的回调函数会自动收集依赖,以后当 count
变更时,会自动调用这些回调逻辑。
在 setup
那一小节中,咱们知道,setup
在调用时,组件实例还未建立,那意味着咱们没法使用 this
访问当前实例,那我想经过 this
在 vue2
中访问一些内置属性,怎么办?好比 attrs
或者 emit
。咱们能够经过 setup
的第二个参数:
setup(props, context) { do anything... } 复制代码
这个 context
对象也是一个 Proxy
对象,当咱们经过 context.attrs
访问其属性时,本质上代理对象会将访问指向组件的内部实例(即之间文章中说起的 componentInternalInstance
)。
每个 vue2
中的组件生命周期函数,当前都对应一个生命周期 hook
,好比:
import { onMounted, onUpdated, onUnmounted } from "vue"; setup() { onMounted(() => { ... }); onUpdated(() => { ... }); onUnmounted(() => { ... }); } 复制代码
这里值得注意的一点在于,对于 beforeCreate
和 created
生命周期,虽然有响应的 hook
,可是我觉的没有必要单独使用了,由于这些逻辑代码大部分是一些初始化逻辑的代码,直接写在 setup
方法中便可。
在这个基础上,复用代码的方式也再也不像 vue2
中的那样,经过 mixin
或者 HOC
来达到复用代码的目的,这里稍微说一下,这些复用代码方式中比较显著的缺点有:
mixin
中HOC
中mixin
中在 vue-next
中,复用代码的逻辑本质上是利用这些 hook
来拆分业务逻辑与状态,若是你比较熟悉 react hooks
的话,应该很快就能明白我指的是什么意思。若是咱们将逻辑都写在 setup
方法中,很快代码就会变得难以维护,在这方面,咱们能够将一些耦合在一块儿的代码抽离出来,同时以 use
前缀命名,声明一个自定义的 hook
,以下:
export default { setup() { const data = useSearch() const { sortedData, sort } = useSorting(data) return { data, sortedData, sort } } } function useSearch() { ...fetch data } function useSort(data) { ...sort data } 复制代码
除了以 inline
的方式来声明,还能够将这些自定义的 hook
声明在单独的文件中,直接经过 import
导入便可,好比:
import useSearch from '@hooks/search' import useSort from '@hooks/sort' 复制代码
react hooks
对比vue-next
在这方面借鉴了 react hooks
的设计思想,可是从实现层来说,它们是不同的,主要有如下几点:
vue-next
不依赖于其调用顺序,而 react
依赖vue-next
提供了生命周期方法,而 react
刻意模糊生命周期的概念vue-next
基于响应式系统实现,意味它的依赖不须要显示声明(并且是自动的),而 react
须要手动声明依赖数组关注公众号 全栈_101,只谈技术,不谈人生