Vue2相应式原理模仿(个人条记)

源代码 2024-9-9 19:08:04 27 0 来自 中国


  • Vue
    把 data 中的成员注入到 Vue 实例,而且把 data 中的成员转成 getter/setter
  • Observer
    可以或许对数据对象的所有属性举行监听,如有变动可拿到最新值并关照 Dep
  • Compiler
    分析每个元素中的指令/插值表达式,并更换成相应的数据
  • Dep
    添加观察者(watcher),当数据变革关照所有观察者
  • Watcher
    数据变革更新视图
Vue

功能:

  • 负责吸取初始化的参数(选项)
  • 负责把 data 中的属性注入到 Vue 实例,转换成 getter/setter
  • 负责调用 observer 监听 data 中所有属性的变革
  • 负责调用 compiler 分析指令/插值表达式
class Vue {  constructor (options) {    // 1. 通过属性生存选项的数据    this.$options = options || {}    this.$data = options.data || {}    this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el    // 2. 把data中的成员转换成getter和setter,注入到vue实例中    this._proxyData(this.$data)    // 3. 调用observer对象,监听数据的变革    new Observer(this.$data)    // 4. 调用compiler对象,分析指令和差值表达式    new Compiler(this)  }  _proxyData (data) {    // 遍历data中的所有属性    Object.keys(data).forEach(key => {      // 把data的属性注入到vue实例中      Object.defineProperty(this, key, {        enumerable: true,        configurable: true,        get () {          return data[key]        },        set (newValue) {          if (newValue === data[key]) {            return          }          data[key] = newValue        }      })    })  }}Observer

功能:

  • 负责把 data 选项中的属性转换成相应式数据
  • data 中的某个属性也是对象,把该属性转换成相应式数据
  • 数据变革发送关照
class Observer {  constructor (data) {    this.walk(data)  }  walk (data) {    // 1. 判断data是否是对象    if (!data || typeof data !== 'object') {      return    }    // 2. 遍历data对象的所有属性    Object.keys(data).forEach(key => {      this.defineReactive(data, key, data[key])    })  }  defineReactive (obj, key, val) {    let that = this    // 负责网络依赖,并发送关照    let dep = new Dep()    // 假如val是对象,把val内部的属性转换成相应式数据    this.walk(val)    Object.defineProperty(obj, key, {      enumerable: true,      configurable: true,      get () {        // 网络依赖        Dep.target && dep.addSub(Dep.target)        return val      },      set (newValue) {        if (newValue === val) {          return        }        val = newValue        that.walk(newValue)        // 发送关照        dep.notify()      }    })  }}Compiler

功能:

  • 负责编译模板,分析指令/插值表达式
  • 负责页面的首次渲染
  • 当数据变革后重新渲染视图
class Compiler {  constructor (vm) {    this.el = vm.$el    this.vm = vm    this.compile(this.el)  }  // 编译模板,处理处罚文本节点和元素节点  compile (el) {    let childNodes = el.childNodes    Array.from(childNodes).forEach(node => {      // 处理处罚文本节点      if (this.isTextNode(node)) {        this.compileText(node)      } else if (this.isElementNode(node)) {        // 处理处罚元素节点        this.compileElement(node)      }      // 判断node节点,是否有子节点,假如有子节点,要递归调用compile      if (node.childNodes && node.childNodes.length) {        this.compile(node)      }    })  }  // 编译元素节点,处理处罚指令  compileElement (node) {    // console.log(node.attributes)    // 遍历所有的属性节点    Array.from(node.attributes).forEach(attr => {      // 判断是否是指令      let attrName = attr.name      if (this.isDirective(attrName)) {        // v-text --> text        attrName = attrName.substr(2)        let key = attr.value        this.update(node, key, attrName)      }    })  }  update (node, key, attrName) {    let updateFn = this[attrName + 'Updater']    updateFn && updateFn.call(this, node, this.vm[key], key)  }  // 处理处罚 v-text 指令  textUpdater (node, value, key) {    node.textContent = value    new Watcher(this.vm, key, (newValue) => {      node.textContent = newValue    })  }  // v-model  modelUpdater (node, value, key) {    node.value = value    new Watcher(this.vm, key, (newValue) => {      node.value = newValue    })    // 双向绑定    node.addEventListener('input', () => {      this.vm[key] = node.value    })  }  // 处理处罚其他指令  ...  // 编译文本节点,处理处罚差值表达式  compileText (node) {    // console.dir(node)    // {{  msg }}    let reg = /\{\{(.+?)\}\}/    let value = node.textContent    if (reg.test(value)) {      let key = RegExp.$1.trim()      node.textContent = value.replace(reg, this.vm[key])      // 创建watcher对象,当数据改变动新视图      new Watcher(this.vm, key, (newValue) => {        node.textContent = newValue      })    }  }  // 判断元素属性是否是指令  isDirective (attrName) {    return attrName.startsWith('v-')  }  // 判断节点是否是文本节点  isTextNode (node) {    return node.nodeType === 3  }  // 判断节点是否是元素节点  isElementNode (node) {    return node.nodeType === 1  }}Dep

2.png 功能:

  • 网络依赖,添加观察者(watcher)
  • 关照所有观察者
class Dep {  constructor () {    // 存储所有的观察者    this.subs = []  }  // 添加观察者  addSub (sub) {    if (sub && sub.update) {      this.subs.push(sub)    }  }  // 发送关照  notify () {    this.subs.forEach(sub => {      sub.update()    })  }}Watcher

3.png 功能:

  • 当数据变革触发依赖, dep 关照所有的 Watcher 实例更新视图
  • 自身实例化的时间往 dep 对象中添加自己
class Watcher {  constructor (vm, key, cb) {    this.vm = vm    // data中的属性名称    this.key = key    // 回调函数负责更新视图    this.cb = cb    // 把watcher对象记载到Dep类的静态属性target    Dep.target = this    // 触发get方法,在get方法中会调用addSub    this.oldValue = vm[key]    Dep.target = null  }  // 当数据发生变革的时间更新视图  update () {    let newValue = this.vm[this.key]    if (this.oldValue === newValue) {      return    }    this.cb(newValue)  }}总结



  • Vue
    记载传入的选项,设置 $data/$el
    把 data 的成员注入到 Vue 实例
    负责调用 Observer 实现数据相应式处理处罚(数据挟制)
    负责调用 Compiler 编译指令/插值表达式等
  • Observer
    数据挟制
    负责把 data 中的成员转换成 getter/setter
    负责把多层属性转换成 getter/setter
    假如给属性赋值为新对象,把新对象的成员设置为 getter/setter
    添加 Dep 和 Watcher 的依赖关系
    数据变革发送关照
  • Compiler
    负责编译模板,分析指令/插值表达式
    负责页面的首次渲染过程
    当数据变革后重新渲染
  • Dep
    网络依赖,添加订阅者(watcher)
    关照所有订阅者
  • Watcher
    自身实例化的时间往dep对象中添加自己
    当数据变革dep关照所有的 Watcher 实例更新视图
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-24 10:24, Processed in 0.154931 second(s), 36 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表