目录
基本理解和使用
React面向组件编程
函数式组件:
此处this是undefined babel编译后开启了严格模式
执行了 ReactDOM.render(<Demo />, document.getElementById('test'))之后
- React解析组件标签,找到Demo组件
- 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM呈现在页面中
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom 用于react操作dom -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel 用于jsx转 js-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 一定要写babel -->
<script type="text/babel">
//创建函数式组件
function Demo() {
console.log(this);//undefined
return <h2>我是一个函数定义的组件(适用于[简单组件]的定义)</h2>
}
//渲染组件到组件
ReactDOM.render(<Demo />, document.getElementById('test'))
</script>
注意
- 组件名必须首字母大写标签
- 虚拟DOM元素只能有一个根元素
- 虚拟DOM元素必须有结束标签
- 函数必须有返回值
复习类的相关知识
- 类中的构造器,不是必须写的,要对实例进行一些初始化的操作 如添加指定属性时才写
- 如果子类继承了父类,且子类写了构造器,那么子类构造器中super是必须要定义的
- 类中所定义的方法,都放在了类的原型对象上供实例使用
- a=1 类中可以直接写赋值语句 给实例对象添加一个属性名为a 值为1
<script>
//创建一个parson类
class Parson {
constructor(name, age) {
//构造器中的this是实例对象
this.name = name
this.age = age
}
a=1//类中可以直接写赋值语句 给实例对象添加一个属性名为a 值为1
// 一般方法
speak() {
// spak 放在了类的原型对象上,供实例使用的
// 通过parson实例调用speak时,speak中的this就是parson实例
console.log(`我叫${this.name}我的年龄是${this.age}`);
}
}
// 创建一个Student类,继承于Parson类
class Student extends Parson {
constructor(name, age, grade) {
//继承属性必须写super
super(name, age)
this.grade = grade
}
speak() {
console.log(`我叫${this.name}我的年龄是${this.age},我的年级是${this.grade}`);
}
study() {
// study 放在了类的原型对象上,供实例使用的
// 通过Student实例调用study时,study中的this就是Student实例
console.log('我很努力的学习');
}
}
</script>
类式组件:
必须继承React.Component,要有返回值
必须写render rouder放在类的原型对象上,供实例实用 this是 组件的实例对象
执行了 ReactDOM.render(<Demo />, document.getElementById('test'))之后
- React解析组件标签,找到Demo组件
- 发现组件是使用类定义的,随后new出该类的实例,并通过该实例调用到原型上的render方法
- 将render返回的虚拟DOM转为真实DOM随后呈现在页面中
<script type="text/babel">
//创建类组件
class Demo extends React.Component {
// rouder放在类的原型对象上,供实例实用
// this是 Demo 的实例对象
render() {
return <h2>我是一个类定义的组件(适用于[复杂组件]的定义)</h2>
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))
组件实例的三大核心属性
类中方法的this指向
通过实例调用this为实例
把方法赋值给x变量相当于把方法赋值给了x变量 这时this发生变化(类中的方法开启局部严格模式)所以为undefined
bind可以生成一个新函数改变this指向
//创建一个parson类
class Parson {
constructor(name, age) {
this.name = name
this.age = age
}
speak() {
// spak 放在了类的原型对象上,供实例使用的
// 通过parson实例调用speak时,speak中的this就是parson实例
console.log(this);
}
}
const p1 = new Parson()
p1.speak()//通过实例调用speak方法
const x = p1.speak
x() //undefined
state
- state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
- 组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
强烈注意
- 组件中render方法中的this为组件实例对象
- 组件自定义的方法中this为undefined,如何解决?
- 强制绑定this: 通过函数对象的bind()
- 箭头函数
- 状态数据,不能直接修改或更新,要使用 React.Component 父类提供的setState方法
- 构造器执行一次,renderd调用1+n次 1是初始化 n是状态更新的次数 方法点几次调用几次
需求: 定义一个展示天气信息的组件 默认展示天气炎热 或 凉爽 点击文字切换天气
完整版
<script type="text/babel">
class Weather extends React.Component {
//构造器执行一次
constructor(props) {
super(props)
this.state = { isHot: true }
//解决change中的this指向问题(把右侧类中的change修改后的新函数赋值给change)
this.change = this.change.bind(this)//this为实例
}
// renderd调用1+n次 1是初始化 n是状态更新的次数
render() {
// 读取状态
console.log(this);//组件的实例对象
const { isHot } = this.state
return <h2 onClick={this.change} > 天气很{isHot ? '炎热' : '凉爽'}</h2 >
}
//点几次调用几次
change() {
//change放在 Weather 的实例对象上
//由于 change 是作为onClick的回调所以不是通过实例调用的,是直接调用
//类中的方法默认开启局部严格模式所以change中的this为 undefined
//获取原来的 isHot 值
const isHot = this.state.isHot
// 状态数据,不能直接修改或更新,要使用 React.Component 父类提供的setState方法.且更新是一种合并,不是替换
this.setState({ isHot: !isHot })
// //状态不可以直接更改
// this.state.isHot = !isHot
}
}
ReactDOM.render(<Weather />, document.getElementById('test'))
简化版
<!-- 一定要写babel -->
<script type="text/babel">
class Weather extends React.Component {
//初始化状态 给实例对象添加
state = { isHot: true }
render() {
const { isHot } = this.state
return <h2 onClick={this.change} > 天气很{isHot ? '炎热' : '凉爽'}</h2 >
}
//自定义方法--->赋值语句+箭头函数,放在实例身上
change = () => {
const isHot = this.state.isHot
this.setState({ isHot: !isHot })
}
}
ReactDOM.render(<Weather />, document.getElementById('test'))
</script>
props
- 每个组件对象都会有props(properties的简写)属性
- 组件标签的所有属性都保存在props中
- 通过标签属性从组件外向组件内传递变化的数据
- 注意: 组件内部不要修改props数据
展开运算符
- [...arr1, ...arr2] 连接数组
- let obj2 = { ...obj } 构建字面量对象时使用展开运算符,展开运算符不能展开对象
- 合并 let obj3 = { ...obj, name: 'lisa', address: '地球' }
- reduce 依次迭代数组每一项,他会把上一次迭代中,回调函数处理的结果传递给下一次迭代,以此实现结果的累积 a之前的值,b当前的值 有返回值
<script>
let arr1 = [1, 3, 5, 7, 9]
let arr2 = [2, 4, 6, 8, 10]
console.log(...arr1);//展开一个数组
let arr3 = [...arr1, ...arr2]
console.log(arr3);//连接数组
//在函数中使用
function sum(...numbers) {
// 依次迭代数组每一项,他会把上一次迭代中,回调函数处理的结果传递给下一次迭代,以此实现结果的累积 a之前的值,b当前的值 有返回值
return numbers.reduce((a, b) => {
console.log(a, b);
return a + b
})
}
console.log(sum(sum(1, 2, 3, 4)));
//构建字面量对象时使用展开运算符
let obj = { name: 'tom', age: 18 }
// console.log(...obj);//展开运算符不能展开对象
let obj2 = { ...obj }
console.log(obj2);//克隆一个对象
//合并
let obj3 = { ...obj, name: 'lisa', address: '地球' }
console.log(obj3);
</script>
编码操作
- 内部读取某个属性值 this.props.name
- 对props中的属性值进行类型限制和必要性限制
3.扩展属性: 将对象的所有属性通过props传递 <Person{...p} />
4. 默认属性值:
5.组件类的构造函数
效果
需求: 自定义用来显示一个人员信息的组件
- 姓名必须指定,且为字符串类型;
- 性别为字符串类型,如果性别没有指定,默认为男
- 年龄为字符串类型,且为数字类型,默认值为18
完整版
<!-- prop-types 用于对组件标签属性进行限制 -->
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom 用于react操作dom -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel 用于jsx转 js-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- prop-types 用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- 一定要写babel -->
<script type="text/babel">
class Person extends React.Component {
render() {
console.log(this);
const { name, age, sex } = this.props
// this.props.name = 'ls' 此处会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
}
const p = { name: 'tom', age: 18, sex: '女' }
//对标签属性进行类型,必要性的限制
Person.propTypes = {
name: PropTypes.string.isRequired,//限制name 必传,且为字符串
age: PropTypes.number,
sex: PropTypes.string,
speak: PropTypes.func,//限制 speak 为函数
}
//指定默认标签属性值
Person.defaultProps = {
sex: '女',//sex默认值为女
age: 18
}
// ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex} />, document.getElementById('test'))
ReactDOM.render(<Person{...p} speak={speak} />, document.getElementById('test'))
function speak() {
console.log('我说话了');
}
简写
- status={}直接在类里写相当于给实例对象添加属性方法
- static 相当于给类加属性和方法
<script type="text/babel">
class Person extends React.Component {
render() {
console.log(this);
const { name, age, sex } = this.props
// this.props.name = 'ls' 此处会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
//对标签属性进行类型,必要性的限制
static propTypes = {
name: PropTypes.string.isRequired,//限制name 必传,且为字符串
age: PropTypes.number,
sex: PropTypes.string,
speak: PropTypes.func,//限制 speak 为函数
}
//指定默认标签属性值
static defaultProps = {
sex: '女',//sex默认值为女
age: 18
}
}
const p = { name: 'tom', age: 18, sex: '女' }
ReactDOM.render(<Person{...p} speak={speak} />, document.getElementById('test'))
function speak() {
console.log('我说话了');
}
类中的构造器
//构造器中是否接收props 是否传递给super 取决于是否希望在构造器中通过this访问props
class Person extends React.Component {
constructor(props) {
//构造器中是否接收props 是否传递给super 取决于是否希望在构造器中通过this访问props
super(props)
console.log(this.props);
}
//对标签属性进行类型,必要性的限制
static propTypes = {
name: PropTypes.string.isRequired,//限制name 必传,且为字符串
age: PropTypes.number,
sex: PropTypes.string,
}
//指定默认标签属性值
static defaultProps = {
sex: '女',//sex默认值为女
age: 18
}
render() {
console.log(this);
const { name, age, sex } = this.props
// this.props.name = 'ls' 此处会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
}
ReactDOM.render(<Person name="lisa" />, document.getElementById('test'))
函数式组件使用props
可以通过形参接收props
限制标签属性要写在外面,函数组件没有static
<script type="text/babel">
function Person(props) {
const { name, age, sex } = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
Person.propTypes = {
name: PropTypes.string.isRequired,//限制name 必传,且为字符串
age: PropTypes.number,
sex: PropTypes.string,
speak: PropTypes.func,//限制 speak 为函数
}
//指定默认标签属性值
Person.defaultProps = {
sex: '女',//sex默认值为女
age: 18
}
ReactDOM.render(<Person name="lisa" />, document.getElementById('test'))
</script>
符串形式的ref
- 组件内的标签可以定义ref属性来标识自己
- refs获取真实dom元素
- 字符串的ref存在效率问题不推荐使用
//创建类组件
class Demo extends React.Component {
render() {
return (
<div>
<input ref="input1" type="text" placeholder="点我提示数据" />
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref="input2" type="text" onBlur={this.showData2} placeholder="失去焦点提示数据" />
</div>
)
}
//展示左侧输入框数据
showData = () => {
console.log(this.refs);
const { input1 } = this.refs
alert(input1.value)
}
//展示右侧输入框数据
showData2 = () => {
console.log(this.refs);
const { input2 } = this.refs
alert(input2.value)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))
回调形式的ref
- 回调 自己定义的,自己没调用 函数最终执行了
- 把ref当前所在节点挂在实例自身上 const { input2 } = this
- 如果
ref
回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数null
,然后第二次会传入参数 DOM 元素,这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的 - 过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,
<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="点我提示数据" />
<input onBlur={this.showData2} ref={this.saveInput} type="text" placeholder="点我提示数据" />
</div>
saveInput = (c) => {
this.input = c
console.log('@', c);
}
createRef创建ref容器·
- //React.createRef 调用后可以返回一个容器,该容器可以存储被ref所标识的节点
- //该容器是专人专业的
class Demo extends React.Component {
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点我提示数据" />
<button onClick={this.showData}>点我提示左侧的数据</button>
</div>
)
}
//React.createRef 调用后可以返回一个容器,该容器可以存储被ref所标识的节点
//该容器是专人专业的
myRef = React.createRef()
//展示左侧输入框数据
showData = () => {
console.log(this.myRef.current.value);
}
}
ReactDOM.render(<Demo />, document.getElementById('test'))