前言
【内容】
【目的】
【学习资源】
- 【视频资源】
- 【参考笔记】
初识React
React的基本使用
依赖包
文件名 | 作用 |
---|---|
babel.min.js | 1、ES6转ES5,2、js转jsx |
react.development.js | react核心库 |
react-dom.development.js | react扩展库(功能之一react操作DOM) |
如何引入依赖包?
-
首先在项目中创建js文件夹
-
将依赖包拖入js文件夹
-
在页面文件中引入依赖包
【==注意==】引入页面文件中需要按顺序引入
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
* 【解释】核心库必须先就位了,扩展库才能进一步去引入,否则找不到东西
### 如何运行第一个React?
【示例】
````html
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入reat-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>
<script type="text/babel">/* 此处一定要写babel*/
// 1.创建虚拟DOM
const VDOM =<h1>Hello,React</h1>/* 此处一定不要写引号,因为不是字符串*/
// 2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
常见问题
没有准备容器
【报错类型】所给的容器不是DOM结点
创建虚拟DOM时使用引号
【错误示例】上述代码第13行,使用引号
const VDOM ="<h1>Hello,React</h1>"
-
【效果】
【正确示例】上述代码第13行,不使用引号
const VDOM =<h1>Hello,React</h1>
-
【效果】
脚步类型不是babel
【错误示例】
<script type="text/javascript">/* 此处没改babel*/
// 1.创建虚拟DOM
const VDOM =<h1>Hello,React</h1>
// 2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
-
【报错类型】语法错误,不认识
<
创建虚拟DOM的两种方式
1、纯JS方式(一般不用)
【示例】创建一个id
为title
内容为Hello,React
的h1
标签
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入reat-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript">
// 1.创建虚拟DOM
const VDOM =React.createElement("h1",{id:'title'},'Hello,React')
// 2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
【拓展】
createElement()
的用法
【语法】
createElement('标签名',{标签属性},'标签内容')
【区别】
document.createElement()
和React.createElement()
的区别
document.createElement()
React.createElement()
创建真实DOM 创建虚拟DOM
【示例】若在上述案例的基础上在h1
标签内套入span
标签
//其他代码于上述代码一样
//第11行代码改为
const VDOM =React.createElement("h1",{id:'title'},React.createElement("span",{},'Hello,React')
2、JSX方式
【示例】创建一个id
为title
内容为Hello,React
的h1
标签
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入reat-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>
<script type="text/babel">/* 此处一定要写babel*/
// 1.创建虚拟DOM
const VDOM =<h1 id="title" >Hello,React</h1>/* 此次一定不要写引号,因为不是字符串*/
// 2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
【示例】若在上述案例的基础上在h1
标签内套入span
标签
//其他代码于上述代码一样
//第13行代码改为
const VDOM =(
<h1 id="title" >
<span>Hello,React</span>
</h1>
)
【==注意==】
-
浏览器并不认识JSX(下图蓝色框所示)代码
-
通过
babel
(上图红色圈)翻译成JS创建虚拟DOM(如下图蓝框所示)代码- 浏览器最终读的是JS代码
-
JSX用来干啥?(存在的意义)
-
帮助开发人员更好的创建虚拟DOM,写起来更流畅
-
【解释】通过上述JSX方式创建多层嵌套虚拟DOM案例与JS方式创建多层嵌套虚拟DOM案例就体现出JSX创建虚拟DOM的方便性。
-
JSX里面这种写法创建虚拟DOM其实就是原始JS创建虚拟DOM的语法糖
-
【拓展】啥是语法糖?
可以简单的理解为一种简写方式,更加便捷的方式
-
虚拟DOM与真实DOM
关于虚拟DOM
-
本质是Object类型的对象(一般对象)
console.log('虚拟DOM',VDOM);
-
虚拟DOM比较**“轻”,真实DOM比较“重”**
【解释】因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
-
【图示】虚拟DOM属性
-
【图示】真实DOM属性
-
-
虚拟DOM最终会被React转化为真实DOM,呈现在页面上
React JSX
啥是JSX?
-
全称: JavaScript XML
-
react定义的一种类似于XML的JS扩展语法: JS + XML
【拓展】啥是XML?
-
早期用于存储和传输数据
-
【示例】
<student>
<name>Tom</name>
<age>19</age>
</student>-
【不足之处】实际上需要的数据只是Tom和19,但是标签比数据还要多。于是就演变出JSON来存储和传输数据。
【示例】上述的XML数据写成JSON
"{"name":"Tom","age":19}"
- JSON中常用的方法
- parse:奖JSON字符串解析成JS对应的数组和对象
- stringfy:将JS中的字符串转成JSON中的对象
- JSON中常用的方法
-
-
JSX语法规则
-
定义虚拟DOM时,不要写引号。
【解释】之前对创建虚拟DOM时使用引号有提到
-
标签中混入==JS表达式==时要用
{}
。【示例】通过用
toLowerCase
方法修改虚拟DOM的id
,通过用toUpperCase
方法修改DOM的内容<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入reat-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>
<script type="text/babel">
const myId ='Lao'
const myData = 'Hello,react'
// 1.创建虚拟DOM
const VDOM =(
<h1 id={myId.toLowerCase()} >
<span>{myData.toUpperCase()}</span>
</h1>
)
// 2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>【==注意==】区分:js语句(代码)与js表达式
JS语句(代码) JS表达式 控制代码走向的,没有值 一个表达式会产生一个值,可以放在任何一个需要值的地方 if(){}
变量(例如: a
)for(){}
a+b
switch(){case:xxxx}
函数调用表达式(例如: demo(1)
)有返回值的函数(例如: arr.map()
)定义一个函数(例如: function test () {}
)
-
样式的类名指定不要用
class
,要用className
。【示例】给H1标签引入
title
样式-
CSS样式
.title{
background-color: orange;
width: 200px;
} -
html
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入reat-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>
<script type="text/babel">/* 此处一定要写babel*/
const myId ='Lao'
const myData = 'Hello,react'
// 1.创建虚拟DOM
const VDOM =(
<h1 className='title' id={myId.toLowerCase()} >
<span>{myData.toUpperCase()}</span>
</h1>
)
// 2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
-
-
内联样式,要用
style={{key:value}}
的形式去写。【示例】在上述示例的基础上给
span
标签使用内联样式-
CSS样式同上
-
HTML(上例代码HTML的14~18行代码改为下述代码)
const VDOM =(
<h1 className='title' id={myId.toLowerCase()} >
<span style={{color:'white',fontSize:'29px'}}>{myData.toUpperCase()}</span>
</h1>
)【==注意==】
-
花括号的区别
- 最外层花括号(如图红框所 示):表示要写入JS表达式
- 最里层花括号(如图蓝框所示):表示写的不是JS中的函数数组,而是对象
-
写多个单词组成的属性时,要用小驼峰形式(如
fontSize
)
-
-
-
只有一个根标签
-
标签必须闭合
-
标签首字母
【==注意==】创建虚拟DOM中的不是HTML标签,而是JSX标签。
- 但是这些JSX标签会被转化成HTML标签
- 若小写字母开头
- 则将该标签转为html中同名元素,
- 若html中无该标签对应的同名元素,则报错。
- 若大写字母开头
- react就去渲染对应的组件,
- 若组件没有定义,则报错。
- 若小写字母开头
- 但是这些JSX标签会被转化成HTML标签
模块与组件、模块化与组件化的理解
模块与组件
模块 | 组件 | |
---|---|---|
是啥? | 向外提供特定功能的js程序, 一般就是一个js文件 | 用来实现局部功能效果的代码和资源的集合(html/css/js/image等等) |
为什么要用? | 随着业务逻辑增加,代码越来越多且复杂。 | 一个界面的功能更复杂 |
能干啥? | 复用js, 简化js的编写, 提高js运行效率 | 复用编码, 简化项目编码, 提高运行效率 |
模块化与组件化
模块化 | 组件化 |
---|---|
当应用的js都以模块来编写的, 这个应用就是一个模块化的应用 | 当应用是以多组件的方式实现, 这个应用就是一个组件化的应用 |
React面向组件编程
【==注意==】
为了缩短篇幅,专注于核心内容,后续代码块中只展示核心内容
请读者根据视频自行补全(下面内容)
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入reat-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>
使用React开发者工具调试
- 具体安装过程尚硅谷react教程_开发者工具的安装视频已经很详细了。
如何定义组件?
函数式组件
- 用函数定义出来的组件
- 函数名就是组件名
【示例】
<script type="text/babel">
// 1.创建函数式组件
function MyComponent(){
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>
-
【==注意==】
-
函数名首字母必须大写 (如上述代码第11行
MyComponent
)【错误示例报错】出错原因JSX语法规则第7条
-
函数必须有返回值
-
渲染组件到页面必须写组件标签,别直接写组件名字 (如上述代码第15行第一个参数)
【错误示例报错】函数类型不能作为React结点
-
-
【使用开发者工具】观察上述示例
-
【图示】
MyComponent
组件名*(如下图右侧)所对应的区域(如下图左侧)*【图示】React身上的属性*(如下图最上层蓝框),当前页面React渲染版本(如下图最下层蓝框)*
-
React对函数式组件的工作原理
执行了ReactDOM.render(…
之后,发生了什么?
-
React解析组件标签,找到了
MyComponent
组件。 -
发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
类式组件
【 复习】类的相关知识
- 类中的构造器不是必须写的,要对实例 进行一些初始化的操作,如添加指定属性是才写。
- 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
- 类中所定义的方法,都是放在了类的原型对象上,供实例去使用。
- 类名就是组件名
【示例】
<script type="text/babel">
// 1.创建类式组件
class MyComponent extends React.Component{
render() {
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>
-
【==注意==】
-
使用类式创建组件,必须继承
React.Component
类*(如上述代码第11行)* -
render()
*(如上述代码第12行)*是必须写的,而且必须要有返回值(返回值就是想要渲染的内容)-
【疑问】
render
是放在哪里的?-
MyComponent
的原型对象上,供实例使用。- 【疑问】实例是谁创建的?
- 在React对类式组件的工作原理的第二步被new出来的。
- 【疑问】实例是谁创建的?
-
-
【疑问】render中的this是谁?
-
MyComponent
的实例对象 ( 又称MyComponent
组件实例对象)。
-
-
-
React对类式组件的工作原理
执行了ReactDOM.render(…
之后,发生了什么?
-
React解析组件标签,找到了
MyComponent
组件。 -
发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
-
将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
组件实例(类式组件)的三大核心属性
state 状态
通过构建组件的两种方式可以知道
函数式 | 类式 |
---|---|
适用于【简单组件】的定义 | 适用于【复杂组件】的定义 |
-
【疑问】啥是复杂组件?啥是简单组件?
复杂组件 简单组件 有无状态 有 无
啥是状态?
- 组件的状态里面存着数据
- 数据的改变驱动着页面的展示
state初始化
- state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
- 【疑问】为啥是对象?
- 因为方便获取,直接
this.想要获取的属性名
,如果用数组还要知道具体下标。
- 因为方便获取,直接
- 【疑问】为啥是对象?
【示例】
<script type="text/babel">
// 1.创建函数式组件
class Weather extends React.Component{
constructor(props){
super(props);
//初始化一个isHot属性,设置为true
this.state={isHot:true}
}
render(){
const{isHot}=this.state
return <h1>今天天气很{isHot?'炎热':'凉爽'}</h1>
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>
事件绑定
【复习】原生JS事件绑定
方法一
【示例】
<button id="btn1">按钮1</button>
<script type="text/javascript">
const btn1=document.getElementById('btn1')
btn1.addEventListener('click',()=>{
alert('按钮1被点击了')
})
</script>
方法二
【示例】
<button id="btn2">按钮2</button>
<script type="text/javascript">
const btn2=document.getElementById('btn2')
btn2.onclick=()=>{
alert('按钮2被点击了')
}
</script>
方法三(React中更推荐这种方式)
【示例】
<button onclick="demo()">按钮3</button>
<script type="text/javascript">
function demo(){
alert('按钮3被点击了')
}
</script>
React事件绑定
【示例】
<script type="text/babel">
// 1.创建函数式组件
class Weather extends React.Component{
constructor(props){
super(props);
// 初始化状态
this.state={isHot:true}
}
render(){
// 读取状态
const{isHot}=this.state
return <h1 onClick={demo}>今天天气很{isHot?'炎热':'凉爽'}</h1>
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
function demo(){
console.log('标题被点击了')
}
</script>
-
【==注意==】
-
React对事件封装采用小驼峰形式 (如上述代码第20行
onClick
与原生JSonclick
不同)-
【错误示例】上述代码第20行使用
onclick
指定点击事件-
【报错提示】
-
-
-
React事件监听必须是一个函数 (如上述代码第20行需要使用
{}
将函数包裹)-
【错误示例】上述代码第20行使用字符串(
"demo"
)-
【报错提示】
-
-
-
{}
里面不要写小括号形式的函数调用 (如上述代码第20行{demo}
而不是{demo()}
)-
【解释】
onClick={demo()}
*(如上图第一个红框)*是一个赋值语句(要把右边的{demo()}
赋值给左边的onClick
)- 右边
demo()
是一个函数调用表达式,把demo()
调用的返回值交给了onClick
作为回调demo()
函数本身*(如上图第而二个红框)*也有返回值undefined
,undefined
交给onClick
作为回调
- 导致onClick无法实现事件正常绑定
-
-
类中的this
-
组件中render方法中的this为组件实例对象
-
组件自定义的方法中this为
undefined
-
【原因】组件自定义的方法不是通过实例调用的
-
【解释】
-
由于自定义的方法是作为
onClick
的回调,所以不是通过实例调用的,是直接调用-
【解释】通过实例调用和直接调用的区别
class Person{
constructor(name,age){
this.name = name;
this.age=age;
}
study(){
// study方法放在哪里?——类的原型对象上,供实例使用
// 通过Person实例调用study时,study中的this就是Person实例
console.log(this);
}
}
const p1=new Person('tom',18)
p1.study() //通过实例调用study方法
const x=p1.study
x()//直接调用【图示】第一行:通过实例调用,第二行:直接调用
- 【疑问】为什么
x()
直接调用为undefined
?- 【解释】往下看
- 【疑问】为什么
-
-
类中方法默认开启了局部的严格模式,所以组件自定义的方法中this为
undefined
-
【==注意==】类中方法默认开启了局部的严格模式,与
babel
没有关系 -
【解释】直接调用时,局部开启严格模式和未开启的区别
//开启局部的严格模式
function demo(){
'use strict'
console.log(this);
}
//直接调用
demo()
//未开启
function demo2(){
console.log(this);
}
//直接调用
demo2()【图示】第一行:开启局部的严格模式,第二行:未开启
-
-
-
-
如何解决组件自定义的方法中this指向组件实例对象?
方法一:强制绑定this: 通过函数对象的bind()
【示例】
class Weather extends React.Component{
constructor(props){
super(props);
// 初始化状态
this.state={isHot:true}
//通过函数对象的bind(),强制绑定this
this.changeWeather=this.changeWeather.bind(this)
}
render(){
// 读取状态
const{isHot}=this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot?'炎热':'凉爽'}</h1>
}
changeWeather(){
//changeWeather放在哪里?——Weather的原型对象上,供实例使用
// 由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
// 类中方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
console.log(this)
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
-
【解释】
-
this.changeWeather
*(如图红色方框)*即便自身没有changeWeather
也能找到changeWeather()
- 【原因】
this.changeWeather
(如图红色方框) 会顺着原型找到,在原型对象上的changeWeather()
- 【原因】
-
.bind
能干两件事- 改变函数中的
this
- 【疑问】
.bind(this)
这个this
是啥?- 构造器*(上图第二个红圈)中的
this
就是实例对象(上图第一个红圈)*
- 构造器*(上图第二个红圈)中的
- 【疑问】
- 生成一个新的函数
- 改变函数中的
-
this.changeWeather.bind(this)
一旦执行完了,获得一个新的函数而且这个this
就是实例对象。 -
然后该函数通过赋值语句放到了实例的自身并起了个名字叫
changeWeather
(this.changeWeather
)-
【图示】上述结果就会让实例自身也有
changeWeather
方法*(如下图第一个红框),原型上也有changeWeather
方法(下图第二个红框)*- 【==注意==】虽然此时自身也有
changeWeather
方法*(如上图第一个红框),但是原型上的changeWeather
方法(如上图第二个红框)*不能没有- 【解释】自身的方法是通过原型上的方法生成一个新的挂载到自身才得到的
- 【==注意==】虽然此时自身也有
-
-
方法二:箭头函数
如何修改state值
【==注意==】状态(state)数据,不能直接修改或更新
-
【示例】
class Weather extends React.Component{
constructor(props){
super(props);
// 初始化状态
this.state={isHot:true}
this.changeWeather=this.changeWeather.bind(this)
}
render(){
// 读取状态
const{isHot}=this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot?'炎热':'凉爽'}</h1>
}
changeWeather(){
const isHot = this.state.isHot
//严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
//this.state.isHot =!isHot //这是错误的写法
console.log(this)
}
}
ReactDOM.render(<Weather/>,document.getElementById('test'))
使用setState()方法
【==注意==】状态(state)必须通过setState
进行更新
-
【示例】在上述示例的基础上修改第16行代码
this.setState({isHot:!isHot})
- 【疑问】
setState
进行状态更新的动作到底是替换还是合并?
- 【疑问】
-
是合并,不是替换
-
【疑问】
constructor
构造器调用几次?-
1次。
-
【原因】只有一个
<Weather>
标签,只new了一次ReactDOM.render(<Weather/>,document.getElementById('test'))
-
-
-
【疑问】
rander
调用几次?-
1+n
次 -
【原因】
1
是初始化的那次,n
是状态更新的次数
-