其他分享
首页 > 其他分享> > vue2响应式通过数组下标赋值响应式问题

vue2响应式通过数组下标赋值响应式问题

作者:互联网

通过学习vue2响应式,我写的不知道为什么,直接通过数组下标赋值它也是响应式的。下面是源码,你们可以试试。

直接复制新建一个html通过控制台测试

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script crossorigin="anonymous" integrity="sha512-pSyYzOKCLD2xoGM1GwkeHbdXgMRVsSqQaaUoHskx/HF09POwvow2VfVEdARIYwdeFLbu+2FCOTRYuiyeGxXkEg==" src="https://lib.baomitu.com/vue/2.6.14/vue.js"></script>
</head>
<body>
    <div id="app">
        <div>
           <p class="a1" style="color:red">{{name}}</p>
           <input id="name"/>
        </div>
        <p>{{b}}</p>
    </div>
    <script>
        
        const CE=['push','pop'];
        let array=Object.create(Array.prototype)
        CE.forEach(method=>{
            array[method]=function(){
                [...arguments].forEach(i=>{
                    reactive(i)});
                return Array.prototype[method].apply(this,arguments);
            }
            
        });
        function definReactive(target,key,value){
            if(Array.isArray(value)){
                value.__proto__=array;
                reactive(value)
            }
            if(value instanceof Object){
                reactive(value)
            }
            Object.defineProperty(target,key,{
                enumerable:true,
                configurable:true,
                get(){
                    return value;
                },
                set(newVal){
                    if(typeof newVal =='object' && newVal!=null)
                        reactive(newVal);
                    console.log(`响应式${target[key]},${value},新的值${newVal}`);
                    value=newVal
                }
            });
        }
        function reactive(data){
            Object.keys(data).forEach((key)=>{
                    definReactive(data,key,data[key])
            });
        }
        
        // 为什么使用虚拟dom
        // 提升性能
        // 生成虚拟节点
        class VNode{
            constructor(tag,attr,value,type){
                this.tag=tag;
                this.attrs=attr;
                this.value=value;
                this.type=type;
                this.children=[];
            }
            appendChild(vnode){
                this.children.push(vnode);
            }
        }
        function createVNode(node){
            let vnode;
            if(node.nodeType==3){
                vnode= new VNode(undefined,undefined,node.nodeValue,node.nodeType);
            }else if(node.nodeType==1){
                let attrObj={};
                for (let i = 0; i < node.attributes.length; i++) {
                    attrObj[ node.attributes[i].nodeName ] = node.attributes[i].nodeValue;
                }
                vnode=new VNode(node.nodeName,attrObj,undefined,node.nodeType); 
                // 考虑到有子节点
                node.childNodes.forEach(i => {
                    vnode.appendChild(createVNode(i))
                });
            }
            return vnode;
        }
        // let vnode=createVNode(document.querySelector('#app'))
        // // 将虚拟dom转换成真实dom
        function parseVNode(vnode){
            // 3是值,1是元素
            if(vnode.type==3){
                return document.createTextNode(vnode.value);
            }else if(vnode.type==1){
                let  el=document.createElement(vnode.tag);
                for (const key in vnode.attrs) {
                    el.setAttribute(key,vnode.attrs[key]);
                }
                vnode.children.forEach((i)=>{
                    el.appendChild(parseVNode(i)); 
                });
                return el        
            }
        }
        // let t=parseVNode(vnode)
        // console.log(t);

        // let a= create(document.querySelector('#app'))
        // parseVNode(a)

        class MyVue{
            constructor(options){
                this._data=options.data
                this._template=document.querySelector(options.el)
                this._parent=this._template.parentNode
                this.initData(this._data);
                this.mount();
            }
            initData(){
                reactive(this._data);
                Object.keys(this._data).forEach(key=>{
                    Object.defineProperty(this,key,{
                        enumerable:true,
                        configurable:true,
                        get(){
                            return this._data[key];
                        },
                        set(newVal){
                            if(typeof newVal =='object' && newVal!=null)
                                reactive(newVal);
                            this._data[key]=newVal
                            this.mountComponent();
                        }
                    });
                })
            }
            mount(){
                // 需要提供一个render方法,生成虚拟dom
                this.render=this.createRenderFn();
                this.mountComponent();
            }
            mountComponent(){
                let mount=()=>{
                    this.update(this.render())
                }
                // 本质应该交给watcher来调用
                mount();
            }
            // 这里生成render函数,目的缓存抽象语法树
            createRenderFn(){
                // 将AST+data => VNode
                let ast=createVNode(this._template);
                return function(){
                    // 将带{{}}的vnode和数据结合
                    return combine(ast,this._data);
                }
            }
            // 将虚拟dom渲染到页面中, diff算法在这里
            // 在真正的vue中使用了二次提交的设计结构
            update(vnode){
                let realDom = parseVNode(vnode)
                // 每次会全部替换,模拟的,不是真正的diff算法
                this._parent.replaceChild(realDom,document.querySelector('#app'));
            }
            compile(template){
                let childNodes=template.childNodes
                for(let i=0;i<childNodes.length;i++){
                    let e=childNodes[i];
                    if(e.nodeType==3){
                        e.nodeValue=e.nodeValue.replace(/\{\{(.*)?\}\}/,(a,b)=>{
                            let key=b.trim();
                            if(key.indexOf('.')!=-1){
                                return MyVue.getValue(key);
                            }
                            return    this._data[key];
                            
                        });
                    }else if(e.nodeType==1){
                        this.compile(e)
                    }
                }
            }
            // 递归取出对象里的值
            static getValue(key,data){
                let arr=key.split('.');
                let tmp=data
                for (let i = 0; i < arr.length; i++) {
                    tmp = tmp[arr[i]];
                }
                return tmp;
            }
        }
        function combine(vnode,data){
            let {type,value,tag,children,attrs}=vnode;
            let _vnode
            if(type==3){
                value=value.replace(/\{\{(.*)?\}\}/,(a,b)=>{
                    let key=b.trim();
                    if(key.indexOf('.')!=-1){
                        return MyVue.getValue(key,data);
                    }
                    return    data[key];
                });
                _vnode=new VNode(tag,attrs,value,type);
            }else if(type==1){
                _vnode=new VNode(tag,attrs,value,type);
                children.forEach(subchild=>{
                    _vnode.appendChild(combine(subchild,data))
                });
            }
            return _vnode;
        }
        let app=new MyVue({
            el:'#app',
            data:{
                name:'zhangsan',
                b:5,
                c:[1,2,3]
            }
        })
        
    </script>
</body>
</html>

标签:下标,vue2,value,vnode,响应,let,key,return,data
来源: https://blog.csdn.net/Lxh19980906/article/details/122811316