简单实现一个双向绑定

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

  1. 需要一个方法来识别哪一个的view被绑定了相应的数据
  2. 需要监视数据和view的变化
  3. 需要将所有变化传播到绑定的对象和对应的view

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

  num: <input type="number" data-bind-num>
  <div data-bind-num></div>

为了解决第二个问题,一方面监听数据改变,需要这样添加一个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.cnblogs.com/homehtml/p/11798456.html

时间: 2024-10-28 15:52:38

简单实现一个双向绑定的相关文章

非常简单的js双向绑定框架(二):控制器继承

初衷 上一篇已经实现了数据的双向绑定,但model的控制范围是整个文档,在实际工程中必须要有作用范围,以便做ui模块的拆分. 这一篇,我们希望实现像angularjs一样的控制器继承: 1. 父controller的Model可以在子controller里被访问到 2. 子controller的model不影响父controller 3. controller继承关系在html中指定,而不是js中指定 目标 html里,用isi-controller属性去声明控制器: <body> <d

自定义一个双向绑定组件

父组件: <k-input v-model="model.username"></k-input> 子组件KInput: <template> <div> <!--实现自定义组件双向绑定 v-model是语法糖,实现自定义组件双绑定需要指定 :value和@input即可--> <input type="text" :value="value" @input="onInp

Angular数据双向绑定

Angular数据双向绑定 AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Google所收购.是一款优秀的前端JS框架,已经被用于Google的多款产品当中.AngularJS有着诸多特性,最为核心的是:MVVM.模块化.自动化双向数据绑定.语义化标签.依赖注入等等. 一.什么是数据双向绑定 Angular实现了双向绑定机制.所谓的双向绑定,无非是从界面的操作能实时反映到数据,数据的变更能实时展现到界面. 一个最简单的示例就是这样: <div ng-control

剖析Vue原理&amp;实现双向绑定MVVM

剖析Vue原理&实现双向绑定MVVM vue.js 双向绑定 javascript 邓木琴居然被盗用了 2016年08月16日发布 推荐 24 推荐 收藏 195 收藏,10.6k 浏览 本文能帮你做什么?1.了解vue的双向数据绑定原理以及核心代码模块2.缓解好奇心的同时了解如何实现双向绑定为了便于说明原理与实现,本文相关代码主要摘自vue源码, 并进行了简化改造,相对较简陋,并未考虑到数组的处理.数据的循环依赖等,也难免存在一些问题,欢迎大家指正.不过这些并不会影响大家的阅读和理解,相信看完

angularjs系列之双向绑定

把之前学到ng的一些东西和大家分享一下.首先要讲的就是ng最重要的一个特性,双向绑定.(angular源码全部是1.5.0版本) 那么一个双向绑定的代码是什么样子.来看ng官网上的例子,代码就是这么简单. <script> angular.module('bindExample', []) .controller('ExampleController', ['$scope', function($scope) { $scope.test = 'Whirled'; $scope.testFn =

VUE的数据双向绑定

1.概述 让我们先来看一下官网的这张数据绑定的说明图: 原理图告诉我们,a对象下面的b属性定义了getter.setter对属性进行劫持,当属性值改变是就会notify通知watch对象,而watch对象则会notify到view上对应的位置进行更新(这个地方还没讲清下面再讲),然后我们就看到了视图的更新了,反过来当在视图(如input)输入数据时,也会触发订阅者watch,更新最新的数据到data里面(图中的a.b),这样model数据就能实时响应view上的数据变化了,这样一个过程就是数据的

深入理解Proxy 及 使用Proxy实现vue数据双向绑定

阅读目录 1.什么是Proxy?它的作用是? 2.get(target, propKey, receiver) 3.set(target, propKey, value, receiver) 4.has(target, propKey) 5.construct(target, args, newTarget): 6.apply(target, object, args) 7.使用Proxy实现简单的vue双向绑定 回到顶部 1.什么是Proxy?它的作用是? 据阮一峰文章介绍:Proxy可以理解

Angular系列----AngularJS入门教程05:双向绑定(转载)

在这一步你会增加一个让用户控制手机列表显示顺序的特性.动态排序可以这样实现,添加一个新的模型属性,把它和迭代器集成起来,然后让数据绑定完成剩下的事情. 请重置工作目录: git checkout -f step-4 你应该发现除了搜索框之外,你的应用多了一个下来菜单,它可以允许控制电话排列的顺序. 步骤3和步骤4之间最重要的不同在下面列出.你可以在GitHub里看到完整的差别. 模板 app/index.html Search: <input ng-model="query"&g

[转载]AngularJS入门教程04:双向绑定

在这一步你会增加一个让用户控制手机列表显示顺序的特性.动态排序可以这样实现,添加一个新的模型属性,把它和迭代器集成起来,然后让数据绑定完成剩下的事情. 请重置工作目录: git checkout -f step-4 你应该发现除了搜索框之外,你的应用多了一个下来菜单,它可以允许控制电话排列的顺序. 步骤3和步骤4之间最重要的不同在下面列出.你可以在GitHub里看到完整的差别. 模板 app/index.html Search: <input ng-model="query"&g