内容简介:第一次在掘金写文章,难免有点小坎坷,各位看官请轻拍。最近有刷面试题,在刷的过程中,发现如果把下面题目考察的原理结合起来即可实现一个简单的 MVVM 框架。注: 下面不会给出直接答案,只是我的一些历程。
第一次在掘金写文章,难免有点小坎坷,各位看官请轻拍。
最近有刷面试题,在刷的过程中,发现如果把下面题目考察的原理结合起来即可实现一个简单的 MVVM 框架。
注: 下面不会给出直接答案,只是我的一些历程。
面试题
Vue 中是如何解析 template 字符串为 VNode 的?
在接触 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 写法:
- 当我们需要遍历列表
render() { return ( <ul> { list.map(item => <li>item</li>) } </ul> ) } 复制代码
- 当我们渲染值
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框架》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Mybatis: 动手封装ORM框架
- 动手造轮子:写一个日志框架
- 动手造轮子:实现一个简单的 AOP 框架
- 动手搭建后端框架-Velocity模板引擎的应用
- 自己动手实现深度学习框架-3 自动分批训练, 缓解过拟合
- 《自己动手写Docker》
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ruby元编程(第2版)
[意] Paolo Perrotta / 廖志刚 / 华中科技大学出版社 / 2015-8-1 / 68.80
《Ruby元编程(第2版)》在大量剖析实例代码的基础上循序渐进地介绍Ruby特有的实用编程技巧。通过分析案例、讲解例题、回顾Ruby类库的实现细节,作者不仅向读者展示了元编程的优势及其解决问题的方式,更详细列出33种发挥其优势的编程技巧。本书堪称动态语言设计模式。Ruby之父松本行弘作序推荐。一起来看看 《Ruby元编程(第2版)》 这本书的介绍吧!