小程序
1.小程序组件通信
- 父传子通过属性传值,子组件用properties接收。
- 子传父通过父对子组件绑定一个事件,子组件用
this.triggerEvent("事件名",值)
以入参的方式传回父。 - 父组件用this.selectComponent获取子组件实例,可以获取子组件的数据,使用子组件的方法。
- 全局传值,相当于事件总线把,在app.js中定义globalData,然后在任意组件就可以读值和设置值。
- url传值,只能传字符串,
wx.navigateTo()
带上query参数,页面在onLoad(options)中options可以获取参数。 - 页面之间要传递复杂的数据的话,
wx.navigateTo()
中配置events事件以及成功的success回调,目标页面就能通过getOpenerEventChannel获取。 - 本地存储数据,
wx.setStorageSync
和wx.getStorageSync
。
2.小程序setData
用来修改state中的数据,修改是同步的,但是页面渲染是异步的。
3.页面下拉刷新与上拉加载
可以在全局 config 中的 window 配置中设置 enablePullDownRefresh 属性,当然建议 enablePullDownRefresh 属性是在页面级配置中设置, 在 Page 中定义 onPullDownRefresh 事件函数 到达下拉刷新条件后,该事件函数执行,发起请求方法,下拉刷新请求返回后,调用 wx.stopPullDownRefresh 停止下拉刷新。 至于上拉加载则可以利用 onReachBottom 事件函数来确认后续的功能操作,一般它与 onReachBottomDistance 进行配合处理,确认距离底部的距离,而它的单位是px
4.bindtap和catchtap的区别是什么
- 相同点:首先他们都是作为点击事件函数,就是点击时触发。在这个作用上他们是一样的,可以不做区分。
- 不同点:他们的不同点主要是bindtap是不会阻止冒泡事件的,catchtap是阻值冒泡的。
5.小程序有哪些导航API,它们各自的应用场景与差异区别是什么
wx.navigateTo():保留当前页面,跳转到应用内的某个页面,但是不能跳到 tabbar 页面 wx.redirectTo():关闭当前页面,跳转到应用内的某个页面,但是不允许跳转到 tabbar 页面 wx.switchTab():跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面 wx.navigateBack():关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层 wx.reLaunch():关闭所有页面,打开到应用内的某个页面
6.小程序中如何使用第三方npm模块进行功能开发
- npm init项目的初始化操作
- npm install lodash --save等模块安装操作
- 微信开发者工具->详情->本地设置->使用npm模块
- 微信开发者工具->工具->构建npm
在设置里面使用npm模块,在工具里面构建npm
7.小程序的定位在开发环境的设置
- 使用定位操作需要授权处理,需在app.json中设置permission节点信息,将useLocation授权添加上。
- 开发环境使用定位需要在调试器中开启,在**Sensor(传感器)**中将Geolocation的enable设置为开启状态
8.小程序的地图应用可以使用什么方式处理
wx.getLocation获取位置的经纬度等信息,再使用wx.openLocation打开微信内置地图查看位置,但是能看到的东西很少,只有经纬度。
更详细的需要用第三方的API。
9.小程序如何实现分享功能
- 在页面中如果不设置 onShareAppMessage 分享的事件回调函数,那么小程序右上角三个点的操作中不包含分享功能
- 通过button按钮的open-type属性设置为share则将调用页面中的 onShareAppMessage 事件,可以通过事件的 res中的 from 内容来判断是按钮button的分享处理还是右上角三个点menu的页面分享操作
10.小程序是否支持双向数据绑定
-
但仅限于简易双向数据绑定,绑定的对象只能是一个单一字段的绑定,不支持对象等形式的数据路径设置操作
-
在自定义组件中也可以实现传递双向绑定操作
-
它的基本语法操作是
html 体验AI代码助手 代码解读 复制代码 <input model:value="{{value}}" />
11.授权验证登录怎么做,用户退出后下次进入还需要再次授权吗
- wx.login获取到一个code,拿这code去请求后台得到openId, sessionKey, unionId。
- 调wx.getUserInfo获取用户信息内容
- 一次性授权:每次授权都需要与后台进行权限认证操作
- 永久授权:调取授权登录接口并把获取到的用户公开信息存入数据库
12.授权验证是怎么做的
- 按钮触发的,open-type指定为getUserInfo类型,可以从bindgetuserinfo回调中获取到用户信息
- 授权验证操作只执行一次,不会二次执行
- 授权以后可以通过wx.getUserInfo获取基础的用户信息
13.微信小程序之用户授权
用户授权包括很多内容:用户信息、地理位置、后台定位、录音功能、保存到相册、摄像头等
授权操作主要分两种不同的情况
-
弹出授权框用户点击允许,授权信息会直接记录,后续不再确认授权操作
-
弹出授权框用户点击拒绝,授权信息会直接记录,但用户还想再次操作对应功能,需要弹窗再次授权
- 查看所拥有权限
- wx.authorize 发起请求用户授权,利用 wx.showModal 弹窗授权确认
- wx.showModal 确认后利用 wx.openSetting 打开授权设置
- 确认授权设置打开授权信息
14.使用webview直接加载要注意哪些事项?
- 个人类型的小程序暂不支持使用
- H5地址需要在小程序后台添加H5域名白名单,如果webview里面还有跳转到其他的H5页面,也是需要添加域名白名单的
- 在网页内可通过window.__wxjs_environment变量判断是否在小程序环境
- webview内可以通过桥接方式进行监听,监听事件onPageStateChange可以确认小程序是否在前台
- 每个页面只能有一个 web-view,web-view 会自动铺满整个页面,并覆盖其他组件
- 避免在链接中带有中文字符,在 iOS 中会有打开白屏的问题,建议加一下 encodeURIComponent
- web-view 网页与小程序之间不支持除 JSSDK 提供的接口之外的通信
15.webview中的页面怎么跳转回小程序
wx.miniProgram.navigateTo()
16.怎么获取手机号
个人不行,要完成认证的小程序,先去微信平台认证。
- 利用wx.login获取登录凭证code,通过code与开发者服务器交互获取加密后的openId,并将openId与session_key进行服务器数据库信息存储
- 对openId进行加密传递回小程序端
- 利用button进行open-type的类型设置,值为getPhoneNumber,并且需要进行bindgetphonenumber事件绑定
- 绑定回调getPhoneNumber中可以找到手机加密数据
- 将加密的openId、encryptedData、iv等数据发送至服务器端
- 服务器端解密
- 可以将解密信息等内容进行返回小程序端处理
17.生命周期
应用级别的
- onLaunch 小程序初始化
- onShow 小程序启动
- onHide 小程序切后台
页面级别的
- onLoad 页面加载
- onShow 页面显示
- onReady 页面渲染完成
- onHide 页面隐藏
- onUnload 页面卸载
组件级别的
- created 组件实例创建
- attached 组件被添加到页面节点树种
- ready 组件初次渲染完成
- moved 组件被移动到新的节点
- detached 组件被页面节点树种移除
React
1.React中的事件机制
JSX 上写的事件并没有绑定在对应的真实 DOM 上,而是通过事件代理的方式,将所有的事件都统一绑定在了组件树的根上。这样的方式不仅减少了内存消耗,还能在组件挂载销毁时统一订阅和移除事件。
2.React 高阶组件、Render props、hooks 有什么区别,为什么要不断迭代
-
HOC
简单来说就是一个函数接受一个组件,这个函数中又定义了一个组件,在这个新定义的组件中可以写一些属性方法等公共代码,然后把这些属性方法传给接受的组件,最后return新定义的组件。
// 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));
-
Render props
父组件向Render props组件传递了一个render函数,这个render函数定义了渲染内容;Render props组件通过this.props.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> )}/>
-
Hooks
可以在里面用其他的hook,直接return出数据,使用的时候useXxxx就行了,非常方便。
3.对React-Fiber的理解,它解决了什么问题?
-
可中断的渲染
以前react更新过程是同步的,一旦开始就会进行到底,这可能导致主线程被占据,页面卡顿做不了其他的事情。现在react把更新过程拆分成多个小任务,这样渲染的过程就能被其他优先级更高的任务插队,从而更好的响应用户交互。
-
优先级管理
比如用户输入,图表更新,在浏览器原生的任务调度机制下,用户的输入可能会被图表更新所堵塞,但是在Fiber的操作下,用户的输入是优先于图表更新的。 React Fiber 如何在任务调度方面比浏览器提供更细粒度的控制。
-
并发渲染
在传统架构中,React 是单线程的,无法充分利用多核 CPU 的优势。Fiber 使得 React 能够更好地利用现代多核处理器,提高渲染性能。
4.React.PureComponent
在函数组件里面就是React.memo
,react会自动浅比较一下传入props,变化了就更新组件。
5.类组件中的生命周期
-
挂载阶段
-
constructor
用于初始化 state 和绑定方法。
constructor(props) { super(props); this.state = { count: 0 }; }
-
static getDerivedStateFromProps
根据 props 更新 state。是一个静态方法。返回值会合并到state中
static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.someValue !== prevState.someValue) { return { someValue: nextProps.someValue }; } return null; }
-
render
返回要渲染的元素。
render() { return <div>{this.state.count}</div>; }
-
componentDidMount
组件挂载后调用,可以进行 DOM 操作或数据请求。
componentDidMount() { // 进行数据请求或订阅操作 }
-
-
更新阶段
-
getDerivedStateFromProps
在每次组件更新时调用(见上)。
-
shouldComponentUpdate
决定组件是否应该重新渲染。返回
true
或false
。shouldComponentUpdate(nextProps, nextState) { return nextState.count !== this.state.count; }
-
render
重新渲染
-
getSnapshotBeforeUpdate
在更新前获取一些信息(例如,DOM 状态)。
getSnapshotBeforeUpdate(prevProps, prevState) { if (prevProps.someValue !== this.props.someValue) { return { scrollPosition: window.scrollY }; } return null; }
-
componentDidUpdate
组件更新后调用,可以进行 DOM 操作或数据请求。
componentDidUpdate(prevProps, prevState, snapshot) { if (snapshot !== null) { window.scrollTo(0, snapshot.scrollPosition); } }
-
-
卸载阶段
componentWillUnmount
componentWillUnmount() { // 进行清理工作,如取消订阅 }
6.哪些方法会触发 React 重新渲染?重新渲染会做些什么?
-
State 变化:
- 当组件的 state 发生变化时,会触发重新渲染。
- 使用
this.setState
方法更新 state 会导致重新渲染。
-
Props 变化:
- 当父组件传递给子组件的 props 发生变化时,子组件会重新渲染。
- 这也是为什么组件应该是纯函数的一个原因,即它们的输出应该完全由输入(props 和 state)决定。
-
强制更新:
- 使用
this.forceUpdate
方法可以强制组件重新渲染。通常不推荐使用这个方法,因为它绕过了 React 的优化机制。
- 使用
-
Context 变化:
- 当使用 React Context 时,如果 Context 的值发生变化,所有使用该 Context 的组件都会重新渲染。
-
父组件变化:
- 父组件的渲染会带着子组件一起重新渲染一遍。
重新渲染就会用diff算法来对新旧VNode进行比对。
7.React如何判断什么时候重新渲染组件?
写了componentShouldUpdate就根据返回值来判断,true就重新渲染,false就放弃这次渲染。
8.无状态组件
就是没有自己的状态,就负责展示,可以传递props给它展示。
9.如何获取DOM元素
-
在类组件中使用
ref
在类组件中,你可以通过创建一个
ref
并将其附加到一个 DOM 元素上来获取对该元素的引用。import React, { Component } from 'react'; class MyComponent extends Component { constructor(props) { super(props); // 创建一个 ref this.myRef = React.createRef(); } componentDidMount() { // 访问 DOM 元素 console.log(this.myRef.current); } render() { return <div ref={this.myRef}>Hello, World!</div>; } } export default MyComponent;
10.React中可以在render访问refs吗?为什么?
不能,要在render之后。
11.对React的插槽(Portals)的理解,如何使用,有哪些使用场景
Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。
React 提供了 ReactDOM.createPortal
方法来创建一个 Portal。这个方法接收两个参数:
- 子组件(或元素)
- DOM 节点
import React from 'react';
import ReactDOM from 'react-dom';
class Modal extends React.Component {
render() {
return ReactDOM.createPortal(
<div className="modal">
{this.props.children}
</div>,
document.getElementById('modal-root')
);
}
}
// 在你的 HTML 文件中,需要有一个 id 为 'modal-root' 的元素,就是入口文件
// <div id="modal-root"></div>
class App extends React.Component {
render() {
return (
<div className="app">
<h1>My App</h1>
<Modal>
<p>This is a modal!</p>
</Modal>
</div>
);
}
}
export default App;
应用:弹窗类用的最多。
12.在React中如何避免不必要的render?
- shouldComponentUpdate
- PureComponent
- React.memo
13.对 React context 的理解
用来组件通信的
import React from 'react';
// 创建一个 Context 对象,并设置默认值
const ThemeContext = React.createContext('light');
export default ThemeContext;
//父组件
<ThemeContext.Provider value={this.state.theme}>
<div>
<button onClick={this.toggleTheme}>Toggle Theme</button>
<ThemedComponent />
</div>
</ThemeContext.Provider>
//子组件
<ThemeContext.Consumer>
{theme => (
<div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
<h2>Themed Component</h2>
<p>The current theme is {theme}</p>
</div>
)}
</ThemeContext.Consumer>
14.React中什么是受控组件和非控组件
指那些表单数据由React组件状态state控制的表单元素,表单元素的值和组件的状态同步。
15.React中refs的作用是什么?有哪些应用场景?
直接获取DOM元素或者React组件实例,但是函数组件没有实例,直接获取的话会报错,所以要用到forwardRef
const A = forwardRef((props,ref)=>{
//接收两个参数
// props表示传参
// 转发ref给其他能获取到实例的元素
return <input ref={ref} type="text" />
})
import React, { useRef, useEffect, forwardRef } from 'react';
// 定义一个函数组件并使用 React.forwardRef 转发 ref
const FunctionComponent = forwardRef((props, ref) => (
<input ref={ref} type="text" />
));
//------------------------------------------------------------------
const ParentComponent = () => {
const inputRef = useRef(null);
useEffect(() => {
// 组件挂载后,让输入框自动获得焦点
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
return <FunctionComponent ref={inputRef} />;
};
export default ParentComponent;
16.React中除了在构造函数中绑定this,还有别的方式吗?
一起列举出来
-
在构造函数中绑定
this
class MyComponent extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState({ count: this.state.count + 1 }); } render() { return <button onClick={this.handleClick}>Click me</button>; } }
-
使用箭头函数定义类方法(类字段语法)
class MyComponent extends React.Component { state = { count: 0 }; handleClick = () => { this.setState({ count: this.state.count + 1 }); }; render() { return <button onClick={this.handleClick}>Click me</button>; } }
-
在jsx中使用箭头函数
class MyComponent extends React.Component { state = { count: 0 }; handleClick() { this.setState({ count: this.state.count + 1 }); } render() { return <button onClick={()=>this.handleClick()}>Click me</button>; } }
-
在jsx中用bind,
this.handleClick.bind(this);
17.React组件的构造函数有什么作用?它是必须的吗?
构造函数的主要作用:初始化状态和绑定事件处理器中的 this
。
所以如果props不涉及初始化,又不需要处理this的问题的话,那就可以不用构造函数。
class LikeButton extends React.Component {
constructor() {
super();
this.state = {
liked: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({liked: !this.state.liked});
}
render() {
const text = this.state.liked ? 'liked' : 'haven't liked';
return (
<div onClick={this.handleClick}>
You {text} this. Click to toggle.
</div>
);
}
}
ReactDOM.render(
<LikeButton />,
document.getElementById('example')
);
18.setState的用法和原理
推荐使用函数式的,可以获取到正确的状态。
this.setState((prevState, props) => ({
...
}),callback);
//第二个参数是回调函数,state完成更新后执行
用useState()
也是的,如果依赖旧状态,也建议写函数式的
import React, { useState } from 'react';
function Counter() {
// 声明一个叫做 "count" 的状态变量,并赋初始值为 0
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount((prevCount )=>prevCount +1)}>
Click me
</button>
</div>
);
}
- 状态更新请求: 当
setState
被调用时,React 接收到一个状态更新请求,这个请求可以是一个对象或者一个函数。 - 合并状态: React 将新的状态与当前状态合并(浅合并),但不会立即更新
this.state
。相反,React 会将状态更新放入一个队列中,稍后处理。 - 批量更新: 为了提高性能,React 会批量处理多个
setState
调用。在事件处理、生命周期方法或合成事件中,状态更新不会立即生效,而是会被暂存并在稍后的一个批处理中一起处理。但是如果在一些原生事件中,setState
可能是同步的,因为合成事件不会处理。 - 重新渲染: 批处理完成后,React 会重新计算组件的状态和属性,并触发组件的重新渲染。React 会调用
render
方法生成新的虚拟 DOM,然后通过与旧的虚拟 DOM 进行对比(diffing),找到需要更新的部分,并进行实际的 DOM 更新。
19.React中defaultProps
在 React 16.3 及更高版本中,推荐使用 defaultProps
静态属性来定义默认属性值:
-
类组件
class MyComponent extends React.Component { static defaultProps = { name: 'Default Name' }; render() { return <div>{this.props.name}</div>; } } // 使用 defaultProps 的效果与 getDefaultProps 相同 <MyComponent /> // 渲染结果:<div>Default Name</div>
-
函数组件
直接使用默认入参就行了
20.React中的setState和replaceState的区别是什么?
replaceState已经被废弃了,不了解。
21.React性能优化在哪个生命周期?它优化的原理是什么?
shouldComponentUpdate(nextProps,nextState),可以决定组件是否更新。
22.state和 props 触发更新的生命周期分别有什么区别?
getDerivedStateFromProps
仅在 props
变化时调用。
23.React-Router的实现原理是什么?
客户端路由实现的思想:
- 基于 hash 的路由:通过监听
hashchange
事件,感知 hash 的变化 -
- 改变 hash 可以直接通过 location.hash=xxx
- 基于 H5 history 路由:
-
- 改变 url 可以通过 history.pushState 和 resplaceState 等,会将URL压入堆栈,同时能够应用
history.go()
等 API - 监听 url 的变化可以通过自定义事件触发实现
- 改变 url 可以通过 history.pushState 和 resplaceState 等,会将URL压入堆栈,同时能够应用
24.React-Router怎么设置重定向?
使用 <Navigate>
组件
25.React-Router如何获取URL的参数?
-
路径参数
// 定义路由 <Route path="/user/:id" component={UserComponent} /> // 获取参数 const { id } = useParams();
-
查询参数
// 定义路由 <Route path="/search" component={SearchComponent} /> // 获取查询参数 const [searchParams] = useSearchParams(); const query = searchParams.get('query');
-
state参数
const navigate = useNavigate(); navigate('/user', { state: { id: 123, name: 'John Doe' } }); // 导航并通过 state 传递参数 --------------------------- const location = useLocation(); const userState = location.state; // 获取通过 state 传递的参数
-
其他的钩子
import React from 'react'; import { useMatch } from 'react-router-dom'; const About = () => { const match = useMatch('/about'); return ( <div> <h1>About Page</h1> {match && <p>This is the About page</p>} </div> ); }; export default About;
26.useLayoutEffect
用法和useEffect一样,普通的useEffect会再浏览器完成绘制之后执行,但是useLayoutEffect会在DOM变更后就执行(浏览器绘制之前) ,这有什么卵用呢?
比如说要写一个tooltip,当上方的空间不够了,那就应该尝试出现在下方,所以要在浏览器完成绘制之前也就是DOM一旦变更就可以去测量上方的空间够不够了,上方空间不够用立马就更改他的位置,让浏览器再次渲染一遍。
27.React 数据持久化有什么实践吗?
redux-persist
28.Redux原理
在React中,组件通过connect连接到 Redux ,如果要访问 Redux,需要dispatch一个包含 type和负载(payload) 的 Action。Action 中的 payload 是可选的,Action 将其转发给 Reducer。
当Reducer收到Action时,通过 switch…case 语法比较 Action 中type。 匹配时,更新对应的内容返回新的 state。
当Redux状态更改时,连接到Redux的组件将接收新的状态作为props。当组件接收到这些props时,它将进入更新阶段并重新渲染 UI。
import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from './actions';
function App({ count, increment, decrement }) {
return (
<div>
<button onClick={increment}> + </button>
<p>{count}</p>
<button onClick={decrement}> - </button>
</div>
)
}
//把store中的数据映射到组件的props中
const mapStateToProps = state => {
return {
count: state.count,
}
}
//把actions映射到组件的props中
const mapDispatchToProps = {
increment,
decrement,
}
// 使用 connect 函数连接组件和 Redux store
export default connect(mapStateToProps, mapDispatchToProps)(App)
29.reducer
reducer
函数接收当前状态 state
和一个动作 action
,然后根据 action.type
返回新的状态。
函数组件中使用useReducer
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
</div>
);
}
export default Counter;
30.useTransition
延迟的更新:使用 useTransition
标记的状态更新会被延迟,直到所有高优先级的更新完成。
可中断的更新:过渡更新可以被高优先级的更新中断,以保持应用的响应性。
指示过渡状态:可以获取过渡是否进行中的状态,以便在 UI 中展示加载指示器等。
import React, { useState, useTransition } from 'react';
function App() {
const [isPending, startTransition] = useTransition();
const [count, setCount] = useState(0);
const handleClick = () => {
startTransition(() => {
setCount(count + 1);
});
};
return (
<div>
<button onClick={handleClick}>Increment</button>
{isPending ? <p>Loading...</p> : <p>Count: {count}</p>}
</div>
);
}
export default App;
31.useDeferredValue
创造一个延迟更新的值,第一次返回旧值,当其他优先级高的任务执行完了再创造一个新值。
32.React.lazy()和Suspense
用于路由懒加载
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Suspense, lazy } from 'react';
const Home = lazy(() => import('./routes/Home'))
const count = lazy(() => import('./routes/count'))
export default class App extends Component {
render() {
return (
<div>
<Router>
<Suspense fallback={<h1>Loading...</h1>}>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/count" component={count}/>
...
</Switch>
</Suspense>
</Router>
</div>
)
}
}
33.JSX是怎么变成真实的DOM的
JSX 解析和编译:JSX 通过 Babel 编译成 React.createElement
调用。
创建虚拟 DOM:React.createElement
创建虚拟 DOM 对象。
构建虚拟 DOM 树:React 通过虚拟 DOM 对象构建虚拟 DOM 树。
比较和更新虚拟 DOM 树:React 比较新旧虚拟 DOM 树,找出差异。
更新真实 DOM:React 将差异转换为实际的 DOM 操作,更新真实 DOM。
渲染真实 DOM:React 将虚拟 DOM 树中的元素渲染成真实的 DOM 元素,并插入页面。
34.为什么 Hooks 不能在条件判断中使用?
React 依赖 Hook 调用顺序来正确关联状态和副作用。
35.如何通过 DOM 元素找到对应的 Fiber 对象
具体来说,React 在内部创建 DOM 元素时,会在 DOM 元素上附加一个 __reactFiber$
前缀的属性,该属性的值是 fiber
对象。
36.React源码
36-1.JSX转换成ReactElement的过程
调用了createElement方法
/**
* 创建React Element
* type 元素类型
* config 配置属性
* children 子元素
* type 元素类型
* 1.分离props属性和特殊属性
* 2.将子元素挂在到props.children中
* 3.为props属性赋默认值
* 4.创建并返回ReactElement
*/
function createElement(type,config,children){
...
}
如何判断一个参数是ReactElement?
看对象的$$typeof属性
object.$$typeof === REACT_ELEMENT_TYPE
36-2.React架构
- Scheduler(调度层):调度任务的优先级,高优先级的先进入协调器。
- Reconciler(协调层):构建Fiber数据结构,对比Fiber对象找出差异,记录Fiber对象要进行的DOM操作。
- Renderer(渲染层):将变化的部分渲染到页面上。
调度层和协调层的工作是在内存当中进行的,渲染层设定的就是不可以打断的,所以不会出现DOM渲染不完整的问题。
36-3. Fiber数据结构
type Fiber= {
/************DOM实例相关************/
//表示Fiber节点的类型 用于协调和渲染的时候做判断的
tag:WorkTag,
//React元素的类型 DOM元素就是字符串("div","span") React元素就是组件的构造函数或者类
type:any,
//与Fiber节点关联的实例对象,对于DOM元素来说是实际的DOM节点,对于类组件来说是组件实例
stateNode: any,
/************构建Fiber树相关的************/
//父Fiber节点,表示树结构中的父节点
return: Fiber | null,
//子Fiber节点,表示树结构中的第一个子节点。
child: Fiber | null,
//兄弟Fiber节点,表示树结构中当前节点的下一个兄弟节点。
sibling: Fiber | null,
//有了这些数据,无论出于Fiber树种哪个位置,都能快速的找到父级 子级 兄弟级节点了
//指的是 workInProgress Fiber节点
alternate: Fiber | null,
/************状态数据相关的************/
//将用于渲染的新属性
pendingProps: any,
//上一次渲染时使用的属性
memoizedProps: any,
//上一次渲染时使用的状态
memoizedState: any,
/************副作用相关************/
//该Fiber对应的组件所产生的状态更新都会放在这个队列里
updateQueue:updateQueue<any> | null,
//用来记录当前Fiber要执行的DOM操作,比如说当前对应的DOM节点要做插入 删除等操作
effectTag:sudeEffectTag,
//指向当前Fiber节点的第一个副作用Fiber节点。
firstEffect:Fiber | null,
//指向当前Fiber节点的下一个副作用Fiber节点。
nextEffect:Fiber | null,
//指向当前Fiber节点的最后一个副作用Fiber节点。
lastEffect:Fiber | null,
//通过设置不同的expirationTime,React可以控制各个更新任务的执行顺序,确保用户交互等高优先级任务能及时响应。
expirationTime:ExpirationTime,
//表示当前Fiber节点的渲染模式 并发渲染,同步渲染,不使用特定的渲染模式等等
mode:TypeOfMode,
}
36-4.双缓存技术
React最多同时存在两颗Fiber树,屏幕中看到的讲座current Fiber树,当发生更新的时候,React会在内存里面重新构建一颗workInProgress Fiber树。当workInProgress Fiber树构建好了直接替换current Fiber树,更新就更流畅了。
36-5.FiberRoot和RootFiber
-
FiberRoot
是React应用的顶层结构,包含了整个应用的渲染状态和调度信息。它主要负责管理整个应用的渲染和更新过程。
-
RootFiber
是整个React应用的顶层Fiber节点,是
FiberRoot
的一个属性。rootFiber
表示当前渲染的Fiber树的根节点,是从这个节点开始递归遍历整个Fiber树。
36-6.整个过程
render
方法的调用其实是returnlegacyRenderSubtreeIntoContainer
的结果。legacyRenderSubtreeIntoContainer
是为container
(<div id = 'app'>
)创建或者获取FiberRoot
,然后开始下一步调用updateContainer
。- 更新过程就是调用了
updateContainer
,updateContainer
会创建任务(初始化渲染或者更新渲染)放在任务队列里面,等待浏览器空闲执行。 - 任务执行调用
scheduleUpdateOnFiber
,这会进入渲染阶段,渲染阶段包括了协调和提交两个子阶段。 - 在协调阶段会对比新旧Fiber树的差异,React会建立一个Effect List,用于在提交阶段执行所有的副作用。每个Fiber节点都会有一个effectTag,用于标记要执行的操作,例如更新、插入、删除等等。
- 提交阶段又分了三个小阶段,Before Mutation、Mutation和Layout。
- Before Mutation会处理DOM更新前要处理的副作用,例如
getSnapshotBeforeUpdate
。 - Mutation就处理DOM节点的添加更新等。
- Layout会执行所有需要再DOM变更之后处理的副作用例如
componentDidMount
和componentDidUpdate
。 - DOM的更新。
- 在所有的更新完成之后,React会执行需要清理的任务,例如
useEffect
中的回调函数。
《Java 后端开发面试指南》题来自 2025 阿里、字节真题,问的是 MySQL 9.0 JSON 索引、Redis 8.0 多线程性能、网关动态路由、分布式事务选型。这些要的是原理理解、场景适配、量化思维 ——2025 年不缺 “会写接口的人”,缺 “写优、做稳、解决透” 的人,别用 “能跑通” 定义自己。
总结
专注收集整理Java面试题,不定期给大伙分享面试中的高频题与大厂难题。
如果你觉得文章对你有用,可以点赞和关注哦!❤
欢迎大家在评论区留下你宝贵的建议📢