vue多层级属性的渲染—Vue源码的学习

在vue基本的执行流程存在如下问题:
1) Vue使用的是虚拟DOM
2 )只考虑了单属性 ( {{name}} ),没有考虑层级 ( {{a.b.c.d}} ),而vue中大量的使用层级 ( {{ child.name.firstName }} )
3 )代码没有整合

为了解决上述问题,可以采用如下方法:
1)将真正的DOM和虚拟的DOM相互转换,提供如下方案:

  • 采用深拷贝,(递归函数)
  • 采用栈结构,使用栈存储父元素来实现递归生成(Vue的源码采用的方式,因为Vue里面是把元素转换为字符串处理)

2)考虑多属性,封装一个通过路径获取对象值的函数。(可采用函数柯里化技巧)
3)可以采用面向对象的方法整合代码。

案例

	<div id="root">
        <p>
            {{name.firstName}}
        </p>
        <p>
            {{a.b.c.d.e}}
        </p>
    </div>
	  // 利用了函数柯里化 的技巧,实现获取对象内部属性的值
	   function createGetValueByPath(path){
           let paths = path.split('.'); 
           return function getValueByPath(obj){
               let res = obj;
               let prop;
               while(prop = paths.shift()){
                   res = res[ prop ];
               }
               return res;
           }
      }
	
	 //将模板和数据结合起来
	  function compiler(template, data){
            let regKuohao = /\{\{(.+?)\}\}/g; // 匹配{{}}括号

            // 把 template 的子元素带有 {{ }} 的给处理一下
            let childNodes = template.childNodes; // 子元素
            for(let i=0; i < childNodes.length; i++ ){
                let type = childNodes[i].nodeType; //节点类型 1 元素  3 文本节点
                if( type === 3){
                    // 文本节点,可以判断里面是否有 {{}} 插值
                    let txt = childNodes[i].nodeValue; // 该属性只有文本节点才有意义

                       // 有没有双括号? //正则表达式
                    txt = txt.replace(regKuohao,function( _ , g1 ){  
                        let path = g1.trim();
                        
                        // let result = data[path];
						let getValueByPath = createGetValueByPath(path); //返回了一个函数
                        let result = getValueByPath(data)

                        return result
                    })

                    // 注意 txt 现在和 DOM元素 是没有关系的
                    childNodes[i].nodeValue = txt;

                }else if( type === 1){
                    // 元素,有没有子元素?是否需要将其子元素 进行判断是否插值
                    compiler(childNodes[i], data)
                }
            }
        }

        function JGVue( options ){
            // 习惯: 内部的数据使用下划线开头,只读数据使用 $ 开头
            this._data = options.data;
            this._el = options.el;

            //准备工作(准备模板)
            this.$el = this._templateDOM = document.querySelector(this._el); // 找到模板
            this._parent = this._templateDOM.parentNode;

            //渲染工作
            this.render()
        }

        // 原型方法
        // 将模板结合数据,得到 HTML, 加到页面中
        JGVue.prototype.render = function(){
            this.compiler();
        }

        //编译,,将模板与数据结合得到真正的DOM元素
        JGVue.prototype.compiler = function(tepNode){
            let realHTMLDOM = this._templateDOM.cloneNode( true ); //克隆模板 //用模板拷贝 得到一个 准DOM
            compiler(realHTMLDOM, this._data);
            this.update(realHTMLDOM); //替换到页面中
        }
        
        //将DOM的元素 放到页面中
        JGVue.prototype.update = function(realDom){
            this._parent.replaceChild(realDom , document.querySelector(this._el));
        }

        // 怎么用?
        let app = new JGVue({
            el:'#root',
            data:{
                name:{
                    firstName:'张'
                    ,lastName:'三丰',
                }, 
                a:{
                   b:{
                        c:{
                            d:{
                                e:'哈哈哈'
                            }
                        }
                    }
                }
            }
        })

总结

以上案例考虑了对象多层级属性问题,但是尚未实现虚拟DOM。
下一节将会结合虚拟DOM实现。

发布了2 篇原创文章 · 获赞 3 · 访问量 487

猜你喜欢

转载自blog.csdn.net/Aglaia_web/article/details/103911343
今日推荐