精华内容
下载资源
问答
  • React虚拟DOM原理

    2019-11-30 14:25:54
    1.什么是虚拟DOM 我们在前端面试的时候,经常会被问到什么是虚拟DOM。这个概念,感觉很熟悉,但又说不出它到底是什么。现在我们来探索一下到底什么是虚拟DOM。 首先我们看下什么是DOM,对于DOM,我们应该都很熟悉了...

    1.什么是虚拟DOM

    我们在前端面试的时候,经常会被问到什么是虚拟DOM。这个概念,感觉很熟悉,但又说不出它到底是什么。现在我们来探索一下到底什么是虚拟DOM

    首先我们看下什么是DOM,对于DOM,我们应该都很熟悉了,下面是MDN对于DOM的定义

    文档对象模型 (DOM) 将 web 页面与到脚本或编程语言连接起来。通常是指 JavaScript,但将 HTML、SVG 或 XML 文档建模为对象并不是 JavaScript 语言的一部分。DOM模型用一个逻辑树来表示一个文档,树的每个分支的终点都是一个节点(node),每个节点都包含着对象(objects)。DOM的方法(methods)让你可以用特定方式操作这个树,用这些方法你可以改变文档的结构、样式或者内容。节点可以关联上事件处理器,一旦某一事件被触发了,那些事件处理器就会被执行。

    虚拟DOM自然就是跟DOM有很大关系的了。我们在使用原生JS开发或者使用Jquery开发,经常就会操作DOM,但是我们使用的时候发现,每次我们改变DOM的时候,页面再次渲染,会花费不短的一段时间,这样用户体验就不太好了。如果我们每次操作的不是DOM或者每次只操作更少的DOM呢,是不是会花费的时间更短呢,基于这个想法,就有了虚拟DOM

    在React中,会把DOM转换成JavaScript对象,然后再把JavaScript对象转化成DOM,这样我们对于DOM的操作,实际上是在操作这个JavaScript对象

    2.DOM是如何创建虚拟DOM的

    我们利用在线babel工具来看下。左边是JSX,右边是JSX经过babel转换后的效果,事实上JSX是右边这种写法的语法糖,我们在React项目中写的JSX的写法都会转换成右边这种写法。
    在这里插入图片描述
    在React项目中,使用以下这种写法,渲染出的效果也是一样的。

    import React from 'react';
    import ReactDOM from 'react-dom';
    
    let element = React.createElement("h1", {
        id: "test",
        className: "testClass"
      }, "test");
    
    ReactDOM.render(element, document.getElementById('root'));
    

    现在我们来分析以下上面的代码

    • React.createElement()方法传入了3个参数,第1个参数对应的是标签名称,第2个参数是属性,第三个参数是内容,然后返回某个值
    • ReactDOM.render()方法接收了两个参数,第一个参数是刚刚提到的某个值,第二个参数是获取到的root元素,对应的是index.html中的<div id="root"></div>

    在上面的代码中加入console.log(element),打印出element的值,然后看到,原来某个值是这样的:
    在这里插入图片描述
    由此说明:React.createElement()方法创建了虚拟DOM

    3.模拟实现React.createElement()

    有上图可以这个对象有多个属性,目前来说对我们比较重要的是propstype属性,所以先实现对于这两个属性的操作。
    React.createElement()接收3个参数,现在要把这3个参数合并到typeprops中。
    React.createElement()接收3个以上参数,说明该元素里面有多个子元素(这些子元素仍然是React.createElement()),那么把第二个参数后面的所有参数转换成数组放入children

    function ReactElement(type, props) {
        const element = { type, props };
        return element;
    }
    
    function createElement(type, config = {}, children) {
        let propName;
        const props = {};  // 定义props
        for(propName in config) {
            props[propName] = config[propName];  // 复制config的属性到props中
        }
    
        // 处理children
        const childrenLength = arguments.length - 2;
        if(childrenLength === 1) {
            props.children = children;
        } else {
            // 有多个子元素的情况
            props.children = Array.from(arguments).slice(2);
        }
    
        return ReactElement(type, props);
    }
    

    加入以下代码测试下效果

    const element = createElement("h1", {
        id: "test",
        className: "testClass"
      }, createElement("span", null, "span1"), createElement("span", null, "span2"));
    
    console.log(JSON.stringify(element))
    

    打印结果为:
    在这里插入图片描述
    可以看到,最终,DOM转换成了JavaScript对象

    展开全文
  • react虚拟dom原理This is part of my “React for beginners” series on introducing React, its core features and best practices to follow. More articles are coming! 这是我的“初学者React”系列的一部分,...

    react虚拟dom原理

    This is part of my “React for beginners” series on introducing React, its core features and best practices to follow. More articles are coming!
    这是我的“初学者React”系列的一部分,该系列介绍了React,其核心功能和可遵循的最佳实践。 更多文章来了!

    Next article >

    下一篇文章>

    Do you want to learn React without crawling the documentation (well written by the way)? You clicked on the right article.

    您是否想在不爬行文档的情况下学习React(顺便说一下)? 您单击了正确的文章。

    We will learn how to run React with a single HTML file and then expose ourselves to a first snippet.

    我们将学习如何使用单个HTML文件运行React,然后将自己暴露给第一个片段。

    By the end, you will be able to explain these concepts: props, functional component, JSX, and Virtual DOM.

    最后,您将能够解释以下概念:道具,功能组件,JSX和虚拟DOM。

    The goal is to make a watch which displays hours and minutes. React offers to architect our code with components. `Let’s create our watch component.

    目的是制作一块显示小时和分钟的手表。 React提供了使用组件构建代码的方法。 `让我们创建手表组件。

    <!-- Skipping all HTML5 boilerplate -->
    <script src="https://unpkg.com/react@16.2.0/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16.2.0/umd/react-dom.development.js"></script>
    
    <!-- For JSX support (with babel) -->
    <script src="https://unpkg.com/babel-standalone@6.24.2/babel.min.js" charset="utf-8"></script> 
    
    <div id="app"></div> <!-- React mounting point-->
    
    <script type="text/babel">
      class Watch extends React.Component {
        render() {
          return <div>{this.props.hours}:{this.props.minutes}</div>;
        }
      }
    
      ReactDOM.render(<Watch hours="9" minutes="15"/>, document.getElementById('app'));
    </script>

    Ignore HTML boilerplate and script imports for dependencies (with unpkg, see React example). The few remaining lines are actually React code.

    忽略依赖关系HTML样板文件和脚本导入(使用unpkg ,请参见React示例 )。 剩下的几行实际上是React代码。

    First, define the Watch component and its template. Then mount React into the DOM and ask to render a watch.

    首先,定义监视组件及其模板。 然后将React安装到DOM中并要求渲染手表。

    将数据注入组件 (Inject data into the component)

    Our watch is quite stupid, it displays the hours and minutes we provided to it.

    我们的手表很傻,它显示了我们提供给它的小时和分钟。

    You can try to play around and change the value for those properties (called props in React). It will always display what you asked for even if it’s not numbers.

    您可以尝试修改这些属性的值(在React中称为props )。 即使不是数字,它也会始终显示您要的内容。

    This kind of React component with only a render function are functional component. They have a more concise syntax compared to classes.

    这种只有渲染功能的React组件是功能组件。 与类相比,它们的语法更简洁。

    const Watch = (props) =>
      <div>{props.hours}:{props.minutes}</div>;
    
    ReactDOM.render(<Watch hours="Hello" minutes="World"/>, document.getElementById('app'));

    Props are only data passed to a component, generally by a surrounding component. The component uses props for business logic and rendering.

    道具只是通常由周围组件传递到组件的数据。 该组件将道具用于业务逻辑和渲染。

    But as soon as props do not belong to the component they are immutable. Thus, the component which provided the props is the only piece of code able to update props values.

    但是,一旦道具不属于组件,它们就不会改变 。 因此,提供道具的组件是唯一能够更新道具值的代码。

    Using props is pretty straightforward. Create a DOM node with your component name as the tag name. Then give it attributes named after props. Then the props will be available through this.props in the component.

    使用道具非常简单。 使用您的组件名称作为标记名称创建一个DOM节点。 然后赋予它以道具命名的属性。 然后,可通过this.props中的this.props获得道具。

    不带引号HTML呢? (What about unquoted HTML ?)

    I was sure you will notice the unquoted HTML returned by the render function. This code is using JSX language, it’s a shorthand syntax to define HTML template in React components.

    我确定您会注意到render函数返回的未引用HTML。 这段代码使用的是JSX 语言,这是在React组件中定义HTML模板的简写语法。

    // Equivalent to JSX: <Watch hours="9" minutes="15"/>
    React.createElement(Watch, {'hours': '9', 'minutes': '15'});

    Now you may want to avoid JSX to define the component’s template. Actually, JSX looks like syntactic sugar.

    现在,您可能要避免使用JSX定义组件的模板。 实际上,JSX看起来像语法糖

    Take a look at the following snippet which shows both JSX and React syntax to build your opinion.

    请看以下显示JSX和React语法的代码片段,以建立您的意见。

    // Using JS with React.createElement
    React.createElement('form', null, 
      React.createElement('div', {'className': 'form-group'},
        React.createElement('label', {'htmlFor': 'email'}, 'Email address'),
        React.createElement('input', {'type': 'email', 'id': 'email', 'className': 'form-control'}),
      ),
      React.createElement('button', {'type': 'submit', 'className': 'btn btn-primary'}, 'Submit')
    )
    
    // Using JSX
    <form>
      <div className="form-group">
        <label htmlFor="email">Email address</label>
        <input type="email" id="email" className="form-control"/>
      </div>
      <button type="submit" className="btn btn-primary">Submit</button>
    </form>

    虚拟DOM更进一步 (Going further with the Virtual DOM)

    This last part is more complicated but very interesting. It will help you to understand how React is working under the hood.

    最后一部分比较复杂,但非常有趣。 它将帮助您了解React在幕后的工作方式。

    Updating elements on a webpage (a node in the DOM tree) involves using the DOM API. It will repaint the page but it can be slow (see this article for why).

    更新网页(DOM树中的一个节点)上的元素需要使用DOM API。 它将重新绘制页面,但是速度可能很慢(有关原因,请参见本文 )。

    Many frameworks such as React and Vue.js get around this problem. They come up with a solution called the Virtual DOM.

    React和Vue.js等许多框架都可以解决此问题。 他们提出了一种称为虚拟DOM的解决方案。

    {
       "type":"div",
       "props":{ "className":"form-group" },
       "children":[
         {
           "type":"label",
           "props":{ "htmlFor":"email" },
           "children":[ "Email address"]
         },
         {
           "type":"input",
           "props":{ "type":"email", "id":"email", "className":"form-control"},
           "children":[]
         }
      ]
    }

    The idea is simple. Reading and updating the DOM tree is very expensive. So make as few changes as possible and update as few nodes as possible.

    这个想法很简单。 读取和更新DOM树非常昂贵。 因此,请进行尽可能少的更改并尽可能少地更新节点。

    Reducing calls to DOM API involves keeping DOM tree representation in memory. Since we are talking about JavaScript frameworks, choosing JSON sounds legitimate.

    减少对DOM API的调用涉及将DOM树表示形式保留在内存中。 由于我们在谈论JavaScript框架,因此选择JSON听起来很合理。

    This approach immediately reflects changes in the Virtual DOM.

    这种方法立即反映了虚拟DOM中的更改。

    Besides, it gathers a few updates to apply later on the Real DOM at once (to avoid performance issues).

    此外,它收集了一些更新,以便稍后立即应用于Real DOM(以避免性能问题)。

    Do you remember React.createElement ? Actually, this function (called directly or through JSX) creates a new node in the Virtual DOM.

    您还记得React.createElement吗? 实际上,此函数(直接调用或通过JSX调用)在虚拟DOM中创建一个新节点。

    // React.createElement naive implementation (using ES6 features)
    function createElement(type, props, ...children) {
      return { type, props, children };
    }

    To apply updates, the Virtual DOM core feature comes into play, the reconciliation algorithm.

    为了应用更新, 对帐算法是Virtual DOM核心功能。

    Its job is to come up with the most optimized solution to resolve the difference between previous and current Virtual DOM state.

    它的工作是提出最优化的解决方案,以解决以前和当前虚拟DOM状态之间的差异。

    And then apply the new Virtual DOM to the real DOM.

    然后将新的虚拟DOM应用于真实DOM。

    进一步阅读 (Further readings)

    This article goes far on React internal and Virtual DOM explanations. Still, it’s important to know a bit about how a framework works when using it.

    本文对React内部和虚拟DOM的解释进行了深入介绍。 尽管如此,了解框架在使用时的工作原理还是很重要的。

    If you want to learn how the Virtual DOM works in details, follow my reading recommendations. You can write your own Virtual DOM and learn about DOM rendering.

    如果您想详细了解Virtual DOM的工作原理,请遵循我的阅读建议。 您可以编写自己的虚拟DOM并了解DOM渲染

    How to write your own Virtual DOM‌‌

    如何编写自己的虚拟DOM

    There are two things you need to know to build your own Virtual DOM. You do not even need to dive into React’s source…

    要构建自己的虚拟DOM,需要了解两件事。 您甚至不需要深入研究React的源代码……

    Thank you for reading. Sorry if this is too technical for your first step in React. But I hope now you are aware of what props, functional component, JSX, and Virtual DOM are.

    感谢您的阅读。 抱歉,这对于您在React的第一步来说太技术性了。 但是,我希望您现在知道什么是道具,功能组件,JSX和虚拟DOM。

    If you found this article useful, please click on the ? button a few times to make others find the article and to show your support! ?

    如果您发现本文有用,请单击“ ?”。 几次单击以使其他人找到该文章并表示您的支持!

    Don’t forget to follow me to get notified of my upcoming articles ?

    别忘了跟随我以获取有关我即将发表的文章的通知吗?

    This is part of my “React for beginners” series on introducing React, its core features and best practices to follow.
    这是我的“初学者React”系列的一部分,该系列介绍了React,其核心功能和可遵循的最佳实践。

    Next article >

    下一篇文章>

    ➥JavaScript (➥ JavaScript)

    ➥提示与技巧 (➥ Tips & tricks)

    Originally published at www.linkedin.com on February 6, 2018.

    最初于2018年2月6日发布在www.linkedin.com上。

    翻译自: https://www.freecodecamp.org/news/a-quick-guide-to-learn-react-and-how-its-virtual-dom-works-c869d788cd44/

    react虚拟dom原理

    展开全文
  • 学习虚拟dom之前,当然的知道jsx是干嘛用的,就像我们吃饭,吃西餐时得学习用刀叉,刚开始会吃饭时得学会怎么用筷子。 JSX 1、 什么是JSX JSX就是大家所说的语法糖,React 使用 JSX 来替代常规的 JavaScript。此外,...

    学习虚拟dom之前,当然的知道jsx是干嘛用的,就像我们吃饭,吃西餐时得学习用刀叉,刚开始会吃饭时得学会怎么用筷子。

    JSX

    1、 什么是JSX

    JSX就是大家所说的语法糖,React 使用 JSX 来替代常规的 JavaScript。此外,JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。

    2、为什么需要JSX

    • 开发效率:使用 JSX 编写模板简单快速。
    • 执行效率:JSX编译为 JavaScript 代码后进行了优化,执行更快。
    • 类型安全:在编译过程中就能发现错误。

    3、原理

    babel-loader会预编译JSX为React.createElement(...),后面会展开说明。

    4、与vue的不同之处

    • react中虚拟dom+jsx的设计一开始就有,vue则是演进过程中才出现的
    • jsx本来就是js扩展,转义过程简单直接的多。而vue把template编译为render函数的过程需要
      复杂的编译器转换字符串-ast-js函数字符串

    5、举个栗子,看看JSX编译前后有什么不一样

    编译前:

    import React from "react"; 
    import ReactDOM from "react-dom"; 
    import "./index.css"; 
    class ClassCmp extends React.Component { 
    	render() { 
    		return ( 
    			<div className='app'> Hello {this.props.name} </div> 
    			); 
    	}
    }
    function FuncCmp(props) { 
    	return <div>name: {props.name}</div>; 
    }
    const jsx = ( 
    	<div> 
    		<p>我是内容</p> 
    		<FuncCmp name="我是function组件" /> 
    		<ClassCmp name="我是class组件" /> 
    	</div> 
    );
    ReactDOM.render( jsx, document.getElementById('hello-example') );
    

    编译后:

    class ClassCmp extends React.Component { 
    render() { 
    	return React.createElement( 
    		"div", 
    		{ "class": "app" }, 
    		"Hello ", 
    		this.props.name 
    	); 
    } }
    function FuncCmp(props) { 
    	return React.createElement( 
    		"div", 
    		null, 
    		"name: ", 
    		props.name 
    	); 
    }
    const jsx = React.createElement( 
    	"div", 
    	null, 
    	React.createElement( 
    		"p", 
    		null,
    		 "我是内容" 
    	 ),
    	 React.createElement(FuncCmp, { name: "我是function组件" }),
    	 React.createElement(ClassCmp, { name: "我是class组件" }) );
    ReactDOM.render(jsx, document.getElementById('hello-example'));
    

    React核心api

    核心精简后:

    const React = { createElement, Component }
    

    最核心的api:

    • React.createElement:创建虚拟DOM
    • React.Component:实现自定义组件
    • ReactDOM.render:渲染真实DOM

    render()

    ReactDOM.render(element, container[, callback])
    

    当首次调用时,容器节点里的所有 DOM 元素都会被替换,后续的调用则会使用 React 的 DOM 差分算法(DOM diffing algorithm)进行高效的更新。

    如果提供了可选的回调函数,该回调将在组件被渲染或更新之后被执行。

    实现React.createElement、ReactDom.render、Component

    1、CreateElement

    首先,我们将传入的节点定义转换为vdom。

    注意节点类型:文本节点、HTML标签节点、function组件、class组件、fragment、其他如portal等节点。

    创建src/index.js

    import React from "./kreact/";
    import ReactDOM from "./kreact/react-dom"; 
    import Component from "./kreact/Component"; 
    import "./index.css";
    
    class ClassComponent extends Component{
    	render(){
    		return 
    			<div ClassName="border">{this.props.name}</div>
    	}
    }
    function FunctionComponent({name}){
    	return(
    		<div className="border">
    			{name}
    			<button onClick={()=>console.log("hello")}>click</button>
    		</div>
    	);
    }
    const Com2 = React.cloneElement(
    	<input/>,
    	{
    	placeholder:"this is placeholder",
    	value: '',
    	onChange:()=>{}
    });
    
    const jsx = (
    	<div classname="border">
    		<p>world</p>
    		<FunctionComponent name="函数组件"/>
    		<ClassComponent name="class组件"/>
    		{Com2}
    		<>
    			<h1>文字1</h1>
    			<h2>文字2</h2>
    		</>
    
    		{[1,2,3].map(item)=>(
    			<div key={item}>文字{item}
    		))}
    		</div>
    );
    ReactDOM.render(jsx, document.getElementById("root"));
    console.log("version", React.version); //sy-log
    

    创建./src/kreact/index.js,它需要包含createElement方法

    import {TEXT} from "./const";
    function createElement(type, config, ...children) {
    	if (config) {
    		delete config.__self; 
    		delete config.__source;
    	}
    	const props = { 
    		...config, 
    		children: 
    		children.map(child => 
    		typeof child === "object" ? child : createTextNode(child) 
    	) };
    	return { 
    		type, 
    		props 
    	}; 
    }
    function createTextNode(text) { 
    	return { 
    		type: TEXT,
    		props: { 
    			children: [],
    			nodeValue: text 
    		} 
    	}; 
    }
    export default { createElement };
    

    2、ReactDOM.render

    import {TEXT} from "./const";
    function render(vnode, container) {
     	console.log("vnode", vnode); //sy-log 
     	// vnode _> node 
     	const node = createNode(vnode, container);
     	container.appendChild(node); 
    }
    // 返回真实的dom节点
    function createNode(vnode, parentNode) {
    	let node = null; 
    	const {type, props} = vnode;
    	if (type === TEXT) {
    		node = document.createTextNode(""); 
    	} else if (typeof type === "string") {
    		node = document.createElement(type);
    	} else if (typeof type === "function") {
    		node = type.isReactComponent
    		? updateClassComponent(vnode, parentNode) 
    		: updateFunctionComponent(vnode, parentNode);
    	} else { 
    		node = document.createDocumentFragment(); 
    	}
    	reconcileChildren(props.children, node);
     	updateNode(node, props);
     	return node;
    }
    
    function reconcileChildren(children, node) {
    	for (let i = 0; i < children.length; i++) {
    		let child = children[i]; 
    		if (Array.isArray(child)) {
    			for (let j = 0; j < child.length; j++) { 
    				render(child[j], node); } 
    		} else{ 
    				render(child, node); 
    		}
    	}
    }
    function updateNode(node, nextVal) {
    	Object.keys(nextVal)
    		.filter(k => k !== "children")
    		.forEach(k => {
    			if (k.slice(0, 2) === "on") {
    				let eventName = k.slice(2).toLocaleLowerCase();
    				node.addEventListener(eventName, nextVal[k]);
    			} else {
    				node[k] = nextVal[k]; 
    			} 
    	}); 
    }
    function updateFunctionComponent(vnode, parentNode) { 
    	const {type, props} = vnode; 
    	let vvnode = type(props); 
    	const node = createNode(vvnode, parentNode); 
    	return node; 
    }
    
    function updateClassComponent(vnode, parentNode) { 
    	const {type, props} = vnode; 
    	let cmp = new type(props); 
    	const vvnode = cmp.render(); 
    	const node = createNode(vvnode, parentNode); 
    	return node; 
    }
    export default { render };
    

    3、Component

    class Component { 
    	static isReactComponent = {};
    	constructor(props) { 
    		this.props = props; 
    	} 
    }
    export default Component;
    

    总结

    1. webpack+babel编译时,替换JSX为React.createElement(type,props,...children)
    2. 所有React.createElement()执行结束后得到一个JS对象即vdom,它能够完整描述dom结构
    3. ReactDOM.render(vdom, container)可以将vdom转换为dom并追加到container中
    4. 实际上,转换过程需要经过一个diff过程。
    展开全文
  • React虚拟DOM机制 虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象 状态变更时,记录新树和旧树的差异 最后把差异更新到真正的dom中 React引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了...

    React虚拟DOM机制

    • 虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象
    • 状态变更时,记录新树和旧树的差异
    • 最后把差异更新到真正的dom中

    React引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。

      基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。

      而且React能够批量处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A变成B,然后又从B变成A,React会认为UI不发生任何变化。

      尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,而对实际DOM进行操作的仅仅是Diff部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要关心在任意一个数据状态下,整个界面是如何Render的。

    总之一句话:根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上

    React diff 算法

    diff 算法作为Virtual DOM的加速器,其算法的改进优化是React整个界面渲染的基础和性能的保障,同时也是React源码中最神秘的,最不可思议的部分。

    1. 传统 diff 算法
    计算一棵树形结构转换为另一棵树形结构需要最少步骤,如果使用传统的diff算法通过循环递归遍历节点进行对比,其复杂度要达到O(n^3),其中n是节点总数,效率十分低下,假设我们要展示1000个节点,那么我们就要依次执行上十亿次的比较。

    下面附上一则简单的传统diff算法:

    let result = [];
    // 比较叶子节点
    const diffLeafs = function (beforeLeaf, afterLeaf) {
        // 获取较大节点树的长度
        let count = Math.max(beforeLeaf.children.length, afterLeaf.children.length);
        // 循环遍历
        for (let i = 0; i < count; i++) {
            const beforeTag = beforeLeaf.children[i];
            const afterTag = afterLeaf.children[i];
            // 添加 afterTag 节点
            if (beforeTag === undefined) {
                result.push({ type: "add", element: afterTag });
                // 删除 beforeTag 节点
            } else if (afterTag === undefined) {
                result.push({ type: "remove", element: beforeTag });
                // 节点名改变时,删除 beforeTag 节点,添加 afterTag 节点
            } else if (beforeTag.tagName !== afterTag.tagName) {
                result.push({ type: "remove", element: beforeTag });
                result.push({ type: "add", element: afterTag });
                // 节点不变而内容改变时,改变节点
            } else if (beforeTag.innerHTML !== afterTag.innerHTML) {
                if (beforeTag.children.length === 0) {
                    result.push({
                        type: "changed",
                        beforeElement: beforeTag,
                        afterElement: afterTag,
                        html: afterTag.innerHTML
                    });
                } else {
                    // 递归比较
                    diffLeafs(beforeTag, afterTag);
                }
            }
        }
        return result;
    }

    2. react diff 算法
    1. diff 策略
    下面介绍一下react diff算法的3个策略

    • Web UI 中DOM节点跨层级的移动操作特别少,可以忽略不计
    • 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构。
    • 对于同一层级的一组子节点,它们可以通过唯一id进行区分。

    对于以上三个策略,react 分别对 tree diff, component diff, element diff 进行算法优化。

    2. tree diff
    基于策略一,WebUI中DOM节点跨层级的移动操作少的可以忽略不计,React对Virtual DOM树进行层级控制,只会对相同层级的DOM节点进行比较,即同一个父元素下的所有子节点,当发现节点已经不存在了,则会删除掉该节点下所有的子节点,不会再进行比较。这样只需要对DOM树进行一次遍历,就可以完成整个树的比较。复杂度变为O(n);

    疑问:当我们的DOM节点进行跨层级操作时,diff 会有怎么样的表现呢?

    如下图所示,A节点及其子节点被整个移动到D节点下面去,由于React只会简单的考虑同级节点的位置变换,而对于不同层级的节点,只有创建和删除操作,所以当根节点发现A节点消失了,就会删除A节点及其子节点,当D发现多了一个子节点A,就会创建新的A作为其子节点。 
    此时,diff的执行情况是:

    createA-->createB-->createC-->deleteA


     

    由此可以发现,当出现节点跨层级移动时,并不会出现想象中的移动操作,而是会进行删除,重新创建的动作,这是一种很影响React性能的操作。因此官方也不建议进行DOM节点跨层级的操作。

    3. component diff
    React是基于组件构建应用的,对于组件间的比较所采用的策略也是非常简洁和高效的。

    • 如果是同一个类型的组件,则按照原策略进行Virtual DOM比较。
    • 如果不是同一类型的组件,则将其判断为dirty component,从而替换整个组价下的所有子节点。
    • 如果是同一个类型的组件,有可能经过一轮Virtual DOM比较下来,并没有发生变化。如果我们能够提前确切知道这一点,那么就可以省下大量的diff运算时间。因此,React允许用户通过shouldComponentUpdate()来判断该组件是否需要进行diff算法分析。

    如下图所示,当组件D变为组件G时,即使这两个组件结构相似,一旦React判断D和G是不用类型的组件,就不会比较两者的结构,而是直接删除组件D,重新创建组件G及其子节点。虽然当两个组件是不同类型但结构相似时,进行diff算法分析会影响性能,但是毕竟不同类型的组件存在相似DOM树的情况在实际开发过程中很少出现,因此这种极端因素很难在实际开发过程中造成重大影响。 

    4. element diff
    当节点属于同一层级时,diff提供了3种节点操作,分别为INSERT_MARKUP(插入),MOVE_EXISTING(移动),REMOVE_NODE(删除)。

    • INSERT_MARKUP:新的组件类型不在旧集合中,即全新的节点,需要对新节点进行插入操作。
    • MOVE_EXISTING:旧集合中有新组件类型,且element是可更新的类型,这时候就需要做移动操作,可以复用以前的DOM节点。
    • REMOVE_NODE:旧组件类型,在新集合里也有,但对应的element不同则不能直接复用和更新,需要执行删除操作,或者旧组件不在新集合里的,也需要执行删除操作。
       

    总结

    1. 通过diff策略,将算法从O(n^3)简化为O(n)。
    2. 分层求异,对tree diff进行优化。
    3. 分组件求异,相同类生成相似树形结构、不同类生成不同树形结构,对component diff进行优化。
    4. 设置key,对element diff进行优化。
    5. 尽量保持稳定的DOM结构、避免将最后一个节点移动到列表首部、避免节点数量过大或更新过于频繁。

     

     

     

     

     

    展开全文
  • 在上一篇博客React虚拟DOM原理中,我们探索了如何创建虚拟DOM,那么创建了虚拟DOM之后,又是怎么转换成真实DOM,渲染在页面中的呢,下面我们继续探索。 ...
  • 关于React 虚拟DOM

    2020-07-08 21:24:12
    React 虚拟dom原理小结虚拟DOM原理简述JS原生代码模拟实现 虚拟DOM原理 简述 虚拟Dom相当于在真实dom和js中间加了一个缓存,将真实的dom转化成一个json对象,从而利用diff算法避免了不必要的dom操作,以此提高性能...
  • react 虚拟dom原理简单理解

    千次阅读 2018-03-29 18:55:59
    原理react 在内存中生成维护一个跟真实DOM一样的虚拟DOM 树,在改动完组件后,会再生成一个新得DOM,react 会把新虚拟DOM 跟原虚拟DOM 进行比对,找出两个DOM 不同的地方diff ,然后把diff放到队列里面,然后批量...
  • 什么是虚拟DOM?传统的 DOM 操作是直接在 DOM 上操作的,当需要修改一系列元素中的值时,就会直接对 DOM 进行操作。而采用 Virtual DOM 则会对需要修改的 DOM 进行比较(DIFF),从而只选择需要修改的部分。也因此...
  • react虚拟DOM的机制

    万次阅读 2019-04-20 23:13:30
    基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的...
  • React虚拟Dom

    2020-06-11 19:41:09
    react 在内存中生成维护一个跟真实DOM一样的虚拟DOM 树,在改动完组件后,会再生成一个新得DOM,react 会把新虚拟DOM 跟原虚拟DOM 进行比对,找出两个DOM 不同的地方diff ,然后把diff放到队列里面,然后批量更新...
  • React虚拟DOM浅析

    2017-11-29 14:13:45
    React虚拟DOM浅析  在Web开发中,需要将数据的变化实时反映到UI上,这时就需要对DOM进行操作,但是复杂或频繁的DOM操作通常是性能瓶颈产生的原因,为此,React引入了虚拟DOM(Virtual DOM)的机制。 ...
  • ReactReact-Dom 是核心模块 React:是核心库,当使用JSX语法时,必须让React 存在当前作用域下 React元素:是通过JSX语法创建的在JS中... 把虚拟DOM转换成真实的DOM 2> 把真实的DOM插入到根元素中 3> ...
  • react 虚拟dom 浅析

    2018-11-25 14:59:08
    react 虚拟dom 浅析 虚拟dom 的概念 随着 react vue 等框架的普及 在前端圈一度成为一个热议的话题 争论点在于 虚拟dom 真的可以提高 操作dom的性能么 与传统的jq 相比 性能到底有多大提升 于是带着这两个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,508
精华内容 3,003
关键字:

react虚拟dom原理