响应数据绑定
4.1创建监听器,进行数据劫持和监听
3.4.1 接3.4 对当前节点进行事件的监听
4.2当数据改变时,改变vm一级下的数据,进行赋值,因为劫持的数据在vm的一级根下,这时触发监听器
4.3监听器,数据改变,触发set方法,给vm.data相对应数据赋值

 

/**
 * vue 的监听器
 * @param {Object} obj vm.data 
 * @param {Object} vm 
 */
function observe(obj, vm) {
  console.log(obj,vm);
  Object.keys(obj).forEach(key => { //对 vm.data 进行遍历
    Object.defineProperty(vm, key, {
      enumerable: true, // 当 enumerable 为 true 时,该属性才能出现在对象的枚举属性中
      configurable: true, // 当 configurable 为 true 时,该属性描述符才能够被改变
      get() {
        return obj[key];
      },
      set(newVal) {
        if (obj[key] === newVal) return;
        obj[key] = newVal;
      }
    })
  })
}

/**
    *  使用documentfragment处理节点速度和性能要高于直接操作dom。vue编译的时候,就是将挂载目标的所有子节点劫持到documentfragment中,经过处理后再将documentfragment整体返回到挂载目标中。
   */
// DocumentFragment(文档片段,节点容器) 劫持vue挂载的所有节点
function nodeToFragment(node, vm) {
  let fragment = document.createDocumentFragment(); //创建代码片段
  let child
  while (child = node.firstChild) {
    compile(child, vm)
     //appendChild 会删掉所有原来的节点
    fragment.appendChild(child) //改变原节点
  }
  return fragment
}

//解析指令和赋值
//做用对框架的指令进行双向绑定(数据响应,和视图更新渲染)
function compile(node, vm) {
  //这里讲解一下,nodeType等于1说明节点是标签,所有解析标签所有的属性; nodeType等于3说明节点是文本,可以操作文本的文本名和值(node.nodeName,node.nodeValue)
  if (node.nodeType === 1) {
    let attr = node.attributes // 获取所有属性
    // 解析属性
    for (var i in attr) {
      if (attr.hasOwnProperty(i)) {
        if (attr[i].nodeName === 'v-model') {
          var name = attr[i].nodeValue
          node.addEventListener('input', function (event) {
            // 给相应的data属性赋值,进而触发该属性的set方法
            //注意这里是给vm一级下的数据赋值,因为劫持的数据在vm的一级根下
            //这里会促发监听器,从而改变 data 中的数据
            vm[name] = event.target.value
            console.log(event.target.value, vm.data)
          })
          node.value = vm.data[name]
          node.removeAttribute('v-model')
        }
      }
    }
  }
  let regex = /\{\{(.*?)\}\}/
  if (node.nodeType === 3) {
    //文本节点没有 attributes
    if (regex.test(node.nodeValue)) {
      const name = RegExp.$1.trim(); //text
      node.nodeValue = vm.data[name];
    }
  }
}
//定义一个Vue
class Vue {
  constructor(opt) {
    this.el = document.querySelector(opt.el)
    this.data = opt.data
    observe(this.data, this) // 启动监听所有的 data 数据
    this.el.appendChild(nodeToFragment(this.el, this))
  }
}

 


2. 在observe方法中,遍历vue实例中data的属性,逐一调vue实例中的data的属性重新定义为访问器属性,并在set方法中将新的值更新到这个属性上,把他们定义为访问器属性
3. 在compile方法中,如果是input这样的标签,给它添加事件(也可以是keyup,change),监听input值变化,并给vue实例中相应的访问器属性赋值
4. 在Vue类方法中,调用observer方法,传入当前实例对象和对象的data属性,将data属性中的子元素重新定义为当前对象的访问器属性

set方法被触发之后,vue实例的text属性跟着变化,但是<span>的内容并没有变化,下面的内容将会介绍“订阅/发布模式”来解决这个问题。

posted on 2021-01-29 14:09  京鸿一瞥  阅读(241)  评论(0)    收藏  举报