feat(runtime-core): hot module replacement
This commit is contained in:
parent
3116b5d6c3
commit
efe39db023
1
packages/global.d.ts
vendored
1
packages/global.d.ts
vendored
@ -2,6 +2,7 @@
|
|||||||
declare var __DEV__: boolean
|
declare var __DEV__: boolean
|
||||||
declare var __TEST__: boolean
|
declare var __TEST__: boolean
|
||||||
declare var __BROWSER__: boolean
|
declare var __BROWSER__: boolean
|
||||||
|
declare var __BUNDLER__: boolean
|
||||||
declare var __RUNTIME_COMPILE__: boolean
|
declare var __RUNTIME_COMPILE__: boolean
|
||||||
declare var __COMMIT__: string
|
declare var __COMMIT__: string
|
||||||
declare var __VERSION__: string
|
declare var __VERSION__: string
|
||||||
|
@ -66,6 +66,10 @@ export interface ComponentOptionsBase<
|
|||||||
directives?: Record<string, Directive>
|
directives?: Record<string, Directive>
|
||||||
inheritAttrs?: boolean
|
inheritAttrs?: boolean
|
||||||
|
|
||||||
|
// SFC & dev only
|
||||||
|
__scopeId?: string
|
||||||
|
__hmrId?: string
|
||||||
|
|
||||||
// type-only differentiator to separate OptionWithoutProps from a constructor
|
// type-only differentiator to separate OptionWithoutProps from a constructor
|
||||||
// type returned by createComponent() or FunctionalComponent
|
// type returned by createComponent() or FunctionalComponent
|
||||||
call?: never
|
call?: never
|
||||||
|
@ -37,6 +37,7 @@ export interface FunctionalComponent<P = {}> {
|
|||||||
props?: ComponentPropsOptions<P>
|
props?: ComponentPropsOptions<P>
|
||||||
inheritAttrs?: boolean
|
inheritAttrs?: boolean
|
||||||
displayName?: string
|
displayName?: string
|
||||||
|
__hmrId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Component = ComponentOptions | FunctionalComponent
|
export type Component = ComponentOptions | FunctionalComponent
|
||||||
|
85
packages/runtime-core/src/hmr.ts
Normal file
85
packages/runtime-core/src/hmr.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import {
|
||||||
|
ComponentInternalInstance,
|
||||||
|
ComponentOptions,
|
||||||
|
RenderFunction
|
||||||
|
} from './component'
|
||||||
|
|
||||||
|
// Expose the HMR runtime on the global object
|
||||||
|
// This makes it entirely tree-shakable without polluting the exports and makes
|
||||||
|
// it easier to be used in toolings like vue-loader
|
||||||
|
// Note: for a component to be eligible for HMR it also needs the __hmrId option
|
||||||
|
// to be set so that its instances can be registered / removed.
|
||||||
|
if (__BUNDLER__ && __DEV__) {
|
||||||
|
const globalObject: any =
|
||||||
|
typeof global !== 'undefined'
|
||||||
|
? global
|
||||||
|
: typeof self !== 'undefined'
|
||||||
|
? self
|
||||||
|
: typeof window !== 'undefined'
|
||||||
|
? window
|
||||||
|
: {}
|
||||||
|
|
||||||
|
globalObject.__VUE_HMR_RUNTIME__ = {
|
||||||
|
isRecorded: tryWrap(isRecorded),
|
||||||
|
createRecord: tryWrap(createRecord),
|
||||||
|
rerender: tryWrap(rerender),
|
||||||
|
reload: tryWrap(reload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HMRRecord {
|
||||||
|
comp: ComponentOptions
|
||||||
|
instances: Set<ComponentInternalInstance>
|
||||||
|
}
|
||||||
|
|
||||||
|
const map: Map<string, HMRRecord> = new Map()
|
||||||
|
|
||||||
|
export function registerHMR(instance: ComponentInternalInstance) {
|
||||||
|
map.get(instance.type.__hmrId!)!.instances.add(instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unregisterHMR(instance: ComponentInternalInstance) {
|
||||||
|
map.get(instance.type.__hmrId!)!.instances.delete(instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRecorded(id: string): boolean {
|
||||||
|
return map.has(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRecord(id: string, comp: ComponentOptions) {
|
||||||
|
if (map.has(id)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
map.set(id, {
|
||||||
|
comp,
|
||||||
|
instances: new Set()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function rerender(id: string, newRender: RenderFunction) {
|
||||||
|
map.get(id)!.instances.forEach(instance => {
|
||||||
|
instance.render = newRender
|
||||||
|
instance.renderCache = []
|
||||||
|
instance.update()
|
||||||
|
// TODO force scoped slots passed to children to have DYNAMIC_SLOTS flag
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function reload(id: string, newComp: ComponentOptions) {
|
||||||
|
// TODO
|
||||||
|
console.log('reload', id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryWrap(fn: (id: string, arg: any) => void): Function {
|
||||||
|
return (id: string, arg: any) => {
|
||||||
|
try {
|
||||||
|
fn(id, arg)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
console.warn(
|
||||||
|
`Something went wrong during Vue component hot-reload. ` +
|
||||||
|
`Full reload required.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -66,7 +66,9 @@ export {
|
|||||||
TransitionHooks
|
TransitionHooks
|
||||||
} from './components/BaseTransition'
|
} from './components/BaseTransition'
|
||||||
|
|
||||||
// Internal, for compiler generated code
|
// Internal API ----------------------------------------------------------------
|
||||||
|
|
||||||
|
// For compiler generated code
|
||||||
// should sync with '@vue/compiler-core/src/runtimeConstants.ts'
|
// should sync with '@vue/compiler-core/src/runtimeConstants.ts'
|
||||||
export { withDirectives } from './directives'
|
export { withDirectives } from './directives'
|
||||||
export {
|
export {
|
||||||
@ -87,7 +89,7 @@ import { capitalize as _capitalize, camelize as _camelize } from '@vue/shared'
|
|||||||
export const capitalize = _capitalize as (s: string) => string
|
export const capitalize = _capitalize as (s: string) => string
|
||||||
export const camelize = _camelize as (s: string) => string
|
export const camelize = _camelize as (s: string) => string
|
||||||
|
|
||||||
// Internal, for integration with runtime compiler
|
// For integration with runtime compiler
|
||||||
export { registerRuntimeCompiler } from './component'
|
export { registerRuntimeCompiler } from './component'
|
||||||
|
|
||||||
// Types -----------------------------------------------------------------------
|
// Types -----------------------------------------------------------------------
|
||||||
|
@ -53,6 +53,7 @@ import {
|
|||||||
} from './components/Suspense'
|
} from './components/Suspense'
|
||||||
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
|
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
|
||||||
import { KeepAliveSink, isKeepAlive } from './components/KeepAlive'
|
import { KeepAliveSink, isKeepAlive } from './components/KeepAlive'
|
||||||
|
import { registerHMR, unregisterHMR } from './hmr'
|
||||||
|
|
||||||
export interface RendererOptions<HostNode = any, HostElement = any> {
|
export interface RendererOptions<HostNode = any, HostElement = any> {
|
||||||
patchProp(
|
patchProp(
|
||||||
@ -857,6 +858,11 @@ export function createRenderer<
|
|||||||
parentComponent
|
parentComponent
|
||||||
))
|
))
|
||||||
|
|
||||||
|
// HMR
|
||||||
|
if (__BUNDLER__ && __DEV__ && instance.type.__hmrId != null) {
|
||||||
|
registerHMR(instance)
|
||||||
|
}
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
pushWarningContext(initialVNode)
|
pushWarningContext(initialVNode)
|
||||||
}
|
}
|
||||||
@ -1549,6 +1555,11 @@ export function createRenderer<
|
|||||||
parentSuspense: HostSuspenseBoundary | null,
|
parentSuspense: HostSuspenseBoundary | null,
|
||||||
doRemove?: boolean
|
doRemove?: boolean
|
||||||
) {
|
) {
|
||||||
|
// HMR
|
||||||
|
if (__BUNDLER__ && __DEV__ && instance.type.__hmrId != null) {
|
||||||
|
unregisterHMR(instance)
|
||||||
|
}
|
||||||
|
|
||||||
const { bum, effects, update, subTree, um, da, isDeactivated } = instance
|
const { bum, effects, update, subTree, um, da, isDeactivated } = instance
|
||||||
// beforeUnmount hook
|
// beforeUnmount hook
|
||||||
if (bum !== null) {
|
if (bum !== null) {
|
||||||
|
@ -147,6 +147,8 @@ function createReplacePlugin(
|
|||||||
__TEST__: isBundlerESMBuild ? `(process.env.NODE_ENV === 'test')` : false,
|
__TEST__: isBundlerESMBuild ? `(process.env.NODE_ENV === 'test')` : false,
|
||||||
// If the build is expected to run directly in the browser (global / esm builds)
|
// If the build is expected to run directly in the browser (global / esm builds)
|
||||||
__BROWSER__: isBrowserBuild,
|
__BROWSER__: isBrowserBuild,
|
||||||
|
// is targeting bundlers?
|
||||||
|
__BUNDLER__: isBundlerESMBuild,
|
||||||
// support compile in browser?
|
// support compile in browser?
|
||||||
__RUNTIME_COMPILE__: isRuntimeCompileBuild,
|
__RUNTIME_COMPILE__: isRuntimeCompileBuild,
|
||||||
// support options?
|
// support options?
|
||||||
|
Loading…
x
Reference in New Issue
Block a user