• Vue源码学习之代码实现生成原理及render函数执行准备


    第一步

    在我们有了这样一棵语法树之后,我们需要将这棵树进行代码生成,生成如下格式:

    // 生成树用的的DOM
    {{name}} hello {{age}}
    word
    • 1
    render() {return _c('div', { id: 'app' }, _c('div', { style: { color: 'red' } }, _v(_s(name) + 'hello'),_c('span', null, _v(_s(age) + 'hello'))))
    } 
    
    • 1
    • 2

    这样我们就调用codegen()方法来生成对应的代码,核心思想就是字符串拼接,直接上代码:

    // src/compiler/index
    
    import { parseHTML } from "./parse";
    // 生成孩子
    const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g; // 匹配到的内容就是表达式的变量
    function gen(node) {// 判断是文本还是元素if (node.type === 1) {// 为1为元素return codegen(node)} else {// 文本 有可能是{{name}}hello 或者只有{{name}}等各种格式let text = node.text.trim()if (!defaultTagRE.test(text)) {return `_v(${JSON.stringify(text)})`} else {// _v( _s(name)+'helool'+_s(name)) s是JSON.stringifylet tokens = []let match// 用了exec并且正则中有g,他就从lastindex开始向后寻找,所以每次运行需要先重置一下lastindexdefaultTagRE.lastIndex = 0let lastIndex = 0while (match = defaultTagRE.exec(text)) {let index = match.index //匹配的位置 {{name}} hello {{name}}tokens.push(`_s(${match[1].trim()})`)if (index > lastIndex) {tokens.push(JSON.stringify(text.slice(lastIndex, index)))}lastIndex = index = match[0].length}if (lastIndex < text.length) {tokens.push(JSON.stringify(text.slice(lastIndex)))}return `_v(${tokens.join('+')})`}}
    }
    // 处理孩子
    function genChildren(children) {if (children) {return children.map(child => gen(child)).join(',')}
    }
    // 处理属性
    function genProps(attrs) {let str = '' //{name,value}for (let i = 0; i < attrs.length; i++) {let attr = attrs[i]if (attr.name === 'style') {// background:pink => {background:pink}let obj = {}attr.value.split(';').forEach(item => { // qs库或者正则表达式也可以处理let [key, value] = item.split(':')obj[key] = value});attr.value = obj}str += `${attr.name}:${JSON.stringify(attr.value)},` // a:b,c:d,}return `{${str.slice(0, -1)}}`
    }
    // 代码生成
    function codegen(ast) {let children = genChildren(ast.children)let code = `_c('${ast.tag}',${ast.attrs.length > 0 ? genProps(ast.attrs) : 'null'}${ast.children.length ? `,${children}` : ''})`return code
    }
    
    export function compileToFcuntion(template) {......
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    第二步

    通过第一步的操作我们就成功将ast语法树拼接成了字符串代码code

    // src/compiler/index
    
    export function compileToFcuntion(template) {// 1.将template转换为ast语法树let ast = parseHTML(template)// 2.生成render方法 render方法执行后的结果就是虚拟DOMlet code = codegen(ast)code = `with(this){return ${code}}`let render = new Function(code) // 根据代码生成render函数return render
    } 
    
    • 1
    • 2
    • 3
    • 4

    :所有的模板引擎实现的原理都是 with + new Function

    // init.js
    
     Vue.prototype.$mount = function (el) { ...... mountCompontent(vm,el) // 组建的挂载
     } 
    
    • 1
    • 2
    • 3
    • 4
    // src/index.js
    import { initLifeCycle } from "./lifecycle";
    ......
    initLifeCycle(Vue)
    ...... 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    // src/lifecycle.js
    
    xport function initLifeCycle(Vue) {Vue.prototype._update = function () {console.log('update');}Vue.prototype._render = function () {console.log('render');}
    }
    export function mountCompontent(vm, el) {// 1.调用render方法产生虚拟节点 虚拟DOMvm._update(vm._render())// vm._render() // vm.$options.render()虚拟节点// vm._update(vm._render()) 把虚拟节点变成真实节点// 2. 根据虚拟DOM产生真实DOM// 3. 插入到el元素中
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    mountCompontent()方法中第一步就是调用render方法产生虚拟节点,第二步根据虚拟DOM产生真实DOM,第三步插入到el元素中。如何去做源码中用了两个方法vm._render()执行返回虚拟节点和vm._update()执行后将虚拟节点变为真实DOM,那接下来就扩展这两个方法,需要在src/index.js中进行扩展。

    最后

    总结一下vue的核心流程:

    • 创造力响应式数据
    • 将模板转换成ast语法树
    • 将ast语法树转化成render函数
    • 后续每次数据更新可以只执行render函数,无需再次执行ast转化过程
    • render函数会去产生虚拟节点(使用响应式数据)
    • 根据生成的虚拟节点创造真实的DOM

    最后就是调用render函数产生虚拟节点变成真实DOM,这个放到下一篇写,持续学习,加油!

    最后

    整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

    有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

    部分文档展示:



    文章篇幅有限,后面的内容就不一一展示了

    有需要的小伙伴,可以点下方卡片免费领取

  • 相关阅读:
    关于浙江22年下半年教师资格证面试报名注册时间
    Python Streamlit 教程之使用 Python 和 Streamlit 打造令人惊叹的仪表板(教程含源码)
    oaid SDK 调用问题 F&Q
    Java 定时任务技术发展历程
    PyTorch 源码解读之 torch.utils.data:解析数据处理全流程(非常好,一篇足够)
    springcloud仓库管理系统源码
    专业课138,总分390+,西工大,西北工业大学827信号与系统考研分享
    机器人虚拟仿真工作站考试
    企业使用CRM会获得什么成效?
    os实训课程模拟考试(选择题复习)
  • 原文地址:https://blog.csdn.net/pfourfire/article/details/127606286