1. React 组件间怎么进行通信?
-
Props:组件的数据流向一般是单向的,父组件通过 Props 将数据传递给子组件。子组件可以通过 this.props 来访问这些数据。父组件也可以通过 Props 向子组件传递函数,使得子组件可以向父组件发送消息。
-
Context:React 的 Context API 可以用于在组件树中共享数据,从而避免 Props drilling(即将 Props 层层传递给深层组件)。父组件可以通过 React.createContext 创建一个 context,并通过 Provider 组件将数据传递给子组件,子组件可以通过 Consumer 组件或 useContext 钩子来访问这些数据。
-
Refs:Refs 可以用来获取组件实例或 DOM 节点的引用。父组件可以通过 Refs 获取子组件的实例,从而调用子组件暴露的方法或访问子组件的属性。
-
自定义事件:父组件可以通过 EventEmitter 或其他第三方库创建自定义事件,子组件可以监听这些事件并触发相应的回调函数。
-
全局状态管理器:使用全局状态管理器(例如 Redux 或 MobX)可以将应用程序的状态集中管理,任何组件都可以访问和修改这些状态,从而实现跨组件通信。
2. 说说你在React项目是如何捕获错误的?
-
使用 Error Boundaries:Error Boundaries (错误边界)是 React 提供的一种机制,用于在组件树中捕获 JavaScript 异常并显示备用 UI,以便让应用程序在出现错误时仍能正常运行。可以在组件中实现 componentDidCatch 生命周期方法捕获错误。
-
使用 try-catch:在 JavaScript 中,可以使用 try-catch 语句捕获可能出现的异常,并在 catch 语句块中处理这些异常。在 React 中,可以在组件的 render 方法中使用 try-catch 语句捕获异常。
-
使用 Sentry 等错误跟踪工具:Sentry 是一款常用的错误跟踪工具,可以通过集成 Sentry SDK 来捕获 JavaScript 异常、HTTP 请求异常等。在 React 项目中,可以通过集成 Sentry SDK 来捕获错误,并收集错误信息、堆栈信息等。Sentry 还提供了一系列的工具和服务,帮助开发人员更好地分析和处理错误。
-
使用代码检查工具:使用一些代码检查工具如 ESLint 和 TypeScript 等,可以在编码阶段检查代码中的潜在错误,从而减少运行时错误的出现。
错误边界
为了解决出现的错误导致整个应用崩溃的问题,react16引用了错误边界新的概念。
错误边界是一种 React 组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript 错误,并打印这些错误,同时展示降级 UI,而并不会渲染那些发生崩溃的子组件树。
错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。
形成错误边界组件的两个条件:
- 使用了 static getDerivedStateFromError()
- 使用了 componentDidCatch()
抛出错误后,请使用 static getDerivedStateFromError() 渲染备用 UI ,使用 componentDidCatch() 打印错误信息,如下:
js
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 你同样可以将错误日志上报给服务器
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 你可以自定义降级后的 UI 并渲染
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
然后就可以把自身组件的作为错误边界的子组件,如下:
js
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
下面这些情况无法捕获到异常:
- 事件处理
- 异步代码
- 服务端渲染
- 自身抛出来的错误
在react 16版本之后,会把渲染期间发生的所有错误打印到控制台
除了错误信息和 JavaScript 栈外,React 16 还提供了组件栈追踪。现在你可以准确地查看发生在组件树内的错误信息:
3. 说说你对immutable的理解?如何应用在react项目中?
Immutable 是一种函数式编程的概念,指的是数据不可变性。在 Immutable 数据结构中,一旦创建了一个对象,就无法修改该对象的值,而只能创建一个新的对象。
在 React 项目中,Immutable 数据结构可以用于管理组件的状态,以避免直接修改状态而引起的副作用问题。使用 Immutable 数据结构可以保证状态的不可变性,从而避免在更新状态时不小心修改了原有状态的值。
在 React 项目中,可以使用一些第三方库来实现 Immutable 数据结构,例如 Immutable.js 和 immer 等。这些库提供了许多函数和方法,用于创建和操作不可变的数据结构,如 List、Map、Set 等。在使用这些库时,需要注意避免直接修改状态,而是通过创建新的不可变对象来更新状态。
下面是一个使用 Immutable.js 的示例:
js
import { Map } from 'immutable';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: Map({
name: 'Tom',
age: 20,
hobbies: ['reading', 'running'],
}),
};
}
handleDataChange = () => {
// 使用 set 方法创建新的 Map 对象更新数据
const newData = this.state.data.set('age', 21);
this.setState({
data: newData,
});
}
render() {
const { name, age, hobbies } = this.state.data.toObject();
return (
<div>
<p>{name}</p>
<p>{age}</p>
<ul>
{hobbies.map(hobby => <li key={hobby}>{hobby}</li>)}
</ul>
<button onClick={this.handleDataChange}>Change Age</button>
</div>
);
}
}
在上述示例中,我们使用 Immutable.js 的 Map 数据结构来管理组件的状态。在构造函数中,我们使用 Map 创建了一个包含名字、年龄和爱好的 Map 对象,并将其存储在组件的状态中。在 handleDataChange 方法中,我们使用 set 方法创建了一个新的 Map 对象,更新了年龄的值。最后,我们使用 setState 方法将新的数据存储到组件的状态中,并在 render 方法中显示出来。
使用方法扩展
- fromJS():将一个js数据转换为Immutable类型的数据
js
const obj = Immutable.fromJS({a:'123',b:'234'})
- toJS():将一个Immutable数据转换为JS类型的数据
- is():对两个对象进行比较
js
import { Map, is } from 'immutable'
const map1 = Map({ a: 1, b: 1, c: 1 })
const map2 = Map({ a: 1, b: 1, c: 1 })
map1 === map2 //false
Object.is(map1, map2) // false
is(map1, map2) // true
-
get(key):对数据或对象取值
-
getIn([]) :对嵌套对象或数组取值,传参为数组,表示位置
js
let abs = Immutable.fromJS({a: {b:2}});
abs.getIn(['a', 'b']) // 2
abs.getIn(['a', 'c']) // 子级没有值
let arr = Immutable.fromJS([1 ,2, 3, {a: 5}]);
arr.getIn([3, 'a']); // 5
arr.getIn([3, 'c']); // 子级没有值
在React中应用
使用 Immutable 可以给 React 应用带来性能的优化,主要体现在减少渲染的次数
在做react性能优化的时候,为了避免重复渲染,我们会在shouldComponentUpdate()中做对比,当返回true执行render方法
Immutable通过is方法则可以完成对比,而无需像一样通过深度比较的方式比较
在使用redux过程中也可以结合Immutable,不使用Immutable前修改一个数据需要做一个深拷贝
js
import '_' from 'lodash';
const Component = React.createClass({
getInitialState() {
return {
data: { times: 0 }
}
},
handleAdd() {
let data = _.cloneDeep(this.state.data);
data.times = data.times + 1;
this.setState({ data: data });
}
}
使用 Immutable 后:
js
getInitialState() {
return {
data: Map({ times: 0 })
}
},
handleAdd() {
this.setState({ data: this.state.data.update('times', v => v + 1) });
// 这时的 times 并不会改变
console.log(this.state.data.get('times'));
}
同理,在redux中也可以将数据进行fromJS处理
js
import * as constants from './constants'
import {fromJS} from 'immutable'
const defaultState = fromJS({ //将数据转化成immutable数据
home:true,
focused:false,
mouseIn:false,
list:[],
page:1,
totalPage:1
})
export default(state=defaultState,action)=>{
switch(action.type){
case constants.SEARCH_FOCUS:
return state.set('focused',true) //更改immutable数据
case constants.CHANGE_HOME_ACTIVE:
return state.set('home',action.value)
case constants.SEARCH_BLUR:
return state.set('focused',false)
case constants.CHANGE_LIST:
// return state.set('list',action.data).set('totalPage',action.totalPage)
//merge效率更高,执行一次改变多个数据
return state.merge({
list:action.data,
totalPage:action.totalPage
})
case constants.MOUSE_ENTER:
return state.set('mouseIn',true)
case constants.MOUSE_LEAVE:
return state.set('mouseIn',false)
case constants.CHANGE_PAGE:
return state.set('page',action.page)
default:
return state
}
}
4. 说说react中引入css的方式有哪几种?区别?
在 React 中,引入 CSS 样式的方式有以下几种:
- 在 JSX 中直接使用样式对象:可以在 JSX 中直接使用样式对象,将样式属性作为对象的属性,例如:
js
const styles = {
container: {
background: '#eee',
padding: '10px',
},
title: {
fontWeight: 'bold',
fontSize: '18px',
},
};
function MyComponent() {
return (
<div style={styles.container}>
<h1 style={styles.title}>Hello, world!</h1>
</div>
);
}
这种方式的优点是方便快捷,不需要额外的工具和库,适用于简单的样式需求。缺点是样式和组件混杂在一起,不够模块化和可维护。
- 使用 CSS 模块:CSS 模块是一种使用 CSS 的方式,可以将 CSS 样式模块化,使得样式和组件可以分离。在 React 中,可以使用 css-loader 和 style-loader 这两个 Loader,将 CSS 样式文件转换成模块,例如:
- 创建一个 CSS 样式文件 styles.module.css:
css
.container {
background: #eee;
padding: 10px;
}
.title {
font-weight: bold;
font-size: 18px;
}
- 在组件中引用 CSS 文件并使用 CSS 模块:
js
import styles from './styles.module.css';
function MyComponent() {
return (
<div className={styles.container}>
<h1 className={styles.title}>Hello, world!</h1>
</div>
);
}
这种方式的优点是可以将样式和组件分离,使得组件更加模块化和可维护。缺点是需要额外的工具和库,并且需要注意类名的命名,以避免类名重复的问题。
- 使用 CSS 预处理器:CSS 预处理器是一种使用类似编程语言的方式来编写 CSS 的工具,例如 Sass、Less 等。在 React 中,可以使用 node-sass 和 less-loader 等工具来预处理 CSS,例如:
js
import './styles.scss';
function MyComponent() {
return (
<div className="container">
<h1 className="title">Hello, world!</h1>
</div>
);
}
这种方式的优点是可以使用类似编程语言的方式来编写 CSS,使得样式更加灵活和易于维护。缺点是需要额外的工具和库,并且需要学习预处理器的语法和特性。
5. state 和 props有什么区别?
在 React 中,组件的状态可以通过 state 和 props 来管理,它们的区别如下:
-
定义:state 是组件内部的状态,需要通过 setState 方法来修改;而 props 是由父组件传递给子组件的属性,是只读的。
-
来源:state 是组件自己管理的状态,可以在组件内部初始化和修改;而 props 是由父组件传递给子组件的属性,在组件外部进行初始化和传递。
-
生命周期: state 可以在组件的生命周期中被修改,从而触发组件的重新渲染;而 props 在组件渲染之后就不会再被修改了。因为 React 鼓励单向数据流的思想,即数据只能从父组件传递给子组件,子组件不能直接修改传递过来的数据,只能通过调用父组件传递的方法来修改数据。
-
作用:state 和 props 都用于管理组件的状态,但是 state 通常用于管理组件的内部状态,而 props 通常用于传递数据和方法,使得组件可以与父组件和其他组件进行交互。
综上所述,state 和 props 是 React 组件中用于管理组件状态的两个重要机制。它们的区别在于定义、来源、生命周期和作用等方面,开发人员需要根据具体的场景和需求来选择合适的方式。