Vue 的实现原理粗介
Vue 再 data
中定义的属性都是访问器属性,访问器属性可以在读取和设置时进行一些操作,比如更新视图,正是因为这些强大的 API,可以让我们减少 DOM 操作,通过数据驱动视图,这也就是vue的优点所在。
定义所有的属性
class Vue { constructor (options) { this.$options = options; this.$el = options.el; this.$data = options.data; new Observer(this.$data) } } class Observer { constructor (data) { this.observe(data) } observe (data) { if ((data != null) && (typeof data === 'object')) { for (const key in data) { this.defineReflect(data, key, data[key]) } } } defineReflect (data, key, value) { this.observe(value) Object.defineProperty(data, key, { get () { return value }, set: (newValue) => { if (value !== newValue) { value = newValue this.observe(newValue) } } }) } }
编译的方法
let compileUtil = { text (node, content, vm) { let fn = this.updater.updateText let value = content.replace(/\{\{(.*)\}\}/g, (...args) => { return this.getValue(vm, args[1]) }) fn(node, value) }, updater: { updateText (node, value) { node.textContent = value } }, getValue (vm, expr) { let arr = expr.split('.') return arr.reduce((prev, cur) => { return prev[cur] }, vm.$data) } } class Compiler { constructor (el, vm) { this.vm = vm; this.el = document.querySelector(el) if (this.el) { let fragment = this.node2fragment(this.el) this.compile(fragment) this.el.appendChild(fragment) } } node2fragment (node) { let fragment = document.createDocumentFragment() let child; while (child = node.firstChild) { fragment.appendChild(child) } return fragment } compile (node) { let childNodes = node.childNodes // 所有节点(我们需要处理文本节点和元素节点) Array.from(childNodes, (child) => { if (this.isElementNode(child)) { this.compile(child) } else { this.compileText(child) } }) } isElementNode (node) { return node.nodeType === 1 } compileText (node) { let content = node.textContent if (/\{\{(.*)\}\}/.test(content)) { compileUtil['text'](node, content, this.vm) } } }
给每个属性都添加观察者
let compileUtil = { text (node, content, vm) { let fn = this.updater.updateText let value = content.replace(/\{\{(.*)\}\}/g, (...args) => { new Watcher(vm, args[1], () => { fn(node, this.getValue(vm, args[1])) }) return this.getValue(vm, args[1]) }) fn(node, value) }, updater: { updateText (node, value) { node.textContent = value } }, getValue (vm, expr) { let arr = expr.split('.') return arr.reduce((prev, cur) => { return prev[cur] }, vm.$data) } } class Dep { constructor () { this.subs = [] } addSub (sub) { this.subs.push(sub) } notify () { this.subs.forEach(watcher => { watcher.update() }) } } class Watcher { constructor (vm, expr, fn) { this.vm = vm; this.expr = expr; this.fn = fn; this.oldValue = this.getValue() } getValue () { // 每个属性都有watcher Dep.target = this; let oldValue = compileUtil.getValue(this.vm, this.expr); Dep.target = null; return oldValue } update () { let newValue = compileUtil.getValue(this.vm, this.expr) if (this.oldValue !== newValue) { this.fn() } } }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

上一篇: dva 简单的封装原理
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论