资讯专栏INFORMATION COLUMN

简单实现一个双向绑定

不知名网友 / 3202人阅读

摘要:看了一些关于双向绑定的文章,现在来整理一下思路。另一边监听对应视图改变就直接监听事件。改变效果修改效果文章相关代码已经同步到,欢迎查阅

看了一些关于双向绑定的文章,现在来整理一下思路。
首先实现双向绑定有三个步骤:

需要一个方法来识别哪一个的view被绑定了相应的数据

需要监视数据和view的变化

需要将所有变化传播到绑定的对象和对应的view

为了解决第一个问题,要在对应的dom上添加相应的data-bind-属性,比如:

  num: 
  

为了解决第二个问题,一方面监听数据改变,需要这样添加一个set()方法进行监听:

const Vue = {
  data: {
    num: 0
  },
  set(key, val) {
    this.data[key] = val
  }
}

规定通过set(key, val)的方式来修改数据。
另一边监听对应视图改变就直接监听input事件。

为了解决第三个问题就需要用发布订阅模式实现一个事件枢纽:

const EventHub = {
  callbacks: {},

  on(eventName, callback){
    this.callbacks[eventName] = this.callbacks[eventName] || [];
    this.callbacks[eventName].push(callback);
  },

  emit(eventName, ...rest){
    this.callbacks[eventName] = this.callbacks[eventName] || [];
    for(let i = 0; i < this.callbacks[eventName].length; i++){
      this.callbacks[eventName][i].call(this,...rest);
    }
  }
}

一方面将数据层的变化传播到视图,需要用特定名称与dom上的属性对应:

//触发事件就修改视图
eventHub.on("num:change", (val) => {
  $(`input[data-bind-num]`).val(val)
  $(`div[data-bind-num]`).text(val)
})
//通过set()修改data来触发对应的change事件
set(key, val) {
  this.data[key] = val
  EventHub.emit("num:change", val)
}

将视图层的变化传播到数据:

$(`input[data-bind-num]`).on("input", function() {
  let val = $(this).val() === "" ? 0 : parseInt($(this).val())
  Vue.set(key, val)
})

至此双向绑定就实现完成!但是这样一个个写事件名和属性名有点蠢,优化一下

const fn = (prop_name) => {     
  return {
    dataBind: `data-bind-${prop_name}`,//对应dom的data属性名
    eventName: `${prop_name}:change`//对应数据的change事件名称
  }      
}

//给所有data绑定change事件,给所有data对应的view绑定input事件
Object.keys(Vue.data).map((key) => {
  //data修改改变view
  EventHub.on(fn(key).eventName, (val) => {

    $(`input[${fn(key).dataBind}]`).val(val)
    $(`div[${fn(key).dataBind}]`).text(val)

  })

  //view改变data
  $(`input[${fn(key).dataBind}]`).on("input", function() {

    let val = $(this).val() === "" ? "" : parseInt($(this).val())
    Vue.set(key, val)

  })
})

这样实现的双向绑定依赖于用set()来改变数据,而我们都希望通过 vm.property = value 这种方式直接来修改数据,这就需要用到Object.defineProperty()来劫持各个数据的getter,setter

//给各个数据添加监听器,用数据劫持替换原先的set(key,value)
const Observer = {
  mapProp(obj) {
    if(!obj || typeof obj !== "object") {
      return
    }
    Object.keys(obj).map((key) => {
      this.defineReactive(obj, key, obj[key])
    })
  },
  defineReactive(obj, key, val) {
    this.mapProp(val)
    Object.defineProperty(obj, key, {
      enumerable: true, // 可枚举
      configurable: false, // 不能再define
      get() {
        return val
      },
      set(newVal) {
        console.log(`数据 ${key} 从${val}->${newVal}`)
        //当数据变化就贵触发对应的change事件
        EventHub.emit(fn(key).eventName, newVal)
        val = newVal
      }
    })
  }
}

这样只需要调用一次Observer.mapProp(Vue.data)就会监听所有data,原先的set()都可以用直接赋值代替。

改变data效果:

修改input效果:

文章相关代码已经同步到Github,欢迎查阅~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95424.html

相关文章

  • 最近老是有兄弟问我,Vue双向绑定的原理,以及简单的原生js写出来实现,我就来一个简单双向绑定

    摘要:废话不多说直接看效果图代码很好理解,但是在看代码之前需要知道双向绑定的原理其实就是基于实现的双向绑定官方传送门这里我们用官方的话来说方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。 废话不多说直接看效果图 showImg(https://segmentfault.com/img/bVbiYY5?w=282&h=500); 代码很好理解,但是在看代码之前...

    zhangfaliang 评论0 收藏0
  • 最近老是有兄弟问我,Vue双向绑定的原理,以及简单的原生js写出来实现,我就来一个简单双向绑定

    摘要:废话不多说直接看效果图代码很好理解,但是在看代码之前需要知道双向绑定的原理其实就是基于实现的双向绑定官方传送门这里我们用官方的话来说方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。 废话不多说直接看效果图 showImg(https://segmentfault.com/img/bVbiYY5?w=282&h=500); 代码很好理解,但是在看代码之前...

    aristark 评论0 收藏0
  • JavaScript数据双向绑定简单演示

    摘要:对于前端,有时候需要实现视图层和数据层的双向绑定例如当前流行的各种框架和类库。为代表前端数据劫持。参考资料实现数据双向绑定的三种方式谈谈中的双向数据绑定非常简单的双向数据绑定框架三 对于前端,有时候需要实现视图层和数据层的双向绑定(two-way-binding), 例如当前流行的各种框架和类库:Vue.js、Angular.js、React.js。 然而,他们最原始的实现方式其实都相...

    snifes 评论0 收藏0
  • 实现一个简单双向绑定

    摘要:下图展示了实现双向绑定的流程实现一个简单的双向绑定双向绑定最最最初级进阶版操作是非常耗时和好性能,所以在优化过程中先从操作入手。 接触Vue有一段时间了,但是对于其双向绑定的实现一直是似懂非懂,今天看到一篇写的比较好的文章 传送门1 根据原作者的指导自己也去实现了一遍简单的 demo (本文的demo均基于Object.defineProperty 实现数据劫持,利用了对Vue.js实...

    elisa.yang 评论0 收藏0

发表评论

0条评论

不知名网友

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<