vue 3.0 源码解读
2020-11-11 21:45:28 1 举报
AI智能生成
vue3.0源码分析
作者其他创作
大纲/内容
import { createApp,reactive,toRefs, ref , watch} from 'vue'
初始化vue全局函数和变量
@Import
类:EnableAutoConfigurationImportSelector
方法:SpringFactoriesLoader.loadFactoryNames
扫描:META-INF/spring.factories
const emptyAppContext = createAppContext()
执行createAppContext,初始化createApp的appContext(执行上下文)
config
isNativeTag: NO, // 是否原始标签,默认返回false
devtools: true, // 使用devtools
performance: false, // 性能优化
globalProperties: {}, // 全局属性
optionMergeStrategies: {}, // 同源合并策略
isCustomElement: NO, // 自定义标签
errorHandler: undefined, // 错误处理函数
warnHandler: undefined // 警告处理函数
mixins: []
components: {}
directives: {},
provides: Object.create(null) // 提供属性
执行packages/vue/index.ts文件
__DEV__ && initDev()
/ 初始化dev
export function initDev() {
// 判断运行环境,引入全局变量window或者global
const target: any = __BROWSER__ ? window : global
// window/global上添加属性version
target.__VUE__ = version
// 设置dev hook
setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__)
// 浏览器提示
if (__BROWSER__) {
console[console.info ? 'info' : 'log'](
`You are running a development build of Vue.\n` +
`Make sure to use the production build (*.prod.js) when deploying for production.`
)
}
}
export function initDev() {
// 判断运行环境,引入全局变量window或者global
const target: any = __BROWSER__ ? window : global
// window/global上添加属性version
target.__VUE__ = version
// 设置dev hook
setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__)
// 浏览器提示
if (__BROWSER__) {
console[console.info ? 'info' : 'log'](
`You are running a development build of Vue.\n` +
`Make sure to use the production build (*.prod.js) when deploying for production.`
)
}
}
// 设定dev hook
export function setDevtoolsHook(hook: DevtoolsHook) {
devtools = hook
}
export function setDevtoolsHook(hook: DevtoolsHook) {
devtools = hook
}
初始化compileToFunction
注册compile函数:registerRuntimeCompiler(compileToFunction)
let compile: CompileFunction | undefined
// 注册compile函数
export function registerRuntimeCompiler(_compile: any) {
compile = _compile
}
// 注册compile函数
export function registerRuntimeCompiler(_compile: any) {
compile = _compile
}
app = createApp(App)
执行createApp方法
const app = ensureRenderer().createApp(...args)
执行ensureRender生成renderer函数
function ensureRenderer() {
return renderer || (renderer = createRenderer<Node, Element>(rendererOptions))
}
function ensureRenderer() {
return renderer || (renderer = createRenderer<Node, Element>(rendererOptions))
}
执行createRender函数
export function createRenderer<
HostNode = RendererNode,
HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
return baseCreateRenderer<HostNode, HostElement>(options)
}
export function createRenderer<
HostNode = RendererNode,
HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
return baseCreateRenderer<HostNode, HostElement>(options)
}
执行baseCreateRenderer函数:
注意这里使用了ts语法的函数重载,分别用于生成SPA和服务端渲染的renderer
注意这里使用了ts语法的函数重载,分别用于生成SPA和服务端渲染的renderer
baseCreateRenderer的返回值:
return {
render, // 渲染函数
hydrate, // 混入SSR
// createAppAPI返回createApp方法,其中createApp会返回app实例
createApp: createAppAPI(render, hydrate)
}
return {
render, // 渲染函数
hydrate, // 混入SSR
// createAppAPI返回createApp方法,其中createApp会返回app实例
createApp: createAppAPI(render, hydrate)
}
执行createAppAPI方法,生成app:
将参数render传入,返回一个新的函数,即正真的creteApp方法
return function createApp(rootComponent, rootProps = null){ ... }
将参数render传入,返回一个新的函数,即正真的creteApp方法
return function createApp(rootComponent, rootProps = null){ ... }
执行完ensureRenderer方法后,放回的对象中含有createApp方法,
下一步开始执行这个方法,生成app
下一步开始执行这个方法,生成app
执行createApp方法
// 初始化app中context上下文的选项
const context = createAppContext()
const context = createAppContext()
// 初始化安装插件集合
const installedPlugins = new Set()
const installedPlugins = new Set()
// 初次渲染isMounted设置为false
let isMounted = false
let isMounted = false
// 初始化app中的选项
const app: App = {...}
const app: App = {...}
_component
_props
_container
_context
get/set config
use
mixin
component
directive
unmount
provide
// 在app中执行上下文挂载app实例对象
context.__app = app
context.__app = app
最后返回app(return app)
包装app.mount方法,最后返回app实例,即最终的app
app.mount('#app')
执行app中的mount方法,挂载vue实例
如果第一次挂载实例,执行里面的逻辑
1. 创建根vnode:
创建根vnode:rootComponent为App中 export default内容,rootProps为createApp(App, {})中的配置项
const vnode = createVNode(rootComponent as Component, rootProps)
创建根vnode:rootComponent为App中 export default内容,rootProps为createApp(App, {})中的配置项
const vnode = createVNode(rootComponent as Component, rootProps)
执行createVnode方法:
// 创建虚拟dom入口,开发环境使用createVNodeWithArgsTransform将createVNode映射到_createVNode,
// 生产环境_createVNode
// 最后都是调用_createVNode这个方法
export const createVNode = (__DEV__
? createVNodeWithArgsTransform
: _createVNode) as typeof _createVNode
// 创建虚拟dom入口,开发环境使用createVNodeWithArgsTransform将createVNode映射到_createVNode,
// 生产环境_createVNode
// 最后都是调用_createVNode这个方法
export const createVNode = (__DEV__
? createVNodeWithArgsTransform
: _createVNode) as typeof _createVNode
dev时执行createVNodeWithArgsTransform方法:
// render时,code中使用的createVNode,需要找到实际的_createVNode,进行映射
// 执行_createVNode方法,创建dom节点
const createVNodeWithArgsTransform = (
// args为数组:args[rootComponent,rootProps],
...args: Parameters<typeof _createVNode>
): VNode => {
return _createVNode(
...(vnodeArgsTransformer
? vnodeArgsTransformer(args, currentRenderingInstance)
: args)
)
}
// render时,code中使用的createVNode,需要找到实际的_createVNode,进行映射
// 执行_createVNode方法,创建dom节点
const createVNodeWithArgsTransform = (
// args为数组:args[rootComponent,rootProps],
...args: Parameters<typeof _createVNode>
): VNode => {
return _createVNode(
...(vnodeArgsTransformer
? vnodeArgsTransformer(args, currentRenderingInstance)
: args)
)
}
执行_createVnode方法
**第一次执行这个方法的时候,只传入type一个参数
1. 首先会将传入的template(app)编译为ast
2. 将ast转为vnode
3. 生成对应平台的render哈数
**第一次执行这个方法的时候,只传入type一个参数
1. 首先会将传入的template(app)编译为ast
2. 将ast转为vnode
3. 生成对应平台的render哈数
1. 判断是否传入了type
// type不存在
if (!type || type === NULL_DYNAMIC_COMPONENT) {
if (__DEV__ && !type) {
warn(`Invalid vnode type when creating vnode: ${type}.`)
}
type = Comment
}
// type不存在
if (!type || type === NULL_DYNAMIC_COMPONENT) {
if (__DEV__ && !type) {
warn(`Invalid vnode type when creating vnode: ${type}.`)
}
type = Comment
}
2. 判断传入的type是否为合法的Vnode类型
// 判断是不是虚拟dom
if (isVNode(type)) {
return cloneVNode(type, props, children)
}
// 判断是不是虚拟dom
if (isVNode(type)) {
return cloneVNode(type, props, children)
}
3. 判断是不是函数组件
4. 初始化传入的type的shapeFlag类型,如果为component组件,值为4
5. 检查该type是否已经响应式处理,如果初次挂载时,响应式处理了,则报错
6. 初始化vnode tree根节点
7. 排除vnode中键为NaN的情况
8. 执行normalizeChildren函数解析子节点children,component初始化时子节点为null
判断children的类型:null,Object,Array,Function,
进行相应的处理
进行相应的处理
设置树根节点的children和shapeFlag属性
9. 保存vnode
10. 返回当前树根节点vnode
2. 将app执行上下文appContext挂载到vnode根节点属性中
3. dev模式,实现模块热更新加载
4. 判断是ssr还是spa渲染
1. 如果是ssr渲染,则执行ssr render
2. 如果是spa单页面应用,则render单页面
执行render方法
路径:packages\runtime-core\src\renderer.ts
路径:packages\runtime-core\src\renderer.ts
判断传入的vnode是否为null
1. vnode为null
unmount已经挂载的节点
2. vnode 不为null
编译template模板为vnode
执行patch(renderer.ts)方法:
1. 初始化时,生成vnode;
2. 如果vnode已经渲染过,则执行diff算法更新对应的旧vnode;
1. 初始化时,生成vnode;
2. 如果vnode已经渲染过,则执行diff算法更新对应的旧vnode;
1. 判断是更新节点还是初次渲染:
已经渲染过,并且新的vnode和旧的vnode不一样,则卸载节点
已经渲染过,并且新的vnode和旧的vnode不一样,则卸载节点
2. PatchFlags为-2(PatchFlags.Bail)时,不做性能优化,optimized为false
3. 判断type(Text,Comment,Static,Fragmentdeng)的类型,节点解析
notice:初次渲染是只走default选项,在render函数调用时使用,用于生成真正dom
notice:初次渲染是只走default选项,在render函数调用时使用,用于生成真正dom
判断vnode的shapeFlag,这里以shapeFlag为ShapeFlags.COMPONENT为例
执行processComponent方法,处理组件,生成vnode:
判断是首次渲染还是已经渲染过
1. 初次渲染,即n1为null
缓冲组件,即传入了keep-alive
不缓冲组件
执行mountComponent方法,初次挂载组件
1. 初始化化组件实例:instance
(执行ComponentInternalInstance(packages\runtime-core\src\component.ts))
(执行ComponentInternalInstance(packages\runtime-core\src\component.ts))
执行ComponentInternalInstance(packages\runtime-core\src\component.ts)
1. 初始化实例instance选项
2. 设置instance的执行上下文context属性
dev模式
1. 执行createRenderContext方法,生成instance上下文
2. 返回target对象:return target as ComponentRenderContext
// 定义组件渲染上下文接口
export interface ComponentRenderContext {
[key: string]: any // 共有和全局属性
_: ComponentInternalInstance // 组件实例instance
}
***这里与pro不同之处在于,在instance上挂载了共有和全局配置属性,这些属性只可以配置,不可以枚举***
2. 返回target对象:return target as ComponentRenderContext
// 定义组件渲染上下文接口
export interface ComponentRenderContext {
[key: string]: any // 共有和全局属性
_: ComponentInternalInstance // 组件实例instance
}
***这里与pro不同之处在于,在instance上挂载了共有和全局配置属性,这些属性只可以配置,不可以枚举***
pro模式
instance的执行上下文为self
3. 设置instance实例的root
4. 设置instance的emit方法
5. dev模式:将instance添加到devtools中
6. 返回当前实例对象:instance
2. dev模式:开启热模块更新功能
3. dev模式:将vnode存储到stack中
4. 如果使用了keep-alive(缓冲组件),则将
instance的ctx中的renderer设置为internals
instance的ctx中的renderer设置为internals
5. 初始化组件:setupComponent(instance)--> runtime-core\src\component.ts
这里已经完成了setup函数初始化
--> ast -->ast.codegenNode --> code
--> 组件实例选项初始化(data/mixins/computed/lifeCycle等)
此时实例instace已经生成render函数
这里已经完成了setup函数初始化
--> ast -->ast.codegenNode --> code
--> 组件实例选项初始化(data/mixins/computed/lifeCycle等)
此时实例instace已经生成render函数
1. 设置ssr:
// 重新设置isInSSRComponentSetup的值
isInSSRComponentSetup = isSSR
// 重新设置isInSSRComponentSetup的值
isInSSRComponentSetup = isSSR
2. 判断是不是状态组件/函数组件
const { props, children, shapeFlag } = instance.vnode
// 这里判断是不是状态组件/无状态组件
const isStateful = shapeFlag & ShapeFlags.STATEFUL_COMPONENT
const { props, children, shapeFlag } = instance.vnode
// 这里判断是不是状态组件/无状态组件
const isStateful = shapeFlag & ShapeFlags.STATEFUL_COMPONENT
3. 初始化props属性:
initProps(instance, props, isStateful, isSSR)
initProps(instance, props, isStateful, isSSR)
执行initProps方法
1. 初始化props和attrs属性
const props: Data = {}
const attrs: Data = {}
const props: Data = {}
const attrs: Data = {}
2. attrs中定义InternalObjectKey属性
// InternalObjectKey即为 __vInternal
def(attrs, InternalObjectKey, 1)
// InternalObjectKey即为 __vInternal
def(attrs, InternalObjectKey, 1)
执行def方法
3. 设置props属性
setFullProps(instance, rawProps, props, attrs)
setFullProps(instance, rawProps, props, attrs)
1. 格式组件type中的options中的props属性.
得到options, needCastKeys
得到options, needCastKeys
执行normalizePropsOptions方法
1. 如果comp._props存在,直接返回comp._props
2. 初始化normalize、needCastKeys、hasExtends变量
3. 判断comp不是是函数组件并且全局__FEATURE_OPTIONS__为true时
__FEATURE_OPTIONS__ && !isFunction(comp),处理组件中的extends和mixins的props
__FEATURE_OPTIONS__ && !isFunction(comp),处理组件中的extends和mixins的props
1. 处理comp.extends
2. 处理comp.mixins
4. !raw && !hasExtends:props和混入的属性不存在
if (!raw && !hasExtends) {
return (comp.__props = EMPTY_ARR)
}
if (!raw && !hasExtends) {
return (comp.__props = EMPTY_ARR)
}
5. raw为为数组
遍历数组,将数组中每项最小化后存储到normalized中
6. raw为对象Object
1. 验证raw是否为对象
2. 遍历raw,将每项分别存储到normalized和needCastKeys中
7. 初始化对象normalizedEntry
const normalizedEntry: NormalizedPropsOptions = [normalized, needCastKeys]
const normalizedEntry: NormalizedPropsOptions = [normalized, needCastKeys]
8. 在comp上挂载normalizedEntry属性
9. 返回normalizedEntry对象
2. rawProps存在,例如id/class/value等,格式化
3. needCastKeys存在, 循环遍历找到propValue并添加到props中
needCastKeys为[],保存的是属性嵌套属性,比如obj.a.b,保存a,b
needCastKeys为[],保存的是属性嵌套属性,比如obj.a.b,保存a,b
4. 开发模式下验证instance.type中的属性名
if (__DEV__) {
validateProps(props, instance.type)
}
if (__DEV__) {
validateProps(props, instance.type)
}
5. 判断是状态组件还是函数组件
1. 状态组件:isStateful=true
1. ssr渲染:实例上直接挂载props属性
instance.props = props
instance.props = props
2. spa:设置为一层响应对象
instance.props = shallowReactive(props)
instance.props = shallowReactive(props)
2. 函数组件:isStateful=false
判断instance.type,props属性是否存在
1. 不存在:instance.props = attrs
2. 存在: instance.props = props
6. 设置实例instance的attrs属性:
instance.attrs = attrs
instance.attrs = attrs
4. 初始化slots属性
initSlots(instance, children)
initSlots(instance, children)
1. instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN:
判断是否shapeFlag为slots插槽
判断是否shapeFlag为slots插槽
1. true: 设置instance的slots
2. false: instance.slots = {},同时
循环遍历字节点children
循环遍历字节点children
执行normalizeVNodeSlots方法
1. 判断是不是缓冲组件
2. 格式化插槽中的值
const normalized = normalizeSlotValue(children)
const normalized = normalizeSlotValue(children)
执行normalizeSlotValue函数
执行normalizeVNode方法将slot生成vnode
判断出入的child类型
判断出入的child类型
1. child == null || typeof child === 'boolean'
执行
// 如果为空的占位符
return createVNode(Comment)
// 如果为空的占位符
return createVNode(Comment)
2. isArray(child)
执行
// fragment
return createVNode(Fragment, null, child)
// fragment
return createVNode(Fragment, null, child)
3. typeof child === 'object'
执行
// 这是vnode已经是真正的vnode,这应该是最常见的,
// 因为编译模板总是生成所有vnode子数组
return child.el === null ? child : cloneVNode(child)
// 这是vnode已经是真正的vnode,这应该是最常见的,
// 因为编译模板总是生成所有vnode子数组
return child.el === null ? child : cloneVNode(child)
4. default
执行
/ /strings and numbers:字符串或者数字情况
return createVNode(Text, null, String(child))
/ /strings and numbers:字符串或者数字情况
return createVNode(Text, null, String(child))
3. 设置instance.slots.default
instance.slots.default = () => normalized
instance.slots.default = () => normalized
3. 将instance.slots标记为内部使用对象
def(instance.slots, InternalObjectKey, 1)
def(instance.slots, InternalObjectKey, 1)
5. 初始化全局状态组件
const setupResult = isStateful
? setupStatefulComponent(instance, isSSR)
: undefined
const setupResult = isStateful
? setupStatefulComponent(instance, isSSR)
: undefined
执行setupStatefulComponent(instance, isSSR)方法
1. 验证组件的名字是否合法
1. 验证组件名字,执行validateComponentName方法
2. 验证组件中的子组件Component.components,
循环遍历
循环遍历
3. 验证component.directives
4. 创建渲染代理属性访问缓存
instance.accessCache = {}
instance.accessCache = {}
5. 设置instance.ctx为响应式对象
// 将instance.ctx上的属性全部proxy,设置成响应式set/get
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)
// 将instance.ctx上的属性全部proxy,设置成响应式set/get
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)
6. dev模式:将props属性挂载到ctx上
exposePropsOnRenderContext(instance)
exposePropsOnRenderContext(instance)
7. Component中setup存在,执行该函数
1. 生成setup执行上下文setupContext
执行createSetupContext方法
2. 设置当前实例:
// 保存当前实例
currentInstance = instance
// 保存当前实例
currentInstance = instance
3. 初始化停止依赖收集
// 初始化时,停止依赖收集
pauseTracking()
// 初始化时,停止依赖收集
pauseTracking()
4. 执行callWithErrorHandling方法,完成setup函数初始化设置
返回setupResult结果
返回setupResult结果
执行callWithErrorHandling方法
执行fn(setup函数)
- 1. ref api执行
执行ref(value)方法
执行createRef(value)
1. 判断传入的value是否已经是响应式
(value中存在__v_isRef属性为响应式)
(value中存在__v_isRef属性为响应式)
2. 判断是否为浅响应式
// 是否为一层响应
let value = shallow ? rawValue : convert(rawValue)
// 是否为一层响应
let value = shallow ? rawValue : convert(rawValue)
不是浅响应,执行convert方法,将其变成深响应对象
3. 初始化响应式对象set/get:r
get value 方法,收集依赖,执行track方法
执行track(target: object, type: TrackOpTypes, key: unknown)方法
1. 判断是否允许收集依赖,不允许直接返回
if (!shouldTrack || activeEffect === undefined) {
return
}
if (!shouldTrack || activeEffect === undefined) {
return
}
2. 判断依赖图谱targetMap中是否存在该target,不存在则将其保存到targetMap
3. targetMap存在该target,获取对应的key值,如果dep
不存在,则设置该值为new Set()
不存在,则设置该值为new Set()
4. 开始收集依赖
set value,执行trigger,触发更新
4. 返回r
2. 执行reactive方法
路径:\reactivity\src\reactive.ts
路径:\reactivity\src\reactive.ts
执行reactive(target: object)方法
如果target上存在__v_isReadonly属性,
直接返回
直接返回
创建响应式proxy对象,
执行createReactiveObject
执行createReactiveObject
1. 判断传入的target是否为object,
不是直接返回
不是直接返回
2. target为只读属性,直接返回target
3. target已经是响应式proxy对象,
返回该响应式对象
返回该响应式对象
4. target是否允许观察,不允许
直接返回target
直接返回target
5. 使用new Proxy将target变成响应式对象
6. 在target上设置响应式对象observed
7. 返回observed
3. 执行toRefs方法:toRefs(state)
路径:reactivity\src\ref.ts
路径:reactivity\src\ref.ts
遍历state,将里面的属性执行toRef操作
返回结果: res
5. 重新收集依赖:
resetTracking()
resetTracking()
6. 清空当前实例
currentInstance = null
currentInstance = null
7. 判断setupResult是否为Promise对象
1. promise对象,执行
1. isSSR:true:
return setupResult.then((resolvedResult: unknown) => {
handleSetupResult(instance, resolvedResult, isSSR)
})
return setupResult.then((resolvedResult: unknown) => {
handleSetupResult(instance, resolvedResult, isSSR)
})
2. __FEATURE_SUSPENSE__:true
// async setup returned Promise.
// bail here and wait for re-entry.
instance.asyncDep = setupResult
// async setup returned Promise.
// bail here and wait for re-entry.
instance.asyncDep = setupResult
3. dev模式:
warn(
`setup() returned a Promise, but the version of Vue you are using ` +
`does not support it yet.`
)
warn(
`setup() returned a Promise, but the version of Vue you are using ` +
`does not support it yet.`
)
2. 不是promise对象
// 处理setupResult结果,结束组件的内部的所有初始化工作
handleSetupResult(instance, setupResult, isSSR)
// 处理setupResult结果,结束组件的内部的所有初始化工作
handleSetupResult(instance, setupResult, isSSR)
执行handleSetupResult方法:
路径:runtime-core\src\component.ts
路径:runtime-core\src\component.ts
1. setupResult为函数
2. setupResult为对象
3. setupResult其他情况,则报错
4. 执行finishComponentSetup方法:
// 结束组件setup
finishComponentSetup(instance, isSSR)
// 结束组件setup
finishComponentSetup(instance, isSSR)
执行finishComponentSetup方法
1. 获取组件:
const Component = instance.type as ComponentOptions
const Component = instance.type as ComponentOptions
2. ssr渲染,instance.render = Component.render
3. spa渲染,instance.render不存在,生成render函数
Component.render = compile(Component.template, {
isCustomElement: instance.appContext.config.isCustomElement || NO
// 判断是不是自定义标签
})
Component.render = compile(Component.template, {
isCustomElement: instance.appContext.config.isCustomElement || NO
// 判断是不是自定义标签
})
执行compile方法,生成render函数
(编译过程单独分支,请看下面的compile)
(编译过程单独分支,请看下面的compile)
4. 设置instance.render函数
instance.render = (Component.render || NOOP) as InternalRenderFunction
instance.render = (Component.render || NOOP) as InternalRenderFunction
5. // 对于使用'with'块的运行时编译render,
// 使用的render代理需要一个不同的'has'处理方式,
// 这个代理render更高效,而且只允许全局的白名单失效。
// 使用的render代理需要一个不同的'has'处理方式,
// 这个代理render更高效,而且只允许全局的白名单失效。
6. 支持vue 2.0 传参,解析通过2.0传入的参数
8. setup不存在,直接执行finishComponentSetup(instance, isSSR)方法
6. 重置ssr:
isInSSRComponentSetup = false
isInSSRComponentSetup = false
2. 初次渲染已完成,节点更新
执行updateComponent方法,更新组件
4. 设置ref
5. 说明:后面定义了一些vnode的操作方法,
例如:processText、mountStaticNode等
例如:processText、mountStaticNode等
5. 设置挂载状态为已经挂载:isMounted = true
6. 设置apps属性_container
app._container = rootContainer // #app
app._container = rootContainer // #app
7. dev模式下,开启devtool调试工具
__DEV__ && initApp(app, version)
__DEV__ && initApp(app, version)
8. 返回: return vnode.component!.proxy
(这里的!含义:如果vnode没有component这个属性,访问proxy也不会报错)
(这里的!含义:如果vnode没有component这个属性,访问proxy也不会报错)
如果重复调用$mount方法,则报错
compile(compileToFunction):编译过程,生成render
路径:\vue\src\index.ts
路径:\vue\src\index.ts
1. template不是字符串,如果template的nodeType存在,
则template = template.innerHTML,否自报错
则template = template.innerHTML,否自报错
2. 判断compileCache是否有缓冲template,
存在直接返回缓冲值
存在直接返回缓冲值
3. 如果template是以'#'开始,表明是传入的id选择器,
获取对应id的el,el存在,则设置template = el.innerHTML,
否则为''
获取对应id的el,el存在,则设置template = el.innerHTML,
否则为''
4. 编译template生成code:
const { code } = compile(template, options)
const { code } = compile(template, options)
执行compile(template, options)方法,生成render函数:
(compiler-dom\src\index.ts)
(compiler-dom\src\index.ts)
执行baseCompile函数,这里在compile函数options
中添加指令转换、节点转换等方法
中添加指令转换、节点转换等方法
1. 获取编译compile错误处理函数,
const onError = options.onError || defaultOnError // 编译错误处理函数
const onError = options.onError || defaultOnError // 编译错误处理函数
2. 判断传入的template是不是module,即.vue文件
// 是不是模块模式
const isModuleMode = options.mode === 'module' // 判断是不是模块
// 是不是模块模式
const isModuleMode = options.mode === 'module' // 判断是不是模块
3. options.prefixIdentifiers ===true,或者
isModuleMode= true,则报错
isModuleMode= true,则报错
4. 处理编译错误
5. 生成ast:执行baseParse(template, options)方法
// 解析html模板字符串为ast抽象语法树
路径:compiler-core\src\parse.ts
const ast = isString(template) ? baseParse(template, options) : template
// 解析html模板字符串为ast抽象语法树
路径:compiler-core\src\parse.ts
const ast = isString(template) ? baseParse(template, options) : template
执行baseParse(template, options)方法
1. 执行createParseContex方法,生成解析
字符串template上下文
const context = createParserContext(content, options)
字符串template上下文
const context = createParserContext(content, options)
2. 获取解析模板开始位置start
const start = getCursor(context)
// 信息:line/column/offset
const start = getCursor(context)
// 信息:line/column/offset
3. 执行parseChildren(context,TextModes.DATA, []),
生成子节点ast
生成子节点ast
执行parseChildren方法,
使用while语句循环遍历,直到结束
使用while语句循环遍历,直到结束
1. 获取父元素,初始化时为undefined
const parent = last(ancestors)
const parent = last(ancestors)
// 获取数组中最后一个元素
function last<T>(xs: T[]): T | undefined {
return xs[xs.length - 1]
}
function last<T>(xs: T[]): T | undefined {
return xs[xs.length - 1]
}
2. 获取命名空间:
const ns = parent ? parent.ns : Namespaces.HTML
const ns = parent ? parent.ns : Namespaces.HTML
3. 定义nodes,保存节点
const nodes: TemplateChildNode[] = []
const nodes: TemplateChildNode[] = []
4. 开始标签,不是结束标签:
!isEnd(context, mode, ancestors)
!isEnd(context, mode, ancestors)
1. 获取字符串模板template,即source
const s = context.source
const s = context.source
2. 初始化节点node:
const s = context.source
let node: TemplateChildNode | TemplateChildNode[] | undefined = undefined
const s = context.source
let node: TemplateChildNode | TemplateChildNode[] | undefined = undefined
3. 判断mode,初始化为TextModes.DATA,
mode === TextModes.DATA || mode === TextModes.RCDATA,
执行node解析
mode === TextModes.DATA || mode === TextModes.RCDATA,
执行node解析
1. 如果context不是v-pre标签,且以'{{'开始,
node = parseInterpolation(context, mode) // 解析模板插入值:'{{}}'
node = parseInterpolation(context, mode) // 解析模板插入值:'{{}}'
2. 如果mode === TextModes.DATA && s[0] === '<',则正常解析,
表示为开始标签
表示为开始标签
1. 如果s.length === 1(template),
执行emitError(context, ErrorCodes.EOF_BEFORE_TAG_NAME, 1)
,抛出错误
执行emitError(context, ErrorCodes.EOF_BEFORE_TAG_NAME, 1)
,抛出错误
2. 如果s[1] === ‘!’,
表示注释节点,
表示注释节点,
1. 如果startsWith(s, '<!--'),表明注释,
node = parseComment(context)
node = parseComment(context)
2. startsWith(s, '<!DOCTYPE'),表明为文档类型,
node = parseBogusComment(context)
node = parseBogusComment(context)
3. startsWith(s, '<![CDATA['),条件注释
1. ns !== Namespaces.HTML,
node = parseCDATA(context, ancestors)
node = parseCDATA(context, ancestors)
2. emitError(context, ErrorCodes.CDATA_IN_HTML_CONTENT)
node = parseBogusComment(context)
node = parseBogusComment(context)
4. 抛出错误:
emitError(context, ErrorCodes.INCORRECTLY_OPENED_COMMENT)
node = parseBogusComment(context)
emitError(context, ErrorCodes.INCORRECTLY_OPENED_COMMENT)
node = parseBogusComment(context)
3. 如果s[1] === '/',则会抛出解析错误
1. 如果 s.length === 2,
emitError(context, ErrorCodes.EOF_BEFORE_TAG_NAME, 2)
emitError(context, ErrorCodes.EOF_BEFORE_TAG_NAME, 2)
2. 如果s[2] === '>',
emitError(context, ErrorCodes.MISSING_END_TAG_NAME, 2)
advanceBy(context, 3)
emitError(context, ErrorCodes.MISSING_END_TAG_NAME, 2)
advanceBy(context, 3)
3. 如果/[a-z]/i.test(s[2]),
emitError(context, ErrorCodes.X_INVALID_END_TAG)
parseTag(context, TagType.End, parent)
emitError(context, ErrorCodes.X_INVALID_END_TAG)
parseTag(context, TagType.End, parent)
4. 抛出错误:
emitError(
context,
ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
2
)
node = parseBogusComment(context)
emitError(
context,
ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME,
2
)
node = parseBogusComment(context)
4. 如果/[a-z]/i.test(s[1]),则解析元素节点
5. 如果s[1] === '?',则抛出错误
emitError(
context,
ErrorCodes.UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME,
1
)
node = parseBogusComment(context)
emitError(
context,
ErrorCodes.UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME,
1
)
node = parseBogusComment(context)
6. 上述都不满足,直接抛出错误
emitError(context, ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME, 1)
emitError(context, ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME, 1)
执行createCompilerError函数,
生成错误error
生成错误error
4. 解析后的node节点为null/undefined时,
node = parseText(context, mode)
node = parseText(context, mode)
执行parseText方法,解析文本节点
1. 初始化结束标识符变量endTokens=
['<', context.options.delimiters[0]]
['<', context.options.delimiters[0]]
2. 如果mode === TextModes.CDATA,则
endTokens.push(']]>')
endTokens.push(']]>')
3. 获取结束索引位置:
let endIndex = context.source.length
let endIndex = context.source.length
4. 遍历寻找context中的结束标记:['<','{{']的位置,
查找第一找到的位置赋给endIndex
查找第一找到的位置赋给endIndex
5.重新获取开始位置start,
const start = getCursor(context)
const start = getCursor(context)
6. 解析s(template)中的内容:
const content = parseTextData(context, endIndex, mode)
const content = parseTextData(context, endIndex, mode)
1. 截取源数据中的给定长度的文本
// 截取找到 '<' 或 '{{'之前的字符串
const rawText = context.source.slice(0, length)
// 截取找到 '<' 或 '{{'之前的字符串
const rawText = context.source.slice(0, length)
2. 截取context中的source,并标记line/column/offset
advanceBy(context, length)
advanceBy(context, length)
执行advanced方法,
移动行、列,逐步解析字符串
移动行、列,逐步解析字符串
执行advancePositionWithMutation,
更新line,column,offset,以及context
.source
更新line,column,offset,以及context
.source
截取context中source字符串
context.source = source.slice(numberOfCharacters)
context.source = source.slice(numberOfCharacters)
4. 满足如下条件,直接返回原始文本内容:return rawText
mode === TextModes.RAWTEXT ||
mode === TextModes.CDATA ||
rawText.indexOf('&') === -1
mode === TextModes.RAWTEXT ||
mode === TextModes.CDATA ||
rawText.indexOf('&') === -1
5. 不满足上述条件,
// DATA or RCDATA containing "&"". Entity decoding required.
return context.options.decodeEntities(
rawText,
mode === TextModes.ATTRIBUTE_VALUE
)
// DATA or RCDATA containing "&"". Entity decoding required.
return context.options.decodeEntities(
rawText,
mode === TextModes.ATTRIBUTE_VALUE
)
7. 返回结果:
return {
type: NodeTypes.TEXT, // 文本类型数据
content, // 内容
loc: getSelection(context, start)
// 位置信息,添加结束位置end
}
return {
type: NodeTypes.TEXT, // 文本类型数据
content, // 内容
loc: getSelection(context, start)
// 位置信息,添加结束位置end
}
5. 解析后的node节点为数组:遍历数组,
for (let i = 0; i < node.length; i++) {
pushNode(nodes, node[i])
}
for (let i = 0; i < node.length; i++) {
pushNode(nodes, node[i])
}
6. 不是数组,直接执行pushNode(nodes, node)方法,
将解析完的node节点保存到nodes中
将解析完的node节点保存到nodes中
1. 判断解析完的node是否为文本节点:
node.type === NodeTypes.TEXT
node.type === NodeTypes.TEXT
获取nodes中最后一项,
判断是文本节点,进行相应的合并操作
判断是文本节点,进行相应的合并操作
2. 将node存储到nodes中
4. 获取新的位置,执行getSelection(context, start)方法
5. 执行createRoot方法,生成ast
// 这里生成ast根
return createRoot(
parseChildren(context, TextModes.DATA, []), // 数据模式,遍历子节点
getSelection(context, start) // 获取新的位置
)
// 这里生成ast根
return createRoot(
parseChildren(context, TextModes.DATA, []), // 数据模式,遍历子节点
getSelection(context, start) // 获取新的位置
)
执行createRoot函数,返回最终生成的ast,
路径:compiler-core\src\ast.ts
路径:compiler-core\src\ast.ts
6. 获取基本的转换解析器,例如指令转换,对于不同平台使用不同的解析器
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset(
prefixIdentifiers // 是否使用前缀表示符
)
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset(
prefixIdentifiers // 是否使用前缀表示符
)
执行getBaseTransformPreset函数
7. 执行transform方法,在ast每个节点上生成codegenNode属性
执行transform函数,
路径:compiler-core\src\transform.ts
路径:compiler-core\src\transform.ts
1. 初始化转换上下文:context,
执行createTransformContext(root, options函数)
执行createTransformContext(root, options函数)
2. 循环遍历,转换节点:
traverseNode(root, context)
traverseNode(root, context)
1. 设置context上下文中currentNode为node,
context.currentNode = node
context.currentNode = node
2. 获取节点解析器,主要是once,
if, for, slot, text等
const { nodeTransforms } = context
if, for, slot, text等
const { nodeTransforms } = context
3. 循环遍历节点,进行指令转换:
分别执行对应的指令转换函数
1. v-once
执行findDir(node,‘once’,true)函数,
查找node节点上是否有name(once/text等)指令
查找node节点上是否有name(once/text等)指令
2. v-if/v-else/v-else-if:createStructuralDirectiveTransform
1. 判断传入name是字符串还是正则:
const matches = isString(name)
? (n: string) => n === name
: (n: string) => name.test(n)
const matches = isString(name)
? (n: string) => n === name
: (n: string) => name.test(n)
2. 返回参数为node/context的函数
节点type为元素:
node.type === NodeTypes.ELEMENT
node.type === NodeTypes.ELEMENT
1. 获取节点中的props:
const { props } = node
const { props } = node
2. // 结构指令不关心slots,因为他们通过vSlot.ts文件单独处理
// 如果node的标签类型为template并且属性中包含isVslot,直接返回
if (node.tagType === ElementTypes.TEMPLATE && props.some(isVSlot)) {
return
}
// 如果node的标签类型为template并且属性中包含isVslot,直接返回
if (node.tagType === ElementTypes.TEMPLATE && props.some(isVSlot)) {
return
}
3. 遍历循环props,解析v-if/v-else-if/v-else
1. 前属性类型为指令并且为v-if/v-else/v-else-if:
prop.type === NodeTypes.DIRECTIVE && matches(prop.name
prop.type === NodeTypes.DIRECTIVE && matches(prop.name
1. 从props中删除当前项:
props.splice(i, 1)
i--
props.splice(i, 1)
i--
2. 调用fn转换指令:
const onExit = fn(node, prop, context)
const onExit = fn(node, prop, context)
1. 转换v-if/v-else-if/v-else
2. 执行transformFor转换v-for
3. 如果onExit存在,存储到exitFns:
// 将fn(node,prop,context)函数推入onExit数组中
if (onExit) exitFns.push(onExit)
// 将fn(node,prop,context)函数推入onExit数组中
if (onExit) exitFns.push(onExit)
2. 返回exitFns
3. {{ ... }}:transformExpression
4. v-slot:transformSlotOutlet
1. 判断是不是slot插槽:
isSlotOutlet(node)
isSlotOutlet(node)
2. 不是slot,直接退出当前函数
5. element:transformElement
路径:compiler-core\src\transforms\transformElement.ts
路径:compiler-core\src\transforms\transformElement.ts
1. 获取节点的标签和属性:
const { tag, props } = node
const { tag, props } = node
2. 判断标签类型是不是组件:
const isComponent = node.tagType === ElementTypes.COMPONENT
const isComponent = node.tagType === ElementTypes.COMPONENT
3. 定义vnodeTag:
const vnodeTag = isComponent
? resolveComponentType(node as ComponentNode, context)
: `"${tag}"`
const vnodeTag = isComponent
? resolveComponentType(node as ComponentNode, context)
: `"${tag}"`
1. 组件的情况:执行resolveComponentType方法
1. 获取node中的tag:
const { tag } = node
const { tag } = node
2. 动态组件的情况:调用findProp和findDir方法,查找属性和指令
const isProp =
node.tag === 'component' ? findProp(node, 'is') : findDir(node, 'is')
const isProp =
node.tag === 'component' ? findProp(node, 'is') : findDir(node, 'is')
isProp为true
1. 获取表达式值:
const exp =
isProp.type === NodeTypes.ATTRIBUTE
? isProp.value && createSimpleExpression(isProp.value.content, true)
: isProp.exp
const exp =
isProp.type === NodeTypes.ATTRIBUTE
? isProp.value && createSimpleExpression(isProp.value.content, true)
: isProp.exp
执行createSimpleExpression函数
2. 如果exp存在,返回解析完的表达式:
if (exp) {
return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
exp
])
if (exp) {
return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
exp
])
执行createCallExpression方法
3. 内置组件的情况:Teleport, Transition, KeepAlive, Suspense...
const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag)
const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag)
1. SSR渲染:return builtIn
2. 不是SSR渲染:
if (!ssr) context.helper(builtIn)
if (!ssr) context.helper(builtIn)
4.使用异步组件,将解析component
context.helper(RESOLVE_COMPONENT)
context.helper(RESOLVE_COMPONENT)
5. 将组件添加到context.components:
context.components.add(tag)
context.components.add(tag)
6. 返回toValidAssetId(tag, `component`)
2. 不是组件component,值为 `"${tag}"`
4. 判断是不是动态组件:
const isDynamicComponent =
isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT
const isDynamicComponent =
isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT
5. 初始化一些变量:
let vnodeProps: VNodeCall['props']
let vnodeChildren: VNodeCall['children']
let vnodePatchFlag: VNodeCall['patchFlag']
let patchFlag: number = 0
let vnodeDynamicProps: VNodeCall['dynamicProps']
let dynamicPropNames: string[] | undefined
let vnodeDirectives: VNodeCall['directives']
let vnodeProps: VNodeCall['props']
let vnodeChildren: VNodeCall['children']
let vnodePatchFlag: VNodeCall['patchFlag']
let patchFlag: number = 0
let vnodeDynamicProps: VNodeCall['dynamicProps']
let dynamicPropNames: string[] | undefined
let vnodeDirectives: VNodeCall['directives']
6. 判断是否使用块状标记:shouldUseBlock
7. 如果节点上的属性props.length > 0,
处理节点属性
处理节点属性
1. 执行buildProps方法,生成propsBuildResult:
// 返回处理完成的节点属性
return {
props: propsExpression,
directives: runtimeDirectives,
patchFlag,
dynamicPropNames
}
// 返回处理完成的节点属性
return {
props: propsExpression,
directives: runtimeDirectives,
patchFlag,
dynamicPropNames
}
8. 如果node的children.length > 0,处理子节点
9. 碎片标记不等于0,处理动态属性,生成碎片标记:
patchFlag !== 0
设置vnodePatchFlag和vnodeDynamicProps
patchFlag !== 0
设置vnodePatchFlag和vnodeDynamicProps
10. 生成该节点的codegenNode属性,用于后续生成code:
node.codegenNode = createVNodeCall(
context, // 当前节点的执行上下文
vnodeTag, // ""input""
vnodeProps, // vnode节点属性
vnodeChildren, // vnode子节点
vnodePatchFlag, // "8 /* PROPS */"
vnodeDynamicProps, //"["onUpdate:modelValue"]"
vnodeDirectives, // vnode指令
!!shouldUseBlock,
false /* disableTracking */,
node.loc // 位置
)
node.codegenNode = createVNodeCall(
context, // 当前节点的执行上下文
vnodeTag, // ""input""
vnodeProps, // vnode节点属性
vnodeChildren, // vnode子节点
vnodePatchFlag, // "8 /* PROPS */"
vnodeDynamicProps, //"["onUpdate:modelValue"]"
vnodeDirectives, // vnode指令
!!shouldUseBlock,
false /* disableTracking */,
node.loc // 位置
)
执行createVNodeCall方法,生成vnode回调环境
6. slotScopes
7. v-text
8. ignoreSideEffect
9. style
10. transition
4. 初始化退出执行函数(节点指令解析完后,
会将函数存储到exitFns中,解析完后再执行)
会将函数存储到exitFns中,解析完后再执行)
5. 判断节点的类型(node,type),
在contex.helper中加入对应的指令转换器
在contex.helper中加入对应的指令转换器
1. node.type为NodeTypes.COMMENT: // 3:注释
if (!context.ssr) {
//为注释符号注入import,
// 这是使用“createVNode”创建注释节点所需要的
context.helper(CREATE_COMMENT)
}
}
if (!context.ssr) {
//为注释符号注入import,
// 这是使用“createVNode”创建注释节点所需要的
context.helper(CREATE_COMMENT)
}
}
2. node.type为 NodeTypes.INTERPOLATION: // 5:模板{{}}
// 不需要遍历,但是需要注入 toString 解析器
if (!context.ssr) {
// 将TO_DISPLAY_STRING引入
context.helper(TO_DISPLAY_STRING)
}
// 不需要遍历,但是需要注入 toString 解析器
if (!context.ssr) {
// 将TO_DISPLAY_STRING引入
context.helper(TO_DISPLAY_STRING)
}
3. node.type为NodeTypes.IF: // 9
for (let i = 0; i < node.branches.length; i++) {
traverseNode(node.branches[i], context)
}
for (let i = 0; i < node.branches.length; i++) {
traverseNode(node.branches[i], context)
}
4. node.type为
case NodeTypes.IF_BRANCH: // 10
case NodeTypes.FOR: // 11
case NodeTypes.ELEMENT: // 1
case NodeTypes.ROOT: // 0
// 根节点情况下,循环遍历子节点
traverseChildren(node, context)
break
case NodeTypes.IF_BRANCH: // 10
case NodeTypes.FOR: // 11
case NodeTypes.ELEMENT: // 1
case NodeTypes.ROOT: // 0
// 根节点情况下,循环遍历子节点
traverseChildren(node, context)
break
执行traverseChildren(parent,context)方法
1. 初始化变量i,用于记录节点数量
let i = 0
let i = 0
2. 定义nodeRemoved方,
节点删除了,对应的i要减1
const nodeRemoved = () => {
i--
}
节点删除了,对应的i要减1
const nodeRemoved = () => {
i--
}
2. 遍历父节点的children,依次对子节点
执行traverseNode(child, context)方法
执行traverseNode(child, context)方法
1. 获取子节点:
const child = parent.children[i]
const child = parent.children[i]
2. 判断子节点为字符串,continue:
if (isString(child)) continue
if (isString(child)) continue
3. 在context上挂载父节点:
contex.parent = parent
contex.parent = parent
4. 设置子节点索引i:
context.childIndex = i
context.childIndex = i
5. 设置context中子节点移除方法:
context.onNodeRemoved = nodeRemoved
context.onNodeRemoved = nodeRemoved
6. 转换子节点:
ctraverseNode(child, context)
ctraverseNode(child, context)
6. 执行exitFns中的函数
// 退出转换
let i = exitFns.length
while (i--) {
// 执行退出函数
exitFns[i]()
}
// 退出转换
let i = exitFns.length
while (i--) {
// 执行退出函数
exitFns[i]()
}
3. 提升静态节点
if (options.hoistStatic) {
hoistStatic(root, context)
}
if (options.hoistStatic) {
hoistStatic(root, context)
}
4. 根节点生成codegenNode属性,后续用于生成code,
if (!options.ssr) {
createRootCodegen(root, context)
}
if (!options.ssr) {
createRootCodegen(root, context)
}
执行createRootCodegen方法,生成
根上的codegenNode属性
根上的codegenNode属性
5. 将context中的属性挂载到root上,即ast,
root.helpers = [...context.helpers]
root.components = [...context.components]
root.directives = [...context.directives]
root.imports = [...context.imports]
root.hoists = context.hoists
root.temps = context.temps
root.cached = context.cached
root.helpers = [...context.helpers]
root.components = [...context.components]
root.directives = [...context.directives]
root.imports = [...context.imports]
root.hoists = context.hoists
root.temps = context.temps
root.cached = context.cached
8. 执行generate(ast, options)函数,
生成render函数
生成render函数
1. 生成code generate上下文context,
执行createCodegenContext(ast,options)方法
执行createCodegenContext(ast,options)方法
2. 从context中获取变量:
mode,push,prefixIdentifiers,
indent,deindent,newline,scopeId,ssr
mode,push,prefixIdentifiers,
indent,deindent,newline,scopeId,ssr
3. ast解析器,即将ast转为函数可运行时里面用到的函数,例如_createBlock
const hasHelpers = ast.helpers.length > 0
const hasHelpers = ast.helpers.length > 0
4. 判断是否使用块标记:
const useWithBlock = !prefixIdentifiers && mode !== 'module'
const useWithBlock = !prefixIdentifiers && mode !== 'module'
5. 判断是否生成作用域id:
const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module'
const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module'
6. 根据不同的运行环境,来编译生成的代码,生成前缀部分,例如with
1. 非浏览器并且是模块module,获取模块前缀:
if (!__BROWSER__ && mode === 'module') {
genModulePreamble(ast, context, genScopeId)
}
if (!__BROWSER__ && mode === 'module') {
genModulePreamble(ast, context, genScopeId)
}
1. 判断是否需要生成scopeId:
if (genScopeId) {
ast.helpers.push(WITH_SCOPE_ID)
if (ast.hoists.length) {
ast.helpers.push(PUSH_SCOPE_ID, POP_SCOPE_ID)
}
}
if (genScopeId) {
ast.helpers.push(WITH_SCOPE_ID)
if (ast.hoists.length) {
ast.helpers.push(PUSH_SCOPE_ID, POP_SCOPE_ID)
}
}
2.ast.helpers.length大于0, 生成解析器导入语句
1. import需要优化处理:optimizeImports为true
2. 不需要优化处理:
push(
`import { ${ast.helpers
.map(s => `${helperNameMap[s]} as _${helperNameMap[s]}`)
.join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`
)
push(
`import { ${ast.helpers
.map(s => `${helperNameMap[s]} as _${helperNameMap[s]}`)
.join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`
)
3. ssr渲染:从@vue/server-renderer获取解析器
ast.ssrHelpers && ast.ssrHelpers.length
ast.ssrHelpers && ast.ssrHelpers.length
4. ast.imports.length大于0,生成import语句:
if (ast.imports.length) {
genImports(ast.imports, context)
newline()
}
if (ast.imports.length) {
genImports(ast.imports, context)
newline()
}
执行genImports函数:生成import语句
执行genNode方法,生成引入表达式:
genNode(imports.exp, context)
genNode(imports.exp, context)
1. 判断传入的节点是不是字符串:
if (isString(node)) {
context.push(node)
return
}
if (isString(node)) {
context.push(node)
return
}
2. node为symbol类型:
if (isSymbol(node)) {
context.push(context.helper(node))
return
}
if (isSymbol(node)) {
context.push(context.helper(node))
return
}
3. node为其他类型,判断node.type:
1. node.type为:ELEMENT/IF/FOR
执行genNode(node.codegenNode!, context),循环遍历
2. node.type: NodeTypes.TEXT
执行genText(node, context)方法
3. node.type: NodeTypes.NodeTypes.SIMPLE_EXPRESSION: // 4
执行genExpression(node, context)方法
4. node.type: NodeTypes.INTERPOLATION: // 5
执行genInterpolation(node, context)
5. node.type: NodeTypes.TEXT_CALL: // 12
执行genNode(node.codegenNode, context)
6. node.type: NodeTypes.COMPOUND_EXPRESSION: // 8
执行genCompoundExpression(node, context)
7. node.type: NodeTypes.COMMENT: // 3
执行genComment(node, context)
8. node.type: NodeTypes.VNODE_CALL: // 13
执行genVNodeCall(node, context) // 调用vnode生成代码方法
9. node.type:NodeTypes.JS_CALL_EXPRESSION:
执行genCallExpression(node, context)
10. node.type:NodeTypes.JS_OBJECT_EXPRESSION: // 15
执行genObjectExpression(node, context)
11. node.type:NodeTypes.JS_ARRAY_EXPRESSION:
执行genArrayExpression(node, context)
12. node.type: NodeTypes.JS_FUNCTION_EXPRESSION:
执行genFunctionExpression(node, context)
13. node.type:NodeTypes.JS_CONDITIONAL_EXPRESSION:
执行 genConditionalExpression(node, context)
14. node.type: NodeTypes.JS_CACHE_EXPRESSION:
执行genCacheExpression(node, context)
15. SSR端渲染
1. node.type: NodeTypes.JS_BLOCK_STATEMENT:
执行!__BROWSER__ && genNodeList(node.body, context, true, false)
2. node.type: NodeTypes.JS_TEMPLATE_LITERAL: // 22
执行!__BROWSER__ && genTemplateLiteral(node, context)
3. node.type: NodeTypes.JS_IF_STATEMENT:
执行!__BROWSER__ && genIfStatement(node, context)
4. node.type: NodeTypes.JS_ASSIGNMENT_EXPRESSION
执行 !__BROWSER__ && genAssignmentExpression(node, context)
5. node.type: NodeTypes.JS_SEQUENCE_EXPRESSION:
执行!__BROWSER__ && genSequenceExpression(node, context)
6. node.type: NodeTypes.JS_RETURN_STATEMENT
执行!__BROWSER__ && genReturnStatement(node, context)
16. node.type: NodeTypes.IF_BRANCH: // 10
直接退出
17. default:
if (__DEV__) {
assert(false, `unhandled codegen node type: ${(node as any).type}`)
// make sure we exhaust all possible types
const exhaustiveCheck: never = node
return exhaustiveCheck
}
if (__DEV__) {
assert(false, `unhandled codegen node type: ${(node as any).type}`)
// make sure we exhaust all possible types
const exhaustiveCheck: never = node
return exhaustiveCheck
}
5. 如果需要生成scopeId:
if (genScopeId) {
push(
`const _withId = ${PURE_ANNOTATION}${helper(WITH_SCOPE_ID)}("${scopeId}")`
)
newline()
}
if (genScopeId) {
push(
`const _withId = ${PURE_ANNOTATION}${helper(WITH_SCOPE_ID)}("${scopeId}")`
)
newline()
}
6. 生成静态hoists:
genHoists(ast.hoists, context)
newline()
push(`export `)
genHoists(ast.hoists, context)
newline()
push(`export `)
2. 其他执行:// 生成函数的前缀部分
genFunctionPreamble(ast, context)
genFunctionPreamble(ast, context)
9. 返回结果:
{ ast,
code: context.code,
map
}
{ ast,
code: context.code,
map
}
5. 执行code,生成render函数,并返回render:
render = new Function(code)
retrurn render
render = new Function(code)
retrurn render

收藏
0 条评论
下一页