在接触 React 时候,我只了解到通过 babel 可以把 JSX 转成 VNode(通过调用 React.createElement 方法),但是对其具体是如何转换的却不了解。

很明显,回答失败。通过 github 上搜索 template+vnode 的关键词,让我搜到了htm库,发现简直就是我想要的。让我们看下用法:

const htm = require("htm");
function h(type, props, ...children) { return { type, props, children }; } const html = htm.bind(h); html` <div>Hello World</div> `; // 返回: { type: 'div', props: null, children: ['Hello World'] } 复制代码

htm 的大概思路是通过一个个字符遍历 template 字符串,并设置状态类型,当遇到<>表示进入元素状态,遇到="'则表示属性状态。子元素的关系通过数组的 push 和 slice 某一位来确定。 更详细可以看看这篇文章如何解析 template 成 VNODE

为什么要用 VNode?

我想这里应该是通过比较 VNode 和 DOM,并给出 VNode 的优势和 DOM 的不足。

当前 Vue 和 React 都使用了 VNode,是出于什么原因,让两大目前最火热的框架都选择使用了 VNode 呢?

这里我们直接看下写的比较好的文章吧. 深度剖析:如何实现一个 Virtual DOM 算法


了解到上面知识的大致原理后,回顾了下 React 的 JSX 写法:

  1. 当我们需要遍历列表
render() {return (<ul>{list.map(item => <li>item</li>) } </ul> ) } 复制代码
  1. 当我们渲染值
render() {return (<p>{{ msg }}</p> ) } 复制代码

思考了下,如果结合 ejs 等模板引擎(这些模板引擎大致的思路是结合 template+data->html->设置到 DOM 的 innerHTML),先把数据填充进去,转变成 html 字符串。

之后使用htm转成 VNode,再使用 Virtual Dom,使用 Virtual Dom 的 diff 和 patch,便可以实现了简单的 MVVM 体验。

没错,就是这么简单,废话不多说,开干吧。

MVVM

模板引擎

<!-- 比如我们需要渲染数组列表: -->
<ul><% for (let item of list) { %> <li></li> <% } %> </ul> <!-- 比如我们需要条件渲染 --> <% if (condition) { %> <span>open</span> <% } else { %> <span>close</span> <% } %> <!-- 比如我们需要渲染数据 --> <p><%= msg %></p> 复制代码

我的思路的先处理逻辑运算如:(for,if 等), 通过正则/<%[^=]([^%]*)%>/g来匹配,并通过str += 匹配内容, 因为 exec 会含有 index 属性,所以匹配之前的 html 通过 slice 来获取,并拼接到 str。

let _str = 'let str = "";\n';
let exec;
let index = 0; let content; while ((exec = REG.exec(str))) { content = str_format(str.slice(index, exec.index)); if (content) { _str += `str += '${content}';\n`; } _str += `${str_format(exec[1])}\n`; index = exec.index + exec[0].length; } // some code 复制代码

处理完逻辑的代码,通过正则/<%=([^%]*)%>/g直接对上面的字符串进行 replace 操作替换。

具体代码: template.js

html 字符串 -> VNode

这里我们使用simple-virtual-dom库来实现虚拟 DOM 处理,我们对上面函数 h 做一点调整。

import { el } from "simple-virtual-dom";
import htm from "htm"; function h(tagName, props, ...children) { return new el(tagName, props, children); } const html = htm.bind(h); const vnode = html([html_str]); 复制代码

这里我们就实现了template+data -> html str -> VNode的转换。使用 VNode 库提供的 render 转成具体的 DOM 并挂载到 document 上。

但是我们貌似还没有对事件进行处理,这里我使用了事件委托机制,也就是挂载事件到 window 对象上进行监听处理。所以这里需要对simple-virtual-dom库的 element.js 做一点小调整.

// 唯一Id
let uid = 0;function Element(tagName, props, children) { // 给每个VNode增加uid this.uid = uid++; } Element.prototype.render = function() { for (var propName in props) { var propValue = props[propName]; // 这里模仿vue的事件绑定 if (propName.startsWith("@")) { // 事件处理 const callback = (vm.$methods[propValue] || function() {}).bind(vm); delegate(window, `[dance-el-${this.uid}]`, propName.slice(1), callback); continue; } } // 添加uid属性, 为了事件代理 _.setAttr(el, "dance-el-" + this.uid, ""); }; 复制代码

这样,事件处理我们也解决好了,哦对了,对 delegate 实现原理感兴趣的可以阅读delegate源码

如何更新呢?

这里我加入了 React 中的 setState,当我们调用这个方法,我们会得到新的 data 数据,这个时候再次触发template+data -> html str -> VNode的转换.

然后使用 virtual dom 的 diff 和 patch 差异比较,修改只需改变的 DOM 元素。

整体实现

大家可以点击这里进行查看MVVM

如果可以,还请给个 star,star 是面试加分项。?

基于我们创建的 MVVM 的例子

  1. Count
  2. Todo App 有一点 bug, ?

水平有限,难免有不对之处,还请指出,谢谢.?

转载于:https://www.cnblogs.com/zhangycun/p/10511531.html

Vue 中是如何解析 template 字符串为 VNode 的?相关推荐

  1. vue中使用CodeMirror解析yaml语言

    vue中使用CodeMirror解析yaml语言 一.CodeMirror插件使用 1.CodeMirror插件安装 二.CodeMirror基本配置 三.CodeMirror具体使用 1.首先创建一 ...

  2. 解决vue中使用v-html解析后table表格的线不见了

    vue中使用 v-html 解析富文本 1.在编辑器中输入什么就显示什么 2.使用v-html解析后有问题 可以看看☞☞ 官网地址 需要注意的是:从编辑器中获取的 html 代码是不包含任何样式的纯 ...

  3. Vanilla JavaScript 和 Vue 中的 HTML <template>标签

    HTML Template Tag in Vanilla JavaScript and Vue - DEV Communityhttps://dev.to/therealdanvega/html-te ...

  4. vue中v-for写在template上,不能加key怎么办

    v-for写在非template上,添加:key没有任何问题,但是写在template上就不行了,加了就报错.有些时候由于内容过多,不得不写在上面,以下是解决方法: <template v-fo ...

  5. vue 中 数字0和空字符串,=== 和== 用法和区别

    一.数字0和空字符串 以下是数字和字符串隐试转换规则: 1.任何非零的数为true,0为false. 2.字符串来说任何非空字符串为 true,空字符串为false 3.再用==比较时会把" ...

  6. vue中v-for写在template上,加key提示错误

    v-for写在非template上,添加:key没有任何问题,但是写在template上就不行了,加了就报错. '<template>' cannot be keyed. Place th ...

  7. Vue中,在<template>内进行页面链接跳转

    1.vuejs 是单页面应用,应用内的跳转,可以用router-link标签 我的: <router-link :to="'/'" >没有帐户?点击现在注册.</ ...

  8. Vue中虚拟DOM的理解

    Vue中虚拟DOM的理解 Virtual DOM是一棵以JavaScript对象作为基础的树,每一个节点称为VNode,用对象属性来描述节点,实际上它是一层对真实DOM的抽象,最终可以通过渲染操作使这 ...

  9. 前端笔记-vue中引入Bootstrap

    目录 过程演示 源码打包下载 过程演示 首先在项目中导入插件: npm install jquery --save npm install bootstrap --save npm install p ...

最新文章

  1. Hyper-v Server虚拟光纤通道
  2. 数据分析基础教程Numpy指南笔记
  3. LeetCode Algorithm 面试题 10.05. 稀疏数组搜索
  4. HTML5纯Web前端也能开发直播,不用开发服务器(使用face2face)
  5. Please let us know in case of any issues
  6. C#中的 Stream
  7. leetcode 79.单词搜索 dfs
  8. opencv绘制基本形状的二值图像
  9. Atitit.播放系统的选片服务器,包厢记时系统 的说明,教程,维护,故障排查手册p825
  10. 世界超级计算机比赛,【启明之星】何斌:刷新超级计算机比赛世界纪录
  11. 计算机telnet命令大全,telnet 命令使用方法详解,telnet命令怎么用
  12. linux 批量解压war,图文处理war包解压【搞定方式】
  13. 色彩特征提取-色彩属性HSV空间
  14. macbook系统占用硬盘大_Mac系统文件过大——如何清理Mac系统空间?
  15. HTML5_用语义化标记重新定义博客
  16. Es6模板字符串封装与使用
  17. 用qpython3写一个发送短信的程序
  18. Spring —— Spring 手册官网下载地址
  19. 余涛-Kinect技术在企业级的应用及展望
  20. a non-compliant version of Huawei Mobile Services SDK which contains code to download or install app

热门文章

  1. spring中集成使用jedis(2)
  2. silverlight(二)样式
  3. Flash Video带宽估测
  4. Spring与SpringMVC的区别
  5. 在 Domoticz 中添加插座开关
  6. hibernate one2one 唯一外键关联(双向关联)
  7. 谈谈App的混合开发
  8. 纪念BLives 1.0版本发布
  9. 优先级管理器 IPriorityManager -- ESBasic 可复用的.NET类库(14)
  10. 几种无线充电解决方案特点及原理图