关于react.js:社招前端react面试题整理

41次阅读

共计 15268 个字符,预计需要花费 39 分钟才能阅读完成。

什么是上下文 Context

Context 通过组件树提供了一个传递数据的办法,从而防止了在每一个层级手动的传递 props 属性。

  • 用法:在父组件上定义 getChildContext 办法,返回一个对象,而后它的子组件就能够通过 this.context 属性来获取
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
class Header extends Component{render() {
        return (
            <div>
                <Title/>
            </div>
        )
    }
}
class Title extends Component{
    static contextTypes={color:PropTypes.string}
    render() {
        return (<div style={{color:this.context.color}}>
                Title
            </div>
        )
    }
}
class Main extends Component{render() {
        return (
            <div>
                <Content>
                </Content>
            </div>
        )
    }
}
class Content extends Component{
    static contextTypes={
        color: PropTypes.string,
        changeColor:PropTypes.func
    }
    render() {
        return (<div style={{color:this.context.color}}>
                Content
                <button onClick={()=>this.context.changeColor('green')}> 绿色 </button>
                <button onClick={()=>this.context.changeColor('orange')}> 橙色 </button>
            </div>
        )
    }
}
class Page extends Component{constructor() {super();
        this.state={color:'red'};
    }
    static childContextTypes={
        color: PropTypes.string,
        changeColor:PropTypes.func
    }
    getChildContext() {
        return {
            color: this.state.color,
            changeColor:(color)=>{this.setState({color})
            }
        }
    }
    render() {
        return (
            <div>
                <Header/>
                <Main/>
            </div>
        )
    }
}
ReactDOM.render(<Page/>,document.querySelector('#root'));

React-Router 4 的 Switch 有什么用?

Switch 通常被用来包裹 Route,用于渲染与门路匹配的第一个子 <Route><Redirect>,它外面不能放其余元素。

如果不加 <Switch>

import {Route} from 'react-router-dom'

<Route path="/" component={Home}></Route>
<Route path="/login" component={Login}></Route>

Route 组件的 path 属性用于匹配门路,因为须要匹配 /Home,匹配 /loginLogin,所以须要两个 Route,然而不能这么写。这样写的话,当 URL 的 path 为“/login”时,<Route path="/" /><Route path="/login" /> 都会被匹配,因而页面会展现 Home 和 Login 两个组件。这时就须要借助 <Switch> 来做到只显示一个匹配组件:

import {Switch, Route} from 'react-router-dom'

<Switch>
    <Route path="/" component={Home}></Route>
    <Route path="/login" component={Login}></Route>
</Switch>

此时,再拜访“/login”门路时,却只显示了 Home 组件。这是就用到了 exact 属性,它的作用就是准确匹配门路,常常与<Switch> 联结应用。只有当 URL 和该 <Route> 的 path 属性完全一致的状况下能力匹配上:

import {Switch, Route} from 'react-router-dom'

<Switch>
   <Route exact path="/" component={Home}></Route>
   <Route exact path="/login" component={Login}></Route>
</Switch>

React 中 keys 的作用是什么?

Keys 是 React 用于追踪哪些列表中元素被批改、被增加或者被移除的辅助标识。

在 React 中渲染汇合时,向每个反复的元素增加关键字对于帮忙 React 跟踪元素与数据之间的关联十分重要。key 应该是惟一 ID,最好是 UUID 或收集项中的其余惟一字符串:

<ul>
  {todos.map((todo) =>
    <li key={todo.id}>
      {todo.text}
    </li>
  )};
</ul>

在汇合中增加和删除我的项目时,不应用键或将索引用作键会导致奇怪的行为。

为什么虚构 dom 会进步性能

虚构 dom 相当于在 js 和实在 dom 两头加了一个缓存,利用 dom diff 算法防止了没有必要的 dom 操作,从而进步性能

具体实现步骤如下

  • JavaScript 对象构造示意 DOM 树的构造;而后用这个树构建一个真正的 DOM 树,插到文档当中
  • 当状态变更的时候,从新结构一棵新的对象树。而后用新的树和旧的树进行比拟,记录两棵树差别
  • 把 2 所记录的差别利用到步骤 1 所构建的真正的 DOM 树上,视图就更新

虚构 DOM 肯定会进步性能吗?

很多人认为虚构 DOM 肯定会进步性能,肯定会更快,其实这个说法有点全面,因为虚构 DOM 尽管会缩小 DOM 操作,但也无奈防止 DOM 操作

  • 它的劣势是在于 diff 算法和批量解决策略, 将所有的 DOM 操作收集起来,一次性去扭转实在的 DOM, 但在首次渲染上,虚构 DOM 会多了一层计算,耗费一些性能,所以有可能会比 html 渲染的要慢
  • 留神,虚构 DOM 实际上是给咱们找了一条最短,最近的门路,并不是说比 DOM 操作的更快,而是门路最简略

当渲染一个列表时,何为 key?设置 key 的目标是什么

Keys 会有助于 React 辨认哪些 items 扭转了,被增加了或者被移除了。Keys 应该被赋予数组内的元素以赋予 (DOM) 元素一个稳固的标识,抉择一个 key 的最佳办法是应用一个字符串,该字符串能惟一地标识一个列表项。很多时候你会应用数据中的 IDs 作为 keys,当你没有稳固的 IDs 用于被渲染的 items 时,能够应用我的项目索引作为渲染项的 key,但这种形式并不举荐,如果 items 能够从新排序,就会导致 re-render 变慢。

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 封装的状态管理工具。

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

能够应用 TypeScript 写 React 利用吗?怎么操作?

(1)如果还未创立 Create React App 我的项目

  • 间接创立一个具备 typescript 的 Create React App 我的项目:
 npx create-react-app demo --typescript

(2)如果曾经创立了 Create React App 我的项目,须要将 typescript 引入到已有我的项目中

  • 通过命令将 typescript 引入我的项目:
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
  • 将我的项目中任何 后缀名为‘.js’的 JavaScript 文件重命名为 TypeScript 文件即后缀名为‘.tsx’(例如 src/index.js 重命名为 src/index.tsx)

React setState 调用的原理

具体的执行过程如下(源码级解析):

  • 首先调用了setState 入口函数,入口函数在这里就是充当一个散发器的角色,依据入参的不同,将其散发到不同的性能函数中去;
ReactComponent.prototype.setState = function (partialState, callback) {this.updater.enqueueSetState(this, partialState);
  if (callback) {this.updater.enqueueCallback(this, callback, 'setState');
  }
};
  • enqueueSetState 办法将新的 state 放进组件的状态队列里,并调用 enqueueUpdate 来解决将要更新的实例对象;
enqueueSetState: function (publicInstance, partialState) {
  // 依据 this 拿到对应的组件实例
  var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
  // 这个 queue 对应的就是一个组件实例的 state 数组
  var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
  queue.push(partialState);
  //  enqueueUpdate 用来解决以后的组件实例
  enqueueUpdate(internalInstance);
}
  • enqueueUpdate 办法中引出了一个要害的对象——batchingStrategy,该对象所具备的isBatchingUpdates 属性间接决定了当下是要走更新流程,还是应该排队期待;如果轮到执行,就调用 batchedUpdates 办法来间接发动更新流程。由此能够揣测,batchingStrategy 或者正是 React 外部专门用于管控批量更新的对象。
function enqueueUpdate(component) {ensureInjected();
  // 留神这一句是问题的要害,isBatchingUpdates 标识着以后是否处于批量创立 / 更新组件的阶段
  if (!batchingStrategy.isBatchingUpdates) {
    // 若以后没有处于批量创立 / 更新组件的阶段,则立刻更新组件
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }
  // 否则,先把组件塞入 dirtyComponents 队列里,让它“再等等”dirtyComponents.push(component);
  if (component._updateBatchNumber == null) {component._updateBatchNumber = updateBatchNumber + 1;}
}

留神:batchingStrategy 对象能够了解为“锁管理器”。这里的“锁”,是指 React 全局惟一的 isBatchingUpdates 变量,isBatchingUpdates 的初始值是 false,意味着“以后并未进行任何批量更新操作”。每当 React 调用 batchedUpdate 去执行更新动作时,会先把这个锁给“锁上”(置为 true),表明“当初正处于批量更新过程中”。当锁被“锁上”的时候,任何须要更新的组件都只能临时进入 dirtyComponents 里排队等待下一次的批量更新,而不能随便“插队”。此处体现的“工作锁”的思维,是 React 面对大量状态依然可能实现有序分批解决的基石。

在 React 中组件的 this.state 和 setState 有什么区别?

this.state 通常是用来初始化 state 的,this.setState 是用来批改 state 值的。如果初始化了 state 之后再应用 this.state,之前的 state 会被笼罩掉,如果应用 this.setState,只会替换掉相应的 state 值。所以,如果想要批改 state 的值,就须要应用 setState,而不能间接批改 state,间接批改 state 之后页面是不会更新的。

React 中发动网络申请应该在哪个生命周期中进行?为什么?

对于异步申请,最好放在 componentDidMount 中去操作,对于同步的状态扭转,能够放在 componentWillMount 中,个别用的比拟少。

如果认为在 componentWillMount 里发动申请能提前取得后果,这种想法其实是谬误的,通常 componentWillMount 比 componentDidMount 早不了多少微秒,网络上任何一点提早,这一点差别都可忽略不计。

react 的生命周期: constructor() -> componentWillMount() -> render() -> componentDidMount()

下面这些办法的调用是有秩序的,由上而下顺次调用。

  • constructor 被调用是在组件筹备要挂载的最开始,此时组件尚未挂载到网页上。
  • componentWillMount 办法的调用在 constructor 之后,在 render 之前,在这办法里的代码调用 setState 办法不会触发从新 render,所以它个别不会用来作加载数据之用。
  • componentDidMount 办法中的代码,是在组件曾经齐全挂载到网页上才会调用被执行,所以能够保证数据的加载。此外,在这办法中调用 setState 办法,会触发从新渲染。所以,官网设计这个办法就是用来加载内部数据用的,或解决其余的副作用代码。与组件上的数据无关的加载,也能够在 constructor 里做,但 constructor 是做组件 state 初绐化工作,并不是做加载数据这工作的,constructor 里也不能 setState,还有加载的工夫太长或者出错,页面就无奈加载进去。所以有副作用的代码都会集中在 componentDidMount 办法里。

总结:

  • 跟服务器端渲染(同构)有关系,如果在 componentWillMount 外面获取数据,fetch data 会执行两次,一次在服务器端一次在客户端。在 componentDidMount 中能够解决这个问题,componentWillMount 同样也会 render 两次。
  • 在 componentWillMount 中 fetch data,数据肯定在 render 后能力达到,如果遗记了设置初始状态,用户体验不好。
  • react16.0 当前,componentWillMount 可能会被执行屡次。

constructor 为什么不先渲染?

由 ES6 的继承规定得悉,不论子类写不写 constructor,在 new 实例的过程都会给补上 constructor。

所以:constructor 钩子函数并不是不可短少的,子组件能够在一些状况略去。比方不本人的 state,从 props 中获取的状况

类组件和函数组件有何不同?

解答

在 React 16.8 版本(引入钩子)之前,应用基于类的组件来创立须要保护外部状态或利用生命周期办法的组件(即 componentDidMountshouldComponentUpdate)。基于类的组件是 ES6 类,它扩大了 React 的 Component 类,并且至多实现了 render() 办法。

类组件:

class Welcome extends React.Component {render() {return <h1>Hello, {this.props.name}</h1>;
  }
}

函数组件是无状态的(同样,小于 React 16.8 版本),并返回要出现的输入。它们渲染 UI 的首选只依赖于属性,因为它们比基于类的组件更简略、更具性能。

函数组件:

function Welcome(props) {return <h1>Hello, {props.name}</h1>;
}

留神:在 React 16.8 版本中引入钩子意味着这些区别不再实用(请参阅 14 和 15 题)。

setState 是同步异步?为什么?实现原理?

1. setState 是同步执行的

setState 是同步执行的,然而 state 并不一定会同步更新

2. setState 在 React 生命周期和合成事件中批量笼罩执行

在 React 的生命周期钩子和合成事件中,屡次执行 setState,会批量执行

具体表现为,屡次同步执行的 setState,会进行合并,相似于 Object.assign,雷同的 key,前面的会笼罩后面的

当遇到多个 setState 调用时候,会提取单次传递 setState 的对象,把他们合并在一起造成一个新的
繁多对象,并用这个繁多的对象去做 setState 的事件,就像 Object.assign 的对象合并,后一个
key 值会笼罩后面的 key 值

通过 React 解决的事件是不会同步更新 this.state 的. 通过 addEventListener || setTimeout/setInterval 的形式解决的则会同步更新。
为了合并 setState,咱们须要一个队列来保留每次 setState 的数据,而后在一段时间后执行合并操作和更新 state,并清空这个队列,而后渲染组件。

React 高阶组件是什么,和一般组件有什么区别,实用什么场景

官网解释∶

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。

高阶组件(HOC)就是一个函数,且该函数承受一个组件作为参数,并返回一个新的组件,它只是一种组件的设计模式,这种设计模式是由 react 本身的组合性质必然产生的。咱们将它们称为纯组件,因为它们能够承受任何动静提供的子组件,但它们不会批改或复制其输出组件中的任何行为。

// hoc 的定义
function withSubscription(WrappedComponent, selectData) {
  return class extends React.Component {constructor(props) {super(props);
      this.state = {data: selectData(DataSource, props)
      };
    }
    // 一些通用的逻辑解决
    render() {
      // ... 并应用新数据渲染被包装的组件!
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };

// 应用
const BlogPostWithSubscription = withSubscription(BlogPost,
  (DataSource, props) => DataSource.getBlogPost(props.id));

1)HOC 的优缺点

  • 长处∶ 逻辑服用、不影响被包裹组件的外部逻辑。
  • 毛病∶hoc 传递给被包裹组件的 props 容易和被包裹后的组件重名,进而被笼罩

2)实用场景

  • 代码复用,逻辑形象
  • 渲染劫持
  • State 形象和更改
  • Props 更改

3)具体利用例子

  • 权限管制: 利用高阶组件的 条件渲染 个性能够对页面进行权限管制,权限管制个别分为两个维度:页面级别和 页面元素级别
// HOC.js
function withAdminAuth(WrappedComponent) {
    return class extends React.Component {
        state = {isAdmin: false,}
        async UNSAFE_componentWillMount() {const currentRole = await getCurrentUserRole();
            this.setState({isAdmin: currentRole === 'Admin',});
        }
        render() {if (this.state.isAdmin) {return <WrappedComponent {...this.props} />;
            } else {return (<div> 您没有权限查看该页面,请分割管理员!</div>);
            }
        }
    };
}

// pages/page-a.js
class PageA extends React.Component {constructor(props) {super(props);
        // something here...
    }
    UNSAFE_componentWillMount() {// fetching data}
    render() {// render page with data}
}
export default withAdminAuth(PageA);


// pages/page-b.js
class PageB extends React.Component {constructor(props) {super(props);
    // something here...
        }
    UNSAFE_componentWillMount() {// fetching data}
    render() {// render page with data}
}
export default withAdminAuth(PageB);
  • 组件渲染性能追踪: 借助父组件子组件生命周期规定捕捉子组件的生命周期,能够不便的对某个组件的渲染工夫进行记录∶
class Home extends React.Component {render() {return (<h1>Hello World.</h1>);
        }
    }
    function withTiming(WrappedComponent) {
        return class extends WrappedComponent {constructor(props) {super(props);
                this.start = 0;
                this.end = 0;
            }
            UNSAFE_componentWillMount() {super.componentWillMount && super.componentWillMount();
                this.start = Date.now();}
            componentDidMount() {super.componentDidMount && super.componentDidMount();
                this.end = Date.now();
                console.log(`${WrappedComponent.name} 组件渲染工夫为 ${this.end - this.start} ms`);
            }
            render() {return super.render();
            }
        };
    }

    export default withTiming(Home);   


留神:withTiming 是利用 反向继承 实现的一个高阶组件,性能是计算被包裹组件(这里是 Home 组件)的渲染工夫。

  • 页面复用
const withFetching = fetching => WrappedComponent => {
    return class extends React.Component {
        state = {data: [],
        }
        async UNSAFE_componentWillMount() {const data = await fetching();
            this.setState({data,});
        }
        render() {return <WrappedComponent data={this.state.data} {...this.props} />;
        }
    }
}

// pages/page-a.js
export default withFetching(fetching('science-fiction'))(MovieList);
// pages/page-b.js
export default withFetching(fetching('action'))(MovieList);
// pages/page-other.js
export default withFetching(fetching('some-other-type'))(MovieList);

React Hooks 和生命周期的关系?

函数组件 的实质是函数,没有 state 的概念的,因而 不存在生命周期 一说,仅仅是一个 render 函数 而已。
然而引入 Hooks 之后就变得不同了,它能让组件在不应用 class 的状况下领有 state,所以就有了生命周期的概念,所谓的生命周期其实就是 useStateuseEffect()useLayoutEffect()

即:Hooks 组件(应用了 Hooks 的函数组件)有生命周期,而函数组件(未应用 Hooks 的函数组件)是没有生命周期的

上面是具体的 class 与 Hooks 的 生命周期对应关系

  • constructor:函数组件不须要构造函数,能够通过调用 **useState 来初始化 state**。如果计算的代价比拟低廉,也能够传一个函数给 useState
const [num, UpdateNum] = useState(0)
  • getDerivedStateFromProps:个别状况下,咱们不须要应用它,能够在 渲染过程中更新 state,以达到实现 getDerivedStateFromProps 的目标。
function ScrollView({row}) {let [isScrollingDown, setIsScrollingDown] = useState(false);
  let [prevRow, setPrevRow] = useState(null);
  if (row !== prevRow) {
    // Row 自上次渲染以来产生过扭转。更新 isScrollingDown。setIsScrollingDown(prevRow !== null && row > prevRow);
    setPrevRow(row);
  }
  return `Scrolling down: ${isScrollingDown}`;
}

React 会立刻退出第一次渲染并用更新后的 state 从新运行组件以防止消耗太多性能。

  • shouldComponentUpdate:能够用 **React.memo** 包裹一个组件来对它的 props 进行浅比拟
const Button = React.memo((props) => {// 具体的组件});

留神:**React.memo 等效于 **`PureComponent,它只浅比拟 props。这里也能够应用 useMemo` 优化每一个节点。

  • render:这是函数组件体自身。
  • componentDidMount, componentDidUpdateuseLayoutEffect 与它们两的调用阶段是一样的。然而,咱们举荐你 一开始先用 useEffect,只有当它出问题的时候再尝试应用 useLayoutEffectuseEffect 能够表白所有这些的组合。
// componentDidMount
useEffect(()=>{// 须要在 componentDidMount 执行的内容}, [])
useEffect(() => { 
  // 在 componentDidMount,以及 count 更改时 componentDidUpdate 执行的内容
  document.title = `You clicked ${count} times`; 
  return () => {
    // 须要在 count 更改时 componentDidUpdate(先于 document.title = ... 执行,恪守先清理后更新)// 以及 componentWillUnmount 执行的内容       
  } // 当函数中 Cleanup 函数会依照在代码中定义的程序先后执行,与函数自身的个性无关
}, [count]); // 仅在 count 更改时更新

请记得 React 会期待浏览器实现画面渲染之后才会提早调用,因而会使得额定操作很不便

  • componentWillUnmount:相当于 useEffect 外面返回的 cleanup 函数
// componentDidMount/componentWillUnmount
useEffect(()=>{
  // 须要在 componentDidMount 执行的内容
  return function cleanup() {// 须要在 componentWillUnmount 执行的内容}
}, [])
  • componentDidCatch and getDerivedStateFromError:目前 还没有 这些办法的 Hook 等价写法,但很快会加上。
class 组件 Hooks 组件
constructor useState
getDerivedStateFromProps useState 外面 update 函数
shouldComponentUpdate useMemo
render 函数自身
componentDidMount useEffect
componentDidUpdate useEffect
componentWillUnmount useEffect 外面返回的函数
componentDidCatch
getDerivedStateFromError

React 的工作原理

React 会创立一个虚构 DOM(virtual DOM)。当一个组件中的状态扭转时,React 首先会通过 “diffing” 算法来标记虚构 DOM 中的扭转,第二步是调节(reconciliation),会用 diff 的后果来更新 DOM。

React 中的 props 为什么是只读的?

this.props是组件之间沟通的一个接口,原则上来讲,它只能从父组件流向子组件。React 具备浓厚的函数式编程的思维。

提到函数式编程就要提一个概念:纯函数。它有几个特点:

  • 给定雷同的输出,总是返回雷同的输入。
  • 过程没有副作用。
  • 不依赖内部状态。

this.props就是吸取了纯函数的思维。props 的不能够变性就保障的雷同的输出,页面显示的内容是一样的,并且不会产生副作用

React 事件机制

<div onClick={this.handleClick.bind(this)}> 点我 </div>

React 并不是将 click 事件绑定到了 div 的实在 DOM 上,而是在 document 处监听了所有的事件,当事件产生并且冒泡到 document 处的时候,React 将事件内容封装并交由真正的处理函数运行。这样的形式不仅仅缩小了内存的耗费,还能在组件挂在销毁时对立订阅和移除事件。

除此之外,冒泡到 document 上的事件也不是原生的浏览器事件,而是由 react 本人实现的合成事件(SyntheticEvent)。因而如果不想要是事件冒泡的话应该调用 event.preventDefault()办法,而不是调用 event.stopProppagation()办法。JSX 上写的事件并没有绑定在对应的实在 DOM 上,而是通过事件代理的形式,将所有的事件都对立绑定在了 document 上。这样的形式不仅缩小了内存耗费,还能在组件挂载销毁时对立订阅和移除事件。

另外冒泡到 document 上的事件也不是原生浏览器事件,而是 React 本人实现的合成事件(SyntheticEvent)。因而咱们如果不想要事件冒泡的话,调用 event.stopPropagation 是有效的,而应该调用 event.preventDefault

实现合成事件的目标如下:

  • 合成事件首先抹平了浏览器之间的兼容问题,另外这是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力;
  • 对于原生浏览器事件来说,浏览器会给监听器创立一个事件对象。如果你有很多的事件监听,那么就须要调配很多的事件对象,造成高额的内存调配问题。然而对于合成事件来说,有一个事件池专门来治理它们的创立和销毁,当事件须要被应用时,就会从池子中复用对象,事件回调完结后,就会销毁事件对象上的属性,从而便于下次复用事件对象。

hooks 父子传值

父传子
在父组件中用 useState 申明数据
 const [data, setData] = useState(false)

把数据传递给子组件
<Child data={data} />

子组件接管
export default function (props) {const { data} = props
    console.log(data)
}
子传父
子传父能够通过事件办法传值,和父传子有点相似。在父组件中用 useState 申明数据
 const [data, setData] = useState(false)

把更新数据的函数传递给子组件
<Child setData={setData} />

子组件中触发函数更新数据,就会间接传递给父组件
export default function (props) {const { setData} = props
    setData(true)
}
如果存在多个层级的数据传递,也可按照此办法顺次传递

// 多层级用 useContext
const User = () => {
 // 间接获取,不必回调
 const {user, setUser} = useContext(UserContext);
 return <Avatar user={user} setUser={setUser} />;
};

高阶组件存在的问题

  • 静态方法失落(必须将静态方法做拷贝)
  • refs 属性不能透传 (如果你向一个由高阶组件创立的组件的元素增加ref 援用,那么 ref 指向的是最外层容器组件实例的,而不是被包裹的 WrappedComponent 组件。)
  • 反向继承不能保障残缺的子组件树被解析
    React 组件有两种模式,别离是 class 类型和 function 类型(无状态组件)。

咱们晓得反向继承的渲染劫持能够管制 WrappedComponent 的渲染过程,也就是说这个过程中咱们能够对 elements treestatepropsrender() 的后果做各种操作。

然而如果渲染 elements tree 中蕴含了 function 类型的组件的话,这时候就不能操作组件的子组件了。

正文完
 0