前端面试题视频解说
react 有什么特点
- react 应用过的虚构 DOM,而不是实在 DOM
- react 能够用服务器渲染
- react 遵循单向数据流 或者数据绑定
约束性组件(controlled component)与非约束性组件(uncontrolled component)有什么区别?
在 React 中,组件负责管制和治理本人的状态。
如果将 HTML 中的表单元素(input、select、textarea 等)增加到组件中,当用户与表单产生交互时,就波及表单数据存储问题。依据表单数据的存储地位,将组件分成约東性组件和非约東性组件。
约束性组件(controlled component)就是由 React 管制的组件,也就是说,表单元素的数据存储在组件外部的状态中,表单到底出现什么由组件决定。
如下所示,username 没有存储在 DOM 元素内,而是存储在组件的状态中。每次要更新 username 时,就要调用 setState 更新状态;每次要获取 username 的值,就要获取组件状态值。
class App extends Component {
// 初始化状态
constructor(props) {super(props);
this.state = {username: "有课前端网",};
}
// 查看后果
showResult() {
// 获取数据就是获取状态值
console.log(this.state.username);
}
changeUsername(e) {
// 原生办法获取
var value = e.target.value;
// 更新前,能够进行脏值检测
// 更新状态
this.setState({username: value,});
}
// 渲染组件
render() {
// 返回虚构 DOM
return (
<div>
<p>
{/* 输入框绑定 va1ue*/}
<input type="text" onChange={this.changeUsername.bind(this)} value={this.state.username} />
</p>
<p>
<button onClick={this.showResult.bind(this)}> 查看后果 </button>
</p>
</div>
);
}
}
非约束性组件(uncontrolled component)就是指表单元素的数据交由元素本身存储并解决,而不是通过 React 组件。表单如何出现由表单元素本身决定。
如下所示,表单的值并没有存储在组件的状态中,而是存储在表单元素中,当要批改表单数据时,间接输出表单即可。有时也能够获取元素,再手动批改它的值。当要获取表单数据时,要首先获取表单元素,而后通过表单元素获取元素的值。
留神:为了不便在组件中获取表单元素,通常为元素设置 ref 属性,在组件外部通过 refs 属性获取对应的 DOM 元素。
class App extends Component {
// 查看后果
showResult() {
// 获取值
console.log(this.refs.username.value);
// 批改值,就是批改元素本身的值
this.refs.username.value = "业余前端学习平台";
// 渲染组件
// 返回虚构 DOM
return (
<div>
<p>
{/* 非约束性组件中,表单元素通过 defaultvalue 定义 */}
<input type="text" ref="username" defaultvalue="有课前端网" />
</p>
<p>
<button onClick={this.showResult.bind(this)}> 查看后果 </button>
</p>
</div>
);
}
}
尽管非约東性组件通常更容易实现,能够通过 refs 间接获取 DOM 元素,并获取其值,然而 React 倡议应用约束性组件。次要起因是,约東性组件反对即时字段验证,容许有条件地禁用 / 启用按钮,强制输出格局等。
类组件 (Class component) 和函数式组件 (Functional component) 之间有何不同
- 类组件不仅容许你应用更多额定的性能,如组件本身的状态和生命周期钩子,也能使组件间接拜访
store
并维持状态 - 当组件仅是接管
props
,并将组件本身渲染到页面时,该组件就是一个 ‘ 无状态组件 (stateless component)’,能够应用一个纯函数来创立这样的组件。这种组件也被称为哑组件(dumb components) 或展现组件
React 的生命周期办法有哪些?
componentWillMount
: 在渲染之前执行,用于根组件中的 App 级配置。componentDidMount
:在第一次渲染之后执行,能够在这里做 AJAX 申请,DOM 的操作或状态更新以及设置事件监听器。componentWillReceiveProps
:在初始化render
的时候不会执行,它会在组件承受到新的状态 (Props) 时被触发,个别用于父组件状态更新时子组件的从新渲染shouldComponentUpdate
:确定是否更新组件。默认状况下,它返回true
。如果确定在state
或props
更新后组件不须要在从新渲染,则能够返回false
,这是一个进步性能的办法。componentWillUpdate
:在shouldComponentUpdate
返回true
确定要更新组件之前件之前执行。componentDidUpdate
:它次要用于更新 DOM 以响应props
或state
更改。componentWillUnmount
:它用于勾销任何的网络申请,或删除与组件关联的所有事件监听器。
形容事件在 React 中的解决形式。
为了解决跨浏览器兼容性问题,React 中的事件处理程序将传递 SyntheticEvent 的实例,它是跨浏览器事件的包装器。这些 SyntheticEvent 与你习惯的原生事件具备雷同的接口,它们在所有浏览器中都兼容。
React 实际上并没有将事件附加到子节点自身。而是通过事件委托模式,应用单个事件监听器监听顶层的所有事件。这对于性能是有益处的。这也意味着在更新 DOM 时,React 不须要放心跟踪事件监听器。
React- Router 有几种模式?
有以下几种模式。
HashRouter,通过散列实现,路由要带 #。
BrowerRouter,利用 HTML5 中 history API 实现,须要服务器端反对,兼容性不是很好。
React-Router 的路由有几种模式?
React-Router 反对应用 hash(对应 HashRouter)和 browser(对应 BrowserRouter)两种路由规定,react-router-dom 提供了 BrowserRouter 和 HashRouter 两个组件来实现利用的 UI 和 URL 同步:
- BrowserRouter 创立的 URL 格局:xxx.com/path
- HashRouter 创立的 URL 格局:xxx.com/#/path
(1)BrowserRouter
它应用 HTML5 提供的 history API(pushState、replaceState 和 popstate 事件)来放弃 UI 和 URL 的同步。由此能够看出,BrowserRouter 是应用 HTML 5 的 history API 来管制路由跳转的:
<BrowserRouter
basename={string}
forceRefresh={bool}
getUserConfirmation={func}
keyLength={number}
/>
复制代码
其中的属性如下:
- basename 所有路由的基准 URL。basename 的正确格局是后面有一个前导斜杠,但不能有尾部斜杠;
<BrowserRouter basename="/calendar">
<Link to="/today" />
</BrowserRouter>
复制代码
等同于
<a href="/calendar/today" />
复制代码
- forceRefresh 如果为 true,在导航的过程中整个页面将会刷新。个别状况下,只有在不反对 HTML5 history API 的浏览器中应用此性能;
- getUserConfirmation 用于确认导航的函数,默认应用 window.confirm。例如,当从 /a 导航至 /b 时,会应用默认的 confirm 函数弹出一个提醒,用户点击确定后才进行导航,否则不做任何解决;
// 这是默认的确认函数
const getConfirmation = (message, callback) => {const allowTransition = window.confirm(message);
callback(allowTransition);
}
<BrowserRouter getUserConfirmation={getConfirmation} />
复制代码
须要配合
<Prompt>
一起应用。
- KeyLength 用来设置 Location.Key 的长度。
(2)HashRouter
应用 URL 的 hash 局部(即 window.location.hash)来放弃 UI 和 URL 的同步。由此能够看出,HashRouter 是通过 URL 的 hash 属性来管制路由跳转的:
<HashRouter
basename={string}
getUserConfirmation={func}
hashType={string}
/>
复制代码
其参数如下:
- basename, getUserConfirmation 和
BrowserRouter
性能一样; -
hashType window.location.hash 应用的 hash 类型,有如下几种:
- slash – 前面跟一个斜杠,例如 #/ 和 #/sunshine/lollipops;
- noslash – 前面没有斜杠,例如 # 和 #sunshine/lollipops;
- hashbang – Google 格调的 ajax crawlable,例如 #!/ 和 #!/sunshine/lollipops。
哪些办法会触发 React 从新渲染?从新渲染 render 会做些什么?
(1)哪些办法会触发 react 从新渲染?
- setState()办法被调用
setState 是 React 中最罕用的命令,通常状况下,执行 setState 会触发 render。然而这里有个点值得关注,执行 setState 的时候不肯定会从新渲染。当 setState 传入 null 时,并不会触发 render。
class App extends React.Component {
state = {a: 1};
render() {console.log("render");
return (
<React.Fragement>
<p>{this.state.a}</p>
<button
onClick={() => { this.setState({ a: 1}); // 这里并没有扭转 a 的值 }} > Click me </button>
<button onClick={() => this.setState(null)}>setState null</button>
<Child />
</React.Fragement>
);
}
}
复制代码
- 父组件从新渲染
只有父组件从新渲染了,即便传入子组件的 props 未发生变化,那么子组件也会从新渲染,进而触发 render
(2)从新渲染 render 会做些什么?
- 会对新旧 VNode 进行比照,也就是咱们所说的 Diff 算法。
- 对新旧两棵树进行一个深度优先遍历,这样每一个节点都会一个标记,在到深度遍历的时候,每遍历到一和个节点,就把该节点和新的节点树进行比照,如果有差别就放到一个对象外面
- 遍历差别对象,依据差别的类型,依据对应对规定更新 VNode
React 的解决 render 的根本思维模式是每次一有变动就会去从新渲染整个利用。在 Virtual DOM 没有呈现之前,最简略的办法就是间接调用 innerHTML。Virtual DOM 厉害的中央并不是说它比间接操作 DOM 快,而是说不论数据怎么变,都会尽量以最小的代价去更新 DOM。React 将 render 函数返回的虚构 DOM 树与老的进行比拟,从而确定 DOM 要不要更新、怎么更新。当 DOM 树很大时,遍历两棵树进行各种比对还是相当耗性能的,特地是在顶层 setState 一个渺小的批改,默认会去遍历整棵树。只管 React 应用高度优化的 Diff 算法,然而这个过程依然会损耗性能.
什么是 React Fiber?
Fiber 是 React 16 中新的协调引擎或从新实现外围算法。它的次要指标是反对虚构 DOM 的增量渲染。React Fiber 的指标是进步其在动画、布局、手势、暂停、停止或重用等方面的适用性,并为不同类型的更新调配优先级,以及新的并发原语。
React Fiber 的指标是加强其在动画、布局和手势等畛域的适用性。它的次要个性是增量渲染: 可能将渲染工作宰割成块,并将其扩散到多个帧中。
非嵌套关系组件的通信形式?
即没有任何蕴含关系的组件,包含兄弟组件以及不在同一个父级中的非兄弟组件。
- 能够应用自定义事件通信(公布订阅模式)
- 能够通过 redux 等进行全局状态治理
- 如果是兄弟组件通信,能够找到这两个兄弟节点独特的父节点, 联合父子间通信形式进行通信。
Redux 中间件是怎么拿到 store 和 action? 而后怎么解决?
redux 中间件实质就是一个函数柯里化。redux applyMiddleware Api 源码中每个 middleware 承受 2 个参数,Store 的 getState 函数和 dispatch 函数,别离取得 store 和 action,最终返回一个函数。该函数会被传入 next 的下一个 middleware 的 dispatch 办法,并返回一个接管 action 的新函数,这个函数能够间接调用 next(action),或者在其余须要的时刻调用,甚至基本不去调用它。调用链中最初一个 middleware 会承受实在的 store 的 dispatch 办法作为 next 参数,并借此完结调用链。所以,middleware 的函数签名是({getState,dispatch})=> next => action。
React 中 Diff 算法的原理是什么?
原理如下。
(1)节点之间的比拟。
节点包含两种类型:一种是 React 组件,另一种是 HTML 的 DOM。
如果节点类型不同,按以下形式比拟。
如果 HTML DOM 不同,间接应用新的替换旧的。如果组件类型不同,也间接应用新的替换旧的。
如果 HTML DOM 类型雷同,按以下形式比拟。
在 React 里款式并不是一个纯正的字符串,而是一个对象,这样在款式产生扭转时,只须要扭转替换变动当前的款式。批改完以后节点之后,递归解决该节点的子节点。
如果组件类型雷同,按以下形式比拟。
如果组件类型雷同,应用 React 机制解决。个别应用新的 props 替换旧的 props,并在之后调用组件的 componentWillReceiveProps 办法,之前组件的 render 办法会被调用。
节点的比拟机制开始递归作用于它的子节点。
(2)两个列表之间的比拟。
一个节点列表中的一个节点产生扭转,React 无奈很妤地解决这个问题。循环新旧两个列表,并找出不同,这是 React 惟一的解决办法。
然而,有一个方法能够把这个算法的复杂度升高。那就是在生成一个节点列表时给每个节点上增加一个 key。这个 key 只须要在这一个节点列表中惟一,不须要全局惟一。
(3)取舍
须要留神的是,下面的启发式算法基于两点假如。
类型相近的节点总是生成同样的树,而类型不同的节点也总是生成不同的树
能够为屡次 render 都体现稳固的节点设置 key。
下面的节点之间的比拟算法基本上就是基于这两个假如而实现的。要进步 React 利用的效率,须要依照这两点假如来开发。
如何解决 props 层级过深的问题
- 应用 Context API:提供一种组件之间的状态共享,而不用通过显式组件树逐层传递 props;
- 应用 Redux 等状态库。
React 中的高阶组件使用了什么设计模式?
应用了装璜模式,高阶组件的使用:
function withWindowWidth(BaseComponent) {
class DerivedClass extends React.Component {
state = {windowWidth: window.innerWidth,}
onResize = () => {
this.setState({windowWidth: window.innerWidth,})
}
componentDidMount() {window.addEventListener('resize', this.onResize)
}
componentWillUnmount() {window.removeEventListener('resize', this.onResize);
}
render() {return <BaseComponent {...this.props} {...this.state}/>
}
}
return DerivedClass;
}
const MyComponent = (props) => {return <div>Window width is: {props.windowWidth}</div>
};
export default withWindowWidth(MyComponent);
复制代码
装璜模式的特点是不须要扭转 被装璜对象 自身,而只是在里面套一个外壳接口。JavaScript 目前曾经有了原生装璜器的提案,其用法如下:
@testable
class MyTestableClass {}
在结构函数调用 super 并将 props 作为参数传入的作用
在调用 super() 办法之前,子类构造函数无奈应用 this 援用,ES6 子类也是如此。
将 props 参数传递给 super() 调用的次要起因是在子构造函数中可能通过 this.props 来获取传入的 props
传递了 props
class MyComponent extends React.Component {constructor(props) {super(props);
console.log(this.props); // {name: 'sudheer',age: 30}
}
}
没传递 props
class MyComponent extends React.Component {constructor(props) {super();
console.log(this.props); // undefined
// 然而 Props 参数依然可用
console.log(props); // Prints {name: 'sudheer',age: 30}
}
render() {
// 构造函数内部不受影响
console.log(this.props); // {name: 'sudheer',age: 30}
}
}
在 ReactNative 中,如何解决 adb devices 找不到连贯设施的问题?
在应用 Genymotion 时,首先须要在 SDK 的 platform-tools 中退出环境变量,而后在 Genymotion 中单击 Setting,抉择 ADB 选项卡,单击 Use custom Android SDK tools,浏览本地 SDK 的地位,单击 OK 按钮就能够了。启动虛拟机后,在 cmd 中输出 adb devices 能够查看设施。
这三个点 (…) 在 React 干嘛用的?
...
在 React(应用 JSX)代码中做什么?它叫什么?
<Modal {...this.props} title='Modal heading' animation={false}/>
这个叫扩大操作符号或者开展操作符,例如,如果 this.props
蕴含 a:1
和b:2
,则
<Modal {...this.props} title='Modal heading' animation={false}>
等价于上面内容:
<Modal a={this.props.a} b={this.props.b} title='Modal heading' animation={false}>
扩大符号不仅实用于该用例,而且对于创立具备现有对象的大多数(或全副)属性的新对象十分不便,在更新state
咱们就常常这么做:
this.setState((prevState) => {return { foo: { ...prevState.foo, a: "updated"} };
});
高阶组件
高阶函数:如果一个函数 承受一个或多个函数作为参数或者返回一个函数 就可称之为 高阶函数。
高阶组件:如果一个函数 承受一个或多个组件作为参数并且返回一个组件 就可称之为 高阶组件。
react 中的高阶组件
React 中的高阶组件次要有两种模式:属性代理 和反向继承。
属性代理(Props 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 元素树
应用 React Hooks 益处是啥?
首先,Hooks 通常反对提取和重用跨多个组件通用的有状态逻辑,而无需承当高阶组件或渲染 props
的累赘。Hooks
能够轻松地操作函数组件的状态,而不须要将它们转换为类组件。
Hooks 在类中不起作用,通过应用它们,咱们能够完全避免应用生命周期办法,例如 componentDidMount
、componentDidUpdate
、componentWillUnmount
。相同,应用像 useEffect
这样的内置钩子。
React 中的 setState 和 replaceState 的区别是什么?
(1)setState() setState()用于设置状态对象,其语法如下:
setState(object nextState[, function callback])
复制代码
- nextState,将要设置的新状态,该状态会和以后的 state 合并
- callback,可选参数,回调函数。该函数会在 setState 设置胜利,且组件从新渲染后调用。
合并 nextState 和以后 state,并从新渲染组件。setState 是 React 事件处理函数中和申请回调函数中触发 UI 更新的次要办法。
(2)replaceState() replaceState()办法与 setState()相似,然而办法只会保留 nextState 中状态,原 state 不在 nextState 中的状态都会被删除。其语法如下:
replaceState(object nextState[, function callback])
复制代码
- nextState,将要设置的新状态,该状态会替换以后的 state。
- callback,可选参数,回调函数。该函数会在 replaceState 设置胜利,且组件从新渲染后调用。
总结: setState 是批改其中的局部状态,相当于 Object.assign,只是笼罩,不会缩小原来的状态。而 replaceState 是齐全替换原来的状态,相当于赋值,将原来的 state 替换为另一个对象,如果新状态属性缩小,那么 state 中就没有这个状态了。