
手写一个简单的MVVM框架的Compile(带注释)
发布日期:2021-05-07 18:33:27
浏览次数:28
分类:精选文章
本文共 5391 字,大约阅读时间需要 17 分钟。
Document { {person.name}} -- { {person.age}}
{ {person.fav}}
- 1
- 2
- 3
{ {msg}}
const compileUtil = { getValue(expr, vm) { // reduce 方法 /* return expr.split('.').reduce((data, currentVal) => { console.log(currentVal); return data[currentVal] }, vm.$data) */ // forEach 方法 let result expr.split('.').forEach((item, index) => { if (index === 0) { result = vm.$data[item] } else { result = result[item] } }) return result }, // expr:msg=>学习 MVVM 原理 text(node, expr, vm) { let value // 判断 文本内容是否有双大括号 if (expr.indexOf('{ {') !== -1) { value = expr.replace(/\{\{(.+?)\}\}/g, (...args) => { // console.log(args); return this.getValue(args[1], vm) }) } else { value = this.getValue(expr, vm); } this.updater.textUpdater(node, value) }, html(node, expr, vm) { const value = this.getValue(expr, vm); this.updater.htmlUpdater(node, value) }, model(node, expr, vm) { const value = this.getValue(expr, vm); this.updater.modelUpdater(node, value) }, on(node, expr, vm, eventName) { let fn = vm.$options.methods && vm.$options.methods[expr] // 这里的 this 是指向 node 的,改变 node 指向,让它指向 vm 实例 node.addEventListener(eventName, fn.bind(vm), false) }, // 更新的函数 updater: { textUpdater(node, value) { node.textContent = value }, htmlUpdater(node, value) { node.innerHTML = value }, modelUpdater(node, value) { node.value = value } }}class Compile { constructor(el, vm) { this.el = this.isElementNode(el) ? el : document.querySelector(el); this.vm = vm; // 1. 获取文档碎片对象,放入内存中会减少页面的回流和重绘 const fragment = this.node2Fragment(this.el) // 2. 编译模板 this.compile(fragment) // 3. 追加子元素到根元素 this.el.appendChild(fragment) } /*{ {person.name}} -- { {person.age}}
{ {person.fav}}
- 1
- 2
- 3
{ {msg}}
*/ compile(fragment) { // 1.获取子节点 const childNodes = fragment.childNodes; Array.from(childNodes).forEach(child => { if (this.isElementNode(child)) { // 是元素节点 // 编译元素节点 // console.log('元素节点', child); this.compileElement(child); } else { // 是文本节点 // 编译文本节点 // console.log('文本节点', child); this.compileText(child); } // 判断一下孩子节点中是否还有孩子节点 if (child.childNodes && child.childNodes.length) { this.compile(child); } }) } // 编译元素节点 compileElement(node) { // // console.log(node); const attributes = node.attributes; Array.from(attributes).forEach(attr => { const { name, value } = attr; // console.log(name); // 判断是不是一个指令 v-text v-html v-model v-on:click if (this.isDirective(name)) { const [, directive] = name.split('-'); //text html model on:click const [dirName, eventName] = directive.split(':'); // text html model on /* dirName 指令名 node 当前节点 value 属性值 // msg、person this.vm 为了获取 data 的值 eventName 事件名 */ // 更新数据 数据驱动视图 compileUtil[dirName](node, value, this.vm, eventName) // 删除有指令的标签上的属性 node.removeAttribute('v-' + directive) } else if (this.isEventName(name)) { let [, eventName] = name.split('@') compileUtil['on'](node, value, this.vm, eventName) } }) } // 编译文本节点 compileText(node) { // console.log(node.textContent); const content = node.textContent if (/\{\{(.+?)\}\}/.test(content)) { compileUtil['text'](node, content, this.vm) } } // 判断是不是以 @ 开头 isEventName(attrName) { return attrName.startsWith('@') } // 判断是不是以 v- 开头 isDirective(attrName) { return attrName.startsWith('v-') } // 将所有节点添加到文档碎片中 node2Fragment(el) { // 创建文档碎片 const f = document.createDocumentFragment(); let firstChild; while (firstChild = el.firstChild) { f.appendChild(firstChild); } return f; } // 判断是不是一个元素节点 isElementNode(node) { return node.nodeType === 1; }}class MVVM { constructor(options) { this.$el = options.el; this.$data = options.data; this.$options = options; if (this.$el) { // 1. 实现一个数据观察者 // 2. 实现一个指令解析器 new Compile(this.$el, this) // 3. } }}发表评论
最新留言
哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年04月02日 04时22分02秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Unity Shader之路(五)创建第一个顶点/片元着色器?
2021-05-08
L3-008 喊山 (30分) C++ BFS题解
2021-05-08
Web框架——Flask系列之Flask-SQLAlchemy数据库的基本操作(九)
2021-05-08
六、Numpy的使用(详解)
2021-05-08
python爬虫——代理IP
2021-05-08
二、bootstrap4基础(flex布局)
2021-05-08
三、案例:留言板 & url.parse()
2021-05-08
Python中的filter()函数!!!1
2021-05-08
(新手小白必学!)用Python设计和实现聪明的尼姆游戏(人机对战)!!!!
2021-05-08
LeetCode:283. 移动零!!!1
2021-05-08
Python实验26:计算文件MD5值
2021-05-08
端口探测
2021-05-08
LeetCode:28. 实现 strStr()——————简单
2021-05-08
java 中 private default protected public 范围
2021-05-08
LeetCode:697. 数组的度————简单
2021-05-08
LeetCode:1052. 爱生气的书店老板————中等
2021-05-08
C语言的6大基本数据类型!(学习C语言小白必备!!)
2021-05-08
红黑树学习
2021-05-08
vue中导入导入 Mint-UI的注意事项
2021-05-08
Vue——mock模拟数据的使用
2021-05-08