React速记,分为3个部分。每一个部分由简短且精悍的知识点组成。开发忘记可查看,初见学习可常翻
基础
一、JSX
const element = <h1>Hello, world!</h1>;
JavaScript和UI代码的结合
const element = <h1>Hello, {name}</h1>;
用
包裹表达式(js表达式,变量){}
const element = <img className={imgClass} src={user.avatarUrl} />;
class
`属性写为
`className
- 属性用小驼峰语法
const element = <h1>Hello, world!</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
一个可运行的React Hello World例子
二、组件
- 类式组件
- 函数式组件
function MyComponent(){
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
export defualt class MyComponent extends React.Component {
render(){
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
插入组件使用
,使用组件用 开头大写 的标签,组件可嵌套import
import Hello from './components/Hello'
var MyComponent = function(){
return <Hello />
}
渲染组件,ReactDOM的使用
ReactDOM.render(
MyComponent,
document.getElementById('root')
);
三、组件上的props和state
组件接收的入参为props
,组件自身的状态为state
props
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
传值
const el = <Welcome name="嘉然" />
传入值的类型检查,PropTypes
的使用
static propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为数值
}
默认值,defaulyProps
的使用
static defaultProps = {
sex: "male",
age: 20
}
props
不可修改
state
函数式组件需借助Hooks使用state
class Weather extends React.Component{
constructor(props){
super(props)
//初始化状态
this.state = {isHot:false,wind:'微风'}
}
不可以直接修改state,使用setState()
修改
this.setState({isHot:!isHot})
四、事件处理
- 事件处理回调
- [Ref]()
类式组件里,在回调函数中要注意this指向的问题
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
函数式组件
function ActionLink() {
handleClick = (e) => {
console.log('The link was clicked.');
}
return (
<button href="#" onClick={handleClick}>Click me</button>
);
}
五、虚拟DOM、真实DOM
虚拟DOM
开销极小的普通对象
const Velement = <div>Hello, world</div>;
真实DOM
const Relement = document.getElementById("root");
两者关系
- 调用
ReactDOM.render
将虚拟DOM渲染成真实DOM,组件state改变时,React会创建新VDOM并生成RDOM - 虚拟DOM没有真实DOM过多的属性
六、Diff算法
对象差异比对调和(Reconciliation)算法
-
虚拟DOM的比对
-
三种比对策略
- Tree Diff
- Component Diff
- Element Diff
Tree Diff:DOM树的同层比对,跨层时,直接创建新节点及节点下的树
Component Diff:
- 不同类型组件:使用Tree Diff
-
同类型组件,比对 属性 - 子节点 子节点如根节点比对形式 属性 - 子节点 比对。碰到不同类型节点便使用Tree Diff规则。也可以使用 生命周期函数 来手动控制组件更新
不同类型组件<div> <Counter /> </div> <span> <Counter /> </span>
同类型组件
<div className="before" title="stuff" /> <div className="after" title="stuff" />
<ul> <li>first</li> <li>second</li> </ul> <ul> <li>first</li> <li>second</li> <li>third</li> </ul>
Element Diff
节点在同一层级,会使用 删除、插入、移动 来更新
插入
<ul>
<li>first</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
</ul>
每个节点带有key,避免 一些破坏渲染顺序的操作引发的性能问题
<ul>
<li>first</li>
</ul>
<ul>
<li>second</li>
<li>first</li>
</ul>
PS. React不会做移动操作更新ul下面的两个li,而是直接删除原来ul的li,生成新的两个li。根据key判断原节点是否在原VDOM中存在
七、生命周期函数
生命周期钩子,特定时刻执行
- React16.4 旧生命周期
初始化阶段: 由ReactDOM.render()
触发---初次渲染
constructor()
componentWillMount()
render()
componentDidMount()
更新阶段: 由组件内部this.setSate()
或父组件render
触发
shouldComponentUpdate()
-
componentWillUpdate()
render()
componentDidUpdate()
卸载组件: 由
ReactDOM.unmountComponentAtNode()
触发
componentWillUnmount()
- 新生命周期
-
初始化阶段: 由
ReactDOM.render()
触发---初次渲染constructor()
getDerivedStateFromProps()
render()
componentDidMount()
-
更新阶段: 由组件内部
this.setSate()
或父组件重新render
触发getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
-
卸载组件: 由
ReactDOM.unmountComponentAtNode()
触发componentWillUnmount()
PS.手动控制组件更新
shouldComponentUpdate(){
//do something
reture true // 或者填写false
}
进阶
八、官方脚手架
Create a New React App – React (reactjs.org)
安装
npm install -s create-react-app
使用
create-react-app myapp
九、网络请求
- axios
- fetch
axios
简单使用实例
axios.get(`/api1/users?q=${keyWord}`).then(
response => {
// 成功请求后返回的响应
error => {
//请求失败后的处理
}
)
fetch
MDN:WorkerOrGlobalScope.fetch()
实例
fetch.get(`/api1/users?q=${keyWord}`)
.then( resp => {} err => {})
.then( resp(data) => {} err => {})
async await语法
async() => {
const response= await fetch(`/api1/search/users2?q=${keyWord}`)
const data = await response.json()
}
十、ref
在React中获取DOM
ref.createRef()
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
回调ref
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = element => {
this.textInput = element;
};
}
render() {
// 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React
// 实例上(比如 this.textInput)
return (
<input
type="text"
ref={this.setTextInputRef}
/>
);
}
}
- 函数式组件不可以使用Ref,因为没有this
- string形式ref不推荐使用
十一、react中state的管理
- redux
- react-redux
redux
Don't use Redux just because someone said you should - take some time to understand the potential benefits and tradeoffs of using it
-https://redux.js.org/introduction/getting-started#should-you-use-redux
是否使用redux在于自己的权衡
Redux = Reducer + Flux
解决跨组件数据传递问题
redux简单的工作流程
// 引入redux
import { createStore } from 'redux'
// 创建reducer,从store中根据其他组件传递的action来取store中对应的数据
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}
// 创建一个store
let store = createStore(counterReducer)
// 组件订阅store,store相应数据变化就会返回给订阅的组件
store.subscribe(() => console.log(store.getState()))
// 组件将自己的数据发送给store
store.dispatch({ type: 'counter/incremented' })
react-redux
React-Redux is the official Redux UI binding library for React
连接UI组件和数据
import React from 'react';
import {connect} from 'react-redux';
import {pushState} from 'redux-router';
export function requireAuthentication(Component) {
class AuthenticatedComponent extends React.Component {
componentWillMount() {
this.checkAuth();
}
componentWillReceiveProps(nextProps) {
this.checkAuth();
}
checkAuth() {
if (!this.props.isAuthenticated) {
let redirectAfterLogin = this.props.location.pathname;
this.props.dispatch(pushState(null, `/login?next=${redirectAfterLogin}`));
}
}
render() {
return (
<div>
{this.props.isAuthenticated === true
? <Component {...this.props}/>
: null
}
</div>
)
}
}
const mapStateToProps = (state) => ({
token: state.auth.token,
userName: state.auth.userName,
isAuthenticated: state.auth.isAuthenticated
});
return connect(mapStateToProps)(AuthenticatedComponent);
}