fix(hmr): make hmr working with class components (#2144)
This commit is contained in:
		
							parent
							
								
									57bdaa2220
								
							
						
					
					
						commit
						422f05e085
					
				@ -148,6 +148,49 @@ describe('hot module replacement', () => {
 | 
			
		||||
    expect(mountSpy).toHaveBeenCalledTimes(1)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test('reload class component', async () => {
 | 
			
		||||
    const root = nodeOps.createElement('div')
 | 
			
		||||
    const childId = 'test4-child'
 | 
			
		||||
    const unmountSpy = jest.fn()
 | 
			
		||||
    const mountSpy = jest.fn()
 | 
			
		||||
 | 
			
		||||
    class Child {
 | 
			
		||||
      static __vccOpts: ComponentOptions = {
 | 
			
		||||
        __hmrId: childId,
 | 
			
		||||
        data() {
 | 
			
		||||
          return { count: 0 }
 | 
			
		||||
        },
 | 
			
		||||
        unmounted: unmountSpy,
 | 
			
		||||
        render: compileToFunction(`<div @click="count++">{{ count }}</div>`)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    createRecord(childId)
 | 
			
		||||
 | 
			
		||||
    const Parent: ComponentOptions = {
 | 
			
		||||
      render: () => h(Child)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render(h(Parent), root)
 | 
			
		||||
    expect(serializeInner(root)).toBe(`<div>0</div>`)
 | 
			
		||||
 | 
			
		||||
    class UpdatedChild {
 | 
			
		||||
      static __vccOpts: ComponentOptions = {
 | 
			
		||||
        __hmrId: childId,
 | 
			
		||||
        data() {
 | 
			
		||||
          return { count: 1 }
 | 
			
		||||
        },
 | 
			
		||||
        mounted: mountSpy,
 | 
			
		||||
        render: compileToFunction(`<div @click="count++">{{ count }}</div>`)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    reload(childId, UpdatedChild)
 | 
			
		||||
    await nextTick()
 | 
			
		||||
    expect(serializeInner(root)).toBe(`<div>1</div>`)
 | 
			
		||||
    expect(unmountSpy).toHaveBeenCalledTimes(1)
 | 
			
		||||
    expect(mountSpy).toHaveBeenCalledTimes(1)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // #1156 - static nodes should retain DOM element reference across updates
 | 
			
		||||
  // when HMR is active
 | 
			
		||||
  test('static el reference', async () => {
 | 
			
		||||
 | 
			
		||||
@ -800,3 +800,7 @@ export function formatComponentName(
 | 
			
		||||
 | 
			
		||||
  return name ? classify(name) : isRoot ? `App` : `Anonymous`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function isClassComponent(value: unknown): value is ClassComponent {
 | 
			
		||||
  return isFunction(value) && '__vccOpts' in value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,9 @@ import {
 | 
			
		||||
  ConcreteComponent,
 | 
			
		||||
  ComponentInternalInstance,
 | 
			
		||||
  ComponentOptions,
 | 
			
		||||
  InternalRenderFunction
 | 
			
		||||
  InternalRenderFunction,
 | 
			
		||||
  ClassComponent,
 | 
			
		||||
  isClassComponent
 | 
			
		||||
} from './component'
 | 
			
		||||
import { queueJob, queuePostFlushCb } from './scheduler'
 | 
			
		||||
import { extend } from '@vue/shared'
 | 
			
		||||
@ -83,7 +85,7 @@ function rerender(id: string, newRender?: Function) {
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function reload(id: string, newComp: ComponentOptions) {
 | 
			
		||||
function reload(id: string, newComp: ComponentOptions | ClassComponent) {
 | 
			
		||||
  const record = map.get(id)
 | 
			
		||||
  if (!record) return
 | 
			
		||||
  // Array.from creates a snapshot which avoids the set being mutated during
 | 
			
		||||
@ -92,6 +94,7 @@ function reload(id: string, newComp: ComponentOptions) {
 | 
			
		||||
    const comp = instance.type
 | 
			
		||||
    if (!hmrDirtyComponents.has(comp)) {
 | 
			
		||||
      // 1. Update existing comp definition to match new one
 | 
			
		||||
      newComp = isClassComponent(newComp) ? newComp.__vccOpts : newComp
 | 
			
		||||
      extend(comp, newComp)
 | 
			
		||||
      for (const key in comp) {
 | 
			
		||||
        if (!(key in newComp)) {
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,8 @@ import {
 | 
			
		||||
  Data,
 | 
			
		||||
  ConcreteComponent,
 | 
			
		||||
  ClassComponent,
 | 
			
		||||
  Component
 | 
			
		||||
  Component,
 | 
			
		||||
  isClassComponent
 | 
			
		||||
} from './component'
 | 
			
		||||
import { RawSlots } from './componentSlots'
 | 
			
		||||
import { isProxy, Ref, toRaw, ReactiveFlags } from '@vue/reactivity'
 | 
			
		||||
@ -340,7 +341,7 @@ function _createVNode(
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // class component normalization.
 | 
			
		||||
  if (isFunction(type) && '__vccOpts' in type) {
 | 
			
		||||
  if (isClassComponent(type)) {
 | 
			
		||||
    type = type.__vccOpts
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user