安装create-react-app npm i -g create-react-app
创建React App 在目标目录下打开在终端:
create-react-app react-app # 可以替换为其他app名称
cd react-app
npm start # 启动应用
第一章React
入门
相关js库
- react.js:React核心库。
- react-dom.js:提供操作DOM的react扩展库。
- babel.min.js:解析JSX语法代码转为JS代码的库。
虚拟DOM与真实DOM
- React提供了一些API来创建一种 “特别” 的一般js对象
const VDOM = React.createElement('xx',{id:'xx'},'xx')
上面创建的就是一个简单的虚拟DOM对象
- 虚拟DOM对象最终都会被React转换为真实的DOM
- 我们编码时基本只需要操作react的虚拟DOM相关数据, react会转换为真实DOM变化而更新界。
关于虚拟DOM:
- 本质是
Object类型
的对象(一般对象) - 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
- 虚拟DOM最终会被React转化为真实DOM,呈现在页面上。
JSX
- 全称:
JavaScript XML
- react定义的一种类似于XML的
JS扩展语法
: JS + XML本质是React.createElement(component, props, ...children)
方法的语法糖 - 作用: 用来
简化创建虚拟DOM
1) 写法:var ele = <h1>Hello JSX!</h1>
2) 注意1:不要加引号,因为它不是字符串, 也不是HTML/XML标签
3) 注意2:它最终产生的就是一个JS对象
标签名任意
: HTML标签或其它标签标签属性任意
: HTML标签属性或其它- 基本语法规则
1) 遇到<
开头的代码, 以标签的语法
解析
2) 遇到以{
开头的代码,以JS语法
解析:标签中的js表达式必须用{ }包含
babel.js
的作用
1) 浏览器不能直接解析JSX代码, 需要babel转译为纯JS的代码才能运行
2) 只要用了JSX,都要加上type="text/babel"
, 声明需要babel来处理
jsx语法规则:
- 定义虚拟DOM时,不要写引号。
- 标签中混入
JS表达式
时要用{}
。
- 样式的类名指定不要用class,要用
className
- 内联样式,要用
style={{key:value}}
的形式去写
只能有一个根标签
- 标签必须闭合,input本是单标签,但在这里要写成
<input type="text"/>
或再写一个input标签 - 标签首字母
(1).若小写字母开头,则将该标签转为html中同名元素
,若html中无该标签对应的同名元素,则报错。
(2).若大写字母开头,react就去渲染对应的组件
,若组件没有定义,则报错。
渲染虚拟DOM(元素)
- 语法:
ReactDOM.render(virtualDOM, containerDOM)
- 作用:
将虚拟DOM元素渲染到页面中的真实容器DOM中显示
- 参数说明
1) 参数一: 纯js或jsx创建的虚拟dom对象
2) 参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)
第二章React
面向组件编程
注意
组件名必须首字母大写
- 虚拟DOM元素只能有一个根元素
- 虚拟DOM元素必须有结束标签
函数式组件
渲染类组件标签的基本流程
- React内部会创建组件实例对象
- 调用render()得到虚拟DOM, 并解析为真实DOM
- 插入到指定的页面元素内部
类式组件
类语法:
- 类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
- 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
- 类中所定义的方法,都放在了类的原型对象上,供实例去使用。
事件绑定
类中的this指向
组件三大核心属性1: state
- state是组件对象最重要的属性, 值是对象(可以包含多个
key-value
的组合) - 组件被称为”状态机”, 通过更新组件的state来更新对应的页面显示(重新渲染组件)
组件中render方法中的this为组件实例对象
组件自定义的方法中this为undefined,如何解决?
a) 强制绑定this: 通过函数对象的bind()
b)箭头函数
- 状态数据,不能直接修改或更新,要用
this.setState({})
直接构造函数省了(为什么不在原型上写箭头函数,语法好像不允许)
组件三大核心属性2: props
注意props
是只读的
展开运算符
<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]//连接数组
//在函数中使用
function sum(...numbers){
return numbers.reduce((preValue,currentValue)=>{
return preValue + currentValue
})
}
console.log(sum(1,2,3,4));
//构造字面量对象时使用展开语法
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>
批量传递props
对props中的属性值进行类型限制和必要性限制
- 方式(新):使用prop-types库进限制(需要引入prop-types库)
设置默认属性值:
简写:从类外面挪到类里面
函数式组件使用
组件三大核心属性3: refs与事件处理
组件内的标签可以定义ref
属性来标识自己,类似原生的id。最后收集到refs中
- 回调形式的ref
<input ref={(c)=>{this.input1 = c}}/>
-
开发使用
内联的回调函数
也无关紧要
-
createRef
创建ref容器
myRef = React.createRef()
<input ref={this.myRef}/>
react
中的事件处理
- 通过
onXxx
属性指定事件处理函数(注意大小写)
1) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件—————— 为了更好的兼容性
2) React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)————————为了高效 - 通过
event.target
得到发生事件的DOM元素对象——————————不要过度使用ref
非受控组件 现用现取
受控组件 随着输入维护数据,要用的时候从state里取出来
组件的生命周期
- 组件从创建到死亡它会经历一些特定的阶段。
- React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
- 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
生命周期流程图(旧)经典
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. componentWillMount()
3. render()
4. componentDidMount() =====> 常用。
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件render触发
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render() =====> 必须使用的一个
4. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount() =====> 常用。
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
生命周期流程图(新)
生命周期的三个阶段(新)
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. getDerivedStateFromProps
3. render()
4. componentDidMount()
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1. getDerivedStateFromProps
2. shouldComponentUpdate()
3. render()
4. getSnapshotBeforeUpdate
5. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount()
重要的勾子
render
:初始化渲染或更新渲染调用componentDidMount
:开启监听, 发送ajax请求componentWillUnmount
:做一些收尾工作, 如: 清理定时器
虚拟DOM与DOM Diffing算法
经典面试题:
1.react/vue中的key有什么作用?(key的内部原理是什么?)
2.为什么遍历列表时,key最好不要用index?
-
虚拟DOM中
key
的作用:- 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。
- 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,
随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较
,比较规则如下:
a.旧虚拟DOM中找到了与新虚拟DOM相同的key
:
(1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
(2).若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b. 旧虚拟DOM中未找到
与新虚拟DOM相同的key
根据数据创建新的真实DOM,随后渲染到到页面
-
用
index
作为key
可能会引发的问题:- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新
==> 界面效果没问题, 但效率低。 - 如果结构中还包含输入类的DOM:
会产生错误DOM更新
==> 界面有问题。
{: width=54%}
- 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
仅用于渲染列表用于展示,使用index作为key是没有问题的。
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
-
开发中如何选择key?:
1.最好使用每条数据的唯一标识作为key
, 比如id、手机号、身份证号、学号等唯一值。
2.如果确定只是简单的展示数据,用index也是可以的。
第三章React
应用(基于React脚手架)
react提供了一个用于创建react项目的脚手架库: create-react-app
- 第一步,全局安装:
npm i -g create-react-app
- 第二步,切换到想创项目的目录,使用命令:
create-react-app hello-react
create-react-app :无法加载文件 C:\Users\Administrator\AppData\Roaming\npm\create-react-app.ps1,因为在此系统上禁止运行。
解决办法:
1.搜索框输入:Windos PowerShell 并且以右键管理员身份运行
2、打开了命令行之后,输入set-ExecutionPolicy RemoteSigned,并且把权限改权限为A,然后通过 get-ExecutionPolicy 查看当前的状态: - 第三步,进入项目文件夹:
cd hello-react
- 第四步,启动项目:
npm start
‘react-app-rewired‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
解决方案:yarn add react-app-rewired customize-cra
方便编码插件:
- 生成无状态组件
rfc
- 生成类组件
rcc
功能界面的组件化编码流程(通用)
- 拆分组件: 拆分界面,抽取组件
- 实现静态组件: 使用组件实现静态页面效果
- 实现动态组件
3.1 动态显示初始化数据
3.1.1 数据类型
3.1.2 数据名称
3.1.2 保存在哪个组件?
3.2 交互(从绑定事件监听开始)
案例:组件的组合使用-TodoList
- 拆分组件、实现静态组件,注意:className、style的写法
- 动态初始化列表,如何确定将数据放在哪个组件的state中?
——某个组件使用:放在其自身的state中
——某些组件使用:放在他们共同的父组件state中
(官方称此操作为:状态提升) - 关于父子之间通信:
【父组件】给【子组件】传递数据:通过props传递
【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
- 注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value
状态在哪里,操作状态的方法就在哪里
Window confirm(message)
方法用于显示一个带有指定消息和确认及取消按钮的对话框。如果访问者点击"确定",此方法返回true
,否则返回false。
第四章React ajax
axios
: 轻量级, 建议使用。
安装命令:yarn add axios
导入:import axios from 'axios'
- 封装XmlHttpRequest对象的ajax
- promise风格
- 可以用在浏览器端和node服务器端
react跨域解决办法:代理
react脚手架配置代理总结
方法一:在package.json中追加如下配置
- 3000 -> 代理url:3000 -> 5000
- 一旦中间3000中有的数据,就不会再转发给5000了
"proxy":"http://localhost:5000"
表示想把请求转发给谁,因为服务器开在5000
说明:
- 优点:配置简单,前端请求资源时可以不加任何前缀。
- 缺点:
不能配置多个代理
。 - 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
方法二
- 第一步:创建代理配置文件
在src下创建配置文件:src/setupProxy.js
- 编写setupProxy.js配置具体代理规则:代码不用记
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(
proxy('/api1', { //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
changeOrigin: true, //控制服务器接收到的请求头中host字段的值
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
changeOrigin默认值为false,但我们一般将changeOrigin值设为true
*/
pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
}),
proxy('/api2', {
target: 'http://localhost:5001',
changeOrigin: true,
pathRewrite: {'^/api2': ''}
})
)
}
说明:
- 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
- 缺点:配置繁琐,前端请求资源时必须加前缀。
案例:GitHub搜索
拆分组件
{: width=68%}
消息订阅-发布机制(任意组件间通信)
- 工具库:
PubSubJS
- 下载:
npm install pubsub-js --save
- 使用:
1)import PubSub from 'pubsub-js'
//引入
2)PubSub.subscribe('delete', function(data){ });
//订阅,消息名字delete,一旦有人发布这个消息就调用后面这个函数
3)PubSub.publish('delete', data)
//发布消息
扩展:Fetch(了解即可,但工作岗位其实用得不多)
文档
特点
- fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求
- 老版本浏览器可能不支持
github搜索案例相关知识点
- 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。
- ES6小知识点:连续解构赋值+重命名
let obj = {a:{b:1}}
const {a} = obj; //传统解构赋值
const {a:{b}} = obj; //连续解构赋值
,拿到b
const {a:{b:value}} = obj; //连续解构赋值+重命名
,拿到b并重命名b,console.log(value)
- 消息订阅与发布机制
- 先订阅,再发布(理解:有一种隔空对话的感觉)
- 适用于任意组件间通信
- 要在组件的
componentWillUnmount
中取消订阅
- fetch发送请求(关注分离的设计思想)
try {
const response= await fetch(/api1/search/users2?q=${keyWord}
)看看到底能连成功吗
const data = await response.json()连成功把结果给你
console.log(data);
} catch (error) {
console.log(‘请求出错’,error);
}