深度了解vue.js中hooks的相关知识

最近研究了vue3.0的最新进展,发现变动很大,总体上看,vue也开始向hooks靠拢,而且vue作者本人也称vue3.0的特性吸取了很多hooks的灵感。所以趁着vue3.0未正式发布前,抓紧时间研究一下hooks相关的东西。

源码地址:vue-hooks-poc

为什么要用hooks?

首先从class-component/vue-options说起:

跨组件代码难以复用

大组件,维护困难,颗粒度不好控制,细粒度划分时,组件嵌套存层次太深-影响性能

类组件,this不可控,逻辑分散,不容易理解

mixins具有副作用,逻辑互相嵌套,数据来源不明,且不能互相消费

当一个模版依赖了很多mixin的时候,很容易出现数据来源不清或者命名冲突的问题,而且开发mixins的时候,逻辑及逻辑依赖的属性互相分散且mixin之间不可互相消费。这些都是开发中令人非常痛苦的点,因此,vue3.0中引入hooks相关的特性非常明智。

vue-hooks

深度了解vue.js中hooks的相关知识

在探究vue-hooks之前,先粗略的回顾一下vue的响应式系统:首先,vue组件初始化时会将挂载在data上的属性响应式处理(挂载依赖管理器),然后模版编译成v-dom的过程中,实例化一个Watcher观察者观察整个比对后的vnode,同时也会访问这些依赖的属性,触发依赖管理器收集依赖(与Watcher观察者建立关联)。当依赖的属性发生变化时,会通知对应的Watcher观察者重新求值(setter->notify->watcher->run),对应到模版中就是重新render(re-render)。

注意:vue内部默认将re-render过程放入微任务队列中,当前的render会在上一次render flush阶段求值。

withHooks

export function withHooks(render) { return { data() { return { _state: {} } }, created() { this._effectStore = {} this._refsStore = {} this._computedStore = {} }, render(h) { callIndex = 0 currentInstance = this isMounting = !this._vnode const ret = render(h, this.$attrs, this.$props) currentInstance = null return ret } } }

withHooks为vue组件提供了hooks+jsx的开发方式,使用方式如下:

export default withHooks((h)=>{ ... return <span></span> })

不难看出,withHooks依旧是返回一个vue component的配置项options,后续的hooks相关的属性都挂载在本地提供的options上。

首先,先分析一下vue-hooks需要用到的几个全局变量:

currentInstance:缓存当前的vue实例

isMounting:render是否为首次渲染

isMounting = !this._vnode

这里的_vnode与$vnode有很大的区别,$vnode代表父组件(vm._vnode.parent)

_vnode初始化为null,在mounted阶段会被赋值为当前组件的v-dom

isMounting除了控制内部数据初始化的阶段外,还能防止重复re-render。

callIndex:属性索引,当往options上挂载属性时,使用callIndex作为唯一当索引标识。

vue options上声明的几个本地变量:

_state:放置响应式数据

_refsStore:放置非响应式数据,且返回引用类型

_effectStore:存放副作用逻辑和清理逻辑

_computedStore:存放计算属性

最后,withHooks的回调函数,传入了attrs和$props作为入参,且在渲染完当前组件后,重置全局变量,以备渲染下个组件。

useData

const data = useData(initial) export function useData(initial) { const id = ++callIndex const state = currentInstance.$data._state if (isMounting) { currentInstance.$set(state, id, initial) } return state[id] }

我们知道,想要响应式的监听一个数据的变化,在vue中需要经过一些处理,且场景比较受限。使用useData声明变量的同时,也会在内部data._state上挂载一个响应式数据。但缺陷是,它没有提供更新器,对外返回的数据发生变化时,有可能会丢失响应式监听。

useState

const [data, setData] = useState(initial) export function useState(initial) { ensureCurrentInstance() const id = ++callIndex const state = currentInstance.$data._state const updater = newValue => { state[id] = newValue } if (isMounting) { currentInstance.$set(state, id, initial) } return [state[id], updater] }

useState是hooks非常核心的API之一,它在内部通过闭包提供了一个更新器updater,使用updater可以响应式更新数据,数据变更后会触发re-render,下一次的render过程,不会在重新使用$set初始化,而是会取上一次更新后的缓存值。

useRef

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/228bd9767848751bc72fae15be4f317e.html