第一章 react基本使用
react基础首先不使用脚手架工具,只学习相关语法
Hello World输出
因为react是通过创建虚拟DOM,然后把虚拟DOM添加到DOM树中,以此达到控制视图的目的,所以首先使用react的时候需要先引入react相关文件和babel(把jsx转换成js)。
<!-- 容器 -->
<div id="text"></div>
<!-- 一定要带上babel,告诉编译器里面写的语法需要转换 -->
<script type="text/babel">
//创建 一个虚拟DOM
const VDOM = <h1>Hello World</h1>
//将虚拟DOM添加到DOM树上
ReactDOM.render(VDOM,document.getElementById('text'));
</script>
使用js创建 虚拟DOM
可以看到,要想使用jsx创建虚拟dom,需要引入babel文件,通过babel文件,将jsx转换成js,以此被编译器识别,事实上利用react文件,可以不使用jsx,直接创建 虚拟dom
<!-- 容器 -->
<div id="text"></div>
<script type="text/javascript">
//创建 一个虚拟DOM
const VDOM = React.createElement('h1',{id:'title'},'Hello React')
//将虚拟DOM添加到DOM树上
ReactDOM.render(VDOM,document.getElementById('text'));
</script>
可以看到,用js创建虚拟dom要比用jsx麻烦的多,因此在日常的react学习中,通常 使用jsx来创建虚拟DOM
jsx语法规则
<script type="text/babel">
//创建 一个虚拟DOM
let text = "Hello world"
//根标签为h1
const VDOM = <h1 className = "wrap"><span style={{fontSize:30}}>{text}</span></h1>
//将虚拟DOM添加到DOM树上
ReactDOM.render(VDOM,document.getElementById('text'));
</script>
- 定义虚拟dom就行写标签一样,不需要在外面在引号包裹
- 标签中混入js表达式时要用{}包裹
- 样式的类名不要用class,而应该用className
- 内联样式要用{{}}包裹,外面一层{}表示jsx语法要用{}包裹,里面的{}指一个样式对象
- 只有一个根标签,其余标签要包裹在根标签中
- 标签闭合
- 标签小写表示html自带标签,标签大写表示为自定义的react组件
jsx练习
因为jsx里面可以使用js表达式,因此可以在{}渲染数据
<!-- 容器 -->
<div id="text"></div>
<!-- 一定要带上babel,告诉编译器里面写的语法需要转换 -->
<script type="text/babel">
let text = ['VUE','REACT','ANGULAR']
//创建 一个虚拟DOM
const VDOM = <h1><ul>{text.map((item,index)=>{
return <li key={index}>{item}</li>
})}</ul></h1>
//将虚拟DOM添加到DOM树上
ReactDOM.render(VDOM,document.getElementById('text'));
</script>
函数式组件
组件可以被复用,把相同功能的模块封装成一个组件,那么就可以按需调用,节省时间和效率
<script type="text/babel">
//创建函数式组件
function Mycomponent(){
return <h1>Hello React</h1>
}
//将虚拟DOM添加到DOM树上
ReactDOM.render(<Mycomponent/>,document.getElementById('text'));
</script>
类的复习
https://blog.csdn.net/wenyeqv/article/details/113180003
类式组件
class Mycomponent extends React.Component {
render() {
return <h1>Hello World</h1>
}
}
ReactDOM.render(<Mycomponent/>,document.getElementById('text'))
第二章 组件核心属性
1.state
state存储状态
state是组件从React中继承的属性,用来存放组件状态
class Mycomponent extends React.Component {
constructor(props){
super(props);
this.state = {
isHot:true
}
}
render(){
return (
<h1>今天的天气很{this.state.isHot?"炎热":"凉爽"}</h1>
)
}
}
//将虚拟DOM添加到DOM树上
ReactDOM.render(<Mycomponent/>,document.getElementById('text'));
state中的this指针问题
还是 上面的案例,如果想要修改state,首先需要获取到state,然后修改它的值,而state在Mycomponent实例中,用this获得,这里就需要时刻注意,每次使用的this,究竟是指什么
class Mycomponent extends React.Component {
constructor(props){
super(props);
this.state = {
isHot:true
}
}
changeW(){
console.log(this)
}
render(){
return (
<h1 onClick={changeW}>今天的天气很{this.state.isHot?"炎热":"凉爽"}</h1>
)
}
}
//将虚拟DOM添加到DOM树上
ReactDOM.render(<Mycomponent/>,document.getElementById('text'));
这里在类中创建一个方法,用来输出this,但是运行会报错,提示找不到changeW,原因是类中的函数将会被放在原型上,用来被类的实例来调用,如果直接写成onClick = { changeW },将会变成直接调用,此时全局没有changeW函数,所以会报错,因此render方法和constructor方法中的this都指向类的实例,所以改成onClick = { this.changeW } ,即可变成实例调用changeW,那么就可以在原型上找到这个函数了。
虽然这个函数可以调用了,但是输出的this却是undefine,这又是为什么呢?
原因很简单,还是那句话,只有类的实例调用类的函数,函数的this才指向类的实例,而这里的this肯定不是类的实例在调用。
我们可以用一个简单的例子来验证
class Mycomponent extends React.Component {
constructor(props){
super(props);
this.state = {
isHot:true
}
}
changeW(){
console.log(this)
}
render(){
return (
<h1 onClick={this.changeW}>今天的天气很{this.state.isHot?"炎热":"凉爽"}</h1>
)
}
}
//将虚拟DOM添加到DOM树上
ReactDOM.render(<Mycomponent/>,document.getElementById('text'));
let m1 = new Mycomponent();
m1.changeW();
let x = m1.changeW;
x();
我们自己来创建Mycomponent的实例 m1,用m1调用changeW,可以成功输出Mycomponent实例,而用x调用,却显示undefine
这就是关键所在
当实例调用类中的函数时,函数中的this指向实例,但是如果在全局声明一个x,将类中的函数的地址赋值给x,然后通过x()的方式 就可以调用类中的函数,这时已经不是通过实例在调用了,而是在全局调用,全局调用应该返回window,而此时又是在babel环境下,其严格遵守严格模式,严格模式下,全局的this为undefine
所以回到原来的代码中 onClick = { this.changeW},这里只是把changeW的地址给了onCLick事件,当onClick事件调用changeW的时候,已经不是实例调用changeW了
要想解决这个问题也很简单,其中一条思路就是利用bind()
constructor(props){
super(props);
this.changeW = this.changeW.bind(this)
this.state = {
isHot:true
}
}
changeW(){
console.log(this)
}
在构造器中,实现这样一行代码:this.changeW = this.changeW.bind(this)
首先看等式右边,this.changeW肯定是在原型链上找到了changeW方法,然后bind(),bind()实现两件事:
- 返回一个函数
- 让函数的指向指向bind()传进去的参数
这里bind()传进去了this,所以整个右边的语句就是返回一个函数,函数的指向为constructor,也就是实例
而左边呢,左边则声明了一个属性,用来接受右边返回的函数。
这样,实例中有了一个changeW属性,实例原型上有了一个changeW方法,而在类组件中,跟具原型链查找规则,肯定是先使用changeW属性,所以最后组件执行的其实是实例的属性,这个属性指向一个方法,这个方法里面的指向为实例。
可以 通过这个方法验证:
class Mycomponent extends React.Component {
constructor(props){
super(props);
this.text = this.changeW.bind(this)
this.state = {
isHot:true
}
}
changeW(){
console.log(this)
}
render(){
return (
<h1 onClick={this.text}>今天的天气很{this.state.isHot?"炎热":"凉爽"}</h1>
)
}
}
//将虚拟DOM添加到DOM树上
ReactDOM.render(<Mycomponent/>,document.getElementById('text'));
改成this.text = this.changeW.bind(this),组件中调用this.text,结果不变
state简写形式
- 因为state属性继承自父类,说明实例中已经有state,只不过是null,所以可以直接赋值
- 自定义的函数几乎全部为回调,不可能通过类的实例进行调用,因为React中只有组件,类的实例过程被隐藏,所以要通过bind()方法,这里可以改成回调函数。
class Mycomponent extends React.Component{
state = {
isHot : true
}
changeW = () =>{
let {isHot} = this.state
isHot = !isHot
this.setState({
isHot:isHot
})
}
render(){
return(
<h1 onClick = {this.changeW}>今天天气很{this.state.isHot?"炎热":"凉爽"}</h1>
)
}
}
ReactDOM.render(<Mycomponent/>,document.getElementById('text'));
2.props基本使用
props主要应用于外部给组件传递参数
class Mycomponent extends React.Component{
render(){
return (
<h1>
<ul>
<li>{this.props.name}</li>
<li>{this.props.age}</li>
<li>{this.props.sex}</li>
</ul>
</h1>
)
}
}
let obj = {
name:'张三',
age:18,
sex:'男'
}
ReactDOM.render(<Mycomponent {...obj}/>,document.getElementById('text'));
可以给props加入相关检验规则,限制传递的参数
class Mycomponent extends React.Component{
static propTypes = {
name:PropTypes.string.isRequired,
sex:PropTypes.string,
age:PropTypes.number
}
static defaultProps = {
sex:'男',
age:18
}
render(){
return (
<h1>
<ul>
<li>{this.props.name}</li>
<li>{this.props.age}</li>
<li>{this.props.sex}</li>
</ul>
</h1>
)
}
}
let obj = {
name:'张三',
age:18,
sex:'男'
}
ReactDOM.render(<Mycomponent {...obj}/>,document.getElementById('text'));
因为函数定义时可以 传递参数,因此函数组件也可以使用props
3. refs用来绑定标签
字符串形式ref
class Mycomponent extends React.Component{
showData = ()=>{
console.log(this.refs)
}
render(){
return(
<div>
<input ref="ipt1" type="text" />
<button onClick = {this.showData}>按钮</button>
<input ref="ipt2" type="text" />
</div>
)
}
}
ReactDOM.render(<Mycomponent/>,document.getElementById("text"))
回调形式的ref
在实例自身上挂在节点
内联函数在初次渲染的时候会在{}中执行一次,但是在更新过程会执行两次,第一次传入参数null,第二次 还是标签,原因是创建新的实例的时候会将上一次的ref挂载注销,所以会先null,而类绑定的函数方式则没有这种影响
<script type="text/babel">
class Mycomponent extends React.Component{
showData = ()=>{
console.log(this)
}
render(){
return(
<div>
<input ref= {c => this.inp1 = c}type="text" />
<button onClick = {this.showData}>按钮</button>
</div>
)
}
}
ReactDOM.render(<Mycomponent/>,document.getElementById("text"))
</script>
钩子函数创建ref
创建一个ref容器只能给一个节点用
class Mycomponent extends React.Component{
myRef = React.createRef()
showData = ()=>{
console.log(this.myRef)
}
render(){
return(
<div>
<input ref = {this.myRef}type="text" />
<button onClick = {this.showData}>按钮</button>
</div>
)
}
}
ReactDOM.render(<Mycomponent/>,document.getElementById("text"))
4. 高阶函数(函数柯里化)
- 高阶函数:将函数作为参数或者返回值的函数。
- 如:setInterval、Promis、array.map
- 函数柯里化:把接受多个参数的函数变为接受单个参数的函数,并且返回参数 的计算结果
const sum = (a) => { return (b)=>{ return (c) => { return a+b+c; } } } console.log(sum(1)(2)(3));
-
react中运用高阶函数的案例
class Mycomponent extends React.Component {
state = {
name:'',
password:''
}
show = (name) =>{
return (e) =>{
this.setState({
[name]:e.target.value
})
}
}
render(){
return (
<form>
<div>
<label >name:</label>
<input type="text" value={this.state.name} onChange={this.show('name')} / >
</div>
<div>
<label >password:</label>
<input type="text" value={this.state.password} onChange={this.show('password')} />
</div>
</form>
)
}
}
ReactDOM.render(<Mycomponent/>,document.getElementById('text'));
不用柯里化的写法:
class Mycomponent extends React.Component {
state = {
name:'',
password:''
}
show = (name,e) =>{
this.setState({
[name]:e.target.value
})
}
render(){
return (
<form>
<div>
<label >name:</label>
<input type="text" value={this.state.name} onChange={(e)=>{this.show('name',e)}} / >
</div>
<div>
<label >password:</label>
<input type="text" value={this.state.password} onChange={(e)=>{
this.show('password',e)
}} />
</div>
</form>
)
}
}
ReactDOM.render(<Mycomponent/>,document.getElementById('text'));
第三章 组件的生命周期
基本上旧版本的生命周期就按照上图所示,值得注意的是:
- shouldComponentUpdata 默认返回true
- componentWillReceiveProps在子组件第一次挂在接受props不会调用,在后续传入新的props是才调用
新的生命周期和旧的生命周期相比,废除了三个钩子,新加了两个钩子。