// 1. 创建函数式组件,组件的首字母必须大写
function MyComponent(){
//此处的this是undefined,因为babel编译后开启了严格模式
console.log(this);
return <h2>我是用函数定义的组件(适用于简单组件的定义)</h2>
}
// 2. 渲染组件到页面
ReactDOM.render(<MyComponent/>, document.getElementById('test'))
执行了ReactDOM.render(…之后,发生了什么?
React解析组件标签,找到了MyComponent组件。
发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
function sayThis() {
console.log(this)
}
sayThis() // Window {...}
function sayThis2() {
'use strict'
console.log(this)
}
sayThis2() // undefined
如果不开启严格模式,直接调用函数,函数中的this指向window
如果开启了严格模式,直接调用函数,函数中的this是undefined
对象的
__proto__
==类的prototype
//1.创建类式组件
class MyComponent extends React.Component {
render(){
//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
//render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
console.log('render中的this:',this);
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
执行了ReactDOM.render(…之后,发生了什么?
state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
需求: 定义一个展示天气信息的组件
默认展示天气炎热 或 凉爽
点击文字切换天气
类式组件,在构造器中 初始化状态,在render中通过this.state 读取状态
// 1. 创建组件
class Weather extends React.Component{
constructor(props) {
super(props)
// 初始化状态
this.state = {
isHot: true
}
}
render() {
// 读取状态并使用 this.state.isHot
return <h1>今天天气很{this.state.isHot?'炎热':'凉爽'}</h1>
}
}
// 2. 渲染组件到页面
ReactDOM.render(<Weather/>, document.getElementById('test'))
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<button onclick="demo()">按钮3</button>
<script type="text/javascript" >
const btn1 = document.getElementById('btn1')
btn1.addEventListener('click',()=>{
alert('按钮1被点击了')
})
const btn2 = document.getElementById('btn2')
btn2.onclick = ()=>{
alert('按钮2被点击了')
}
function demo(){
alert('按钮3被点击了')
}
</script>
//1.创建组件
class Weather extends React.Component{
//构造器调用几次? ———— 1次
constructor(props){
console.log('constructor');
super(props)
//初始化状态
this.state = {isHot:false,wind:'微风'}
//解决changeWeather中this指向问题
this.changeWeather = this.changeWeather.bind(this)
}
//render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
render(){
console.log('render',this);
//读取状态
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
//changeWeather调用几次? ———— 点几次调几次
changeWeather(){
//changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
//由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
//类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
console.log('changeWeather');
//获取原来的isHot值
const isHot = this.state.isHot
//严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
this.setState({isHot:!isHot})
console.log(this);
//严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
//this.state.isHot = !isHot //这是错误的写法
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
可以不写构造器,类中直接写赋值语句来初始化状态
不用bind来绑定this(赋值语句的形式+箭头函数)
//1.创建组件
class Weather extends React.Component{
//初始化状态
state = {isHot:false,wind:'微风'}
render(){
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
//自定义方法————要用赋值语句的形式+箭头函数
changeWeather = ()=>{
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
组件中render方法中的this为组件实例对象
组件自定义的方法中this为undefined,如何解决?
a) 强制绑定this: 通过函数对象的bind()
b) 箭头函数 + 赋值语句
状态数据state,不能直接修改或更新
状态必须通过setState()进行更新, 且更新是一种合并,不是替换。
每个组件对象都会有props(properties的简写)属性
组件标签的所有属性都保存在props中
通过标签属性从组件外向组件内传递变化的数据
注意: 组件内部不可修改props数据,是只读的
// 创建组件
class Person extends React.Component{
render() {
const {name,age,sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
// 渲染组件到页面上
ReactDOM.render(<Person name="jerry" age={19} sex="男"/>,document.getElementById('test1'))
ReactDOM.render(<Person name="tom" age={18} sex="女"/>,document.getElementById('test2'))
this.props.name
ReactDOM.render(<Person name="yk" age="18" sex="男"/>, document.getElementById('test'))
const person = {name: 'yk', age: 18, sex: '男'}
// { ...person }: bable+react可以让...解构对象
ReactDOM.render(<Person { ...person }/>, document.getElementById('test'))
let arr1 = [1,3,5,7,9]
let arr2 = [2,4,6,8,10]
console.log(...arr1); //展开一个数组 1 3 5 7 9
let arr3 = [...arr1,...arr2]//连接数组
console.log(arr3); // [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
//在函数中使用
function sum(...numbers){
return numbers.reduce((preValue,currentValue)=>{
return preValue + currentValue
})
}
console.log(sum(1,2,3,4)); // 10
//构造字面量对象时使用展开语法
let person = {name:'tom',age:18}
let person2 = {...person} //克隆
console.log(person2); //{name: 'tom', age: 18}
person.name = 'jerry'
console.log(person); //{name: 'jerry', age: 18}
//合并
let person3 = {...person,name:'jack',address:"地球"}
console.log(person3); //{name: 'jack', age: 18, address: '地球'}
Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number
}
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
//对标签属性进行类型、必要性的限制
Person.propTypes = {
name:PropTypes.string.isRequired, // 限制name必传,且为字符串
sex:PropTypes.string, // 限制sex为字符串
age:PropTypes.number, // 限制age为数值
speak:PropTypes.func, // 限制speak为函数
}
可以写在类的里面,前面加static关键字
//指定默认标签属性值
Person.defaultProps = {
sex:'男', // sex默认值为男
age:18 //age默认值为18
}
可以写在类的里面,前面加static关键字
props是只读的
//创建组件
function Person (props){
const {name,age,sex} = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}
Person.propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string, //限制sex为字符串
age:PropTypes.number, //限制age为数值
}
// 指定默认标签属性值
Person.defaultProps = {
sex:'男',// sex默认值为男
age:18 // age默认值为18
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
组件内的标签可以定义ref属性来标识自己
需求: 自定义组件, 功能说明如下
点击按钮, 提示第一个输入框中的值
当第2个输入框失去焦点时, 提示这个输入框中的值
1. 定义
<input ref="input1"/>
2. 使用
this.refs.input1
3. 示例
//创建组件
class Demo extends React.Component{
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this.refs
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this.refs
alert(input2.value)
}
render(){
return(
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo />,document.getElementById('test'))
1. 定义
<input ref={(currentNode)=>{this.input1 = currentNode}} />
简写一下
<input ref={ c => this.input1 = c } />
2. 使用
this.input1
3. 示例
//创建组件
class Demo extends React.Component{
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this
alert(input2.value)
}
render(){
return(
<div>
<input ref={ c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} ref={c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo />,document.getElementById('test'))
4. 回调执行次数
内联的回调,渲染时调用一次,每次更新都会执行两次
类绑定的回调,就在初始渲染时调用一次
影响不大,日常开发基本都用内联,方便一点
1. 定义
// React.createRef调用后可以返回一个容器
// 该容器可以存储被ref所标识的节点,该容器是“专人专用”的
myRef = React.createRef()
<input ref={this.myRef}/>
2. 使用
this.myRef.current
3. 示例
//创建组件
class Demo extends React.Component{
myRef = React.createRef()
myRef2 = React.createRef()
//展示左侧输入框的数据
showData = ()=>{
console.log(this.myRef)
alert(this.myRef.current.value);
}
//展示右侧输入框的数据
showData2 = ()=>{
alert(this.myRef2.current.value);
}
render(){
return(
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo />,document.getElementById('test'))
console.log(this.myRef)
通过onXxx属性指定事件处理函数(注意大小写)
参考:
面向组件编程 组件实例的三大核心属性—state 状态