当前位置: 首页 > news >正文

46-mini-vue 实现编译 template 为 render 函数

实现编译 template 为 render 函数

  1. 目标:
    实现 compiler 模块与 runtime 模块结合起来,让我们这个 demo 跑起来, 最终把 template 编译成 render 函数,我们之前写了 parse,transform,codegen模块,但是没有一个出口,这个出口内部就是这几个模块,我们把这个出口写成 compiler 模块,
// Demo// App.jsexportconstApp={name:"App",template:`<div>hi,{{message}}</div>`,setup(){return{message:"mini-vue"}}}// main.jsimport{createApp}from'../lib/guide-mini-vue.esm.js'importAppfrom'./App.js'constrootComponent=document.querySelector('#App')createApp(App).mount(rootComponent)// index.html<!DOCTYPEhtml><html lang="en"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><div id="App"></div><script src="./main.js"type="module"></script></body></html>// 将 template 代码编译成 render 函数// compiler-core/src/compiler.tsimport{generate}from"./codegen";import{baseParse}from"./parse";import{transform}from"./transform";import{transformElement}from"./transforms/transformElement";import{transformExpression}from"./transforms/transformExpression";import{transformText}from"./transforms/transformText";exportfunctionbaseCompile(template){constast:any=baseParse(template)transform(ast,{nodeTransforms:[transformExpression,transformElement,transformText],})returngenerate(ast)}// compiler-core/src/index.tsexport*from'./compile'
  1. 我们将写好的 compile 文件在项目中调用,我们可以用在这里
// runtime-core/component.tsfunctionfinishComponentSetup(instance:any){constComponent=instance.type// 之前这块我们直接把 render 函数写好// 但现在用户不会提供 render 函数的,需要我们手动把 template 编译为 render 函数// 我们在这里实现 template 编译为 renderinstance.render=Component.render}
  1. 官网目录依赖关系

  2. mini-vue目录梳理

// 目录关系vue---->compiler-core \----->runtime-dom--->runtime-core--->reactivity
  • 注意: compiler 模块不要直接引入 runtime 模块的东西,runtime 模块也不直接引入 compiler
  • 如果直接引用,就会形成强依赖的关系
  • 我们知道 vue 可以仅存在运行时的,如果运行时的代码依赖编译时的代码,这块运行时的逻辑就是有问题的
  • 我们使用 Webpack,rollup是可以提前把代码 template 编译成 render函数的,在线上运行时,我们只跑运行时逻辑就可以。
  • 如果我们真的想要在运行时使用编译时的方法,可以先把方法导出到根目录,然后再从运行时目录进行引入,这样强依赖关系就没有了
// src/index.tsimport*asruntimeDomfrom'./runtime-dom'import{registerRuntimeCompiler}from'./runtime-core'import{baseCompile}from'./compiler-core/src'functioncompileToFunction(template){// 因为 template 转为 render 函数是一个包含 code 属性的对象const{code}=baseCompile(template)// 我们进一步转换,将该对象转为一个 render 函数constrender=newFunction("Vue",code)(runtimeDom)// Vue 是参数 code 是函数体bodyreturnrender}// 我们生成了 render 函数,如何用到 component.ts 里面呢?// 在 component.ts 里面写一个函数 registerRuntimeCompiler 将 render 函数缓存到变量里面,方便直接使用registerRuntimeCompiler(compileToFunction)
// runtime-core/component.tsletcompiler;exportfunctionregisterRuntimeCompiler(_compiler){compiler=_compiler}functionfinishComponentSetup(instance:any){constComponent=instance.type// 我们在这里实现 template 编译为 renderif(compiler&&!Component.render){// ✅if(Component.template){Component.render=compiler(Component.template)}}instance.render=Component.render}// runtime-core/index.tsexport{getCurrentInstance,registerRuntimeCompiler}from'./component'
  • 解决报 message undefined 问题,这里增加 proxy 参数,可以直接传入 this
functionsetupRenderEffect(instance,vnode,container,anchor){instance.update=effect(()=>{let{proxy}=instanceif(!instance.isMounted){constsubTree=instance.subTree=instance.render.call(proxy,proxy)// ✅ 增加 proxy参数patch(null,subTree,container,instance,anchor)vnode.el=subTree.el instance.isMounted=true}else{const{next,vnode}=instanceif(next){next.el=vnode.elupdateComponentPreRender(instance,next)}const{proxy}=instanceconstsubTree=instance.render.call(proxy,proxy)// ✅ 增加 proxy参数constprevSubTree=instance.subTree instance.subTree=subTreepatch(prevSubTree,subTree,container,instance,anchor)}},{scheduler(){queueJobs(instance.update)}})}
  • 实现 toDisplayString 方法
// shared/toDisplayString.tsexportfunctiontoDisplayString(value){returnString(value)}// shared/indexexport*from'./toDisplayString'// runtime-core/index.tsexport{toDisplayString}from'../shared'
  • 实现 createElementVNode
// runtime-core/index.tsexport{createTextVNode,createElementVNode}from'./vnode'// runtime-core/vnode.tsexport{createVNodeascreateElementVNode}
  • 最后编译渲染出 hi,mini-vue
  • 我们将测试使用的 demo 的变量挂载到 window 上,方便控制台调用测试
import{ref}from"../../lib/guide-mini-vue.esm.js"exportconstApp={name:"App",template:`<div>hi,{{message}}-{{count}}</div>`,setup(){constcount=window.count=ref(0)return{message:"mini-vue",count}}}
  • 优化
  1. reactivity
// 根据上面我们 mini-vue 的依赖图,reactivity 不能放在 src/index.ts 里面,我们调整到 runtime-core 里面export*from'../reactivity'
http://www.jsqmd.com/news/397253/

相关文章:

  • AcWing算法基础课(配套习题)
  • GPT赋能AI原生应用领域的数字化转型
  • 一个人的价值
  • AI原生应用开发指南:工作记忆模块设计与优化
  • 聪明人与社会价值
  • 企业级AI原生应用开发:幻觉缓解架构设计指南
  • 64 搜索平移递增数组中的元素
  • 大专工业大数据应用专业学习数据分析的价值分析
  • 互联网大厂Java面试场景与技术点详解:从Spring到微服务
  • 大厂AI架构师的监控预警心得:这6点让你少走一年弯路
  • 个人博客网站搭建day2-Spring Boot 3 + JWT + Redis 实现后台权限拦截与单点登录(漫画解析)
  • DataFrame数据合并与连接:Pandas中整合数据的全面指南
  • 国内特色GEO服务商能力全景解析(2026年2月) - 品牌2025
  • DataFrame数据聚合与分组:从基础到进阶的Python数据分析指南
  • 题解:洛谷 P3380 【模板】树套树
  • 深入RAG架构:分块策略、混合检索与重排序的工程实现
  • 抢占AI搜索新入口:主流GEO服务商全景解析(2026年版) - 品牌2025
  • 大年初四
  • 引入Lombok时,记得删除<Configuration>
  • VC运行库报错截图收集
  • [豪の算法奇妙冒险] 代码随想录算法训练营第四十二天 | 188-买卖股票的最佳时机Ⅳ、309-最佳买卖股票时机含冷冻期、714-买卖股票的最佳时机含手续费
  • 题解:洛谷 P3834 【模板】可持久化线段树 2
  • oii一键生成动漫,oiioii一键生成动漫,oii邀请码,oiioii邀请码2026年2月20日最新
  • 算力杠杆和人类瓶颈:一个 PhD 的Agentic Workflow 压力测试半月记(二)
  • 《金包银》MV制作教程:DeepSeek+百度AI+剪映,闽南语苦情歌的深度演绎
  • 含分布式电源与电动汽车的配电网潮流计算:考虑风光及电动汽车出力时序特性的IEEE33节点牛拉法...
  • Ubuntu 上 Docker 的配置及代理
  • OpenClaw多Agent协作踩坑实录:从翻车到跑通的全记录
  • 数字员工与AI销冠系统是什么?主要具备哪些智能提升业务效率的功能?
  • 谷歌新模型Gemini 3.1 Pro发布:推理能力翻倍,更新内容一览