feat(hmr): root instance reload
This commit is contained in:
parent
c3e1c812e3
commit
eda495efd8
@ -6,7 +6,7 @@ import { RootRenderFunction } from './renderer'
|
||||
import { InjectionKey } from './apiInject'
|
||||
import { isFunction, NO, isObject } from '@vue/shared'
|
||||
import { warn } from './warning'
|
||||
import { createVNode } from './vnode'
|
||||
import { createVNode, cloneVNode } from './vnode'
|
||||
|
||||
export interface App<HostElement = any> {
|
||||
config: AppConfig
|
||||
@ -47,6 +47,7 @@ export interface AppContext {
|
||||
components: Record<string, Component>
|
||||
directives: Record<string, Directive>
|
||||
provides: Record<string | symbol, any>
|
||||
reload?: () => void // HMR only
|
||||
}
|
||||
|
||||
type PluginInstallFunction = (app: App) => any
|
||||
@ -175,6 +176,14 @@ export function createAppAPI<HostNode, HostElement>(
|
||||
// store app context on the root VNode.
|
||||
// this will be set on the root instance on initial mount.
|
||||
vnode.appContext = context
|
||||
|
||||
// HMR root reload
|
||||
if (__BUNDLER__ && __DEV__) {
|
||||
context.reload = () => {
|
||||
render(cloneVNode(vnode), rootContainer)
|
||||
}
|
||||
}
|
||||
|
||||
render(vnode, rootContainer)
|
||||
isMounted = true
|
||||
return vnode.component!.proxy
|
||||
|
@ -60,7 +60,9 @@ function createRecord(id: string, comp: ComponentOptions): boolean {
|
||||
}
|
||||
|
||||
function rerender(id: string, newRender?: RenderFunction) {
|
||||
map.get(id)!.instances.forEach(instance => {
|
||||
// Array.from creates a snapshot which avoids the set being mutated during
|
||||
// updates
|
||||
Array.from(map.get(id)!.instances).forEach(instance => {
|
||||
if (newRender) {
|
||||
instance.render = newRender
|
||||
}
|
||||
@ -85,13 +87,19 @@ function reload(id: string, newComp: ComponentOptions) {
|
||||
// 2. Mark component dirty. This forces the renderer to replace the component
|
||||
// on patch.
|
||||
comp.__hmrUpdated = true
|
||||
record.instances.forEach(instance => {
|
||||
// Array.from creates a snapshot which avoids the set being mutated during
|
||||
// updates
|
||||
Array.from(record.instances).forEach(instance => {
|
||||
if (instance.parent) {
|
||||
// 3. Force the parent instance to re-render. This will cause all updated
|
||||
// components to be unmounted and re-mounted. Queue the update so that we
|
||||
// don't end up forcing the same parent to re-render multiple times.
|
||||
queueJob(instance.parent.update)
|
||||
} else if (instance.appContext.reload) {
|
||||
// root instance mounted via createApp() has a reload method
|
||||
instance.appContext.reload()
|
||||
} else if (typeof window !== 'undefined') {
|
||||
// root instance inside tree created via raw render(). Force reload.
|
||||
window.location.reload()
|
||||
} else {
|
||||
console.warn(
|
||||
|
@ -1727,12 +1727,12 @@ export function createRenderer<
|
||||
}
|
||||
}
|
||||
|
||||
const render: RootRenderFunction<
|
||||
HostNode,
|
||||
HostElement & {
|
||||
_vnode: HostVNode | null
|
||||
}
|
||||
> = (vnode, container) => {
|
||||
type HostRootElement = HostElement & { _vnode: HostVNode | null }
|
||||
|
||||
const render: RootRenderFunction<HostNode, HostElement> = (
|
||||
vnode,
|
||||
container: HostRootElement
|
||||
) => {
|
||||
if (vnode == null) {
|
||||
if (container._vnode) {
|
||||
unmount(container._vnode, null, null, true)
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
import { RawSlots } from './componentSlots'
|
||||
import { ShapeFlags } from './shapeFlags'
|
||||
import { isReactive, Ref } from '@vue/reactivity'
|
||||
import { AppContext } from './apiApp'
|
||||
import { AppContext } from './apiCreateApp'
|
||||
import { SuspenseBoundary } from './components/Suspense'
|
||||
import { DirectiveBinding } from './directives'
|
||||
import { SuspenseImpl } from './components/Suspense'
|
||||
|
Loading…
Reference in New Issue
Block a user