执行 以下生成代码

const code = generate(ast, options)。

generate函数在src/compiler/codegen/index.ts文件中定义为

export function generate(ast: ASTElement | void,options: CompilerOptions
): CodegenResult {const state = new CodegenState(options)// fix #11483, Root level <script> tags should not be rendered.const code = ast? ast.tag === 'script'? 'null': genElement(ast, state): '_c("div")'return {render: `with(this){return ${code}}`,staticRenderFns: state.staticRenderFns}
}

其中_c定义为

vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)

生成的render函数会根据code创建函数。

function createFunction(code, errors) {try {return new Function(code)} catch (err: any) {errors.push({ err, code })return noop}
}

render的调用是在Vue.prototype._render。

vnode = render.call(vm._renderProxy, vm.$createElement)

其中vm._renderProxy是vm自身。

渲染时会用到的简写函数有,定义在src/core/instance/render-helpers/index.ts

export function installRenderHelpers(target: any) {target._o = markOncetarget._n = toNumbertarget._s = toStringtarget._l = renderListtarget._t = renderSlottarget._q = looseEqualtarget._i = looseIndexOftarget._m = renderStatictarget._f = resolveFiltertarget._k = checkKeyCodestarget._b = bindObjectPropstarget._v = createTextVNodetarget._e = createEmptyVNodetarget._u = resolveScopedSlotstarget._g = bindObjectListenerstarget._d = bindDynamicKeystarget._p = prependModifier
}

1、genElement

生成code的入口。

export function genElement(el: ASTElement, state: CodegenState): string {if (el.parent) {el.pre = el.pre || el.parent.pre}if (el.staticRoot && !el.staticProcessed) {return genStatic(el, state)} else if (el.once && !el.onceProcessed) {return genOnce(el, state)} else if (el.for && !el.forProcessed) {return genFor(el, state)} else if (el.if && !el.ifProcessed) {return genIf(el, state)} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {return genChildren(el, state) || 'void 0'} else if (el.tag === 'slot') {return genSlot(el, state)} else {// component or elementlet codeif (el.component) {code = genComponent(el.component, el, state)} else {let dataif (!el.plain || (el.pre && state.maybeComponent(el))) {data = genData(el, state)}let tag: string | undefined// check if this is a component in <script setup>const bindings = state.options.bindingsif (bindings && bindings.__isScriptSetup !== false) {tag =checkBindingType(bindings, el.tag) ||checkBindingType(bindings, camelize(el.tag)) ||checkBindingType(bindings, capitalize(camelize(el.tag)))}if (!tag) tag = `'${el.tag}'`const children = el.inlineTemplate ? null : genChildren(el, state, true)code = `_c(${tag}${data ? `,${data}` : '' // data}${children ? `,${children}` : '' // children})`}// module transformsfor (let i = 0; i < state.transforms.length; i++) {code = state.transforms[i](el, code)}return code}
}

1.1 genIf

根据el.exp和el.ifConditions数组(由v-if, v-elseif,v-else构成)生成。在genTenaryExp中会调用genElement

export function genIf(el: any,state: CodegenState,altGen?: Function,altEmpty?: string
): string {el.ifProcessed = true // avoid recursionreturn genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)
}function genIfConditions(conditions: ASTIfConditions,state: CodegenState,altGen?: Function,altEmpty?: string
): string {if (!conditions.length) {return altEmpty || '_e()'}const condition = conditions.shift()!if (condition.exp) {return `(${condition.exp})?${genTernaryExp(condition.block)}:${genIfConditions(conditions, state, altGen, altEmpty)}`} else {return `${genTernaryExp(condition.block)}`}// v-if with v-once should generate code like (a)?_m(0):_m(1)function genTernaryExp(el) {return altGen? altGen(el, state): el.once? genOnce(el, state): genElement(el, state)}
}

1.2 genFor

根据v-for生成语句

export function genFor(el: any,state: CodegenState,altGen?: Function,altHelper?: string
): string {const exp = el.forconst alias = el.aliasconst iterator1 = el.iterator1 ? `,${el.iterator1}` : ''const iterator2 = el.iterator2 ? `,${el.iterator2}` : ''el.forProcessed = true // avoid recursionreturn (`${altHelper || '_l'}((${exp}),` +`function(${alias}${iterator1}${iterator2}){` +`return ${(altGen || genElement)(el, state)}` +'})')
}

其中_l函数是renderList,传递的的render函数内又递归调用genElement。其定义如下

export function renderList(val: any,render: (val: any, keyOrIndex: string | number, index?: number) => VNode
): Array<VNode> | null {let ret: Array<VNode> | null = null,i,l,keys,keyif (isArray(val) || typeof val === 'string') {ret = new Array(val.length)for (i = 0, l = val.length; i < l; i++) {ret[i] = render(val[i], i)}} else if (typeof val === 'number') {ret = new Array(val)for (i = 0; i < val; i++) {ret[i] = render(i + 1, i)}} else if (isObject(val)) {if (hasSymbol && val[Symbol.iterator]) {ret = []const iterator: Iterator<any> = val[Symbol.iterator]()let result = iterator.next()while (!result.done) {ret.push(render(result.value, ret.length))result = iterator.next()}} else {keys = Object.keys(val)ret = new Array(keys.length)for (i = 0, l = keys.length; i < l; i++) {key = keys[i]ret[i] = render(val[key], key, i)}}}if (!isDef(ret)) {ret = []};(ret as any)._isVList = truereturn ret
}

1.3 genData

根据属性及指令生成data,供后面渲染使用

export function genData(el: ASTElement, state: CodegenState): string {let data = '{'// directives first.// directives may mutate the el's other properties before they are generated.const dirs = genDirectives(el, state)if (dirs) data += dirs + ','// keyif (el.key) {data += `key:${el.key},`}// refif (el.ref) {data += `ref:${el.ref},`}if (el.refInFor) {data += `refInFor:true,`}// preif (el.pre) {data += `pre:true,`}// record original tag name for components using "is" attributeif (el.component) {data += `tag:"${el.tag}",`}// module data generation functionsfor (let i = 0; i < state.dataGenFns.length; i++) {data += state.dataGenFns[i](el)}// attributesif (el.attrs) {data += `attrs:${genProps(el.attrs)},`}// DOM propsif (el.props) {data += `domProps:${genProps(el.props)},`}// event handlersif (el.events) {data += `${genHandlers(el.events, false)},`}if (el.nativeEvents) {data += `${genHandlers(el.nativeEvents, true)},`}// slot target// only for non-scoped slotsif (el.slotTarget && !el.slotScope) {data += `slot:${el.slotTarget},`}// scoped slotsif (el.scopedSlots) {data += `${genScopedSlots(el, el.scopedSlots, state)},`}// component v-modelif (el.model) {data += `model:{value:${el.model.value},callback:${el.model.callback},expression:${el.model.expression}},`}// inline-templateif (el.inlineTemplate) {const inlineTemplate = genInlineTemplate(el, state)if (inlineTemplate) {data += `${inlineTemplate},`}}data = data.replace(/,$/, '') + '}'// v-bind dynamic argument wrap// v-bind with dynamic arguments must be applied using the same v-bind object// merge helper so that class/style/mustUseProp attrs are handled correctly.if (el.dynamicAttrs) {data = `_b(${data},"${el.tag}",${genProps(el.dynamicAttrs)})`}// v-bind data wrapif (el.wrapData) {data = el.wrapData(data)}// v-on data wrapif (el.wrapListeners) {data = el.wrapListeners(data)}return data
}

1.4 genChildren

生成子节点,子节点生成调用genNode

export function genChildren(el: ASTElement,state: CodegenState,checkSkip?: boolean,altGenElement?: Function,altGenNode?: Function
): string | void {const children = el.childrenif (children.length) {const el: any = children[0]// optimize single v-forif (children.length === 1 &&el.for &&el.tag !== 'template' &&el.tag !== 'slot') {const normalizationType = checkSkip? state.maybeComponent(el)? `,1`: `,0`: ``return `${(altGenElement || genElement)(el, state)}${normalizationType}`}const normalizationType = checkSkip? getNormalizationType(children, state.maybeComponent): 0const gen = altGenNode || genNodereturn `[${children.map(c => gen(c, state)).join(',')}]${normalizationType ? `,${normalizationType}` : ''}`}
}// determine the normalization needed for the children array.
// 0: no normalization needed
// 1: simple normalization needed (possible 1-level deep nested array)
// 2: full normalization needed
function getNormalizationType(children: Array<ASTNode>,maybeComponent: (el: ASTElement) => boolean
): number {let res = 0for (let i = 0; i < children.length; i++) {const el: ASTNode = children[i]if (el.type !== 1) {continue}if (needsNormalization(el) ||(el.ifConditions &&el.ifConditions.some(c => needsNormalization(c.block)))) {res = 2break}if (maybeComponent(el) ||(el.ifConditions && el.ifConditions.some(c => maybeComponent(c.block)))) {res = 1}}return res
}function needsNormalization(el: ASTElement): boolean {return el.for !== undefined || el.tag === 'template' || el.tag === 'slot'
}function genNode(node: ASTNode, state: CodegenState): string {if (node.type === 1) {return genElement(node, state)} else if (node.type === 3 && node.isComment) {return genComment(node)} else {return genText(node)}
}

Vue源码学习之生成代码相关推荐

  1. Vue源码学习 - 组件化一 createComponent

    Vue源码学习 - 组件化一 createComponent 组件化 createComponent 构造子类构造函数 安装组件钩子函数 实例化 VNode 总结 学习内容和文章内容来自 黄轶老师 黄 ...

  2. Vue源码学习 - 准备工作

    Vue源码学习 - 准备工作 准备工作 认识Flow 为什么用 Flow Flow 的工作方式 类型推断 类型注释 数组 类和对象 null Flow 在 Vue.js 源码中的应用 flow实践 总 ...

  3. vue源码学习--vue源码学习入门

    本文为开始学习vue源码的思路整理.在拿到vue项目源码的之后看到那些项目中的文件夹,会很困惑,不知道每个文件夹内的世界,怎么变换,怎样的魔力,最后产生了vue框架.学习源码也无从学起.我解决了这些困 ...

  4. Vue源码学习 - 组件化(三) 合并配置

    Vue源码学习 - 组件化(三) 合并配置 合并配置 外部调用场景 组件场景 总结 学习内容和文章内容来自 黄轶老师 黄轶老师的慕课网视频教程地址:<Vue.js2.0 源码揭秘>. 黄轶 ...

  5. VUE源码学习第一篇--前言

    一.目的 前端技术的发展,现在以vue,react,angular为代表的MVVM模式以成为主流,这三个框架大有三分天下之势.react和angular有facebook与谷歌背书,而vue是以一己之 ...

  6. Vue源码学习之initInjections和initProvide

    Vue源码学习之initInjections和initProvide 在进行源码阅读之前先让我们了解一个概念:provide/inject,这个是Vue在2.2.0版本新增的一个属性,按照Vue官网的 ...

  7. Vue源码学习之Computed与Watcher原理

    前言  computed与watch是我们在vue中常用的操作,computed是一个惰性求值观察者,具有缓存性,只有当依赖发生变化,第一次访问computed属性,才会计算新的值.而watch则是当 ...

  8. Vue源码学习(一):源码的入口在哪里

    Vue源码解读系列 文章目录 Vue源码解读系列 前言 一.源码下载 二.目录解读 三.找到打包入口文件 四.如何进行代码调试 总结 前言   如何设计API和如何使用元编程思想(元编程,简单说是指框 ...

  9. vue实例没有挂载到html上,vue 源码学习 - 实例挂载

    前言 在学习vue源码之前需要先了解源码目录设计(了解各个模块的功能)丶Flow语法. src ├── compiler # 把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能. ├── ...

最新文章

  1. ElasticSearch实战:Linux日志对接Kibana
  2. 【AJAX】DWR使用总结
  3. Linux 常见的六大 IPC 通信方式
  4. 【Boost】boost库中thread多线程详解11——线程的休眠和中断
  5. demo_ajax_json.js,ajax小demo-----ajax中json的使用
  6. php 多数据库联合查询,php如何同时连接多个数据库_PHP教程
  7. linux网络安装gtk2,(一) linux 下gtk2,python的安装
  8. 被单位开除,已经交了14年的养老保险,该怎么办?
  9. java中int边界值_数组中重复的数字2019.12.06
  10. C#socket编程序(二)
  11. okhttp3.4.1+retrofit2.1.0实现离线缓存
  12. 关于消息队列的一些问题
  13. Linux下几种文件传输命令
  14. Labview2018视频教程(共51节)
  15. windows系统无法保存文件的解决方案
  16. 如何取汉字的第一个拼音字母(一)
  17. 【java校招你不知道的那些事儿】校招和社招的区别是什么?为什么不参加社招
  18. Maltab生成棋盘格
  19. 【Graphite】Graphite常用函数使用
  20. 2345手机软件下载

热门文章

  1. 三星校招经验+包含GSAT三星全球工作能力测试
  2. 机械专业就业与计算机专业待遇,机械类专业毕业五年“薪资”排名,车辆工程“逆袭”!...
  3. Android校验应用签名是否被篡改
  4. 数据库原理及应用——熟悉数据库管理工具、数据库和表的创建与管理
  5. IMX6ULL移植LVGL
  6. JAVA简单分布式部署
  7. 计算机学研究生课程,厦门大学计算机学系研究生课程.doc
  8. 利用python实现身份证号验证系统
  9. Linux系统下安装串口调试工具
  10. 解决:Can not deserialize instance of com.xxx.xx.XXModel out of START_ARRAY toke