重新学习react,不是为了找工作,努力学透彻。
关于虚拟DOM:
- 本质是Object类型的对象(一般对象)
- 虚拟DOM比较“轻”,身上的属性少。因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
- 虚拟DOM最终会被转化为真实DOM,呈现在页面上。
JSX语法规则:
- 定义虚拟DOM时,不要写引号
- 标签中,混入JS表达式时,要用{}。
- 样式的类名指定不要用class,要用className
- 内联样式要用style={{key:value}}的形式写
- 虚拟DOM必须只有一个根标签
- 标签必须闭合
- 标签首字母(1)如果小写字母开头,则将该标签转为html中同名元素,如果html中无同名元素,则报错。(2)如果大写字母开头,react就去渲染对应的组件,如果组件没有定义,则报错。
注意区分【js语句(代码)】与【js表达式】:
-
表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
例如:(1)a (2)a+b (3)demo(1) (4)arr.map() (5)function test(){}
2. 语句(代码):
例如:(1)if(){} (2)for(){} (3)switch(){}
类的总结:
- 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时,才写。
- 如果A类继承了B类,并且A类中写了构造器,那么A类构造器中的super()是必须要调用的。
- 类中定义的方法都是放在了类的原型对象上,供实例去使用。
类式组件:
- render放在哪里的?类的原型对象上,供实例使用
- 执行ReactDOM.render之后发生了什么?解析组件标签,找到类组件,发现组件是用类定义的,随后new出来该类的实例,并通过该实例调用到原型链上的render方法
- render中的this是类组件的实例对象
组件三大核心属性:
state:
-
不能直接更改,状态必须通过set State进行更新,且更新是一种合并,不是替换
-
state值是对象
-
组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
-
组件中render方法中的this为组件实例对象
-
组件中自定义方法中this为undefined时如何解决?强制绑定this:bind()或者箭头函数
-
状态数据,不能直接修改或更新
props:
- 在类式组件中,构造器是否接收props,是否传递给super,取决于是否希望在构造器中通过this访问props(罕见)
react中的事件处理:
- 通过onXxxx属性指定事件处理函数(注意大小写)
- react使用的是自定义(合成)事件,而不是使用的原生DOM事件——为了更好的兼容性
- react中的事件是通过事件委托方式处理的(委托给组件最外层的元素)——为了高效
- 通过event.target得到发生事件的DOM元素对象——不要过度使用ref
高阶函数:
如果一个函数符合下面2个规范中的任何一个,那该函数是一个高阶函数。
- 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数
- 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
常见的高阶函数:Promise、setTimeout、arr.map()
**函数的柯里化:**通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式
生命周期(旧):
-
初始化阶段:由React DOM.render()触发——初次渲染
1.constructor() 2.componentWillMount() 3.render() 4.componentDidMount()===>常用 一般在这个钩子中做一些初始化的事,例如:开启定时器,发送网络请求,订阅消息 -
更新阶段:由组件内部this.setState()或父组件render()触发
1.shouldComponentUpdate() 2.componentWillUpdate() 3.render() 4.componentDidUpdate() -
卸载阶段:由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount()===》常用 一般在这个钩子做一些收尾的事,例如:关闭定时器,取消订阅消息
生命周期(新):
-
初始化阶段:由React DOM.render()触发——初次渲染
1.constructor() 2.getDerivedStateFromprops 3.render() 4.componentDidmount() -
更新阶段:由组件内部this.setState()或父组件render()触发
1.getDerivedStateFromProps 2.shouldComponentUpdate() 3.render() 4.getSnapShotBeforeUpdate() 5.componentDidUpdate() -
卸载阶段:由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount()
虚拟DOM中key的作用:
- 简单的说:key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用
- 详细的说:当状态中的数据发生变化时,react会根据新数据生成新的虚拟DOM,随后react进行新虚拟DO M与旧虚拟DOM的diff比较,比较规则如下:
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
(1)若虚拟DOM中内容没有变,直接使用之前的真实DOM
(2)若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
- 旧虚拟DOM中没有找到与新虚拟DOM相同的key:
(1)根据数据创建新的真实DOM,随后渲染到页面
用index作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新==》界面效果没问题,但效率低
- 如果结构中还包含输入类的DOM:会产生错误DOM更新==〉界面有问题
- 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index是没问题的
开发中如何选择key:
- 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号等
- 如果确定只是简单的展示数据,用index也是可以的
vscode中的react插件:
rcc类式组件 rfc函数式组件
在终端中停止运行:control+c
功能界面的组件化编码流程:
- 拆分组件:拆分界面,抽取组件
- 实现静态组件:使用组件实现静态页面效果
- 实现动态组件:
- 动态显示初始化数据:
- 数据类型
- 数据名称
- 保存在哪个组件
- 交互(从绑定事件监听开始)
todolist案件知识点:
- 拆分组件、实现静态组件
- 动态初始化列表,如何确定将数据放在哪个组件的state中?
- 某个组件使用:放在组件自身的state中
- 某些组件使用:放在他们共同的父组件state中(状态提升)
3. 关于父子组件通信
- 父组件给子组件传递数据:通过props传递
- 子组件给父组件传递数据:通过props传递,要求父提前给子传递一个函数
4. 注意defaultChecked和checked的区别
5. 状态在哪里,操作状态的方法就在哪里
react脚手架配置代理总结:
## 方法一
> 在package.json中追加如下配置
```json"proxy":"http://localhost:5000"```
说明:
1. 优点:配置简单,前端请求资源时可以不加任何前缀。
2. 缺点:不能配置多个代理。
3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
## 方法二
1. 第一步:创建代理配置文件
``` 在src下创建配置文件:src/setupProxy.js ```
2. 编写setupProxy.js配置具体代理规则:
```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': ''}
})
)
}
```
说明:
1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
2. 缺点:配置繁琐,前端请求资源时必须加前缀。
github搜索案例相关知识点:
-
设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办
-
ES6知识点:解构赋值+重命名
let obj = {a:{b:1}} const {a}=obj//传统的解构赋值 const {a:{b}}=obj//连续解构赋值 const {a:{b:value}} = obj//连续的解构赋值+重命名
3. 消息订阅与发布机制
- 先订阅,再发布
- 适用于任意组件间的通信
- 要在组件的componentWillUnmount中取消订阅
4. fetch发送请求(关注分离的设计思想)
SPA的理解:
- 单页Web应用
- 整个应用只有一个完整的页面
- 点击页面中的链接不会刷新页面,只会做页面的局部更新
- 数据都需要通过ajax请求获取,并且在前端异步展现
路由的基本使用:
-
明确好界面中的导航区、展示区
-
导航区的a标签改为link标签
Demo -
展示区写Route标签进行路径的匹配
<Route path='/xxx' component={Demo} /> -
的最外层包裹了一个或
路由组件和一般组件的区别:
-
写法不同
-
一般组件:<Demo/> 路由组件:<Route path='/about' component={About}/>
2. 存放位置不同
一般组件:component
路由组件:pages
3. 接收到的props不同:
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性
NavLink与封装NavLink:
- NavLink可以实现路由的连接的高亮,通过activeClassName指定样式名
- 标签体内容是一个特殊的标签属性
- 通过this.props.children可以获取标签体内容
Switch的使用:
- 正常情况下,path和component是一一对应关系
- Switch可以提高路由匹配效率(单一匹配)
解决多级路径刷新页面样式丢失的问题:
-
public/index.html 中引入样式时不写./ 写/(常用)
-
public/index.html 中引入样式时不写./写%PUBLIC_URL%(常用)
-
使用HashRouter
路由的严格匹配与模糊匹配:
-
默认使用的是模糊匹配(输入的路径必须包含要匹配的路径,且顺序要一致)
-
开启严格匹配:
嵌套路由:
- 注册子路由时要写上父路由的path值
- 路由的匹配时按照注册路由的顺序进行的
路由组件传递params参数:
- 路由链接(携带参数):
<Link to='demo/test/tom/18'>详情</Link> - 注册路由(声明接收):
<Route path='demo/test/:name/:age' component={Test}/> - 接收参数:
const {id,name} = this.props.match.params
路由组件传递search参数:
- 路由链接(携带参数):
<Link to='demo/test?name=tome&age=18'>详情</Link> - 注册路由(无需声明,正常注册即可):
<Route path='demo/test' component={test}/> - 接收参数:
const {search} = this.props.location - 获取到的search是urlencoded编码字符串,需要借助querystring解析
路由组件传递state参数:
- 路由链接(携带参数):
<Link to={{path:'/demo/test',state:{name:'tom',age:18}}}'>详情</Link> - 注册路由(无需声明,正常注册即可):
<Route path='demo/test' component={test}/> - 接收参数:
const {name,age} = this.props.location.state - 刷新也可以保留住参数