学习视频地址:https://www.bilibili.com/video/BV1wy4y1D7JT
1. React简介
- React是用于构建用户界面的javascript库。
- 由Facebook开发,且开源。
- React的特点:
1.采用组件化模式、声明式编码,提高开发效率及组件复用率。
2.在React Native中可以使用React语法进行移动端开发。
3.使用虚拟DOM+优秀的Diffing算法,尽量减少与真实DOM的交互。
注:
- 虚拟DOM会比较新、旧两个虚拟DOM,只渲染改变的部分(当数据量大时效率更高)。
- 而原生js实现(直接操作真实DOM)没有虚拟DOM这个环节,会重新渲染整个页面。
2. React的基本使用
- 准备好一个容器
- 引入3个依赖 (react、react-dom、babel)
- 创建虚拟DOM,并渲染到页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello_react</title>
</head>
<body>
<!-- 1.准备好一个容器 -->
<div id="test"></div>
<!-- 2.引入依赖 -->
<!-- react核心库,必须在react-dom扩展库之前引入 -->
<script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<!-- react-dom扩展库,用于支持React操作DOM -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script>
<!-- 将jsx翻译为js(浏览器本身不能识别jsx) -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 3.创建虚拟DOM,并渲染到页面 -->
<!-- type="text/babel"表示内部写的是jsx,并且用babel翻译(不写默认是js) -->
<script type="text/babel">
// 3.1.创建虚拟DOM,jsx语法
const VDOM = (
<h1>
hello react
</h1>
)
// 3.2.渲染虚拟DOM到页面(这是一个替换页面的过程)
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
</body>
</html>
2.1 虚拟DOM的两种创建方式
1.使用jsx创建
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
const VDOM = (
<h1 id="title">
hello react
</h1>
)
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
2.使用js创建
<script type="text/javascript">
// React.createElement(标签名, 标签属性, 标签内容)
const VDOM = React.createElement('h1', {id: 'title'}, 'Hello React')
// 2.渲染虚拟DOM到页面(这是一个替换页面的过程)
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
注:
使用js创建虚拟DOM,不用babel翻译,但是写法繁琐。
而jsx是这种写法的语法糖,通过babel翻译后的形式与js创建虚拟DOM的形式相同。
2.2 虚拟DOM与真实DOM
- 虚拟DOM
1.本质是Object类型的对象
2.虚拟DOM比较"轻",真实DOM比较"重",因为虚拟DOM是React内部在用,无需真实DOM那么多属性
3.虚拟DOM最终会被React转化为真实DOM,呈现在页面上
2.3 jsx
-
定义虚拟DOM时,不用写引号
-
标签中混入js表达式时要用{}
注:
{}中只能写js表达式,不能写js语句
- 表达式:一个表达式会产生一个值。(下面这些都是表达式)
a
a+b
demo(1)
arr.map()
function text(){}- 语句:(下面这些都是语句)
if(){}
for(){}
.switch(){case:XXXX}
-
样式的类名指定不要用class,要用className
-
内联样式,要用style={{key:value}}的形式去写
-
只有一个根标签
-
标签必须闭合
-
标签首字母
(1).若小写字母开头,则将该标签转为html中同名元素,若html中没有,则报错
(2).若大写字母开头,则将该标签转为组件,若组件没有定义,则报错
示例:
<script type="text/babel">
const myId = "hello"
const VDOM = (
<div>
<h1 className="myH1" id={myId.toLowerCase()} style={{color:'red',fontSize:'20px'}}>
hello react
</h1>
{
//浏览器报错,html标签中没有good标签
}
<good>123</good>
{
//Good为组件,需要定义该组件,否则报错
}
<Good>123</Good>
</div>
)
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
2.3.1 jsx小练习
动态渲染数据至页面
<script type="text/babel">
const data = ['angular', 'react', 'vue']
const VDOM = (
<ul>
{
data.map((r,i) => <li key={i}>{r}</li>)
}
</ul>
)
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
2.4 模块化与组件化
模块:向外提供特定功能的js程序,一般就是一个js文件
组件:实现局部功能效果的代码和资源的集合(html、css、js、img、vedio、font…)
2.5 定义组件
2.5.1 函数式组件
简单组件
function Demo(){
console.log(this) //此处的this是undefined,因为babel编译后开启了严格模式
return <h2> 函数式组件(简单组件)</h2>
}
// 解析组件标签,找到组件并调用该函数,将虚拟DOM转化为真实DOM,并渲染到页面
ReactDOM.render(<Demo/>, document.getElementById('test'))
2.5.2 类式组件
复杂组件:有状态
class Demo extends React.Component{
// render放在类的原型对象上,供实例使用
render(){
// this指向Demo组件 实例对象
console.log(this)
return <h2> 类式组件(复杂组件)</h2>
}
}
// 解析组件标签,找到组件并new出该类的实例,并通过该实例调用原型上的render方法,将虚拟DOM转化为真实DOM,并渲染到页面
ReactDOM.render(<Demo/>, document.getElementById('test'))
注:
- 类中的构造器不是必须写的。要对实例进行一些初始化操作,如添加指定属性时才写。
- 如果A类继承了B类,且A类中写了构造器,那么A类构造器中必须调用super。
- 类中定义的方法,都是放在类的原型对象上,供实例去使用。
类的相关知识点示例:
class Person{
// 构造方法
constructor(name, age){
// 构造器中的this指向类的实例对象
this.name = name
this.age = age
}
// 一般方法
// speak方法放在类的原型对象上,供实例使用
// 通过Person实例调用speak方法时,speak中的this就是Person实例
speak(){
console.log(`my name is ${this.name}, my age is ${this.age}`)
}
}
const p1 = new Person('tom', 12)
p1.speak()
class Student extends Person{
constructor(name, age, sex){
super(name, age)
this.sex = sex
}
// 重写从父类继承过来的方法
// 通过Student实例调用speak方法时,speak中的this就是Student实例
speak(){
console.log(`my name is ${this.name}, my age is ${this.age}, I am a ${this.sex}`)
}
}
const s1 = new Student('tim', 13, 'girl')
s1.speak()
2.6 组件实例的三大核心属性
由class创建的组件,才有组件实例,才有三大核心属性
2.6.1 state
用途:存储组件自身的数据
获取状态数据:this.state.xxx
更改状态数据:this.setState({xxx:xxx})
,传入的对象会合并原先的state对象
class Demo extends React.Component{
//构造器执行1次,初始化状态,解决this指向问题
constructor(props){
super(props)
this.state = {
isHot: false
}
// 这里的this是组件实例对象
// 将原型链中的this.btnClick方法挂载到了实例自身上
this.btnClick = this.btnClick.bind(this)
}
btnClick(){
//btnClick放在原型对象上
//通过Demo实例调用btnClick时,this才指向Demo实例
//调用该方法时btnClick是onClick的回调,不是通过实例调用的,是直接调用,this会丢失
//由于类中的方法默认开启了严格模式,所以this指向undefined
this.setState({
//合并
isHot: !this.state.isHot
})
}
// render放在类的原型对象上,供实例使用
// onClick必须绑定一个函数,不能是函数调用表达式
// render调用1+n次,1是初次渲染,n是状态更新的次数
render(){
// render中的this就是组件实例对象
console.log(this)
const {isHot} = this.state
return (
<div>
<h2>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
<button onClick={this.btnClick}>切换</button>
</div>
)
}
}
// 解析组件标签,找到组件并new出该类的实例,并通过该实例调用原型上的render方法,将虚拟DOM转化为真实DOM,并渲染到页面
ReactDOM.render(<Demo/>, document.getElementById('test'))
注:
this指向
- onClick方法绑定的是函数,不能写成下面这种形式,下面这种形式会直接调用(调用的话会在render页面的时候就触发,变为绑定undefined方法)
onClick={this.btnClick()}
- onCLick绑定方法时,是回调函数,是直接调用,不是实例调用,所以this会丢失
- this.btnClick = this.btnClick.bind(this)会把原型上的btnClick 方法绑定到实例上。并在实例对象上生成一个新的btnClick 方法,从而覆盖原型上的btnClick 方法。
效果:
点击切换
简写:
- 简写state
类中可以直接写赋值语句。
通过赋值语句为state赋值,即直接在实例对象上添加了state属性 - 简写方法
通过赋值语句添加方法,即直接在实例对象上添加了方法
通过箭头函数添加方法时,方法中的this指向实例对象
class Demo extends React.Component{
//添加到实例对象上(state是一个对象)
state = {
isHot: false
}
// 自定义方法----赋值语句+箭头函数
btnClick = () => {
//箭头函数中的this找到箭头函数外部的this,为Demo的实例对象
this.setState({
isHot: !this.state.isHot
})
}
render(){
const {isHot} = this.state
return (
<div>
<h2>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
<button onClick={this.btnClick}>切换</button>
</div>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))
2.6.2 props
只读,不能修改
用途:接收传入组件的参数
获取props:this.props.xxx
class Demo extends React.Component{
render(){
const {name, sex, age} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}
}
const p = {name:'zs', sex:'女', age:'18'}
//批量传递标签属性(props)
ReactDOM.render(<Demo {...p} />, document.getElementById('test'))
备注:(展开运算符)
let person = {name:'zs', sex:'女', age:'18'}
console.log(...person) //报错,展开运算符不能展开对象
let person2 = {...person} //可以复制一个对象
2.6.2.1 对props进行类型限制,设置默认值
我的限制没出现警告,不知道为啥???
<!-- 要使用PropTypes,必须引入PropTypes的包 -->
<script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>
<script type="text/babel">
class Demo extends React.Component{
render(){
const {name, sex, age} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
}
//对props进行限制,这里propTypes的p小写
Demo.propTypes = {
// 要使用PropTypes,必须引入PropTypes的包,这里PropTypes的P大写
name: PropTypes.string.isRequired,
sex: PropTypes.string, //第一个字母都是小写,不会与内置的String冲突
age: PropTypes.number,
speak: PropTypes.func //function是关键字,这里改写成func
}
Demo.defaultProps = {
sex: '不详',
age: 18
}
const p = {sex:'女'}
ReactDOM.render(<Demo {...p} />, document.getElementById('test'))
</script>
简写:
通过static关键字,可以为组件类自身添加属性
class Demo extends React.Component{
// 添加static关键字,给Demo自身添加属性
static propTypes = {
// 要使用PropTypes,必须引入PropTypes的包
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func
}
static defaultProps = {
sex: '不详',
age: 18
}
render(){
const {name, sex, age} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
}
2.6.2.2 函数式组件使用props
function Demo(props){
const {name, sex, age} = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
//对props进行限制,这里propTypes的p小写
Demo.propTypes = {
// 要使用PropTypes,必须引入PropTypes的包,这里PropTypes的P大写
name: PropTypes.string.isRequired,
sex: PropTypes.string, //第一个字母都是小写,不会与内置的String冲突
age: PropTypes.number,
speak: PropTypes.func //function是关键字,这里改写成func
}
Demo.defaultProps = {
sex: '不详',
age: 18
}
const p = {name:'zs', sex:'女', age:12}
ReactDOM.render(<Demo {...p} />, document.getElementById('test'))