前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何手写实现《双向数据绑定》

如何手写实现《双向数据绑定》

作者头像
用户9914333
发布2022-07-22 14:49:03
6690
发布2022-07-22 14:49:03
举报
文章被收录于专栏:bug收集

面试中,双向数据绑定几乎是面试必备;

不只是要求会描述出来,而且要求能手写出来

下面就分享一下,

思路:

1. 数据 -> 转为响应式数据 Object.defineProperty Proxy

即:将存放数据的对象属性,与实例化对象的属性对应

2. input -> input / keyup -> 事件处理函数的绑定 -> 改变数据

3. 绑定dom {{}} 中的属性,更新数据的同时,更新对应dom节点

目的:this.domPool[key] = 对应的dom节点

具体代码:

html文件:

代码语言:javascript
复制
<div id="app">
    <input type="text" placeholder="姓名" v-model="name"/>
    <input type="text" placeholder="年龄" v-model="age"/>
    <input type="text" placeholder="年龄" v-model="sex"/>
    <input type="text" placeholder="email" v-model="email"/>
    <input type="text" placeholder="电话" v-model="tel"/>
    <p>姓名:<span>{{ name }}</span></p>
    <p>年龄:<span>{{ age }}</span></p>
    <p>年龄:<span>{{ sex }}</span></p>
    <p>email:<span>{{ email }}</span></p>
    <p>电话:<span>{{ tel }}</span></p>
    <p><button id="btn">change</button></p>
</div>

<script src="MVVM.js"></script>
<script type="text/javascript">
    const app = new MVVM('#app',{
        name:'',
        age:'',
        sex:'',
        email:'',
        tel:''
    })
    // 添加一个按钮事件,测试,数据改变时,dom也跟着改变
    var btn = document.getElementById("btn");
    btn.addEventListener("click",function () {
        app.setData('name','doubleyong');
    },false)
</script>

MVVM.js

对于双向数据绑定的封装

代码语言:javascript
复制
class MVVM {
    constructor(el,data) {
        this.el = document.querySelector(el);
        // this._data = data;
        this.data =data;
        this.domPool={};
        this.init();
    }
    // 初始化
    init(){
      this.initData(); //初始数据
      //实现数据的响应
      this.initDom(); //初始dom
    }
    //目的:暴露的实例,可以直接操作data中的属性
    // 实例对象.属性名 进行获取 
    // 实例对象.属性名 = 值; 进行设置 
    initData(){
       let _this = this;
       this.data = {};
       for(let key in _this._data){ 
       // let key 与var key, 作用域不同
           Object.defineProperty(_this.data,key,{
               get() {
                   return _this._data[key];
               },
               set(v) {
                   _this._data[key] = v;
                   // 数据修改后,对应dom显示也进行更新
                   _this.domPool[key].innerText = v;
               }
           })
       }
    }
    initDom(){
        this.bindDom(this.el); //绑定dom {{}} 中的属性,与dom节点
        this.bindInput(this.el); //实现更新input的value值,数据更新
    }
    // 目的:将对应的键值,和对应显示的dom进行关联
    bindDom(el){
        const childNodes = el.childNodes;
        childNodes.forEach(item =>{
            if(item.nodeType == 3)
            {
                const _value = item.nodeValue;
                if(_value.trim().length){
                    let _isValid = /\{\{(.+?)\}\}/.test(_value);
                    if(_isValid){
                        //将对应{{ }} 中的值,添加到dompool中
                        const _key = _value.match(/\{\{(.+?)\}\}/)[1].trim();
                        this.domPool[_key] = item.parentNode;
                        item.parentNode.innerText = this.data[_key] || undefined;
                    }
                }
            }

            item.childNodes && this.bindDom(item);
        })
    }
    // 目的:绑定input控件,input值改后,更新对应data值
    bindInput(el){
       const _allInputs = document.querySelectorAll('input');
        _allInputs.forEach(input=>{
            const _vModel = input.getAttribute('v-model');
            if(_vModel){
                input.addEventListener('keyup',this.handleInput.bind(this,_vModel,input),false);
            }
        })
    }
    handleInput(key,input){
       const _value = input.value;
       this.data[key] = _value;
    }
    setData(key,value){
        this.data[key] = value;
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 bug收集 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档