react 面试准备题

为什么要使用框架
1、组件化: 其中以 React 的组件化最为彻底,甚至可以到函数级别的原子组件,高度的组件化可以是我们的工程易于维护、易于组合拓展。
2、生态: 现在主流前端框架都自带生态,不管是数据流管理架构还是 UI 库都有成熟的解决方案。
3、开发效率: 现代前端框架隐藏了技术实现细节,我们不需要自己实现,使用框架可以快速构建用户界面,提高开发效率,降低开发成本

说说你对react的理解
思路: react 是什么,有什么特点,优劣势

https://www.xiaohongshu.com/explore/623175dd0000000001026c57

什么是错误边界
错误边界是一种React组件。能捕获JavaScript中所有位置的错误,记录下错误,并且还能显示一个后备界面,避免让用户直接看到组件树的崩溃信息。
错误边界目前只在 Class Component 中实现了,没有在 hooks 中实现(因为Error Boundaries的实现借助了this.setState可以传递callback的特性,useState无法传入回调,所以无法完全对标);

错误边界 无法捕获 以下四种场景中产生的错误:

事件处理函数(因为 Error Boundaries 实现的本质是触发更新,但是事件处理函数不在render或者commit阶段,所以无法进行捕获,如果你需要在事件处理器内部捕获错误,可以使用原生的 try / catch 语句)
异步代码(例如 setTimeout 或 requestAnimationFrame 回调函数)
服务端渲染(因为触发更新只能在客户端进行,不能在serve端进行)
它自身抛出来的错误(因为错误抛出要向父节点冒泡寻找 Error Boundaries 处理,无法处理自身产生的错误)

什么是protal
Protal提供了一种将子节点渲染到存在于父组件以外的DOM节点的优秀的方案
1.第一个参数(child)是任何可渲染的React子元素,例如一个元素,字符串fragment
2.第二个参数(container)是一个DOM元素
什么场景下需要使用:
比如一个居中展示的dialog ,从用户感知角度,应该是一个独立的组件,通常应该显示在屏幕的最中间,现在Dialog被包在其他组件中,要用CSS的position属性控制Dialog位置,就要求从Dialog往上一直到body没有其他postion是relative的元素干扰。
Portal就是建立一个“传送门”,让Dialog这样的组件在表示层和其他组件没有任何差异,但是渲染的东西却像经过传送门一样出现在另一个地方

说一下react的生命周期

react的生命周期有四个阶段,分别是挂载阶段、更新阶段、卸载阶段、错误处理阶段
有三个生命周期方法在react17版本后会被淘汰,但现在仍然有效,分别是:UNSAFE_componentWillMount()、UNSAFE_componentWillReceiveProps(nextProps)、UNSAFE_componentWillUpdate(nextProps, nextState)
16.3中新增的两个方法:static getDerivedStateFromProps、static getDerivedStateFromError

挂载阶段的方法有:
constructor():在这个方法中初始化state及为事件处理函数绑定实例
static getDerivedStateFromProps(props, state) :在渲染前调用,让组件在props变化时更新state
render 渲染函数,创建DOM树
componentDidMount() 组件挂载完成之后调用,一般在这里请求数据

更新阶段的方法有:
static getDerivedStateFromProps(props, state) :在渲染前调用,让组件在props变化时更新state
shouldComponentUpdate(nextProps, nextState) :会对比state和props 只有返回ture才会重新渲染,返回false可以阻止渲染
render()
getSnapshotBeforeUpdate(prevProps, prevState) :能在组件更新之前从DOM中捕获一些信息
componentDidUpdate(prevProps, prevState, snapshot) 组件更新完成后调用

卸载阶段的方法有:
componentWillUnmount():在组件卸载及销毁之前直接调用, 在此方法中执行必要的清理操作,例如,清楚timer,取消网络请求等等

错误处理阶段:
static getDerivedStateFromError(error) 在渲染阶段调用,不允许执行副作用。
componentDidCatch(error, info) 在提交阶段被调用,用于记录错误,允许执行副作用。

什么是副作用

什么情况下需要写constructor 方法?
通常,在 React 中,构造函数仅用于以下两种情况:
通过给 this.state 赋值对象来初始化内部 state。
为事件处理函数绑定实例

我们为什么要写 super(props)?
在 JavaScript 中,super 指代父类的构造函数。JavaScript 强制规定,如果你想在构造函数中只用this,就必须先调用 super。

class Checkbox extends React.Component {
  constructor(props) {
    //   这时候还不能使用 `this`
    super(props);
    // ✅ 现在开始可以了
    this.state = { isOn: true };
  }
  // ...
}

那么这是否意味着你可以只写 super() 而不用 super(props)?
诚然,React 会在你的构造函数运行之后设置 this.props。但在 super 调用一直到构造函数结束之前,this.props 依然是未定义的。如果这发生在某些从构造函数中调用的函数,调试起来会更加麻烦。

React 事件中 this 指向问题
使用 bind 方法绑定 this :

class App extends React.Component<any, any> {  constructor(props: any) {    super(props);    this.clickFun = this.clickFun.bind(this);  }

将需要使用 this 的方法改写为使用箭头函数定义:

 clickFun = () => {
    console.log("React this 指向问题", this.childClickFun); // undefined
  }

回调函数中使用箭头函数:

class App extends React.Component<any, any> {
  // 省略其他代码
  clickFun() {
    console.log("React this 指向问题", this.childClickFun); // undefined
  }
  render() {
    return (
        <div onClick={() => this.clickFun()}>React this 指向问题</div>
    );
  }

react中向事件传参的方式

 <div>
            <h1>第一种:通过 bind 绑定 this 传参</h1>
            {
              List.map(item => <div onClick={this.clickFun.bind(this, item)}>按钮:{item}</div>)
          }
            <h1>第二种:通过箭头函数绑定 this 传参</h1>
            {
              List.map(item => <div onClick={() => this.clickFun(item)}>按钮:{item}</div>)
          }

state和props的区别什么?·
(1)props是一个从外部传进组件的参数,主要作为就是从父组件向子组件传递数据,它具有可读性和不变性,只能通过外部组件主动传入新的props来重新渲染子组件,否则子组件的props以及展现形式不会改变。
(2)state主要作用是用于组件保存、控制以及修改自己的状态,它只能在constructor中初始化,它算是组件的私有属性,不可通过外部访问和修改,只能通过组件内部的this.setState来修改,修改state属性会导致组件的重新渲染。
(3)区别
props 是传递给组件的(类似于函数的形参),而state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)。
props 是不可修改的,所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
state 是在组件中创建的,一般在 constructor中初始化 state。state 是多变的、可以修改,每次setState都异步更新的。

setState式同步的还是异步的,为什么?
在 React 无法控制的地方,比如原生事件,具体就是在 addEventListener 、setTimeout、setInterval 等事件中,就只能同步更新。
在 React 可以控制的地方,就为 true,比如在 React 生命周期事件和合成事件中,都会走合并操作,延迟更新的策略。
setState设计为异步,可以显著的提升性能。如果每次调用 setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,这样效率是很低的;最好的办法应该是获取到多个更新,之后进行批量更新;

React中的setState批量更新的过程是什么?
调用 setState 时,组件的 state 并不会立即改变, setState 只是把要修改的 state 放入一个队列, React 会优化真正的执行时机,并出于性能原因,会将 React 事件处理程序中的多次React 事件处理程序中的多次 setState 的状态修改合并成一次状态修改。这里对于相同属性的设置,React 只会为其保留最后一次的更新

为什么不能直接改变state?
不会引起组件的重新渲染

为什么 useState 要使用数组而不是对象
如果 useState 返回的是数组,那么使用者可以对数组中的元素命名,代码看起来也比较干净
如果 useState 返回的是对象,在解构对象的时候必须要和 useState 内部实现返回的对象同名,想要使用多次的话,必须得设置别名才能使用返回值

const [one, two, three] = foo;
// 第一次使用
const { state, setState } = useState(false);
// 第二次使用
const { state: counter, setState: setCounter } = useState(0) 

React-Router的路由有几种模式?
React-Router 支持使用 hash(对应 HashRouter)和 browser(对应 BrowserRouter) 两种路由规则
BrowserRouter它使用 HTML5 提供的 history API(pushState、replaceState 和 popstate 事件)来保持 UI 和 URL 的同步;
HashRouter使用 URL 的 hash 部分(即 window.location.hash)来保持 UI 和 URL 的同步;

什么是受控组件和非受控组件
将用户输入的表单元素<input>、<textarea>、<select>,使用setState( )进行更新,初始化就是受控组件;
非受控组件:非受控组件指的是,表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值。在非受控组件中,可以使用一个ref来从DOM获得表单值。

使用受控组件有什么缺陷?
表单元素的值都是由React组件进行管理,当有多个输入框,或者多个这种组件时,如果想同时获取到全部的值就必须每个都要编写事件处理函数,这会让代码看着很臃肿,所以为了解决这种情况,出现了非受控组件。

React如何获取组件对应的DOM元素?
①字符串格式②函数格式③createRef方法

为什么不要过度使用refs ?
因为 refs 操纵实际的 DOM,而不是虚拟的 DOM,这与 React 思维方式相矛盾。因此,虽然 refs 不应该是通过应用程序流动数据的默认方法,但是当您需要时,它们是可以从 DOM 元素读取数据的好方法。

React中可以在render访问refs吗?为什么?
不可以,render 阶段 DOM 还没有生成,无法获取 DOM。DOM 的获取需要在 pre-commit 阶段和 commit 阶段
在这里插入图片描述

同时引用这三个库react.js、react-dom.js和babel.js它们都有什么作用?
react:包含react所必须的核心代码
react-dom:react渲染在不同平台所需要的核心代码
babel:将jsx转换成React代码的工具

为什么要使用jsx?
它是一种JavaScript语法的扩展,并且它具有JavaScript的所有特性。在React的开发中,React并不是强制要求一定要使用

  1. JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
  2. 它是类型安全的,在编译过程中就能发现错误,可以有效防止xss攻击(React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击)
  3. JSX 仅仅只是 React.createElement(component, props, …children) 函数的语法糖使用 JSX编写模板更加简单快速,代码结构更加清晰;

jsx的语法规则?
定义虚拟DOM的时候 不需要写引号
标签中要混入js表达式的时候需要用 {}
在jsx中写标签的类名的时候 用className 代替class
内联样式的时候 ,需要style={{key:value}}

说说你对react-hook的理解
Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

hooks解决了什么问题?
① class 组件在组件之间复用状态逻辑很难
在class组件中实现组件复用比如 render props 和 高阶组件。但是这类方案需要重新组织你的组件结构,组件嵌套使你的代码难以理解。hook可以在不改变组件结构的情况下复用状态逻辑
② 复杂组件变得难以理解
class组件中往往有很多状态逻辑和副作用代码,每个生命周期常常包含一些不相关的逻辑。如此很容易产生 bug,并且导致逻辑不一致。
Hook 将组件中相互关联的部分拆分成更小的函数
③ 难以理解的 class
class 需要一定的学习成本,还不能忘记绑定事件处理器,这些代码非常冗余。class 也给目前的工具带来了一些问题。例如,class 不能很好的压缩,并且会使热重载出现不稳定的情况。Hook 使你在非 class 的情况下可以使用更多的 React 特性。

React Hook 的使用限制有哪些?
React Hooks 的限制主要有两条:
只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。
只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。
因为 Hooks 的设计是基于数组实现。在调用时按顺序加入数组中,如果使用循环、条件或嵌套函数很有可能导致数组取值错位,执行错误的 Hook。当然,实质上 React 的源码里不是数组,是链表。可以引入 ESLint 的 Hooks 检查插件进行预防。

组件直接通信的方式?
⽗组件向⼦组件通讯: ⽗组件可以向⼦组件通过传 props 的⽅式,向⼦组件进⾏通讯
⼦组件向⽗组件通讯: props+回调的⽅式,⽗组件向⼦组件传递props进⾏通讯,此props为作⽤域为⽗组件⾃身的函 数,⼦组件调⽤该函数,将⼦组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中
兄弟组件通信: 找到这两个兄弟节点共同的⽗节点,结合上⾯两种⽅式由⽗节点转发信息进⾏通信
跨层级通信: Context 设计⽬的是为了共享那些对于⼀个组件树⽽⾔是“全局”的数据,例如当前认证的⽤户、主题或⾸选语⾔,对于跨越多层的全局数据通过 Context 通信再适合不过
发布订阅模式: 发布者发布事件,订阅者监听事件并做出反应,我们可以通过引⼊event模块进⾏通信
全局状态管理⼯具: 借助Redux或者Mobx等全局状态管理⼯具进⾏通信,这种⼯具会维护⼀个全局状态中⼼Store,并根据不同的事件产⽣新的状态

说说你对context的理解?
当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递

为什么不优先推荐使用? 总结的原因主要包括以下两点:
①Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。
②Context目前还在更新迭代中,后期还会版本迭代。

哪些方法会触发 React 重新渲染?
①setState()方法被调用,只要组件的state发生变化,React就会对组件进行重新渲染。这是因为React中的
shouldComponentUpdate方法默认返回true,这就是导致每次更新都重新渲染的原因。当 setState 传入 null 时,并不会触发 render。
②只要父组件重新渲染了,即使传入子组件的 props 未发生变化,那么子组件也会重新渲染,进而触发 render

重新渲染 render 会做些什么?
①会对新旧 VNode 进行对比,也就是我们所说的Diff算法。
②对新旧两棵树进行一个深度优先遍历,这样每一个节点都会一个标记,在到深度遍历的时候,每遍历到一和个节点,就把该节点和新的节点树进行对比,如果有差异就放到一个对象里面
③遍历差异对象,根据差异的类型,根据对应对规则更新VNode

在React中如何避免不必要的render?
1、shouldComponentUpdate:通过shouldComponentUpdate生命周期函数来比对 state和 props,确定是否要重新渲染
默认情况下返回true表示重新渲染,如果不希望组件重新渲染,返回 false
2、PureComponent:PureComponent会对props和state进行浅比较,跳过不必要的更新,提高组件性能。
class Foo extends PureComponent
3、memo:React.memo用来缓存组件的渲染,避免不必要的更新,其实也是一个高阶组件,与 PureComponent 十分类似。但不同的是, React.memo 只能用于函数组件

import { memo } from 'react';
const Child = memo(() => {
    consolo.log("我被打印了就说明子组件重新构建了")
    return <div><div>
})

如果需要深层次比较,这时候可以给memo第二个参数传递比较函数
function arePropsEqual(prevProps, nextProps) {
  // your code
  return prevProps === nextProps;
}

export default memo(Button, arePropsEqual);

4、useMemo:在useMemo函数内通过复杂计算获取当前值得时候,不需要再父组件每次更新的时候重新计算,只要在依赖项发生变化的时候计算即可

 const timeOption = useMemo(() => {
    return { clickTimeCount }
  }, [clickTimeCount])

5、useCallback: 在往子组件传入了一个函数并且子组件被React.momo缓存了的时候使用seCallback,父组件本身状态变化不不会导致子组件的重新渲染;

为什么使用了memo还是会引发子组件不必要的渲染呢
React.memo检测的是props中数据的栈地址是否改变。而父组件重新构建的时候,会重新构建父组件中的所有函数(旧函数销毁,新函数创建,等于更新了函数地址),新的函数地址传入到子组件中被props检测到栈地址更新。也就引发了子组件的重新渲染。
这个时候就需要使用useCallBack,需要配合React.memo使用

const Child = memo(function ({ val, onChange }) {
  console.log("render...");
  return <input value={val} onChange={onChange} />;
});
const App = () => {
  const [val1, setVal1] = useState("");
  const [val2, setVal2] = useState("");
  // 每次父组件渲染,返回的是不同的函数引用
  const onChange1 = useCallback((evt) => {
    setVal1(evt.target.value);
     //没有使用useCallback,父组件本身状态改变以及其他子组件改变,都会引起重新渲染
  }, []);
  const onChange2 = useCallback((evt) => {
    setVal2(evt.target.value);
  }, []);
  return (
    <>
      <Child val={val1} onChange={onChange1} />
      <Child val={val2} onChange={onChange2} />
    </>
  );
};

React声明组件有哪几种方法,有什么不同?
第一种:函数式定义的无状态组件

function HelloComponent(props, /* context */) {
return <div>Hello {props.name}</div>
}
ReactDOM.render(<HelloComponent name="Sebastian" />, mountNode)

第二种:React.createClassES5的原生的JavaScript来实现的React组件

var InputControlES5 = React.createClass({
    propTypes: {//定义传入props中的属性各种类型
        initialValue: React.PropTypes.string
    },

第三种:React.Component是以ES6的形式来创建react的组件的

class InputControlES6 extends React.Component {
    constructor(props) {
        super(props);

        // 设置 initial state
        this.state = {
            text: props.initialValue || 'placeholder'
        };

React.Component 和 React.PureComponent 的区别
React.PureComponent 会调用 shouldComponentUpdate对组件的props和state进行浅对比,如果组件的props或者state都没有改变,render函数就不会触发,省去虚拟DOM的生成和对比过程,达到提升性能的目的。
而 React.Component 可以手动的通过在shouldComponentUpdate生命周期函数中执行return false来阻止页面的更新

class B extends PureComponent {

React的严格模式如何使用,有什么用处?

return (
    <div>
      <Header />
      <React.StrictMode>        
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>      
      <Footer />
    </div>
  );

StrictMode 目前有助于:
识别不安全的生命周期
关于使用过时字符串 ref API 的警告
关于使用废弃的 findDOMNode 方法的警告
检测意外的副作用
检测过时的 context API
什么是合成事件,为什么要合成?
React 合成事件(SyntheticEvent)是 React 模拟原生 DOM 事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器。比如 click事件合成为onClick事件。比如blur , change , input , keydown , keyup等 , 合成为onChange。
实现合成事件的目的如下:
①合成事件首先抹平了浏览器之间的兼容问题,(事件处理程序将传递 SyntheticEvent 的实例,这是一个跨浏览器原生事件包装器。 它具备与浏览器原生事件相同的接口,在全部浏览器中他们工做方式都相同),赋予了跨浏览器开发的能力;
②对于原生事件来说,浏览器会监听事件是否被触发,当事件触发时会创建一个事件对象,当多个事件被触发时就会创建多个事件对象,这样存在内部分配的问题。对于合成事件来说,有一个专门事件池来管理事件的创建和销毁,当需要使用事件时,就会在事件池中复用对象,事件回调结束后,再销毁事件对象上的属性,以便于下次再复用对象。这样做就不会为每个事件都创建一个事件对象,减少了内存的消耗,提升性能。
③方便 react 统一管理和事务机制。

react 的合成事件主要做了哪些事?
在React底层,主要对合成事件做了两件事:
①事件委派: React会把所有的事件绑定到结构的最外层,使用统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部事件监听和处理函数。
②自动绑定:自动绑定每个方法的上下文this为当前组件的实例。

原生事件和合成事件有什么区别?

① 写法不同。原生事件使用全部小写(onclick, onblur);React事件是用小驼峰如onClick
② 事件函数处理语法不同。原生事件使用字符串定义事件(οnclick=“handleClick()”);React使用函数的形式(onClick={handleClick})
③ 阻止冒泡的方式不同。原生事件可以通过return false或 event.stopPropagation()来阻止冒泡;React事件只能过event.preventDefault()

react的事件机制?
React的事件并没有绑定到具体的dom节点上,而是绑定在了document上,然后由统一的事件监听器去监听事件的触发
React在内部维护了一个映射表来记录事件与组件的事件处理函数的对应关系。当某个事件触发时,React根据映射表将时间分派给指定的事件处理函数。当一个组件挂载与卸载时,相应的事件处理函数会自动被添加到事件监听器的内部映射表中或从表中删除。这样做简化了事件处理和回收机制,效率也提升很大。

在这里插入图片描述
原生事件和合成事件的执行顺序,阻止冒泡是否有用?
浏览器事件的执行需要经过三个阶段,捕获阶段-目标元素阶段-冒泡阶段。
原生事件的执行是在目标阶段,然而合成事件的执行是在冒泡阶段,所以先执行原生事件在执行合成事件,在原生事件上阻止冒泡会影响合成事件的执行,但在合成事件中阻止冒泡只是会阻止其冒泡到window

原生事件和合成事件能不能混合使用?
react事件和原生事件最好不要混用。
原生事件中若是执行了stopPropagation方法,则会致使其余react事件失效。由于全部元素的事件将没法冒泡到document上。

react事件阻止冒泡的方式
react合成事件不能直接采用return false的方式来阻止浏览器的默认行为,而必须明确调用event.preventDefault()来阻止默认行为。
不能使用event.stopPropagation()来阻止React事件冒泡,react在本身的合成事件中重写了stopPropagation方法,将isPropagationStopped设置为true,而后在遍历每一级事件的过程当中根据此遍历判断是否继续执行。这就是react本身实现的冒泡机制。必须调用event.preventDefault()来阻止React事件冒泡。

高阶组件HOC 是什么
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。

render() {
    const { extraProp, ...passThroughProps } = this.props;
    const injectedProp = someStateOrInstanceMethod;
    return (
        <WrappedComponent
            injectedProp={injectedProp}
            {...passThroughProps}
        />
    );
}

优点
通过传递props去影响内层组件的状态,不直接改变内层组件的状态,降低了耦合度
缺点
组件多层嵌套, 增加复杂度与理解成本
hoc传递给被包裹组件的props容易和被包裹后的组件重名,进而被覆盖
不清楚props来源与哪个高阶组件

什么是render props
是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术,更具体的说,render prop 是一个用于告知组件需要渲染什么内容的函数 prop。具有render prop 的组件接受一个返回React元素的函数,将render的渲染逻辑注入到组件内部。在这里,"render"的命名可以是任何其他有效的标识符。

// DataProvider组件内部的渲染逻辑如下
class DataProvider extends React.Components {
     state = {
    name: 'Tom'
  }

    render() {
    return (
        <div>
          <p>共享数据组件自己内部的渲染逻辑</p>
          { this.props.render(this.state) }
      </div>
    );
  }
}

// 调用方式
<DataProvider render={data => (
  <h1>Hello {data.name}</h1>
)}/>

由此可以看到,render props的优缺点也很明显∶
优点:数据共享、代码复用,将组件内的state作为props传递给调用者,将渲染逻辑交给调用者。
缺点:无法在 return 语句外访问数据、嵌套写法不够优

函数式组件的hook的优点如下∶
使用直观;
解决hoc的prop 重名问题;
解决render props 因共享数据 而出现嵌套地狱的问题;
能在return之外使用数据的问题。
需要注意的是:hook只能在组件顶层使用,不可在分支语句中使用。

对SSR的理解
服务端渲染是指由服务端将数据与模版组成html,即 HTML = 数据 + 模版。再发送到浏览器浏览器直接显示;页面没使用服务渲染,当请求页面时,返回的body里为空,之后执行js将html结构注入到body里,结合css显示出来;
好处:
1)更利于SEO
不同爬虫工作原理类似,只会爬取源码,不会执行网站的任何脚本,使用了React或者其它MVVM框架之后,页面大多数DOM元素都是在客户端根据js动态生成,可供爬虫抓取分析的内容大大减少。另外,浏览器爬虫不会等待我们的数据完成之后再去抓取页面数据。服务端渲染返回给客户端的是已经获取了异步数据并执行JavaScript脚本的最终HTML,网络爬中就可以抓取到完整页面的信息。
2)更利于首屏渲染
首屏的渲染是node发送过来的html字符串,并不依赖于js文件了,这就会使用户更快的看到页面的内容。尤其是针对大型单页应用,打包后文件体积比较大,普通客户端渲染加载所有所需文件时间较长,首页就会有一个很长的白屏等待时间。服务端渲染是先向后端服务器请求数据,然后生成完整首屏 html返回给浏览器;而客户端渲染是等js代码下载、加载、解析完成后再请求数据渲染,等待的过程页面是什么都没有的,就是用户看到的白屏。就是服务端渲染不需要等待js代码下载完成并请求数据,就可以返回一个已有完整数据的首屏页面。
SSR的局限:
1)服务端压力较大
本来是通过客户端完成渲染,现在统一到服务端node服务去做。尤其是高并发访问的情况,会大量占用服务端CPU资源;
2)开发条件受限
在服务端渲染中,只会执行到componentDidMount之前的生命周期钩子,因此项目引用的第三方的库也不可用其它生命周期钩子,这对引用库的选择产生了很大的限制;
3)学习成本相对较高除了对webpack、MVVM框架要熟悉,还需要掌握node、 Koa2等相关技术。相对于客户端渲染,项目构建、部署过程更加复杂。

函数式组件和类组件的区别?
1、设计思想不同
函数式组件是函数式编程思想,而类组件是面向对象编程思想。面向对象编程将属性和方法封装起来,屏蔽很多细节,不利于测试
2、状态管理
因为函数组件是一个纯函数,你不能在组件中使用setState(),这也是为什么把函数组件称作为无状态组件。
如果你需要在你的组件中使用state,你可以选择创建一个类组件或者将state提升到你的父组件中,然后通过props对象传递到子组件。
3、生命周期钩子
你不能在函数组件中使用生命周期钩子,原因和不能使用state一样,所有的生命周期钩子都来自于继承的React.Component中。
因此,如果你想使用生命周期钩子,那么需要使用类组件。
4、调用方式 函数式组件可以直接调用,返回一个新的React元素;类组件调用时需要去继承React.Component并且创建render函数返回react元素

虚拟dom的原理是什么呢?
Virtual DOM 是一个轻量级的 JavaScript 对象,它最初只是 real DOM 的副本。它是一个节点树,它将元素、它们的属性和内容作为对象及其属性。 React 的渲染函数从 React 组件中创建一个节点树。然后它响应数据模型中的变化来更新该树,该变化是由用户或系统完成的各种动作引起的。
1.每当底层数据发生改变时,整个 UI 都将在 Virtual DOM 描述中重新渲染。
2.然后计算之前 DOM 表示与新表示的之间的差异。
3.完成计算后,将只用实际更改的内容更新 real DOM。

虚拟DOM的优缺点
优点:
保证性能下限: 虚拟DOM可以经过diff找出最小差异,然后批量进行patch,这种操作虽然比不上手动优化,但是比起粗暴的DOM操作性能要好很多,因此虚拟DOM可以保证性能下限
无需手动操作DOM: 虚拟DOM的diff和patch都是在一次更新中自动进行的,我们无需手动操作DOM,极大提高开发效率
跨平台: 虚拟DOM本质上是JavaScript对象,而DOM与平台强相关,相比之下虚拟DOM可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等等
缺点:
1.无法进行极致优化:虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
2.首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,会比 innerHTML 插入慢。

虚拟dom和真实dom的区别?
1、真实dom可以直接更新 HTML,操作代价很高,虚拟DOM 无法直接更新 HTML,操作非常简单
3、真实dom修改都会触发重回和重排消耗的内存较多,虚拟dom进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分,最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值