手写一个简单的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. } }}
上一篇:实现一个简易Vue(一)
下一篇:Vue面试题总结

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年04月02日 04时22分02秒