概述
声明式编码
React 使创建交互式 UI 变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React 能高效更新并渲染合适的组件。
组件化
使用
安装
可以通过CDN引入
可以通过下载为本地包进行引入
HelloReact的开始
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
</head>
<script src="../react_js/babel.min.js"></script>
<script crossorigin src="../react_js/react.development.js"></script>
<script crossorigin src="../react_js/react-dom.development.js"></script>
<body>
<div id="root"></div>
<script type="text/babel">
const VDOM = <h1>Hello_React</h1>;
ReactDOM.render(VDOM, document.getElementById("root"));
</script>
</body>
</html>
JSX语法的相关规则
react定义的一种类似于XML的JS扩展语法: JS + XML本质是**React.createElement(component, props, ...children)**方法的语法糖
1、定义虚拟DOM时,不要写引号
2、标签中混入JS表达式时要用{ }
3、样式的类名不要用class,用className
4、内联样式,要用style={{key:value}}
的形式去写
5、只有一个根标签
6、标签首字母
(1)若小写字母开头,比如,则将标签转为html同名元素,若html中无该标签对应的同名元素,则报错
(2)若大写字母开头,react就去渲染对应的组件
第一个函数式组件
<body>
<div id="root"></div>
<script type="text/babel">
//1、函数式组件
function MyComponent() {
return <div>Hello,I am Lin</div>;
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));
</script>
</body>
注意:函数名称要大写
执行了ReactDOM.render(,...之后发生了什么?
1、React解析组件标签,找到了MyComponent组件
2、发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转换为真实DOM,随后展示在页面中
第一个类式组件
<body>
<div id="root"></div>
<script type="text/babel">
//1、函数式组件
function MyComponent() {
return <div>Hello,I am Lin</div>;
}
//2.类式组件
class HelloComponent extends React.Component {
render() {
return <h2>我是类式组件</h2>;
}
}
ReactDOM.render(<HelloComponent />, document.getElementById("root"));
</script>
</body>
执行了ReactDOM.render(,....之后发生了什么?
1、React解析组件标签,找到了MyComponent组件
2、发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法
3、将render返回的虚拟DOM转换为真实DOM,随后呈现在页面中。
三大属性——state
<body>
<div id="root"></div>
<script type="text/babel">
class HelloComponent extends React.Component {
constructor(props) {
super(props);
this.state = { isHot: true };
}
changeWeather = () => {
console.log("hhh");
this.setState({ isHot: !this.state.isHot });
};
render() {
return (
<h2 onClick={this.changeWeather}>
今天天气很{this.state.isHot ? "炎热" : "寒冷"}
</h2>
);
}
}
ReactDOM.render(<HelloComponent />, document.getElementById("root"));
</script>
</body>
React 的核心思想是组件化
,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据。
一般是在react组件中的constructor
中写一个this.state
,是一个对象
constructor(props) {
super(props);
this.state = { isHot: true };
}
1、用setState 修改State
2、State 的更新是异步的
- 调用setState后,setState会把要修改的状态放入一个队列中(因而 组件的state并不会立即改变);
- 之后React 会优化真正的执行时机,来优化性能,所以优化过程中有可能会将多个 setState 的状态修改合并为一次状态修改,因而state更新可能是异步的。
- 所以不要依赖当前的State,计算下个State。当真正执行状态修改时,依赖的this.state并不能保证是最新的State,因为React会把多次State的修改合并成一次,这时,this.state将还是这几次State修改前的State。
3、State更新会被合并
4、setState里顺序更新
三大属性——props
<body>
<div id="root"></div>
<script type="text/babel">
class HelloComponent extends React.Component {
state = {};
constructor(props) {
super(props);
}
render() {
const { name } = this.props;
return <div>{name}</div>;
}
}
ReactDOM.render(
<HelloComponent name="Hello" />,
document.getElementById("root")
);
</script>
</body>
<body>
<div id="root"></div>
<script type="text/babel">
function Person(props) {
const { name, age } = props;
return (
<h2>
{name}---{age}
</h2>
);
}
ReactDOM.render(
<Person name="lin" age="20" />,
document.getElementById("root")
);
</script>
</body>
我们可以对props传入的类型做一个限制,和一个默认值
<body>
<div id="root"></div>
<script type="text/babel">
function Person(props) {
const { name, age } = props;
return (
<h2>
{name}---{age}
</h2>
);
}
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
};
Person.defaultProps = {
age: 20,
};
let person = {
name: "lin",
age: "20",
};
ReactDOM.render(<Person {...person} />, document.getElementById("root"));
</script>
</body>
我们需要提前引入propTypes的包,然后进行使用
三大属性——refs
1、字符串形式的ref
<body>
<div id="root"></div>
<script type="text/babel">
class Demo extends React.Component {
showData = () => {
console.log(this.refs.input1.value);
};
render() {
return (
<div>
<input type="text" ref="input1" />
<button onClick={this.showData}>点我提示左侧信息</button>
<input type="text" placeholder="失焦提示信息" ref="input2" onBlur={this.showData}/>
</div>
);
}
}
ReactDOM.render(<Demo/>, document.getElementById("root"));
</script>
</body>
2、回调函数形式的ref
<body>
<div id="root"></div>
<script type="text/babel">
class Demo extends React.Component {
showData = () => {
console.log(this.input1.value);
};
render() {
return (
<div>
<input
type="text"
ref={(c) => {
this.input1 = c;
}}
/>
<button onClick={this.showData}>点我提示左侧信息</button>
</div>
);
}
}
ReactDOM.render(<Demo />, document.getElementById("root"));
</script>
</body>
要注意:回调函数的形式的ref是挂载在实例本身,而不是在refs上的哦
3、createRef创建的ref容器
<body>
<div id="root"></div>
<script type="text/babel">
class Demo extends React.Component {
MyRef = React.createRef();
showData = () => {
console.log(this.MyRef.current.value);
};
render() {
return (
<div>
<input type="text" ref={this.MyRef} />
<button onClick={this.showData}>点我提示左侧信息</button>
</div>
);
}
}
ReactDOM.render(<Demo />, document.getElementById("root"));
</script>
</body>
事件处理
1. 通过onXxx属性指定事件处理函数(注意大小写)
-
React使用的是自定义(合成)事件, 而不是使用的原生DOM事件
-
React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
2. 通过event.target得到发生事件的DOM元素对象
生命周期
旧版本的生命周期
1. 挂载卸载过程
1.1.constructor()
constructor()中完成了React数据的初始化,它接受两个参数:props和context,当想在函数内部使用这两个参数时,需使用super()传入这两个参数。
注意:只要使用了constructor()就必须写super(),否则会导致this指向错误。
1.2.componentWillMount()
componentWillMount()一般用的比较少,它更多的是在服务端渲染时使用。它代表的过程是组件已经经历了constructor()初始化数据后,但是还未渲染DOM时。
1.3.componentDidMount()
组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,开启定时器,返回数据setState后组件会重新渲染
1.4.componentWillUnmount ()
在此处完成组件的卸载和数据的销毁。
-
clear你在组件中所有的setTimeout,setInterval
-
移除所有组件中的监听 removeEventListener
-
有时候我们会碰到这个warning:
Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.
原因:因为你在组件中的ajax请求返回setState,而你组件销毁的时候,请求还未完成,因此会报warning
解决方法:
componentDidMount() {
this.isMount === true
axios.post().then((res) => {
this.isMount && this.setState({ // 增加条件ismount为true时 aaa:res }) }) }
componentWillUnmount() {
this.isMount === false
}
2、更新过程
2.1. componentWillReceiveProps (nextProps)
- 在接受父组件改变后的props需要重新渲染组件时用到的比较多
- 接受一个参数nextProps
- 通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件
2.2.shouldComponentUpdate(nextProps,nextState)——阀门
- 主要用于性能优化(部分更新)
- 唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
- 因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断
2.3.componentWillUpdate (nextProps,nextState)
shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。
2.4.componentDidUpdate(prevProps,prevState)
组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。
2.5.render()
render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染。
新版本的生命周期
3.1.getDerivedStateFromProps
getDerivedStateFromProps
本来(React v16.3中)是只在创建和更新(由父组件引发部分)中调用。如果不是由父组件引发,那么getDerivedStateFromProps也不会被调用,如自身setState引发或者forceUpdate引发。
3.2. getSnapshotBeforeUpdate(prevProps, prevState)
代替componentWillUpdate。
常见的 componentWillUpdate 的用例是在组件更新前,读取当前某个 DOM 元素的状态,并在 componentDidUpdate 中进行相应的处理。
这两者的区别在于:
- 在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在
componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。
- getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。
此生命周期返回的任何值都将作为参数传递给componentDidUpdate()