vue-hooks 的源码目前只有不到 200 行, 非常简明扼要的实现了以上提到的 Hooks 和方法等。
首先大体看一下:
let currentInstance = nulllet isMounting = falselet callIndex = 0 //… export function useState(initial) { //…}export function useEffect(rawEffect, deps) { //…}export function useRef(initial) { //…}export function useData(initial) { //…}export function useMounted(fn) { //…}export function useDestroyed(fn) { //…}export function useUpdated(fn, deps) { //…}export function useWatch(getter, cb, options) { //…}export function useComputed(getter) { //…} 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.$props) currentInstance = null return ret } }} //暴漏的钩子函数export function hooks (Vue) { Vue.mixin({ beforeCreate() { const { hooks, data } = this.$options if (hooks) { this._effectStore = {} this._refsStore = {} this._computedStore = {} this.$options.data = function () { const ret = data ? data.call(this) : {} ret._state = {} return ret } } }, beforeMount() { const { hooks, render } = this.$options if (hooks && render) { this.$options.render = function(h) { callIndex = 0 currentInstance = this isMounting = !this._vnode const hookProps = hooks(this.$props) Object.assign(this._self, hookProps) const ret = render.call(this, h) currentInstance = null return ret } } } })}
基本的结构非常清楚,可以看出:
withHooks 返回一个包装过的 Vue 实例配置
hooks 以 mixin 的形式发挥作用,注入两个生命周期
用模块局部变量 currentInstance 记录了 Hooks 生效的 Vue 实例
其次值得注意的是处理副作用的 useEffect:
export function useEffect(rawEffect, deps) { //… if (isMounting) { const cleanup = () => { const { current } = cleanup if (current) { current() cleanup.current = null } } const effect = () => { const { current } = effect if (current) { cleanup.current = current() effect.current = null } } effect.current = rawEffect currentInstance._effectStore[id] = { effect, cleanup, deps } currentInstance.$on(‘hook:mounted’, effect) currentInstance.$on(‘hook:destroyed’, cleanup) if (!deps || deps.lenght > 0) { currentInstance.$on(‘hook:updated’, effect) } } else { const record = currentInstance._effectStore[id] const { effect, cleanup, deps: prevDeps = [] } = record record.deps = deps if (!deps || deps.some((d, i) => d !== prevDeps[i])) { cleanup() effect.current = rawEffect } }}
其核心大致轨迹如下:
声明 effect 函数和 cleanup 函数
将调用 Hook 时传入的 rawEffect 赋值到 effect.current 属性上
effect() 运行后,将 rawEffect 运行后的返回值赋值到 cleanup.current 上
在 Vue 本身就支持的几个 hook:xxx 生命周期钩子事件中,调用 effect 或 cleanup

这样再去看这两个 Hook 就敞亮多了:

另外常用的 useData 也是利用了 Vue 实例的 $set 方法,清晰易懂:

同样利用实例方法的:

其余几个 Hooks 的实现大同小异,就不逐一展开说明了。
IV. 总结
React Hook 是简化组件定义、复用状态逻辑的一种最新尝试
vue-hooks 很好的实现了相同的功能,并且结合 Vue 实例的特点提供了适用的 Hooks。