react 笔记
jsx中绑定this
为什么this会是undefined ?
- 可以使用bind方式绑定this
<button onClick={this.sayHi.bind(this)}>{this.state.btnDes}</button>
- 可以使用箭头函数方式绑定this
<button onClick={() => this.sayHi()}>{this.state.btnDes2}</button>
jsx中的列表渲染
一般的,都是使用map进行列表渲染,同时返回一个jsx-dom
<ul>
{/* 在这里,一般用map来进行列表渲染 */}
{
this.state.movies.map((item, index) => <li onClick={() => this.getItme(item)} key={index}>{item}</li>)
}
</ul>
计数器案例
- setState的函数形式:
传递一个函数可以让你在函数内访问到当前的 state 的值。因为 setState 的调用是分批的,所以你可以链式地进行更新,并确保它们是一个建立在另一个之上的,这样才不会发生冲突
this.setState((pre, next) => {
console.log(pre, next);
return {
val: data
}
})
几点提示
pre : state更改前的上一次状态
next : state更改后的状态
函数式必须要return一个对象,分别对应state内的键值对
- 绑定属性
绑定属性类似原生小程序,如
<input onChange={(e) => { this.changeVal(e) }} value={this.state.val ? this.state.val : 0} />
-
react并没有双向绑定,因而要使用e事件对象来找到值
-
关于num++ 与num+1
要使用num+1
在使用setState的时候不能使用num++,否则会导致修改失败 ,并且在事件触发时state也只会保持原值
JSX语法
描述:jsx是js的拓展语法,想要使用jsx ,需要给script标签中添加
type="text/babel"
属性
- jsx规范
- jsx顶部只允许有一个根元素,即便是空标签也可以
- jsx外层包裹一个小括号,一来方便阅读,二来可以换行书写
3.jsx中的标签可以是单标签,也可以是双标签,但如果是单标签,需要/>
结尾,如<img />
,<div />
- jsx注释
通常使用
{/* 这里是注释 */}
来表示,在jsx上下文环境下,ctrl+/可以自动生成注释行
-
jsx嵌入变量
*. jsx中可以正常显示这些类型: string,number,array
*. jsx中一般不能渲染以下数据类型(会显示dom,但不会显示dom的内容)- null undefined
如果必须要让null,undefined显示,可以使用String(null/undefined)的方式,也可以直接让其加''
2.boolean
如果必须要对bool显示,可以使用bool.tostring()方法变成字符串
- obj
对象不能作为jsx的子类 =》 不允许直接在jsx中渲染obj本身,但可以渲染obj内部的key-value
jsx嵌入运算符
在render函数内,可以使用解构的方式将state内的数据拿出来
render() {
const {cannotShow1,cannotShow2} = this.state
return(
<div>
<p>render本质上是一个函数,只不过return了一个特殊的对象<p/>
</div>
)
}
jsx可以嵌入以下:
- 表达式
{fN + lN}
- 数学程式
- 三目运算
- 逻辑与
- 函数调用
如果是函数调用,则可以有vue-filter/computed(?有吗 有待考证)
jsx绑定属性
- 一般的可以直接使用
<p title={'这是' + '绑定属性'}> </p>
- 但有的会与js本身的关键字/保留字冲突,此时要自主更换,遵循jsx规约
如:
class => className
for => htmlFor
jsx绑定class(重点)
jsx下的class要用className替代,想要绑定className样式,需要使用大括号语法
元素普通的class名,用引号包裹即可
<span className='hehehe'>这里是普通class</span>
被绑定了class的元素,外部需要用大括号,这个大括号是jsx语法,普通class与三木运算class需要保留间隔
<span className={'hhh jjj ' + (isTrue ? 'istrue' : '')}>绑定class时,外部包裹大括号,因为class也是属性</span>
jsx绑定style(重点)
style属性必须是一个对象格式,当key是两个字符时,遵循小驼峰格式,value不加引号时会被认为是一个变量
<span style={{ border: '1px solid red', background: 'red' ,}}>绑定style</span>
jsx绑定事件
jsx绑定事件,事件名 依旧要使用小驼峰标识
jsx如果不绑定this的话,将不能找到类组件本身的constructor里的state(提示state of undefined),
想要找到正确的this值,需要绑定this,有以下三种方法
- jsx中显式绑定this
<div onClick={this.sayHi.bind(this)}></div>
缺陷,易导致冗余代码产生
- 构造器内,super下绑定this
constructor(props){
super(props)
{/*本质上是给原来的函数事先绑定后this,然后又重新赋值给这个函数
在之前比较常用
*/}
this.sayHi = this.sayHi.bind(this)
}
- jsx中给dom绑定事件时使用箭头函数
<div onClick={() => this.sayHi()}>11111</div>
请注意:箭头函数下,内部函数必须要加小括号表示执行!
- 声明函数时,不再声明普通函数,而是声明箭头函数
原因:箭头函数在声明时无法绑定this,因而自动一层层向上寻找值(隐式),这种本质上是使用es6中给类添加属性的方法 class fileds
//外部触发的函数在声明时使用箭头函数
sayHi = () => {
console.log(this.state.isTrue)
}
<div onClick={this.sayHi}>11111</div>
jsx什么时候写大括号?
大括号里写什么 取决于你希望在这个大括号里返回什么
条件判断
react逻辑判断遵循js原生逻辑
*一: 如果逻辑很多,可以使用if判断,
*二: 如果逻辑很少,可以使用三目运算符判断,
*三: 三木运算符与&&逻辑与, v-if / v-show
*四: 本质上是&&逻辑与完成了与v-if相同的效果,即在dom中先删除再生成
*五: 想要完成v-show,可以操作style的display属性
map与filter的组合渲染
- 过滤再截取
很多时候只希望渲染符合条件的所有list,因而可以先使用filter再使用map的方式
{/*
先使用filter过滤不符合条件的,此时该数组内只剩下符合条件的,然后再使用map渲染列表
*/}
<ul>
{
numArr.filter(item => item > 90)
.map(v => <li>{v}</li>)
}
</ul>
2.过滤截取再渲染
<ul>
{
numArr.filter(item => (item > 30) && (item < 100))
.slice(0, 4)
.map(v => <li>{v}</li>)
}
</ul>
总结:jsx允许在map渲染前对原数组进行任何js允许的操作
jsx源码
- jsx的本质
在普通script
标签中无法写jsx的原因是,jsx最终要经过ReactDOM.createElement()
将其编译成语法糖
ReactDOM.createElement(type,config,children)
传入三个参数:
*. type: 当前ReactElement传入的类型
如果是元素标签,那么就用字符串表示'div'
如果是组件标签,那么就直接使用组件名称
*. 当前ReactEle的属性以及事件,以键值对对象的方式进行存储
*. children
存放在ReactEle中的内容,以children数组的方式进行存储
如果有多个子元素,需要用babel进行转换
问题:为什么参数第三个属性是一个参数并且不是一个可变参数,但是编译后却并不报错?
使用auguments来对后面的参数进行一一匹配:
const sonArr = auguments.length - 2 //减去2的原因是减去精准匹配参数1,精准匹配参数2
通过createElement()最终生成了一颗js dom树,这棵树(js对象)就是虚拟dom树 React.render负责将这颗dom树映射到指定的真实dom节点上,从而将虚拟dom树变成了真实dom树结构(浏览器中),RN中其实都相同,只是没有渲染真实dom,而是渲染成了安卓、ios的原生控件,taro?
流程:jsx => createElement(jsx) => render(createElement(jsx),DOM) = > 真实dom
render.dom将虚拟dom与真实dom一一映射,这个叫做映射
为什么要使用虚拟dom
- 很难追踪状态发生的改变
- 操作真实dom性能较低
dom操作会引起浏览器的回流和重绘
设计原则: 元数据的不可变性,【1.只允许使用setstatus修改数据,2.尽量生成新数据而不修改元数据(如果是引用类型,可以进行浅拷贝,然后再赋值进去)】
以render函数为界:
功能函数一般放在render函数后面
组件函数放在render前面
react-cli 中package.json的版本区别
"dayjs": "^1.10.5"
1.10.5
:表示目标库始终使用这个版本
^1.10.5
:表示小版本更新【"5"可变】
弊端:小版本更新也可能造成版本之间误差,yarn.lock
文件表示安装的具体详细版本
yarn eject的作用
暴露react-cli的webpack配置,但这是不可逆的
执行后会暴露config目录和script目录
执行后package.json会暴露react内部的一些依赖库
执行后package.json的script每一项的value会发生变化:
// 之前是这样
"start": "react-scripts start --open",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
// 现在是这样 script目录:执行eject后暴露出的目录
"start": "node script/start.js --open",
"build": "node script/build.js",
"test": "node script/test.js",
修改src源代码会直接热更新,修改
config目录、script目录
时需要重新运行
vscode自动补全jsx插件
htmltagwrap
react组件的类型
- 根据组件的定义方式 分为 函数式组件与类组件
- 根据组件内部是否需要维护,分为无状态组件与有状态组件
- 根据组件的不同职责,可以分为展示型组件和容器型组件
以上定义方式有很多重叠,但主要都关注数据逻辑与ui展示的分离
- 函数组件,无状态组件,展示型组件主要关注UI的展示
- 类组件,有状态组件,容器型组件主要关注数据逻辑
jsx中标签规范
普通标签不允许大写,jsx会认为是未定义的组件
组件规约
- 凡是组件必须大写字符开头
- 类组件需要继承React.Component(16.3后可直接继承Component)
类组件
constructor
是可选的,通常在内部初始化数据(this指向,定义组件变量,接收父组件数据)- constructor中的
this.state
用于初始化组件内部需要的数据(es7中,可以在constructor外部使用state={xx:xxx}
达到相同结果) - render是类组件中唯一需要实现的方法
函数式组件
- 没有内部状态
- 没有this对象
render函数的返回值
不管是函数式组件还是render函数
-
react元素
-
数组或者fragments:=》 使得render方法可以返回多个元素
不允许返回对象!
插件快捷键~ ES7 React/Redux/React-Native/JS snippets
- rcc => 生成类组件
- rfc => 生成函数式组件
- imd => 从库中全部导入
- imn => 导入库(如css)
- imd => 从库中导入某个变量函数
- cp => 从props中解构
- cs => 从status中解构
- clo => console.log(
obj
,obj) //obj此时被选中 - imr→ import React from 'react'
- imrc => import React,{Component} from 'react'
多退少补
生命周期
人或物从诞生到消亡的整个过程
react的生命周期分为三种:
- mount: 组件初次被渲染的阶段(mounting)
constructor :
comoponentDidmount:
- updata: 组件数据更新,重新更新渲染的阶段updating
render: 接收props或者setstatus或者强制刷新dom时都会重新调用render函数
componentDidUpdate
- unmount:组件从dom树中被移除的阶段unmounting
componentWillUnmount
执行顺序 =》 constructor => render => react内部开始更新或挂载组件 =》 componentDidmount/componentDidUpdate
类组件版父子组件
import { Component } from 'react'
class Son extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
<div>
<p>我是子组件</p>
</div>
)
}
}
class App extends Component {
constructor(prop) {
super(prop)
this.state = {
}
}
render() {
return (
<div>
<p>这是父组件</p>
<Son />
</ div>
)
}
}
export default App
在一个大组件文件里,一个小组件可以定义在这个组件里,然后直接导入大组件内部即可,请注意: 必须保证是纯函数!
纯函数:在输入时就能保证输出的数据类型和值,且无副作用
待续。