js proxy实现双向绑定

在前端开发过程中,如果项目不大,或者有需要使用到双向数据场景的简单页面,完全不需要使用vuejs之类的第三方库。
下面分享一个简介的双向数据绑定js代码。代码只要30+行,直接复制到您项目中即可使用。

给需要双向绑定的元素设置 bind-data 属性,属性值为数据对象的属性名,即可将该元素与数据对象的相应属性进行绑定。
使用示例:

    <input type="number" type="text" bind-data="text1" />
    <p bind-data="text1">默认值</p>
    
    
    
    <script>
    ....,
    
    //使用js动态修改,对应字段会自动更新
    data.text1= '123';//新值
    
    ....,
    </script>

测试:


    <p bind-data="text1">默认值</p>
    <script>
        let data = new Proxy(
            {},
            {
                set(obj, key, value) {
                    obj[key] = value;
                    const dataElements = document.querySelectorAll(`[bind-data="${key}"]`);
                    const funcElements = document.querySelectorAll('[bind-fun]');

                    dataElements.forEach((element) => {
                        element instanceof HTMLInputElement ? (element.value = value) : (element.innerText = value);
                    });

                    if (funcElements.length > 0) {
                        funcElements.forEach((element) => {
                            const funcName = element.getAttribute('bind-fun');
                            if (typeof window[funcName] !== 'function') return;
                            const func = window[funcName].bind(obj);
                            const val = func() || '';
                            element instanceof HTMLInputElement ? (element.value = val) : (element.innerText = val);
                        });
                    }
                    return true;
                },
                get(obj, key) {
                    return obj[key];
                },
            }
        );

        document.addEventListener('input', function (event) {
            if (!event.target.hasAttribute('bind-data')) return;
            data[event.target.getAttribute('bind-data')] = event.target.value;
        });
    </script>

绑定数据进行回调:
使用示例:

  <div>单价:<input type="number" type="text" bind-data="price" /></div>
  <div>数量:<input type="number" type="text" bind-data="quantity" /></div>
  <div>总价:<span bind-fun="totalPrice">0</span></div>



<script>
function totalPrice(){
    //this指向数据对象
    return this.price * this.quantity;
}
</script>

测试:

单价:
<div>数量:<input type="number" type="text" bind-data="quantity" /></div>
<div>总价:<span bind-fun="totalPrice">0</span></div>

    <script>
        let data = new Proxy(
            {},{
                set(obj, key, value) {
                    obj[key] = value;
                    const dataElements = document.querySelectorAll(`[bind-data="${key}"]`);
                    const funcElements = document.querySelectorAll('[bind-fun]');

                    dataElements.forEach((element) => {
                        element instanceof HTMLInputElement ? (element.value = value) : (element.innerText = value);
                    });

                    if (funcElements.length > 0) {
                        funcElements.forEach((element) => {
                            const funcName = element.getAttribute('bind-fun');
                            if (typeof window[funcName] !== 'function') return;
                            const func = window[funcName].bind(obj);
                            const val = func() || '';
                            element instanceof HTMLInputElement ? (element.value = val) : (element.innerText = val);
                        });
                    }
                    return true;
                },
                get(obj, key) {
                    return obj[key];
                },
            }
        );

        document.addEventListener('input', function (event) {
            if (!event.target.hasAttribute('bind-data')) return;
            data[event.target.getAttribute('bind-data')] = event.target.value;
        });

  function totalPrice(){
    return this.price * this.quantity;
  }
    </script>

下面是实现双向绑定代码:

let data = new Proxy({}, {  
  set(obj, key, value) {  
    obj[key] = value;  
    const dataElements = document.querySelectorAll(`[bind-data="${key}"]`);  
    const funcElements = document.querySelectorAll("[bind-fun]");  
  
    dataElements.forEach((element) => {  
      element instanceof HTMLInputElement ? (element.value = value) : (element.innerText = value);  
    });  
  
    if (funcElements.length > 0) {  
      funcElements.forEach((element) => {  
        const funcName = element.getAttribute("bind-fun");  
        if (typeof window[funcName] !== "function") return;  
        const func = window[funcName].bind(obj);  
        const val = func() || "";  
        element instanceof HTMLInputElement ? (element.value = val) : (element.innerText = val);  
      });  
    }  
    return true;  
  },  
  get(obj, key) {  
    return obj[key];  
  },  
});
document.addEventListener("input", function (event) {  
  if (!event.target.hasAttribute("bind-data")) return;  
  data[event.target.getAttribute("bind-data")] = event.target.value;  
});

TAG:none