Vue 的双向绑定原理 MVVM的实现

实现vue 的双向绑定实现 从数据到视图,和从视图到数据

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <div id="id">
        <h1>{{ww}}</h1>
        <h1>{{ww}}</h1>
        <input v-model="ww">
    </div>
</head>

<body>
    <script>
        var ec = new guan()//创建一个观察者
        //主构造器
        function mvvm(options) {
            let { data, el } = options //对象解构赋值
            // this.data = options.data
            for (let key in data) {
                Object.defineProperty(this, key, {
                    set(val) {
                        if (val === data[key]) { //如果设置的值和以前的值相等,就返回
                            return
                        }
                        console.log('开始设置');
                        data[key] = val
                        ec.emit(key)//设置观察者
                    },
                    get() {
                        console.log('我是假的');
                        return data[key]

                    }

                })
            }

            new Compiler(el, this)
        }

        // 获取dom 构造器
        function Compiler(el, vm) {
            this.vm = vm
            this.dom = document.querySelector(el)
            console.log(this.dom, this.vm);

            this.compile(this.dom)
        }
        Compiler.prototype.compile = function (el) {

            // el就是获取到的元素节点
            // console.log(el.childNodes);//显示所有的子节点 以数组形式
            el.childNodes.forEach(node => {//遍历获取到的节点


                if (node.nodeType === 3) {//说明是文本节点
                    //调用解析文本节点的方法
                    // console.log(node);

                    this.compileText(node)


                } else if (node.nodeType === 1) { //说明是元素节点
                    //元素节点的方法
                    this.compileElment(node)
                    // console.log(node);
                    //元素节点中可能含有文本节点,再进行遍历
                    console.log(node);
                    this.compile(node)//利用递归,找出所有的文本节点




                }
            })
        }
        Compiler.prototype.compileText = function (dom) { //处理文本节点 显示数据 {{}}解析其中的值
            console.log('我是文本节点' + dom);

            var shou = []//收集要观察的所有属性名
            var str = dom.textContent//取出文本节点的内容

            var newStr = str.replace(/{{(.+?)}}/g, (objstr, p1) => {
                // console.log('看我'+objstr,p1,this.vm[p1]);
                shou.push(p1)
                return this.vm[p1] //返回vm的数值  用来替换  p1为ww
            });

            dom.textContent = newStr //此时newStr为1000

            // 添加观察者
            shou.forEach(key => {
                // 给事件中心添加事件   
                ec.add(key, () => {
                    console.log(`${key},变化了`)

                    // 1. 取出文本节点的内容 str

                    // 2. 通过正则替换
                    // 一定要使用 =>
                    var newStr = str.replace(/{{(.+?)}}/g, (objstr, p1) => {

                        return this.vm[p1]
                    });
                    // 3. 显示替换的结果
                    dom.textContent = newStr
                })
            })

        }
        Compiler.prototype.compileElment = function (dom) { //处理元素节点  v-model 中的值 替换为具体数值
            console.log('我是元素节点' + dom);
            if (dom.hasAttribute('v-model')) {//判断是否有v-model属性
                var key = dom.getAttribute('v-model')//取出该属性  key为  ww
                dom.value = this.vm[key]  //将属性值替换为 实际值


                // 添加监听者
                // 当key属性发生变化时,去更新input框中的值
                ec.add(key, () => {
                    dom.value = this.vm[key]
                })

                // 给元素本身添加input事件  从视图到  数据的修改
                dom.addEventListener("input", (e) => {
                    // console.dir(e)
                    // 1. 获取当前用户改完之后的值
                    console.log(e.target.value)
                        // 2. 设置给对象的属性值(数据)
                        //    会进入到set拦截器,发布消息
                         this.vm[key] = e.target.value

                    console.log("用户在input中做了修改")
                })

            }

        }


        //引入观察者

        function guan() {
            this.da = {}
        }
        guan.prototype.add = function (ming, zhi) {
            var ll = this.da[ming]
            // this.da[ming].push(zhi)
            if (!ll) {//如果没有这个值,就创建这个值再t添加
                this.da[ming] = []
                this.da[ming].push(zhi)
            }
            else {
                ll.push(zhi)
            }
        }
        guan.prototype.emit = function (ming) {
            var tt = this.da[ming]
            if (tt) {
                this.da[ming].forEach(item => {
                    item()
                })
            }
        }
    </script>
    <script>
        var data = {
            ww: 1000,
            ff: 444
        }
        var vm = new mvvm({
            el: '#id',
            data
        })

    </script>
</body>

</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值