什么是JSX

jsx 是 javascriptML的简写,是react应用的一种文件,它利用 JavaScript 的表现力和相似 HTML 的模板语法,这使得 HTML 文件非常容易了解。此文件能使利用十分牢靠,并可能进步其性能
jsx的语法规定

  • 定义虚构DOM的时候 不须要写引号
  • 标签中要混入js表达式的时候须要用 {}
  • 在jsx中写标签的类名的时候 用className 代替class
  • 内联款式的时候 ,须要style={{key:value}}
  • 标签必须要闭合
  • 标签首字母的约定

    若为小写字母,则将jsx转换为html中同名元素,若html中无该标签明对应的同名元素 则报错

    若为大写字母,react就去渲染对应的组件,若没有定义组件 则报错

  • 当依据数据遍历生成的标签,肯定要给标签设置独自的key 否则会报错

什么起因会促使你脱离 create-react-app 的依赖

当你想去配置 webpack 或 babel presets。

跨级组件的通信形式?

父组件向子组件的子组件通信,向更深层子组件通信:

  • 应用props,利用两头组件层层传递,然而如果父组件构造较深,那么两头每一层组件都要去传递props,减少了复杂度,并且这些props并不是两头组件本人须要的。
  • 应用context,context相当于一个大容器,能够把要通信的内容放在这个容器中,这样不论嵌套多深,都能够随便取用,对于逾越多层的全局数据能够应用context实现。
// context形式实现跨级组件通信 // Context 设计目标是为了共享那些对于一个组件树而言是“全局”的数据const BatteryContext = createContext();//  子组件的子组件 class GrandChild extends Component {    render(){        return (            <BatteryContext.Consumer>                {                    color => <h1 style={{"color":color}}>我是红色的:{color}</h1>                }            </BatteryContext.Consumer>        )    }}//  子组件const Child = () =>{    return (        <GrandChild/>    )}// 父组件class Parent extends Component {      state = {          color:"red"      }      render(){          const {color} = this.state          return (          <BatteryContext.Provider value={color}>              <Child></Child>          </BatteryContext.Provider>          )      }}

什么是状态晋升

应用 react 常常会遇到几个组件须要共用状态数据的状况。这种状况下,咱们最好将这部分共享的状态晋升至他们最近的父组件当中进行治理。咱们来看一下具体如何操作吧。

import React from 'react'class Child_1 extends React.Component{    constructor(props){        super(props)    }    render(){        return (            <div>                <h1>{this.props.value+2}</h1>            </div>         )    }}class Child_2 extends React.Component{    constructor(props){        super(props)    }    render(){        return (            <div>                <h1>{this.props.value+1}</h1>            </div>         )    }}class Three extends React.Component {    constructor(props){        super(props)        this.state = {            txt:"牛逼"        }        this.handleChange = this.handleChange.bind(this)    }    handleChange(e){        this.setState({            txt:e.target.value        })    }    render(){       return (            <div>                <input type="text" value={this.state.txt} onChange={this.handleChange}/>                <p>{this.state.txt}</p>                <Child_1 value={this.state.txt}/>                <Child_2 value={this.state.txt}/>            </div>       )    }}export default Three

当调用 setState的时候,产生了什么操作?**

当调用 setState时, React做的第一件事是将传递给setState的对象合并到组件的以后状态,这将启动一个称为和解( reconciliation)的过程。
和解的最终目标是,依据这个新的状态以最无效的形式更新DOM。
为此, React将构建一个新的 React虚构DOM树(能够将其视为页面DOM元素的对象示意形式)。
一旦有了这个DOM树,为了弄清DOM是如何响应新的状态而扭转的, React会将这个新树与上一个虚构DOM树比拟。
这样做, React会晓得产生的确切变动,并且通过理解产生的变动后,在相对必要的状况下进行更新DOM,即可将因操作DOM而占用的空间最小化。

react中的Portal是什么?

Portals 提供了一种很好的将子节点渲染到父组件以外的 DOM 节点的形式。
第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或碎片。
第二个参数(container)则是一个 DOM 元素。

ReactDOM.createPortal(child, container)

参考 前端进阶面试题具体解答

React 组件中怎么做事件代理?它的原理是什么?

React基于Virtual DOM实现了一个SyntheticEvent层(合成事件层),定义的事件处理器会接管到一个合成事件对象的实例,它合乎W3C规范,且与原生的浏览器事件领有同样的接口,反对冒泡机制,所有的事件都主动绑定在最外层上。

在React底层,次要对合成事件做了两件事:

  • 事件委派: React会把所有的事件绑定到构造的最外层,应用对立的事件监听器,这个事件监听器上维持了一个映射来保留所有组件外部事件监听和处理函数。
  • 主动绑定: React组件中,每个办法的上下文都会指向该组件的实例,即主动绑定this为以后组件。

你了解“在React中,一切都是组件”这句话。

组件是 React 利用 UI 的构建块。这些组件将整个 UI 分成小的独立并可重用的局部。每个组件彼此独立,而不会影响 UI 的其余部分。

为什么应用jsx的组件中没有看到应用react却须要引入react?

实质上来说JSX是React.createElement(component, props, ...children)办法的语法糖。在React 17之前,如果应用了JSX,其实就是在应用React, babel 会把组件转换为 CreateElement 模式。在React 17之后,就不再须要引入,因为 babel 曾经能够帮咱们主动引入react。

diff算法如何比拟?

  • 只对同级比拟,跨层级的dom不会进行复用
  • 不同类型节点生成的dom树不同,此时会间接销毁老节点及子孙节点,并新建节点
  • 能够通过key来对元素diff的过程提供复用的线索
  • 单节点diff
  • 单点diff有如下几种状况:
  • key和type雷同示意能够复用节点
  • key不同间接标记删除节点,而后新建节点
  • key雷同type不同,标记删除该节点和兄弟节点,而后新创建节点

React Hooks在平时开发中须要留神的问题和起因

(1)不要在循环,条件或嵌套函数中调用Hook,必须始终在 React函数的顶层应用Hook

这是因为React须要利用调用程序来正确更新相应的状态,以及调用相应的钩子函数。一旦在循环或条件分支语句中调用Hook,就容易导致调用程序的不一致性,从而产生难以预料到的结果。

(2)应用useState时候,应用push,pop,splice等间接更改数组对象的坑

应用push间接更改数组无奈获取到新值,应该采纳析构形式,然而在class外面不会有这个问题。代码示例:

function Indicatorfilter() {  let [num,setNums] = useState([0,1,2,3])  const test = () => {    // 这里坑是间接采纳push去更新num    // setNums(num)是无奈更新num的    // 必须应用num = [...num ,1]    num.push(1)    // num = [...num ,1]    setNums(num)  }return (    <div className='filter'>      <div onClick={test}>测试</div>        <div>          {num.map((item,index) => (              <div key={index}>{item}</div>          ))}      </div>    </div>  )}class Indicatorfilter extends React.Component<any,any>{  constructor(props:any){      super(props)      this.state = {          nums:[1,2,3]      }      this.test = this.test.bind(this)  }  test(){      // class采纳同样的形式是没有问题的      this.state.nums.push(1)      this.setState({          nums: this.state.nums      })  }  render(){      let {nums} = this.state      return(          <div>              <div onClick={this.test}>测试</div>                  <div>                      {nums.map((item:any,index:number) => (                          <div key={index}>{item}</div>                      ))}                  </div>          </div>      )  }}

(3)useState设置状态的时候,只有第一次失效,前期须要更新状态,必须通过useEffect

TableDeail是一个公共组件,在调用它的父组件外面,咱们通过set扭转columns的值,认为传递给TableDeail 的 columns是最新的值,所以tabColumn每次也是最新的值,然而理论tabColumn是最开始的值,不会随着columns的更新而更新:

const TableDeail = ({    columns,}:TableData) => {    const [tabColumn, setTabColumn] = useState(columns) }// 正确的做法是通过useEffect扭转这个值const TableDeail = ({    columns,}:TableData) => {    const [tabColumn, setTabColumn] = useState(columns)     useEffect(() =>{setTabColumn(columns)},[columns])}

(4)善用useCallback

父组件传递给子组件事件句柄时,如果咱们没有任何参数变动可能会选用useMemo。然而每一次父组件渲染子组件即便没变动也会跟着渲染一次。

(5)不要滥用useContext

能够应用基于 useContext 封装的状态管理工具。

React有哪些优化性能的伎俩

类组件中的优化伎俩

  • 应用纯组件 PureComponent 作为基类。
  • 应用 React.memo 高阶函数包装组件。
  • 应用 shouldComponentUpdate 生命周期函数来自定义渲染逻辑。

办法组件中的优化伎俩

  • 应用 useMemo
  • 应用 useCallBack

其余形式

  • 在列表须要频繁变动时,应用惟一 id 作为 key,而不是数组下标。
  • 必要时通过扭转 CSS 款式暗藏显示组件,而不是通过条件判断显示暗藏组件。
  • 应用 Suspense 和 lazy 进行懒加载,例如:
import React, { lazy, Suspense } from "react";export default class CallingLazyComponents extends React.Component {  render() {    var ComponentToLazyLoad = null;    if (this.props.name == "Mayank") {      ComponentToLazyLoad = lazy(() => import("./mayankComponent"));    } else if (this.props.name == "Anshul") {      ComponentToLazyLoad = lazy(() => import("./anshulComponent"));    }    return (      <div>        <h1>This is the Base User: {this.state.name}</h1>        <Suspense fallback={<div>Loading...</div>}>          <ComponentToLazyLoad />        </Suspense>      </div>    )  }}

react旧版生命周期函数

初始化阶段

  • getDefaultProps:获取实例的默认属性
  • getInitialState:获取每个实例的初始化状态
  • componentWillMount:组件行将被装载、渲染到页面上
  • render:组件在这里生成虚构的DOM节点
  • componentDidMount:组件真正在被装载之后

运行中状态

  • componentWillReceiveProps:组件将要接管到属性的时候调用
  • shouldComponentUpdate:组件承受到新属性或者新状态的时候(能够返回false,接收数据后不更新,阻止render调用,前面的函数不会被继续执行了)
  • componentWillUpdate:组件行将更新不能批改属性和状态
  • render:组件从新描述
  • componentDidUpdate:组件曾经更新

销毁阶段

  • componentWillUnmount:组件行将销毁

高阶组件

高阶函数:如果一个函数承受一个或多个函数作为参数或者返回一个函数就可称之为高阶函数

高阶组件:如果一个函数 承受一个或多个组件作为参数并且返回一个组件 就可称之为 高阶组件

react 中的高阶组件

React 中的高阶组件次要有两种模式:属性代理反向继承

属性代理 Proxy

  • 操作 props
  • 抽离 state
  • 通过 ref 拜访到组件实例
  • 用其余元素包裹传入的组件 WrappedComponent

反向继承

会发现其属性代理和反向继承的实现有些相似的中央,都是返回一个继承了某个父类的子类,只不过属性代理中继承的是 React.Component,反向继承中继承的是传入的组件 WrappedComponent

反向继承能够用来做什么:

1.操作 state

高阶组件中能够读取、编辑和删除WrappedComponent组件实例中的state。甚至能够减少更多的state项,然而十分不倡议这么做因为这可能会导致state难以保护及治理。

function withLogging(WrappedComponent) {        return class extends WrappedComponent {            render() {                return (                    <div>;                        <h2>;Debugger Component Logging...<h2>;                        <p>;state:<p>;                        <pre>;{JSON.stringify(this.state, null, 4)}<pre>;                        <p>props:<p>;                        <pre>{JSON.stringify(this.props, null, 4)}<pre>;                        {super.render()}                    <div>;                );            }        };    }

2.渲染劫持(Render Highjacking)

条件渲染通过 props.isLoading 这个条件来判断渲染哪个组件。

批改由 render() 输入的 React 元素树

组件通信的形式有哪些

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

React中有应用过getDefaultProps吗?它有什么作用?

通过实现组件的getDefaultProps,对属性设置默认值(ES5的写法):

var ShowTitle = React.createClass({  getDefaultProps:function(){    return{      title : "React"    }  },  render : function(){    return <h1>{this.props.title}</h1>  }});

形容 Flux 与 MVC?

传统的 MVC 模式在拆散数据(Model)、UI(View和逻辑(Controller)方面工作得很好,然而 MVC 架构常常遇到两个次要问题:
数据流不够清晰:跨视图产生的级联更新经常会导致凌乱的事件网络,难于调试。
不足数据完整性:模型数据能够在任何中央产生渐变,从而在整个UI中产生不可预测的后果。
应用 Flux 模式的简单用户界面不再蒙受级联更新,任何给定的React 组件都可能依据 store 提供的数据重建其状态。Flux 模式还通过限度对共享数据的间接拜访来增强数据完整性。

什么是 prop drilling,如何防止?

在构建 React 应用程序时,在多层嵌套组件来应用另一个嵌套组件提供的数据。最简略的办法是将一个 prop 从每个组件一层层的传递上来,从源组件传递到深层嵌套组件,这叫做prop drilling
prop drilling的次要毛病是本来不须要数据的组件变得不必要地简单,并且难以保护。
为了防止prop drilling,一种罕用的办法是应用React Context。通过定义提供数据的Provider组件,并容许嵌套的组件通过Consumer组件或useContext Hook 应用上下文数据。

redux是如何更新值得

用户发动操作之后,dispatch发送action ,依据type,触发对于的reducer,reducer 就是一个纯函数,接管旧的 state 和 action,返回新的 state。通过 subscribe(listener)监听器,派发更新。

何为受控组件(controlled component)

在 HTML 中,相似 <input>, <textarea><select> 这样的表单元素会保护本身的状态,并基于用户的输出来更新。当用户提交表单时,后面提到的元素的值将随表单一起被发送。但在 React 中会有些不同,蕴含表单元素的组件将会在 state 中追踪输出的值,并且每次调用回调函数时,如 onChange 会更新 state,从新渲染组件。一个输出表单元素,它的值通过 React 的这种形式来管制,这样的元素就被称为"受控元素"。