Princípio de aprendizagem idiota: modelo de renderização

Este não é o código-fonte
do Vue Este não é o código-fonte do Vue Este não é o código-fonte
do Vue,
apenas para nos ajudar a compreender a ideia do Vue

ordem

  1. Monte el na instância Vue
  2. A função render gera DOM virtual
  3. DOM virtual e dados são combinados e renderizados na página
  4. Uma vez que os dados são alterados, um novo DOM virtual é gerado imediatamente e o novo DOM virtual é comparado com o antigo DOM virtual
  5. Página de atualização

Crie seu próprio Vue e execute o método de montagem nesta função

 function myVue( options ) {
    
    
   this._data = options.data;
   // vue 是字符串, 这里是 DOM 
   this._template = document.querySelector( options.el ); 

   this.mount(); // 挂载
 } 

Aquele com função de cache é este render

  • Esta função de renderização gera um DOM virtual dentro
  • Porque ele precisa ter um cache, então use o texto do criador
  • A montagem aqui realmente publica o modo de assinante, passando um observador
  • Para facilitar o entendimento, escreva aqui
myVue.prototype.mount = function () {
    
    
  // 需要提供一个 render 方法: 生成 虚拟 DOM
  this.render = this.createRenderFn()

  this.mountComponent();
}

Updata é renderizar o DOM virtual na página

myVue.prototype.mountComponent = function () {
    
    
  // 执行 mountComponent() 函数 
  let mount = () => {
    
    
    this.update( this.render() )  // 渲染虚拟DOM
  }
    mount.call( this ); // 本质应该交给 watcher 来调用
}  

Aqui está a função de renderização

  • O objetivo é armazenar em cache a árvore de sintaxe abstrata (usamos DOM virtual para simular)
  • No código-fonte do Vue, aqui está a árvore de sintaxe abstrata em cache
myVue.prototype.createRenderFn = function () {
    
    
  let ast = getVNode( this._template );
  // Vue: 将 AST + data => VNode
  // 我们: 带有插值语法的 VNode + data => 含有数据的 VNode
  return function render () {
    
    
    // 将 带有 插值语法的 VNode 转换为 待数据的 VNode
    let _tmp = combine( ast, this._data );
    return _tmp;
  }
}

Renderize o DOM virtual na página: o algoritmo diff está lá


myVue.prototype.update = function () {
    
    
  // 简化, 直接生成 HTML DOM replaceChild 到页面中

}

A estrutura de design da submissão secundária é usada no Vue real

  1. Há uma correspondência um-para-um entre o DOM e o DOM virtual na página
  2. Primeiro AST e VNode de geração de dados (novo, render)
  3. Compare o VNode existente com o novo VNode (diff), atualize (atualize)

A razão pela qual você não o substitui diretamente por um novo VNode

  • Enquanto os dados mudam, um novo VNode será gerado, que é um VNode com novos dados
  • Compare o VNode com os novos dados com o antigo VNode. Esta comparação é o algoritmo diff
  • O VNode antigo e as tags de página correspondem entre si
  • Se for substituído diretamente, a relação de ligação entre eles terá que ser religada. A religação envolve o percurso recursivo da árvore, o que consome desempenho.
  • Ignore as coisas que são iguais e atualize-as se forem diferentes. É equivalente a atualizar a página

Insira a descrição da imagem aqui

  • As funções correspondentes às três funções escritas acima também são exibidas
  • createRenderFn (árvore de sintaxe abstrata de cache)
  • render (gerar DOM virtual)
  • updata (atualização)

Construtor de DOM virtual

 class VNode {
    
    
   constructor( tag, data, value, type ) {
    
    
     this.tag = tag && tag.toLowerCase();
     this.data = data;
     this.value = value;
     this.type = type;
     this.children = [];
   }

   appendChild ( vnode ) {
    
    
     this.children.push( vnode );
   }
 }

Gere VNode a partir de HTML DOM: trate esta função como uma função de compilador

 function getVNode( node ) {
    
    
   let nodeType = node.nodeType;
   let _vnode = null;
   if ( nodeType === 1 ) {
    
    
     // 元素
     let nodeName = node.nodeName;
     let attrs = node.attributes;
     let _attrObj = {
    
    };
     for ( let i = 0; i < attrs.length; i++ ) {
    
     // attrs[ i ] 属性节点 ( nodeType == 2 )
       _attrObj[ attrs[ i ].nodeName ] = attrs[ i ].nodeValue;
     }
     _vnode = new VNode( nodeName, _attrObj, undefined, nodeType );

     // 考虑 node 的子元素
     let childNodes = node.childNodes;
     for ( let i = 0; i < childNodes.length; i++ ) {
    
    
       _vnode.appendChild( getVNode( childNodes[ i ] ) ); // 递归
     }

   } else if ( nodeType === 3 ) {
    
    

     _vnode = new VNode( undefined, undefined, node.nodeValue, nodeType );
   }

   return _vnode;
 }

Combine Vnode com sintaxe de interpolação com dados de dados para preencher o VNode com dados: Simular AST -> VNode, escrevemos no artigo anterior

 let rkuohao = /\{\{(.+?)\}\}/g; // 用来匹配插值语法的正则表达式
 function combine( vnode, data ) {
    
    
   let _type = vnode.type; // 带上下划线,避免混肴
   let _data = vnode.data;
   let _value = vnode.value;
   let _tag = vnode.tag;
   let _children = vnode.children;
   
   let _vnode = null;
   if ( _type === 3 ) {
    
     // 文本节点 
	 // 对文本处理
     _value = _value.replace( rkuohao, function ( _, g ) {
    
    
       return getValueByPath( data, g.trim() );
     } );
	 _vnode = new VNode( _tag, _data, _value, _type )
	} else if ( _type === 1 ) {
    
     // 元素节点
     _vnode = new VNode( _tag, _data, _value, _type );
     _children.forEach( _subvnode => _vnode.appendChild( combine( _subvnode, data ) ) );
   }

   return _vnode;
 }

Código fonte completo

<script>
    class VNode {
    
    
      constructor( tag, data, value, type ) {
    
    
        this.tag = tag && tag.toLowerCase();
        this.data = data;
        this.value = value;
        this.type = type;
        this.children = [];
      }

      appendChild ( vnode ) {
    
    
        this.children.push( vnode );
      }
    }
    function getVNode( node ) {
    
    
      let nodeType = node.nodeType;
      let _vnode = null;
      if ( nodeType === 1 ) {
    
    
        // 元素
        let nodeName = node.nodeName;
        let attrs = node.attributes;
        let _attrObj = {
    
    };
        for ( let i = 0; i < attrs.length; i++ ) {
    
     
          _attrObj[ attrs[ i ].nodeName ] = attrs[ i ].nodeValue;
        }
        _vnode = new VNode( nodeName, _attrObj, undefined, nodeType );
        let childNodes = node.childNodes;
        for ( let i = 0; i < childNodes.length; i++ ) {
    
    
          _vnode.appendChild( getVNode( childNodes[ i ] ) ); 
        }
			} else if ( nodeType === 3 ) {
    
    
				_vnode = new VNode( undefined, undefined, node.nodeValue, nodeType );
      }
				return _vnode;
    }


   let rkuohao = /\{\{(.+?)\}\}/g;
   function getValueByPath( obj, path ) {
    
    
     let paths = path.split( '.' ); 
     let res = obj;
     let prop;
     while( prop = paths.shift() ) {
    
    
       res = res[ prop ];
     }
     return res;
   }
   function combine( vnode, data ) {
    
    
     let _type = vnode.type;
     let _data = vnode.data;
     let _value = vnode.value;
     let _tag = vnode.tag;
     let _children = vnode.children;


     let _vnode = null;

     if ( _type === 3 ) {
    
     
       _value = _value.replace( rkuohao, function ( _, g ) {
    
    
         return getValueByPath( data, g.trim() );
       } );
			_vnode = new VNode( _tag, _data, _value, _type )

     } else if ( _type === 1 ) {
    
     
       _vnode = new VNode( _tag, _data, _value, _type );
       _children.forEach( _subvnode => _vnode.appendChild( combine( _subvnode, data ) ) );
     }

     return _vnode;
   }

	function myVue( options ) {
    
    
     this._data = options.data;
     this._template = document.querySelector( options.el ); 

     this.mount(); 
   }  

   myVue.prototype.mount = function () {
    
    
     this.render = this.createRenderFn()

     this.mountComponent();
   }
   myVue.prototype.mountComponent = function () {
    
    
     let mount = () => {
    
    
       this.update( this.render() )
     }
     mount.call( this );
   }
   myVue.prototype.createRenderFn = function () {
    
    
     let ast = getVNode( this._template );
     return function render () {
    
    
       let _tmp = combine( ast, this._data );
       return _tmp;
     }
   }
	myVue.prototype.update = function () {
    
    
     // 简化, 直接生成 HTML DOM replaceChild 到页面中

   }


   let app = new myVue( {
    
    
     el: '#root',
     data: {
    
    
       name: '张三', age: 19
     }
   } );
	app.name = '李四'; // 这个赋值已完成, 页面数据就更新
   
 </script>

Acho que você gosta

Origin blog.csdn.net/m0_47883103/article/details/108650582
Recomendado
Clasificación