cyf手写doublebind

myvue.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>myVue</title>
</head>

<body>
    <!-- <script src="./compileUtil.js"></script> -->
    <script src="./dep.js"></script>
    <script src="./watcher.js"></script>
    <script src="./observer.js"></script>
    <script src="./myVue.js"></script>
    <script src="./compile.js"></script>
    <div id="app">
        <input type="text" v-model="msg">
        <div>
            <p>{{msg}} --- {{info}}</p>
        </div>
        <div onclick="aaa()">1111111</div>
    </div>
    <script>
        const mv = new myVue({
            el: '#app',
            data: {
                msg: "hello",
                info: "muji"
            }
        })
        aaa=()=>{
            mv.$data.info='22222'
           
        }
    </script>
</body>

</html>

compile.js

class compile {
    constructor(vm) {
        this.el = vm.$el
        this.vm = vm
        //1. createDocumentFragment()方法,是用来创建一个有虚拟节点的对象
        const fragment = this.nodeToFragment()
        //2.编译解析文档片段
        this.compileFragment(fragment)
        //3.将编译后的文档片段追加到模版容器中
        this.el.appendChild(fragment)
    }
    nodeToFragment() {
        const fragment = document.createDocumentFragment()
        while (this.el.firstChild) {
            fragment.appendChild(this.el.firstChild)
        }
        return fragment
    }

    compileFragment(fragment) {
        const nodeList = fragment.childNodes
        nodeList.forEach(node => {
            const nType = node.nodeType
            if (nType === 1) {
                //元素节点,解析指令
                this.compileElement(node)
            } else if (nType === 3) {
                //文档节点,解析{{}}
                this.compileText(node)
            }
            //如果node有子节点,递归调用compile
            if (node.childNodes && node.childNodes.length > 0) {
                this.compileFragment(node)
            }
        })
    }
    compileText(node) {
        const con = node.textContent
        //匹配 {{xx.xx}}的正则
        const reg = /\{\{(.+?)\}\}/g
        if (reg.test(con)) {
            const newVal = con.replace(reg, (...args) => {
                //key -> args[1]
                //object -> this.vm.$data
                new watcher(this.vm, args[1], () => {
                    node.textContent = con.replace(reg, (...args) => {
                        return this.vm.$data[args[1]]
                    })
                })
                return this.vm.$data[args[1]]
            })
            console.log(newVal)
            node.textContent = newVal
        }
    }
    compileElement(node) {
        //1.获取元素节点的所有属性
        const atrs = node.attributes
        Array.from(atrs).forEach(atr => {
            const { name, value } = atr
            if (name.startsWith('v-')) {
                const [, b] = name.split('-')
                if (b === 'model') {
                    node.value = this.vm.$data[value]
                    //数据驱动视图
                    new watcher(this.vm, value, () => {
                        node.value = this.vm.$data[value]
                    })
                    //dom事件监听,更新数据
                    node.addEventListener('input', (e) => {
                        this.vm.$data[value] = e.target.value
                    })
                }
            }
        })

    }
}

dep.js

class dep {
    //当数据变化时,通知所有观察者更新视图
    constructor() {
        this.watcherList = []
    }
    addWatcher(watcher) {
        this.watcherList.push(watcher)
    }
    //通知所有观察者更新视图
    notify() {
        this.watcherList.forEach(w => w.update())
    }
}

myvue.js

class myVue {
    constructor(option) {
        this.$data = option.data
        this.$el = document.querySelector(option.el)
        //1.数据代理
        this.proxyData()
        //2.数据劫持
        new observer(this.$data)
        //3.模版编译
        new compile(this)
    }
    //数据代理
    proxyData() {
        for (const key in this.$data) {
            Object.defineProperty(this, key, {
                configurable: false,
                enumerable: true,
                get() {
                    return this.$data[key]
                },
                set(newVal) {
                    this.$data[key] = newVal
                }
            })
        }
    }

}

observer.js

class observer {
    constructor(data) {
        this.oberve(data)
    }
    //劫持(监听)数据
    oberve(data) {
        const dependency = new dep()
        for (const key in data) {
            let val = data[key]
            Object.defineProperty(data, key, {
                enumerable: true,
                configurable: false,
                get() {
                    console.log('依赖被添加了', val)
                    //watcher实例化时被追加到依赖收集器中
                    dep.target && dependency.addWatcher(dep.target)
                    return val
                },
                set(newVal) {
                    //对数据变化进行监听
                    val = newVal
                    dependency.notify()
                }
            })
        }

    }
}

watch.js

const compileUtil = {
    /**
     *  获取data中的值 如person.name="小明"
     */
    getValue(exp, vm) {
        // exp = person.fav
        return exp.split(".").reduce((data, curValue) => {
            return data[curValue]
        }, vm.$data)
    },
    /**
     * 
     * @param {*} node 元素节点
     * @param {string} exp 指令表达式 在data中找值需要
     * @param {*} vm Vue实例
     */
    text(node, exp, vm) {
        let value;
        if (exp.indexOf("{{") !== -1) {
            // 对{{person.name}}进行替换
            value = exp.replace(/\{\{(.+?)\}\}/g, (...args) => {
                // 获取到要替换的表达式 args中第二个元素即为需要的元素
                // console.log(args);
                return this.getValue(args[1], vm)
            })
        } else {
            // 获取指令表达式的值
            value = this.getValue(exp, vm);
        }
        // 更新
        this.updater.textUpdater(node, value);
    },
    html(node, exp, vm) {
        let value = this.getValue(exp, vm);
        this.updater.htmlUpdater(node, value);
    },
    model(node, exp, vm) {
        let value = this.getValue(exp, vm);
        this.updater.modelUpdater(node, value);
    },
    /**
     * 
     * @param {*} node 元素节点
     * @param {*} exp 指令表达式
     * @param {*} vm Vue实例
     * @param {*} eventName 事件名称
     */
    on(node, exp, vm, eventName) {
        let fn = vm.$options.methods && vm.$options.methods[exp];
        node.addEventListener(eventName, fn.bind(vm), false)
    },
    /**
     * 
     * @param {*} node 元素节点
     * @param {*} exp 指令表达式
     * @param {*} vm Vue实例
     * @param {*} attrName 属性名称
     */
    bind(node, exp, vm, attrName) {
        let value = this.getValue(exp, vm);
        this.updater.bindUpdater(node, attrName, value);
    },
    updater: {
        // 更新文本内容
        textUpdater(node, value) {
            node.textContent = value;
        },
        // 插入html碎片
        htmlUpdater(node, value) {
            node.innerHTML = value;
        },
        // 绑定input的value
        modelUpdater(node, value) {
            node.value = value;
        },
        // 绑定属性
        bindUpdater(node, attrName, attrValue) {
            node.setAttribute(attrName, attrValue)
        }
    }
}

class watcher {
    //当观察者观察的数据变化时,更新视图
    constructor(vm, key, callback) {
        this.vm = vm
        this.key = key
        this.callback = callback
        //当观察者示例化时,将最初的值保存
        this.oldVal = this.getOldVal()
    }
    getOldVal() {
        dep.target = this
        //本质是对mv实例对象中的mv.$data[key]的访问
        const oldVal = compileUtil.getValue(this.key, this.vm)
        dep.target = null
        return oldVal
    }
    //更新视图
    update() {
        this.callback()
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值