90%前端面试卡在这:DOM操作你真的懂了吗?从青铜到王者的关键突破

0 阅读5分钟

三、虚拟DOM理解:React高效渲染的核心机制​​

​​1. 虚拟DOM的概念​​

虚拟DOM(Virtual DOM)是React中用于描述真实DOM结构的​​轻量级JavaScript对象​​。它是真实DOM的抽象表示,通过高效的Diff算法协调更新,减少直接操作真实DOM的开销17。

​​通俗理解​​: 想象虚拟DOM是建筑的设计蓝图,而真实DOM是建好的房子。修改蓝图(虚拟DOM)比拆墙重建(直接操作真实DOM)更快、更安全。

​​代码示例​​:

// 虚拟DOM对象的结构(简化版)
const virtualNode = {
  type: 'div',          // 元素类型
  props: {              // 属性
    className: 'container',
    children: [
      { type: 'h1', props: { children: 'Hello World' } }
    ]
  }
};

​​关键点​​:

  • 虚拟DOM本质是普通JS对象,操作成本远低于真实DOM9。
  • 每次状态更新时,React会生成​​新的虚拟DOM树​​,与旧树对比后更新差异部分110。

​​2. 创建虚拟DOM的两种方式​​

(1) JSX(推荐)

// JSX语法(Babel编译后转为虚拟DOM)
const VDOM = (
  <div id="app">
    <p>React is awesome!</p>
  </div>
);

(2) React.createElement()

// 原生JS创建(嵌套多层时复杂)
const VDOM = React.createElement(
  'div', 
  { id: 'app' },
  React.createElement('p', {}, 'React is awesome!')
);

​​对比建议​​:

  • ​​JSX更直观​​:接近HTML语法,推荐日常使用。
  • ​​createElement更底层​​:适用于动态生成复杂结构(如循环渲染)。

3. 虚拟DOM vs 真实DOM:核心差异

image.png

4. Diff算法:虚拟DOM高效的秘密​

​核心规则​​:

  1. ​同层比较​​:仅对比同一层级的节点,跨层级直接重建(避免O(n³)复杂度)

  2. ​类型判断​​:节点类型不同(如divspan)则直接替换整个子树

  3. ​Key优化​​:列表项添加唯一key,帮助React识别元素移动/增删

​代码示例(索引值index与数据的唯一标识id)​​:

class Person extends React.Component{
     state = {
	     persons:[
               {id:1,name:'小张',age:18},
	       {id:2,name:'小李',age:19},
	     ]
     }
     add = ()=>{
	     const {persons} = this.state
	     const p = {id:persons.length+1,name:'小王',age:20}
	     this.setState({persons:[p,...persons]})
     }
     render(){
		return (
		   <div>
			<h2>展示人员信息</h2>
			<button onClick={this.add}>添加一个小王</button>
			<h3>使用index(索引值)作为key</h3>
			<ul>
			  {				
                       this.state.persons.map((personObj,index)=>{
		                return <li key={index}>{personObj.name}---{personObj.age}<input type="text"/></li>
			     })
			  }
			</ul>
			<hr/>
		        <hr/>
			<h3>使用id(数据的唯一标识)作为key</h3>
			<ul>
			   {
			      this.state.persons.map((personObj)=>{
				 return <li key={personObj.id}>{personObj.name}---{personObj.age}<input type="text"/></li>
			      })
			   }
			</ul>
		   </div>
		)
	    }
	}

执行效果图解(添加小王前):

image.png

执行效果图解(添加小王后):

image.png

解释

慢动作回放----使用index索引值作为key

		初始数据:
			{id:1,name:'小张',age:18},
			{id:2,name:'小李',age:19},
		初始的虚拟DOM:
			<li key=0>小张---18<input type="text"/></li>
			<li key=1>小李---19<input type="text"/></li>

		更新后的数据:
			{id:3,name:'小王',age:20},
			{id:1,name:'小张',age:18},
			{id:2,name:'小李',age:19},
		更新数据后的虚拟DOM:
			<li key=0>小王---20<input type="text"/></li>
			<li key=1>小张---18<input type="text"/></li>
			<li key=2>小李---19<input type="text"/></li>

-----------------------------------------------------------------

慢动作回放----使用id唯一标识作为key

		初始数据:
			{id:1,name:'小张',age:18},
			{id:2,name:'小李',age:19},
		初始的虚拟DOM:
			<li key=1>小张---18<input type="text"/></li>
			<li key=2>小李---19<input type="text"/></li>

		更新后的数据:
			{id:3,name:'小王',age:20},
			{id:1,name:'小张',age:18},
			{id:2,name:'小李',age:19},
		更新数据后的虚拟DOM:
			<li key=3>小王---20<input type="text"/></li>
			<li key=1>小张---18<input type="text"/></li>
			<li key=2>小李---19<input type="text"/></li>

说明:Key帮助React识别节点移动,避免全列表重新渲染。

5. 虚拟DOM的性能优化策略​

  1. ​合理使用Key​

    • 避免用数组索引,优先用数据唯一ID
  2. ​组件记忆化​

    • React.memo缓存组件,避免无意义重渲染:

      const MemoizedComponent = React.memo(({ data }) => {
        return <div>{data}</div>;
      });
      
  3. ​回调函数缓存​

    • useCallback避免函数重复创建:

      const handleClick = useCallback(() => {
        console.log('Clicked!');
      }, []);
      
  4. ​代码分割(React.lazy)​

    • 减少初次渲染的虚拟DOM树规模:

      const LazyComponent = React.lazy(() => import('./HeavyComponent'));
      

​​结语:为什么虚拟DOM是React的基石?​​

虚拟DOM通过​​内存计算+批量更新​​,解决了直接操作DOM的性能瓶颈,同时为跨平台(如React Native)提供了统一抽象层17。对初学者来说,掌握其核心思想比深究算法更重要——只需记住:​​“数据变,则虚拟DOM变;React负责高效更新”​​

经典面试题:

1). react/vue中的key有什么作用?(key的内部原理是什么)?

2). 为什么遍历列表时,key最好不要用index?

  1. 虚拟DOM中key的作用:

    1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。

    2). 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】, 随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:

    a. 旧虚拟DOM中找到了与新虚拟DOM相同的key: (1).若虚拟DOM中内容没变, 直接使用之前的真实DOM (2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM

    b. 旧虚拟DOM中未找到与新虚拟DOM相同的key 根据数据创建新的真实DOM,随后渲染到到页面

  1. 用index作为key可能会引发的问题:

    1). 若对数据进行:逆序添加、逆序删除等破坏顺序操作: 会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

    2). 如果结构中还包含输入类的DOM: 会产生错误DOM更新 ==> 界面有问题。

    3). 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作, 仅用于渲染列表用于展示,使用index作为key是没有问题的。

互动思考:虚拟DOM一定比原生DOM快吗?