2022/3/11-2022/3/20

今天继续学习react了。
看的案例用的样式是bootstrap,我不妨也用它吧。
npm install bootstrap --save
在src/index.js中需要引入bootstrap:import ‘bootstrap/dist/css/bootstrap.min.css’

之前接受了组件函数的概念,把props当参数对待的想法,现在觉得自从这样想以后,好像打开了一扇通往知识的门。
组件—函数。
比如如下,NameCard是组件, 想传入的参数是 name,
那么写起来就是 < NameCard name = ‘viking’ > 是不是和 function NameCard(name)很像?
这两者是等同关系。

在组件中,通过props就可以拿到参数了。

我有一种莫名恍然大悟的感觉!

react组件有两种写法,一种是函数的形式,另一种是class类的写法,可以看一下:
https://www.runoob.com/react/react-components.html
写法一:通过props直接获取想要的参数
function HelloMessage(props) {
return < h1 >{props.name}< /h1>;
}
写法二:通过ES6 class来定义一个组件,用this.props
class Welcome extends React.Component {
const {name} = this.props
render() {
return < h1>Hello World!< /h1>;
}
}

给组件传值,name=“king”
但是如果传的值是变量或者数字,要 用花括号。 即 number={1234} or tags={tags}

这里关于react组件有一个要求:必须是纯函数。纯函数的意思就是不改变参数。即传入的参数值不可以修改。
由于不能违反这个原则,又想要组件动态相应更新,因此引入了state。

由于state是私有属性,所以需要更改state只有一种方法,那就是:setState


如下讲的是类组件的state:(函数组件用的是useState)

在jsx中,所有的js表达式,包括函数,一律花括号。之前说过,在html中出现js要用花括号,函数也是需要花括号的。
如下:

方法一:
< button onClick ={ this.increase} increase就是函数。
jsx中,回调函数不会绑定类,需要手动判断。
如上,在html中click事件绑定this.increase,this指的是该按钮,触发increase函数。
onClick是一个典型的回调函数,即一个函数触发另一个函数(并传参),这个回调函数increase没有绑定指定的类,即没有拥有者。
如果在这个increase函数里console.log(this)是拿不到this的,这就是原因。
所以我们在类的外部需要给回调函数绑定this:
即 this.increase = this.increase.bind(this) (写在回调函数之外)
这样说好像有点儿绕,简单来讲,就是因为回调函数本身是拿不到this的,所以在回调函数中console.log(this),结果肯定为undefined.
为了能够在回调函数里拿到想要的this,这里想拿到的this就是这个组件类。那么我们通过改变函数的拥有者就可以达到目标:
this.回调函数 = this.回调函数.bind(this) ,这个语句必须在回调函数之外,这样bind(this),括号里的this就是组件类了。
通过bind,回调函数里就能通过this拿到组件类了。
大概就是这样~
ps:前置知识:
回调函数无this,且在jsx中无法实现自动绑定。
知道onclick会触发回调函数。
知道回调函数的概念是:一个函数触发另一个函数(并传参)
回调函数里没有this

一句话总结:回调函数里没有this,this是通过bind更改回调函数的拥有者为组件类。

this.increase = this.increase.bind(this)

终于知道了像这种this.increase = this.increase.bind(this) 是为了解决回调函数无this问题。

但是在increase函数中,console.log(this)将得不到button , 需要在这个increase函数之前写上:
this.increase = this.increase.bind(this)

方法二:直接写箭头函数,这样就不用更改拥有者了。
即onClick = { () => { this.increase() }}
这样在increase函数里console.log(this),this就是组件类。无需加this.increase = this.increase.bind(this)
原理:箭头函数无this,默认this为使用该箭头函数的函数的拥有者。
触发箭头函数是onClick,它的拥有者就是组件类。

代码:
import React from “react”;

class LikeButton extends React.Component{
constructor(props){
super(props)
this.state = {
likes:0
}
}
increaseLikes(){
this.setState({
likes: ++ this.state.likes
})
}
render(){
return(
< div>
< button type=“button” className=“btn btn-outline-primary btn-lg” onClick={()=>{this.increaseLikes()}}>
< /button>
{this.state.likes}
< /div>
)
}
}
export default LikeButton;

注意:state是私有变量,所以外界改变state靠的是方法,也就是触发事件。比如onClick
在Vue中触发事件是@click = " getfunction" 或 @click = “getfunction()” 或@click = “getfunction(arg)”
在react中触发事件是@click ={()=>{this.gefunction()}} (回调和this,因为class类)

关于react的state有一些点想说:
在过去,vue的组件是通过props取值的,当时的props靠接收实现可读,但它还是可写的,可以更改的,组件可以通过$emit更改
prop的值,或者传其它值。
但是,在react中对props有了更严格的定义,在这里,props仅仅用来进行读取,原则上不允许修改,若想修改需要引入另一个变量:
state。通过state完成值修改。


关于时间,获取时分秒的方式:new Date().toLocaleTimeString()
结果:13:37:00
获取年/月/日的方式:new Date().toLocaleDateString()
结果:‘2022/3/11’
获取年/月/日/时/分/秒的方式:new Date().toLocaleString()
结果:‘2022/3/11 13:39:53’

组件生命周期:
(每刷新一次页面就会重绘dom,也就是说每刷新页面,里面的组件,数据也会跟着一块刷新,不管是私有还是公有的)
1.创建时:componentDidMount
2.更新时(new props,setState,foreUpdate()) :componentDidUpdate()
3.销毁时:componentWillUnmount()

举例代码:电子时钟:

import React from “react”;
class DigitalClock extends React.Component{
constructor(props){
super(props)
this.state = {
date:new Date()
}
}
componentDidMount(){
this.timer = setInterval(()=>{
this.setState({
date:new Date()
})
},1000)
}
componentWillUnmount(){
clearInterval(this.timer)
}
render(){
return(
< div className=“digital-clock-component jumbotron”>
< h1>{this.state.date.toLocaleTimeString()}< /h1>
< /div>
)
}
}
export default DigitalClock;

关于componentDidUpdate:
componentDidUpdate(currentProps,currentState){
console.log(currentProps,currentState)
}
打印出的第一个参数是new props,第二个是state


关于react表单:form

当输入框只有value = {this.state.value}的时候,表单值无法修改,所以输入框还需要加入onChange事件,当点击onChange的
时候就去修改state.value的值。

触发事件的回调函数自带event参数,通过event参数,event.target可以获取当前的dom元素。
关于event还有一些事件方法,比如event.preventDefault(),可以阻止一些默认事件的触发。

一个关于表单的例子:
用回调函数:
/* eslint-disable react/no-direct-mutation-state */
import React from “react”;
class CommentBox extends React.Component{
constructor(props){
super(props)
this.state = {
value:’’
}
this.handleChange = this.handleChange.bind(this); //注意这一句发生在constructor里的!!!!!!!!
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(event){
this.setState({
value:event.target.value
})
}
handleSubmit(event){
alert(this.state.value)
event.preventDefault()
}
render(){
return(
< form onSubmit={this.handleSubmit}> !!!onSubmit写在这里的
< div>
留言: < input type=“text” value={this.state.value} onChange={this.handleChange}>< /input>
< button type=“submit”>提交< /button>
< /div>
< /form>
)
}
}
export default CommentBox;

用箭头函数(推荐):
import React from “react”;
class CommentBox extends React.Component{
constructor(props){
super(props)
this.state = {
value:’’
}
}
handleChange(event){
this.setState({
value:event.target.value
})
}
handleSubmit(event){
alert(this.state.value)
event.preventDefault()
}
render(){
return(
<form onSubmit={(event)=>{this.handleSubmit(event)}}>
< div>
留言: < input type=“text” value={this.state.value} onChange={(event)=>{this.handleChange(event)}}>< /input>
< button type=“submit”>提交< /button>
< /div>
< /form>
)
}
}
export default CommentBox;

总结:经过这次我更加了解箭头函数是什么了,其实他也是MyFunction()这种类型(只不过它把一整个函数都在内部执行完了)
,箭头函数中()=>{}第一个括号也是接收的参数。所以()=>{} 第一个圆括号能够接收event。
因为()=> 约等于 函数() ,他们的括号都是参数。

总结:在过去,vue通过双向绑定就能实现数据的双向更新。但是,在react中表单的值是单向的,如果要改变就要跟state一起。
通过event.target.value和setState才能实现双向绑定,需要手写。


优化:上述代码用的是state,所以是可控组件,但是这样代码十分冗余,为了简化代码,使用ref属性,右边函数可以拿到当前
表单输入值。

做法:去掉所有的state,换成ref

/* eslint-disable react/no-direct-mutation-state */
import React from “react”;
class CommentBox extends React.Component{
// eslint-disable-next-line no-useless-constructor
constructor(props){
super(props)
}
handleSubmit(event){
alert(this.textInput.value)
event.preventDefault()
}
render(){
return(
<form onSubmit={(event)=>{this.handleSubmit(event)}}>
< div>
留言: < input type=“text” ref={(textInput)=>{this.textInput=textInput}}>
< button type=“submit”>提交
< /div>
< /form>
)
}
}
export default CommentBox;


react 状态提升:
当两个组件需要对同样的状态数据进行操作,建议将数据放在父组件里,这样就叫做状态提升。
即有两个组件commentList.js和CommentBox.js,用Welcome.js将两个组件包裹起来。由Welcome来管理state。
(状态提升:a有state,b有state=>因为state相同=>把state交给c,c包着a和b)

区别于vue:
vue中是进行同步状态,即组件a和组件b都管着各自的数据,尽管他们的数据相同,vue进行的是同步处理。
而在react,面对组件a和组件b都有相同的数据,采用数据统一由父组件管理的方式进行数据统一。

react先知:都是用回调响应触发事件。当然也可以是箭头函数响应触发事件。
给回调函数取个名字就叫事件切换法。!!!!即通过回调函数的原理可以实现函数间的参数传递!!!(这也是react数据流原理)。
函数a可以把参数传给函数b
react数据流:
由父组件管理state,子组件通过回调的方式将需要修改的数据传给父组件函数,父组件函数接收数据对数据进行管理
(a,b组件都不需要state了,有函数就行)
这里的回调函数在<CommentBox onAddComment = { this.addComment} >
将onAddComment的参数传给了 this.addComment
注意this.addComment之所以加花括号就是因为jsx语法,在html标签中出现的js表达式皆需加花括号。

在看子组件的时候可以看一下子组件是不是单纯的可读,如果是可读就容易多了。子组件直接通过props去拿父组件的state就行。

2022/3/14
今天开始居家办公了。

今天开始学context,只要creatContext,就会出现两个标签,
一个标签是<xx.Provider value = { xxx.xx} > 传值 xx是自定义context名字。

通过<xx.Consumer>

这里xx只是一个容器,可以用来存东西。

react组件传值目前学了两种,一种是state,一种是context.
state:组件b和组件c要传值,那么在它们之上创建父组件a,由a管理它们之间传递的数据state

2022/3/15

React
1.props:NameCard.js V=f(props)
Class在组件标签的属性,都需要通过this.props去获取(区别于state)
比如
那么在class NameCard中,
const { name ,number } = this.props
2.state:LikeButton.js setState在constructor中可以使用
3.生命周期:digitalClock.js,也是在constructor中使用(不是render(),render是生命周期的更新阶段。生命
周期的函数应该写在初始化阶段。如state也是初始化阶段。)
ComponentDidUpdate(currentProps,currentState)

2.state:组件的私有属性,外界通过调用包裹setState的方法去动态改变state的值。
如果用state,props要重写。
constructor(props) {
super(props)
this.state = {
} //this.state是对象
}
render(){
}

!!!!!!!在js类中,this是没有自动绑定的。
(所以需要在constructor中设置 this.xx=this.xx.bind(this)) 给xx的this绑定为当前类。

React语法:
其实就是
class:
constructor(props){
super(props)
this.state={
likes:0
}
//上面这里如果不是想要写state就不需要重写构造函数,即

也不用写super(props)
render(){
这里放js语句 比如 const { name ,number } = this.props
return(

这里放jsx语句

)
}
function:
js语句
return (
jsx语句

)


关于绑定事件中,箭头函数为什么不需要绑定this,函数为什么是{this.xx}而不是{this.xx()}
的诸多解释:https://segmentfault.com/q/1010000010918131
箭头函数并不是没有 this,它的 this 是词法作用域,在函数定义的时候就确定了,而不是函数运行时决定
(个人总结,这么说吧,this就是全局上下文,箭头函数没有this,它的this就取决于它定义在哪个函数里,
取决于定义的地方,也叫做词法作用域。不像普通函数,普通函数取决于调用时,箭头取决于定义时。)
所以:
onClick={ ()=>{this.xx()} } 这里this就是定义时的,这个onclick,也就是这个组件。
因为this是定义时就决定的,因此在xx中也可以拿到该this。箭头函数的this一旦绑定成功就无法更改。
这个总结说的不是很清楚,直到我在这里看到了:https://zhuanlan.zhihu.com/p/28689395
解释的很好,为什么箭头不需要绑定this

this.setState()异步更新,只有在回调函数中能拿到更新后的值


终于知道,componentDidMount 放在render()之前的原因了,因为render()本身也是更新的生命周期
里的。

2022/3/16
留言本:this.props.onAddComment(this.textInput.value) 子组件传值。
App.js:
<CommentBox onAddComment = { this.addComment}
这样,在app.js的addComment就能拿到this.textInput.value了,利用的就是回调函数传参数的原理(闭包)

利用Context的原因:a包b,b包c,c想用a的state,b用不到。那么c就通过context获取state。
b就拿不到state.

今天在看ts才发现字符串不可变。所以每次对字符串的操作都是返回了一个新的字符串。
所以字符串方法都是不会修改字符串的。按这个原理就不用再想每个方法的返回值。

2022/3/17
typescript:
null和undefiend是所有类型的子类型。
所以
let a:undefined = undefined
let b:number= a
不会出错(undefined可以赋值给number)

元组的限制
let user:[string,number] =[“1”,2]
user.push(‘2’)
console.log(user)

在ts中,用接口就是为了对静态类型进行检查。并不会被编译在js中。
接口就约等于一种自定义的类型,就像:number,:number[]这些一样,会让ts进行类型检查。
!!!!接口就是类型检查。
比如interface Person{
name:string;
age:number;
}
let viking : Person = {
name:“viking”
age:20,
} //viking不能多写或少写属性。

接口某个属性不一定要赋值可以用可选。
interface Person{
name:string;
age?:number;
}
这样viking就不一定要给age赋值了,不给age赋值也不会出错了。

readonly 可读,只能赋值一次,不可以再修改。
比如:
interface Person{
readonly id : number;
name:string;
age:number;
}

let viking:Person ={
id:1,
name:‘s’,
age:20
}

viking.id = 9527 --------这样会出错,没办法赋值。

写在函数参数括号的外面的类型是对函数返回值类型的定义。
function add(x:number,y:number):number {
return x+y
}

最后这个:number就是对返回值的类型强制定义。如果不是数字就会报错。

函数声明:let add2:(x:number,y:number) => number ; add2 = add
跟上面的 const add = ( x:number,y:number):number =>{ return x+y } 不一样。
(const add 是一个函数实例,add2只是函数声明,并没有实现)
返回值的写法不同。
还有一种函数声明是利用接口。
interface ISum{
(x:number,y:number):number
}
let add2:ISum = add //这里add就是上面的const add

ts有个类型推论,就是说,let str=‘str’ str=123 这里就会出错,str=123会出错。

常量枚举会提升性能:
const enum Direction {
Up = ‘UP’
Down = ‘DOWN’
}

编译成js,直接翻译枚举的值。比enum Direction{} 性能更快。当然,只有常量值才能做常量枚举
举个变量枚举:
let ab =3
enum Direction {
a=ab
}
这个不是常量枚举就可以做变量枚举

关于泛型T,写在fn和()之间
function fn(a:T):T{
return a
}
这个函数是实现参数和返回值类型相同。(泛型也是利用了类型推论,即当传值时再去判断数据类型。而不是在变量定义的时候就决定数据类型。)
泛型的特点:传值时才定义类型。T就是个神秘的变量。理解这个就等于理解了泛型。
没有用泛型的,在声明时就定义了类型。

泛型T一开始有尖括号,使用的时候就不用了。

关于元组和泛型的综合使用案例:
//元组和泛型的综合使用,传入数组下标0,1,返回1,0数据
//记住,元组本身是数组
function swap<T,U>(tup:[T,U]):[U,T]{
return [tup[1],tup[0]]
}
const result = swap([‘str’,123])
result[0]. (这里点".",会有提示。)因为result是返回值,函数有对返回值数组的元素类型定义。所以会有提示。

泛型约束:数组。(举例,不让arr.length出现错误提示的解决方法)
function echoWithArr(arg:T[]):T[] {
console.log(arr.length)
}
(注意,就是,不会有变化,最多扩展成<T,U>,<T,U,S>。。。
T会有T,T可以直接当基础类型用,基础类型怎么用就怎么用,比如number,如果是number数组
就是number[],那么同理,T数组就变成了T[])

把T当成基础类型用!就是说以后就当基础类型用T了。基础类型怎么用它就怎么用。
比如数字数组,是number[],那么当T数组时,T代替number,就是T[]
例子:

function echoWithArr(arg:T[]):T[] {
}

如上,T如果想要是对象,对象有length属性,那么就要这样写。
先写类
interface IWithLength {
length:number
}

function echoWithLength(arg:T):T{
console.log(arg.length) //这样用arg.length就不会出错了
}
上面,只要传入的T有length属性,就可以使用function echoWithLength了
!!!当然,echoWithLength也可以传入String类型,因为String也有length属性。
自从T extends IWithLength之后:
可以:
const str = echoWithLength(‘str’)
可以
const obj = echoWithLength({ length:10}) --对象
可以
const arr2 = echoWithLength([1,2,3])
因为string,数组本身都有length属性,对象可以加length属性。

2022/3/18
之前学的都是在函数中使用泛型。
在类中使用泛型是这样的:
class Queue{
private data=[]
push(item:T)[
return this.data.push(item)
}
pop():T{
return this.data.shift()
}
}

const queue = new Queue()

在接口中使用泛型是这样的:
interface keyPair<T,U>{
key:T,
value:U
}

let kp1:keyPair<number,string> = { key:1,value:“string”}
let kp2:keyPair<string,number> = { key:‘str’,value:2}

同理可得:let arrTwo : Array = [1,2,3]


类型别名:(type叫自定义类型)type Direction = ‘Up’ | ‘Down’ | ‘Left’ | 'Right
let toWhere : Direction = ‘left’

type StrOrNumber = string | number
let result3:StrOrNumber = ‘123’

2022/3/20
typescript里有很多内置类型,比如Partial,可以把类型转换成可选类型。
举例:
interface IPerson{
name:string
age:number
}
let viking:IPerson = { name:“viking”,age :20}
type IPartial = Partial
let viking2 :IPartial = { name :“viking” }
比如Omit,忽略属性。
type IOmit = Omit<IPerson,‘name’> //此时name属性被忽略
let viking3:IOmit = { age:20}

Vue3.js: v3.vuejs.org
vue2和vue3的区别:vue2用的是mixin来解决compositionapi,但是有很多缺陷,比如命名冲突,
不知道暴露出来变量的作用等。另外,vue2限制了typescript
computed
const double = computed(()=>{
return count.value*2
})
终于知道computed为什么某种意义上来说也是一种监听了,
监听意味着不断变化,如果count是一个不断变化的数据,那么double也可以随着不断变化。
这是一种联动变化。
举例:点击某事件,count会发生变化。若想让double随着count变化,可以用computed.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值