React 元素都是不可变的。当元素被创建之后,你是无法改变其内容或属性的。
目前更新界面的唯一办法是创建一个新的元素,然后将它传入 ReactDOM.render() 方法。
React 只会更新必要的部分,React DOM 首先会比较元素内容先后的不同,而在渲染过程中只会更新改变了的部分。
通常更新分为四种情况,包括
1、REPLACE:内容替换
2、TEXT:文本内容改变
3、PROPS:属性值发生改变
4、REORDER:元素位置发生改变
React 使用 JSX 来替代常规的 JavaScript。
JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。
class
和 for
不建议作为 XML 属性名。作为替代,React DOM 使用 className
和 htmlFor
来做对应的属性。插值表达式类型:
1、在标签内部的注释需要花括号
2、在标签外的的注释不能使用花括号
ReactDOM.render(
/*注释 */
Hello World {/*注释*/}
,
document.getElementById('example')
);
3、React 组件
function HelloMessage(props) {
return Hello World!
;
}
class Welcome extends React.Component {
render() {
return Hello World!
;
}
}
在什么情况下你会优先选择使用 Class Component 而不是 Functional Component?
在组件需要包含内部状态或者使用到生命周期函数的时候使用 Class Component ,否则使用函数式组件。
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。React 里,只需更新组件的 state,然后根据新的 state 调用render()方法,重新渲染用户界面(不要操作 DOM)。
1、不要直接更新状态
例如,此代码不会重新渲染组件:
// Wrong this.state.comment = 'Hello';
应当使用 setState():
// Correct this.setState({comment: 'Hello'});
构造函数是唯一能够初始化 this.state 的地方。
2、状态更新可能是异步的
React 可以将多个 setState() 调用合并成一个调用来提高性能。
因为 this.props 和 this.state 可能是异步更新的,你不应该依靠它们的值来计算下一个状态。
例如,此代码可能无法更新计数器:
// Wrong this.setState({ counter: this.state.counter + this.props.increment, });
要修复它,请使用第二种形式的 setState() 来接受一个函数而不是一个对象。 该函数将接收先前的状态作为第一个参数,将此次更新被应用时的props做为第二个参数:
// Correct this.setState((prevState, props) => ({ counter: prevState.counter + props.increment }));
上方代码使用了箭头函数,但它也适用于常规函数:
// Correct this.setState(function(prevState, props) { return { counter: prevState.counter + props.increment }; });
setState 函数可以接收两个参数,第二个参数是一个回调函数
该函数会在setState函数调用完成并且组件开始重渲染的时候被调用,我们可以用该函数来监听渲染是否完成。也可以用来获取修改后的counter值。
this.setState(function(prevState, props) { return { counter: prevState.counter + props.increment }; },()=>{ console.log("组件开始重新渲染之前 count的值:"+this.state.counter); });
3、状态更新合并
当你调用 setState() 时,React 将你提供的对象合并到当前状态。
例如,你的状态可能包含一些独立的变量:
constructor(props) { super(props); this.state = { posts: [], comments: [] }; }
你可以调用 setState() 独立地更新它们:
componentDidMount() { fetchPosts().then(response => { this.setState({ posts: response.posts }); }); fetchComments().then(response => { this.setState({ comments: response.comments }); }); }
这里的合并是浅合并,也就是说 this.setState({comments}) 完整保留了 this.state.posts,但完全替换了 this.state.comments。
调用 setState 之后发生了什么?
在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个UI界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
自定义配置
使用 react-app-rewired (一个对 create-react-app 进行自定义配置的社区解决方案)
配置按需加载
babel-plugin-import 是一个用于按需加载组件代码和样式的 babel 插件
npm install [email protected] babel-plugin-import --save
配置
package.json和config-overrides.js
1.修改package.json,将启动方式改为react-app-rewired,取代react-scripts
2.在项目根目录创建一个 config-overrides.js
用于修改默认配置。通过react-app-rewired启动时会将webpack中的配置与自定义配置合并。
config-overrides.js
const { injectBabelPlugin } = require('react-app-rewired');
module.exports = function override(config, env) {
config = injectBabelPlugin([
'import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: 'css'
}
], config);
return config;
}
通过以上代码实现antd库的按需加载,例如使用Button组件,则会自动导入Button的样式,不会导入全部样式,影响性能。
PureComponent组件重新定义了shouldComponentUpdate,针对以下的情况可以不用自己重写shouldComponentUpdate,使用PureComponent
注意:PureComponent使用浅比较判断组件是否需要重绘
要判断组件是否需要渲染,则需要用到生命周期,则组件需要用class方式编写,但是展示型组件一般用于函数
Reactv16.6.0之后的版本添加了一个新功能React.memo,让函数式组件也具备了PureComponent的功能
const Comment = React.memo(({body,author})=> {
console.log('render');
return (
{body}
----------------{author}
)
});
export default class CommentList extends Component {
constructor(props) {
super(props)
this.state = {
comments: []
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
comments: [
{ body: 'my name is lilei', author: 'lilei' },
{ body: 'my name is hanmeimei', author: 'hanmeimei' }
]
})
}, 1000);
}
render() {
return (
{this.state.comments.map((v, idx) => (
//
//
))}
)
}
}
React中使用组件复合实现组件的复用,而非继承。将每个组件尽可能拆分成小的、独立的功能块,这样就可以像拼积木似的组装代码,实现组件的复用。
组件复合例子:
function Dialog(props) {
return (
{/* 相当于vue中匿名插槽 */}
{props.children}
{/* 相当于vue中具名插槽 */}
{props.footer}
)
}
function WelcomDialog(props) {
//jsx
const button =
return (
)
}
function AlertDialog(props) {
//jsx
const button =
return (
)
}
// 组件复合,用复合实现组件的复用而非继承
export default class Composition extends Component {
render() {
return (
)
}
}
页面显示效果如下
参考文章:http://www.runoob.com/react
参考文章:https://blog.csdn.net/H1069495874/article/details/54967244