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