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 __TEST__: boolean
|
||||
declare var __BROWSER__: boolean
|
||||
declare var __BUNDLER__: boolean
|
||||
declare var __RUNTIME_COMPILE__: boolean
|
||||
declare var __COMMIT__: string
|
||||
declare var __VERSION__: string
|
||||
|
@ -66,6 +66,10 @@ export interface ComponentOptionsBase<
|
||||
directives?: Record<string, Directive>
|
||||
inheritAttrs?: boolean
|
||||
|
||||
// SFC & dev only
|
||||
__scopeId?: string
|
||||
__hmrId?: string
|
||||
|
||||
// type-only differentiator to separate OptionWithoutProps from a constructor
|
||||
// type returned by createComponent() or FunctionalComponent
|
||||
call?: never
|
||||
|
@ -37,6 +37,7 @@ export interface FunctionalComponent<P = {}> {
|
||||
props?: ComponentPropsOptions<P>
|
||||
inheritAttrs?: boolean
|
||||
displayName?: string
|
||||
__hmrId?: string
|
||||
}
|
||||
|
||||
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
|
||||
} from './components/BaseTransition'
|
||||
|
||||
// Internal, for compiler generated code
|
||||
// Internal API ----------------------------------------------------------------
|
||||
|
||||
// For compiler generated code
|
||||
// should sync with '@vue/compiler-core/src/runtimeConstants.ts'
|
||||
export { withDirectives } from './directives'
|
||||
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 camelize = _camelize as (s: string) => string
|
||||
|
||||
// Internal, for integration with runtime compiler
|
||||
// For integration with runtime compiler
|
||||
export { registerRuntimeCompiler } from './component'
|
||||
|
||||
// Types -----------------------------------------------------------------------
|
||||
|
@ -53,6 +53,7 @@ import {
|
||||
} from './components/Suspense'
|
||||
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
|
||||
import { KeepAliveSink, isKeepAlive } from './components/KeepAlive'
|
||||
import { registerHMR, unregisterHMR } from './hmr'
|
||||
|
||||
export interface RendererOptions<HostNode = any, HostElement = any> {
|
||||
patchProp(
|
||||
@ -857,6 +858,11 @@ export function createRenderer<
|
||||
parentComponent
|
||||
))
|
||||
|
||||
// HMR
|
||||
if (__BUNDLER__ && __DEV__ && instance.type.__hmrId != null) {
|
||||
registerHMR(instance)
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
pushWarningContext(initialVNode)
|
||||
}
|
||||
@ -1549,6 +1555,11 @@ export function createRenderer<
|
||||
parentSuspense: HostSuspenseBoundary | null,
|
||||
doRemove?: boolean
|
||||
) {
|
||||
// HMR
|
||||
if (__BUNDLER__ && __DEV__ && instance.type.__hmrId != null) {
|
||||
unregisterHMR(instance)
|
||||
}
|
||||
|
||||
const { bum, effects, update, subTree, um, da, isDeactivated } = instance
|
||||
// beforeUnmount hook
|
||||
if (bum !== null) {
|
||||
|
@ -147,6 +147,8 @@ function createReplacePlugin(
|
||||
__TEST__: isBundlerESMBuild ? `(process.env.NODE_ENV === 'test')` : false,
|
||||
// If the build is expected to run directly in the browser (global / esm builds)
|
||||
__BROWSER__: isBrowserBuild,
|
||||
// is targeting bundlers?
|
||||
__BUNDLER__: isBundlerESMBuild,
|
||||
// support compile in browser?
|
||||
__RUNTIME_COMPILE__: isRuntimeCompileBuild,
|
||||
// support options?
|
||||
|
Loading…
Reference in New Issue
Block a user