React基础用法查询手册
一、JavaScript基础复习
1. 原生的事件绑定的三种方法
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<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>
</body>
</html>
2. 类的基本知识
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1_类的基本知识</title>
</head>
<body>
<script type="text/javascript" >
/* 总结:
1.类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
3.类中所定义的方法,都放在了类的原型对象上,供实例去使用。*/
//创建一个Person类
class Person {
//构造器方法
constructor(name,age){
//构造器中的this是谁?—— 类的实例对象
this.name = name
this.age = age
}
//一般方法
speak(){
//speak方法放在了哪里?——类的原型对象上,供实例使用
//通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name},我年龄是${this.age}`);
}
}
//创建一个Student类,继承于Person类
class Student extends Person {
constructor(name,age,grade){
super(name,age)
this.grade = grade
this.school = '尚硅谷'
}
//重写从父类继承过来的方法
speak(){
console.log(`我叫${this.name},我年龄是${this.age},我读的是${this.grade}年级`);
this.study()
}
study(){
//study方法放在了哪里?——类的原型对象上,供实例使用
//通过Student实例调用study时,study中的this就是Student实例
console.log('我很努力的学习');
}
}
class Car {
constructor(name,price){
this.name = name
this.price = price
// this.wheel = 4
}
//类中可以直接写赋值语句,如下代码的含义是:给Car的实例对象添加一个属性,名为a,值为1
a = 1
wheel = 4
static demo = 100
}
const c1 = new Car('奔驰c63',199)
console.log(c1);
console.log(Car.demo);
</script>
</body>
</html>
3. 箭头函数
引入箭头函数有两个方面的作用:更简短的函数并且不绑定this
x => x * x
上面的箭头函数相当于:
function (x) {
return x * x;
}
如果参数不是一个,就需要用括号( )括起来
// 两个参数:
(x, y) => x * x + y * y
// 无参数:
() => 3.14
// 可变参数:
(x, y, ...rest) => {
var i, sum = x + y;
for (i=0; i<rest.length; i++) {
sum += rest[i];
}
return sum;
}
4. 展开运算符
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<script type="text/javascript" >
let arr1 = [1,3,5,7,9]
let arr2 = [2,4,6,8,10]
console.log(...arr1); //展开一个数组
let arr3 = [...arr1,...arr2]//连接数组
//构造字面量对象时使用展开语法
let person = {name:'tom',age:18}
let person2 = {...person}
//console.log(...person); //报错,展开运算符不能展开对象
person.name = 'jerry'
console.log(person2);
console.log(person);
//合并
let person3 = {...person,name:'jack',address:"地球"}
console.log(person3);
</script>
</body>
</html>
二、JSX语法
1. 简介
- 全称:JSX的全称是JavaScript XML,是React定义的一种类似于XML的JS扩展语法
- 本质:是React.createElement(component,pros,…children)方法的语法糖
- 作用:用来简化创建虚拟DOM 写法:
- 写法:
var ele = <h1>Hello JSX!</h1>
(注意:它不是字符串, 也不是HTML/XML标签,它最终产生的就是一个JS对象)
2. 如何渲染虚拟DOM(元素)
- 语法:ReactDOM.render(virtualDOM, containerDOM)
- 作用:将虚拟DOM元素渲染到页面中的真实容器DOM中显示
- 参数说明:
1)参数一: 纯js或jsx创建的虚拟dom对象
2)参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)
3. JSX语法规则
- 定义虚拟DOM时,不要写引号
- 标签中混入JS表达式时要用{}
- 样式的类名指定不要用class,要用className
- 内联样式,要用style={{key:value}}的形式去写
- 只有一个根标签
- 标签必须闭合
- 标签首字母
1) 若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错
2) 若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错
4. 实例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsx语法规则</title>
<style>
.title{
background-color: orange;
width: 200px;
}
</style>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入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>
<script type="text/babel" >
const myId = 'aTgUiGu'
const myData = 'HeLlo,rEaCt'
//第一步.创建虚拟DOM
const VDOM = (
<div>
<h2 className="title" id={myId.toLowerCase()}>
<span style={{color:'white',fontSize:'29px'}}>{myData.toLowerCase()}</span>
</h2>
<h2 className="title" id={myId.toUpperCase()}>
<span style={{color:'white',fontSize:'29px'}}>{myData.toLowerCase()}</span>
</h2>
<input type="text"/>
</div>
)
//第二步.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
</html>
5. 小练习
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsx小练习</title>
</head>
<body>
<div id="test"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel" >
//模拟一些数据
const data = ['Angular','React','Vue']
//1.创建虚拟DOM
const VDOM = (
<div>
<h1>前端js框架列表</h1>
<ul>
{
data.map((item,index)=>{
return <li key={index}>{item}</li>
})
}
</ul>
</div>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
</html>
三、React面向组件编程
1. 函数式组件
<script type="text/babel">
//1.创建函数式组件
function MyComponent(){
console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/*
执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/
</script>
2. 类式组件
<script type="text/babel">
//1.创建函数式组件
function MyComponent(){
console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/*
执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/
</script>
render是放在哪里的? --MyComponent的原型对象上,供实例使用
render中的this是谁? --MyComponent的实例对象 <=> MyComponent组件实例对象
复杂组件和简单组件区别:有state的就是复杂组件,没有的则是简单组件
注意
- 组件名必须首字母大写
- 虚拟DOM元素只能有一个根元素
- 虚拟DOM元素必须有结束标签
渲染类组件标签的基本流程
- React内部会创建组件实例对象
- 调用render()得到虚拟DOM, 并解析为真实DOM
- 插入到指定的页面元素内部
执行了ReactDOM.render(…之后,发生了什么?
React解析组件标签,找到了MyComponent组件
发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法
将render返回的虚拟DOM转为真实DOM,随后呈现在页面中
3. 组件实例的三大核心属性
① state
理解
state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
组件被称为‘状态机’,通过更新组件的state来更新对应的页面显示(重新渲染)
注意
组件中render方法中的this为组件实例对象
组件自定义的方法中this为undefined,如何解决?
a) 强制绑定this: 通过函数对象的bind()
b) 箭头函数
状态数据,不能直接修改或更新,需要使用setState
state详细使用方式
//1.创建组件
class Weather extends React.Component{
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');
//读取状态
const {isHot,wind} = this.state
//由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
changeWeather(){
console.log('changeWeather');
//获取原来的isHot值
const isHot = this.state.isHot
//严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
this.setState({isHot:!isHot})
console.log(this);
//严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
//this.state.isHot = !isHot //这是错误的写法
}
}
ReactDOM.render(<Weather/>,document.getElementById('test'))
② props
理解:
每个组件对象都会有props(properties的简写)属性
组件标签的所有属性都保存在props中作用
作用:
通过标签属性从组件外向组件内传递变化的数据
注意: 组件内部不要修改props数据
编码操作:
1.内部读取某个属性值
this.props.name
2.对props中的属性值进行类型限制和必要性限制
使用prop-types库进限制(需要引入prop-types库)
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.
}
3.扩展属性: 将对象的所有属性通过props传递
<Person {...person}/>
4.默认属性值
Person.defaultProps = {
age: 18,
sex:'男'
}
5.组件类的构造函数
constructor(props){
super(props)
console.log(props)//打印所有属性
}
props基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>props基本使用</title>
</head>
<body>
<!-- 准备好“容器” -->
<div id="test1"></div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
//创建组件
class Person extends React.Component{
render(){
// console.log(this);
const {name,age,sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
//渲染组件到页面,和state的区别是我们在组件外面渲染
//如果想使用Number类型参数,则需要写成{arg}形式
ReactDOM.render(<Person name="jerry" age={19} sex="男"/>,document.getElementById('test1'))
ReactDOM.render(<Person name="tom" age="18" sex="女"/>,document.getElementById('test2'))
//批量传参渲染(通过展开运算符)
const p = {name:'老刘',age:18,sex:'女'}
ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
</script>
</body>
</html>
限制props参数
第一步:
<!-- 引入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为函数
}
//指定默认标签属性值
Person.defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
③ refs
理解
组件内的标签可以定义ref属性来标识自己
ref相当于id,生成的键值对中,key为ref=的值,value为当前语句的整个节点
编码
1.字符串形式的ref
<input ref="input1"/>
2.回调形式的ref
<input ref={(c)=>{this.input1 = c}}
3.createRef创建ref容器·
myRef = React.createRef()
<input ref={this.myRef}/>
使用方式
//创建组件
class Demo extends React.Component{
/* React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的*/
//创建ref容器
myRef = React.createRef()
myRef2 = React.createRef()
//展示左侧输入框的数据
showData = ()=>{
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 a="1" b="2"/>,document.getElementById('test'))
4. 事件处理
- 通过onXxx属性指定事件处理函数(注意大小写)
- React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 — 为了更好的兼容性
- React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) — 为了更加的高效
- 通过event.target得到发生事件的DOM元素对象 — 不要过度使用ref
使用方式:
//创建组件
class Demo extends React.Component{
//创建ref容器
myRef = React.createRef()
myRef2 = React.createRef()
//展示左侧输入框的数据
showData = (event)=>{
console.log(event.target);
alert(this.myRef.current.value);
}
//展示右侧输入框的数据
showData2 = (event)=>{
alert(event.target.value);
}
render(){
return(
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
5. 收集表单数据
1、非受控组件
class Login extends React.Component{
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交(因为表单提交默认会页面跳转)
const {username,password} = this
alert(`用户名:${username.value},密码:${password.value}`)
}
render(){
return(
//onsubmit表单提交操作绑定handleSubmit回调方法
<form onSubmit={this.handleSubmit}>
用户名:<input ref={c => this.username = c} type="text" name="username"/>
密码:<input ref={c => this.password = c} type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
//渲染组件
ReactDOM.render(<Login/>,document.getElementById('test'))
2、受控组件
什么是受控组件:由组件控制的,将状态保存在组件中相当于VUE的双向绑定
class Login extends React.Component{
//初始化状态
state = {
username:'', //用户名
password:'' //密码
}
//保存用户名到状态中
saveUsername = (event)=>{
this.setState({username:event.target.value})
}
//保存密码到状态中
savePassword = (event)=>{
this.setState({password:event.target.value})
}
//表单提交的回调
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this.state
alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
}
render(){
return(
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={this.saveUsername} type="text" name="username"/>
密码:<input onChange={this.savePassword} type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
//渲染组件
ReactDOM.render(<Login/>,document.getElementById('test'))
四、组件的生命周期
理解
- 组件从创建到死亡它会经历一些特定的阶段
- React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用
- 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作
1. 生命周期的三个阶段
step1. 初始化阶段
由ReactDOM.render()触发 - 初次渲染
1. constructor()
2. getDerivedStateFromProps
3. render()
4. componentDidMount()
step2. 更新阶段
由组件内部this.setSate()或父组件重新render触发
- getDerivedStateFromProps
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate
- componentDidUpdate()
step3. 卸载组件
由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()
2. 重要的勾子
1. render:初始化渲染或更新渲染调用
2. componentDidMount:开启监听, 发送ajax请求
3. componentWillUnmount:做一些收尾工作, 如: 清理定时器
五、React应用(基于React脚手架)
1. 使用create-react-app创建react应用
react脚手架
- 脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目,包含了所有需要的配置(语法检查、jsx编译、devServer…);下载好了所有相关的依赖;可以直接运行一个简单效果
- react提供了一个用于创建react项目的脚手架库: create-react-app
- 项目的整体技术架构为: react + webpack + es6 + eslint
- 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化
创建项目并启动
第一步:全局安装:npm i -g create-react-app
第二步:切换到想创项目的目录,使用命令:create-react-app hello-react
第三步:进入项目文件夹:cd hello-react
第四步:启动项目:npm start
react脚手架项目结构
public ---- 静态资源文件夹
favicon.icon ------ 网站页签图标
index.html -------- 主页面
logo192.png ------- logo图
manifest.json ----- 应用加载的配置文件
robots.txt -------- 爬虫协议文件
src ---- 源码文件夹
App.css -------- App组件的样式
App.js --------- App组件
App.test.js ---- 用于给App做测试
index.css ------ 样式
index.js ------- 入口文件
logo.svg ------- logo图
reportWebVitals.js --- 页面性能分析文件(需要web-vitals库的支持)
setupTests.js ---- 组件单元测试的文件(需要jest-dom库的支持)
功能界面的组件化编码流程(通用)
第一步:拆分组件: 拆分界面,抽取组件
第二步:实现静态组件: 使用组件实现静态页面效果
第三步:实现动态组件
- 动态显示初始化数据(数据类型 + 数据名称 + 保存在哪个组件)
- 交互(从绑定事件监听开始)
React ajax
理解
3. React本身只关注于界面, 并不包含发送ajax请求的代码
4. 前端应用需要通过ajax请求与后台进行交互(json数据)
5. react应用中需要集成第三方ajax库(或自己封装)
常用的ajax请求库
6. jQuery: 比较重, 如果需要另外引入不建议使用
7. axios: 轻量级, 建议使用
- 封装XmlHttpRequest对象的ajax
- promise风格
- 可以用在浏览器端和node服务器端
- axios使用
相关API - GET请求
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
- POST请求
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
六、消息订阅-发布机制
工具库: PubSubJS
下载:npm install pubsub-js --save
使用:
- import PubSub from ‘pubsub-js’ //引入
- PubSub.subscribe(‘delete’, function(data){ }); //订阅
- PubSub.publish(‘delete’, data) //发布消息
- Fetch
特点 - fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求
- 老版本浏览器可能不支持
相关API - GET请求
fetch(url).then(function(response) {
return response.json()
}).then(function(data) {
console.log(data)
}).catch(function(e) {
console.log(e)
});
- POST请求
fetch(url, {
method: "POST",
body: JSON.stringify(data),
}).then(function(data) {
console.log(data)
}).catch(function(e) {
console.log(e)
})
七、React路由
SPA
- 单页Web应用(single page web application,SPA)
- 整个应用只有一个完整的页面
- 点击页面中的链接不会刷新页面,只会做页面的局部更新
- 数据都需要通过ajax请求获取, 并在前端异步展现
路由
- 什么是路由?
- 一个路由就是一个映射关系(key:value)
- key为路径, value可能是function或component
- 路由分类
- 后端路由:
- 理解: value是function, 用来处理客户端提交的请求
- 注册路由: router.get(path, function(req, res))
- 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
- 前端路由:
- 浏览器端路由,value是component,用于展示页面内容
- 注册路由:
- 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件
react-router-dom
- react的一个插件库
- 专门用来实现一个SPA应用
- 基于react的项目基本都会用到此库
react-router-dom相关API
内置组件:
、
- 基本路由使用
- 嵌套路由使用
- 向路由组件传递参数数据
- 多种路由跳转方式
剩下的还没CV完,歇会再抄