diff --git a/packages/runtime-core/__tests__/hooks.spec.ts b/packages/runtime-core/__tests__/hooks.spec.ts deleted file mode 100644 index e46e5cfa..00000000 --- a/packages/runtime-core/__tests__/hooks.spec.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { useState, h, nextTick, useEffect, Component } from '../src' -import { renderInstance, serialize, triggerEvent } from '@vue/runtime-test' - -describe('hooks', () => { - it('useState', async () => { - class Counter extends Component { - render() { - const [count, setCount] = useState(0) - return h( - 'div', - { - onClick: () => { - setCount(count + 1) - } - }, - count - ) - } - } - - const counter = await renderInstance(Counter) - expect(serialize(counter.$el)).toBe(`
0
`) - - triggerEvent(counter.$el, 'click') - await nextTick() - expect(serialize(counter.$el)).toBe(`
1
`) - }) - - it('should be usable via hooks() method', async () => { - class Counter extends Component { - hooks() { - const [count, setCount] = useState(0) - return { - count, - setCount - } - } - render() { - const { count, setCount } = this as any - return h( - 'div', - { - onClick: () => { - setCount(count + 1) - } - }, - count - ) - } - } - - const counter = await renderInstance(Counter) - expect(serialize(counter.$el)).toBe(`
0
`) - - triggerEvent(counter.$el, 'click') - await nextTick() - expect(serialize(counter.$el)).toBe(`
1
`) - }) - - it('useEffect', async () => { - let effect = -1 - - class Counter extends Component { - render() { - const [count, setCount] = useState(0) - useEffect(() => { - effect = count - }) - return h( - 'div', - { - onClick: () => { - setCount(count + 1) - } - }, - count - ) - } - } - - const counter = await renderInstance(Counter) - expect(effect).toBe(0) - triggerEvent(counter.$el, 'click') - await nextTick() - expect(effect).toBe(1) - }) - - it('useEffect with empty keys', async () => { - // TODO - }) - - it('useEffect with keys', async () => { - // TODO - }) - - it('useData', () => { - // TODO - }) - - it('useMounted/useUnmounted/useUpdated', () => { - // TODO - }) - - it('useWatch', () => { - // TODO - }) - - it('useComputed', () => { - // TODO - }) -}) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 4cfb1569..83d86552 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -45,7 +45,6 @@ interface PublicInstanceMethods { export interface APIMethods

{ data(): Partial - hooks(): any render(props: Readonly

, slots: Slots, attrs: Data, parentVNode: VNode): any } @@ -136,7 +135,6 @@ class InternalComponent implements PublicInstanceMethods { _queueJob: ((fn: () => void) => void) | null = null _isVue: boolean = true _inactiveRoot: boolean = false - _hookProps: any = null constructor(props?: object) { if (props === void 0) { diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 97e7db15..08112569 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -90,7 +90,6 @@ type ReservedKeys = { [K in keyof (APIMethods & LifecycleMethods)]: 1 } export const reservedMethods: ReservedKeys = { data: 1, render: 1, - hooks: 1, beforeCreate: 1, created: 1, beforeMount: 1, diff --git a/packages/runtime-core/src/componentProxy.ts b/packages/runtime-core/src/componentProxy.ts index 0698795d..01871522 100644 --- a/packages/runtime-core/src/componentProxy.ts +++ b/packages/runtime-core/src/componentProxy.ts @@ -1,9 +1,8 @@ import { ComponentInstance } from './component' import { isFunction, isReservedKey } from '@vue/shared' -import { warn } from './warning' import { isRendering } from './componentRenderUtils' -import { isObservable } from '@vue/observer' import { reservedMethods } from './componentOptions' +import { warn } from './warning' const bindCache = new WeakMap() @@ -37,9 +36,6 @@ const renderProxyHandlers = { ) { // computed return i[key]() - } else if ((i = target._hookProps) !== null && i.hasOwnProperty(key)) { - // hooks injections - return i[key] } else if (key[0] !== '_') { if ( __DEV__ && @@ -81,16 +77,6 @@ const renderProxyHandlers = { if ((i = target._rawData) !== null && i.hasOwnProperty(key)) { target.$data[key] = value return true - } else if ((i = target._hookProps) !== null && i.hasOwnProperty(key)) { - if (__DEV__ && !isObservable(i)) { - warn( - `attempting to mutate a property returned from hooks(), but the ` + - `value is not observable.` - ) - } - // this enables returning observable objects from hooks() - i[key] = value - return true } else { return Reflect.set(target, key, value, receiver) } diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index 50b5508c..07588600 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -4,26 +4,13 @@ import { resolveProps } from './componentProps' import { handleError, ErrorTypes } from './errorHandling' import { VNodeFlags, ChildrenFlags } from './flags' import { EMPTY_OBJ, isArray, isObject } from '@vue/shared' -import { setCurrentInstance, unsetCurrentInstance } from './experimental/hooks' export let isRendering = false export function renderInstanceRoot(instance: ComponentInstance): VNode { let vnode - const { - $options: { hooks }, - render, - $proxy, - $props, - $slots, - $attrs, - $parentVNode - } = instance + const { render, $proxy, $props, $slots, $attrs, $parentVNode } = instance try { - setCurrentInstance(instance) - if (hooks) { - instance._hookProps = hooks.call($proxy, $props) || null - } if (__DEV__) { isRendering = true } @@ -31,7 +18,6 @@ export function renderInstanceRoot(instance: ComponentInstance): VNode { if (__DEV__) { isRendering = false } - unsetCurrentInstance() } catch (err) { handleError(err, instance, ErrorTypes.RENDER) } diff --git a/packages/runtime-core/src/createRenderer.ts b/packages/runtime-core/src/createRenderer.ts index 38ecc854..d92557cf 100644 --- a/packages/runtime-core/src/createRenderer.ts +++ b/packages/runtime-core/src/createRenderer.ts @@ -1258,7 +1258,7 @@ export function createRenderer(options: RendererOptions) { const { $proxy, - $options: { beforeMount, renderTracked, renderTriggered } + $options: { beforeMount, mounted, renderTracked, renderTriggered } } = instance instance.$forceUpdate = () => { @@ -1318,9 +1318,6 @@ export function createRenderer(options: RendererOptions) { if (vnode.ref) { vnode.ref($proxy) } - // retrieve mounted value after initial render so that we get - // to inject effects in hooks - const { mounted } = instance.$options if (mounted) { callLifecycleHookWithHandler(mounted, $proxy, ErrorTypes.MOUNTED) } diff --git a/packages/runtime-core/src/experimental/hooks.ts b/packages/runtime-core/src/experimental/hooks.ts deleted file mode 100644 index f667b5d2..00000000 --- a/packages/runtime-core/src/experimental/hooks.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { ComponentInstance } from '../component' -import { mergeLifecycleHooks, WatchOptions } from '../componentOptions' -import { observable, computed } from '@vue/observer' -import { setupWatcher } from '../componentWatch' - -type RawEffect = () => (() => void) | void - -type Effect = RawEffect & { - current?: RawEffect | null | void -} - -type EffectRecord = { - effect: Effect - cleanup: Effect - deps: any[] | void -} - -type Ref = { current: T } - -type HookState = { - state: any - effects: Record - refs: Record> -} - -let currentInstance: ComponentInstance | null = null -let isMounting: boolean = false -let callIndex: number = 0 - -const hooksStateMap = new WeakMap() - -export function setCurrentInstance(instance: ComponentInstance) { - currentInstance = instance - isMounting = !currentInstance._mounted - callIndex = 0 -} - -export function unsetCurrentInstance() { - currentInstance = null -} - -function ensureCurrentInstance() { - if (!currentInstance) { - throw new Error( - `invalid hooks call` + - (__DEV__ - ? `. Hooks can only be called in one of the following: ` + - `render(), hooks(), or withHooks().` - : ``) - ) - } -} - -function getCurrentHookState(): HookState { - ensureCurrentInstance() - let hookState = hooksStateMap.get(currentInstance as ComponentInstance) - if (!hookState) { - hookState = { - state: observable({}), - effects: {}, - refs: {} - } - hooksStateMap.set(currentInstance as ComponentInstance, hookState) - } - return hookState -} - -// React compatible hooks ------------------------------------------------------ - -export function useState(initial: T): [T, (newValue: T) => void] { - const id = ++callIndex - const { state } = getCurrentHookState() - const set = (newValue: any) => { - state[id] = newValue - } - if (isMounting) { - set(initial) - } - return [state[id], set] -} - -export function useEffect(rawEffect: Effect, deps?: any[]) { - const id = ++callIndex - if (isMounting) { - const cleanup: Effect = () => { - const { current } = cleanup - if (current) { - current() - cleanup.current = null - } - } - const effect: Effect = () => { - const { current } = effect - if (current) { - cleanup.current = current() - effect.current = null - } - } - effect.current = rawEffect - getCurrentHookState().effects[id] = { - effect, - cleanup, - deps - } - - injectEffect(currentInstance as ComponentInstance, 'mounted', effect) - injectEffect(currentInstance as ComponentInstance, 'unmounted', cleanup) - if (!deps || deps.length !== 0) { - injectEffect(currentInstance as ComponentInstance, 'updated', effect) - } - } else { - const record = getCurrentHookState().effects[id] - const { effect, cleanup, deps: prevDeps = [] } = record - record.deps = deps - if (!deps || hasDepsChanged(deps, prevDeps)) { - cleanup() - effect.current = rawEffect - } - } -} - -function hasDepsChanged(deps: any[], prevDeps: any[]): boolean { - if (deps.length !== prevDeps.length) { - return true - } - for (let i = 0; i < deps.length; i++) { - if (deps[i] !== prevDeps[i]) { - return true - } - } - return false -} - -function injectEffect( - instance: ComponentInstance, - key: string, - effect: Effect -) { - const existing = instance.$options[key] - ;(instance.$options as any)[key] = existing - ? mergeLifecycleHooks(existing, effect) - : effect -} - -export function useRef(initial?: T): Ref { - const id = ++callIndex - const { refs } = getCurrentHookState() - return isMounting ? (refs[id] = { current: initial }) : refs[id] -} - -// Vue API hooks --------------------------------------------------------------- - -export function useData(initial: T): T { - const id = ++callIndex - const { state } = getCurrentHookState() - if (isMounting) { - state[id] = initial - } - return state[id] -} - -export function useMounted(fn: () => void) { - useEffect(fn, []) -} - -export function useUnmounted(fn: () => void) { - useEffect(() => fn, []) -} - -export function useUpdated(fn: () => void, deps?: any[]) { - const isMount = useRef(true) - useEffect(() => { - if (isMount.current) { - isMount.current = false - } else { - return fn() - } - }, deps) -} - -export function useWatch( - getter: () => T, - cb: (val: T, oldVal: T) => void, - options?: WatchOptions -) { - ensureCurrentInstance() - if (isMounting) { - setupWatcher(currentInstance as ComponentInstance, getter, cb, options) - } -} - -export function useComputed(getter: () => T): T { - ensureCurrentInstance() - const id = `__hooksComputed${++callIndex}` - const instance = currentInstance as ComponentInstance - const handles = instance._computedGetters || (instance._computedGetters = {}) - if (isMounting) { - handles[id] = computed(getter) - } - return handles[id]() -} diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 56f9a487..078e3195 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -32,19 +32,6 @@ export { mixins } from './optional/mixins' export { EventEmitter } from './optional/eventEmitter' export { memoize } from './optional/memoize' -// Experimental APIs -export { - useState, - useEffect, - useRef, - useData, - useWatch, - useComputed, - useMounted, - useUnmounted, - useUpdated -} from './experimental/hooks' - // flags & types export { ComponentType, ComponentClass, FunctionalComponent } from './component' export { VNodeFlags, ChildrenFlags } from './flags'