数据双向绑定(响应式原理)

<div id="app">
  <div>
    <input type="text" v-model="name" placeholder="姓名">
    <input type="text" v-model="age" placeholder="年龄">
    <input type="text" v-model="email" placeholder="邮箱">
    <input type="text" v-model="tel" placeholder="电话号码">
  </div>
  <div>
    <p>姓名:<span>{{name}}</span></p>
    <p>年龄:<span>{{age}}</span></p>
    <p>邮箱:<span>{{email}}</span></p>
    <p>
    <p>电话:<span>{{tel}}</span></p>
    </p>
  </div>
</div>
  
class MVVM {
    constructor(el, data) {
        this.el = document.querySelector(el)
        this.data = data
        this.domPool = {} // dom 池,用来保存 el 下所有 dom 节点的父节点

        this.init() // 初始化
    }

    init() {
        this.initData() // 初始化数据
        this.initDom() // 初始化界面
    }

    initData() {
        const that = this
        that._data = {} // 定义代理(劫持)的数据对象

        for (let property in that.data) { // 对象代理
            (function (property) {
                Object.defineProperty(that._data, property, {
                    set(value) {
                        that.data[property] = value
                        that.domPool[property].innerText = value
                    },
                    get() {
                        return that.data[property]
                    }
                })
            })(property)
        }
    }

    initDom() {
        this.bindDom(this.el) // 解析 dom,把 {{ ... }} 中的属性解析成数据
        this.bindInput(this.el) // 遍历 input,给 input 输入框绑定 keyup 响应事件
    }

    bindDom(el) {
        const childNodes = el.childNodes // el 的子节点

        childNodes.forEach(node => { // 遍历 el 的子节点
            if (node.nodeType === 3) { // 如果节点类型是 3,即文本,因为 {{...}} 属于文本节点
                const value = node.nodeValue // 节点的值

                if (value.trim().length) {
                    const patten = /\{\{(.+?)\}\}/ // 匹配 {{}}
                    let isValid = patten.test(value) // 匹配成功

                    if (isValid) {
                        const property = value.match(patten)[1].trim() // {{}} 中的数据属性
                        this.domPool[property] = node.parentNode // 把父节点放入 dom 池
                        this.domPool[property].innerText = this._data[property] || undefined // 更改父节点的文本内容为代理对象对应属性的值
                    }
                }
            }

            node.childNodes && this.bindDom(node) // 如果子节点还有子节点,继续递归遍历其子节点
        })
    }

    bindInput(el) {
        const allInputs = el.querySelectorAll('input')

        allInputs.forEach(input => { // 遍历 input 输入框
            const vModel = input.getAttribute('v-model') // input 的 v-model 属性

            if (vModel) {
                input.addEventListener('keyup', this.handleInput.bind(this, vModel, input), false) // 为每个 input 注册 keyup 响应时间,具体在 this.handleInput 函数中进行处理,改变该函数的 this 指向,并把 vModel 和 input 作为参数传递过去
            }
        })
    }

    handleInput(property, input) {
        const value = input.value
        this._data[property] = value // 把 input 的值赋值给代理对象对应属性
    }
}

const app = new MVVM('#app', {
    name: 'user1',
    age: '11',
    email: '11@qq.com',
    tel: '124'
})

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值