Vue 的实现原理粗介

发布于 2022-09-18 12:25:29 字数 3478 浏览 125 评论 0

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

手长情犹

暂无简介

文章
评论
28 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文