react -basic
Basic react
Published: 2020-12-20

The article contains the basic knowledge of react…

Code in GitHub: react2021

注意,每一章的总结都在代码文件下的README中

image-20201220124259171

回调函数传参

回调函数里面直接这么写不会被当成回调函数,而会立即执行:

image-20201217163954337

如果我们就想要这么写回调函数,那应该这么写:

image-20201217164053641

onChange调用的函数再返回一个函数

这样就能实现往回调函数传参了

函数的参数的值作为对象的key(多年以来的疑问)

image-20201217164229943

在js中由于没有 "" 来表示到底是变量还是字符串,所以如果想要把一个变量的值作为对象的key只能通过上述方法(在外面套一个 [] )

原理:

原先我们对象取值的时候除了用 . 还能用 [] :

image-20201217164419220

看到上图应该就能理解上上图的做法了

数组元素加和(reduce)

数组的方法,能对数组里面的元素进行加和

函数的柯里化

image-20201217164904614

image-20201217165104598

不使用柯里化完成上述操作

他要函数,直接给他函数就行了

image-20201217165804676

新钩子getSnapshotBeforeUpdate以及getDerivedStateFromProps

getSnapshotBeforeUpdate:

image-20201217202533962

使用场景:

image-20201217203654674

新闻每隔一秒生成一条,下面的新闻会被新的新闻挤下去,导致观看体验不好(没看完就被挤下去了)

这个时候我们就可以使用这个钩子了

我们可以利用element.scrollHeight和element.scrollTop来控制页面从开始有滚动条的时候就不要再将新闻挤下去,同时新的新闻也是在不断加进去的

image-20201217204140820

getDerivedStateFromProps:

参考博客:https://www.jianshu.com/p/50fe3fb9f7c3、https://blog.csdn.net/weixin_43905830/article/details/108760828(getDerivedStateFromProps填坑,包括如何在该静态函数里面使用this等问题的解决方案)

render中使用index为key的问题

image-20201217205526133

点击添加一个小王之后:

image-20201217205515870

顺序错乱了

原因:

image-20201217205612205

比方说key为0的,里面的文字有变化,会引发虚拟DOM更新,但是文字后面的input框,乍一看属性什么的都一样,看不出有什么变化,因此对于这个input框,虚拟DOM不会更新,但其实他残留了用户输入的信息,这就是问题出现的原因

所以render中遍历的时候key最好不要使用index,最好用唯一标识

react脚手架

public文件解读

index.html:

%PUBLIC_URL%

image-20201218092547379

theme-color

image-20201218094547014

apple-touch-icon

image-20201218094608846

这个东西什么用处呢?看下图和下下图的淘宝图标就是他的用处:

image-20201218094811438

image-20201218094944064

应用加壳

image-20201218095016045

什么是应用加壳呢?

比方说我写好的web页面,套一个安卓的壳,就伪装成了一个安卓应用,生成的就是一个apk文件:

image-20201218095024813

同理ios的壳

这其实是一种跨平台技术,我开发了 web端网页 ,加个壳就变成了跟手机app差不多的一个东西,虽然点击这个app打开的实质上还是网页,只不过伪装了,把它弄得像一个手机app

manifest.json

image-20201218095548991

image-20201218095349829

如果要做应用加壳,那手机里面的类似于打开一个新的app弹出你是否要给予xxx权限给该app 这种东西,反正就是所有手机里关于该app所涉及的文字、图片等都需要通过这个manifest.json文件来配置,当然如果不做应用加壳,这个文件是没用的

noscript标签

image-20201218095625415

robots.txt

image-20201219211842992

规定爬虫哪些东西能爬哪些不能爬

src文件解读

React.StrictMode

image-20201218095815830

包裹了这个东西之后能帮我们检查代码里面有哪些语法不对的地方,比方说我们写了一个 ref = “”,这个语法都要弃用了,那他就会警告我们

reportWebVitals

记录网页性能

image-20201219212558368

setupTests

做组件测试的

image-20201219212612736

配置代理

方式一:

直接在package.json中配置:

image-20201219222342307

这样配置假设我们的react端口是3000,那他通过3000端口发送的东西会被转发到5000端口

方式二★:

image-20201219223502061

建立一个setupProxy

解释一下这个/api1:

image-20201219223556997

首先请求的时候http://localhost:3000这个前缀其实就是本机,所以如果请求http://localhost:3000/students,他会先去本机当前项目中的public文件夹(根目录)找students,如果有就不会发送请求,直接返回本机数据,但如果没有,才会去发送数据。

其次,setupProxy中配置/api1的意义是当请求的前缀是/api1的时候就转发到5000端口,因此我们必须这么请求:http://localhost:3000/api1/students,这样才能符合要求,但是真正的请求是http://localhost:3000/students(服务器上写的接口url是 /students),所以这才有了setupProxy中的pathRewrite属性,他将请求中的/api1替换成空字符串了,这下它既能匹配到/api1,又能在服务器端请求/students,一下就解决了这个问题

跨域问题

如果我们希望网站被别人访问而不发生跨域问题,可以通过在后端加上特殊的响应头 cors 就可以了

fetch

fetch关注分离(将过程细化)

一般来讲我们请求数据如果路径不合法就会直接报错,但是fetch不会,因为它遵循关注分离,对于fetch来讲请求数据跟连上服务器是两码事

所以fetch的第一个.then是去连服务器的,先告诉你连没连上

第二个.then才去取数据

所以使用fetch可能会发生:

路径不合法也会告诉你连上服务器了,之后再告诉你404请求数据失败

给箭头函数加async

直接加就行了

image-20201220102852432

history

浏览器中的history的API不好用,可以去下一个包:https://cdn.bootcss.com/history/4.7.2/history.js

里面有两个方法:

  • createBrowserHistory()

    该方法使用H5推出的history上的API

    image-20201220104532386

  • createHashHistory()

    该方法使用hash值,类似于锚点跳转(跳转后浏览器有记录,但不刷新页面,而且节点前面都带一个 # ):

    image-20201220104717877

    image-20201220104643940

这两个方法都可以使用push、replace等api跳转页面(注意页面是不会发生刷新的)

监听

使用history.listen监听,如果页面发生跳转就会被监听到

image-20201220104926886

router

参考:https://blog.csdn.net/qq_45677671/article/details/116168250(【React】路由详解)

BrowserRouter与HashRouter

react中有两个router:

  • BrowserRouter

    事实上这个router就对应了上面history中的createBrowserHistory()

  • HashRouter

    事实上这个router就对应了上面history中的createHashHistory()

区别:

image-20201220154541626

跟history的createBrowserHistory()和createHashHistory()一样,HashRouter多一个#

image-20201220110942959

注意:#后面的资源都不发送给服务器

image-20201220110942959

比方说上图,后面这个/home就不会发送给服务器

比起link,navlink有activeClassName属性,点击哪个标签,哪个标签就加上某个属性高亮

navlink包住的value其实也是某种意义上的props:

image-20201220113207436

image-20201220113225840

效果:

image-20201220113155902

我们可以看到它自带一个children属性

因此我们也可以给他指定children来指定他的标签文本内容:

image-20201220120829202

image-20201220120843909

switch

匹配到一个路由之后就不会再往下匹配了,提高了效率

路由模糊匹配

image-20201220124449638

route中路径少了没问题,这是模糊匹配,如上图

但是如果反过来route多了那就不行了

精准匹配

给route添加exact属性:

image-20201220124828843

也可以省略{true}:

image-20201220124847478

模糊匹配和精准匹配使用原则

能用模糊就用模糊,只有模糊出问题了(比方说好多东西都往一个页面跳),再去使用精准匹配

其他路由参数

除了exact,还有很多其他路由参数,比如:sensitive、strict等,具体请参考:https://segmentfault.com/a/1190000014294604

嵌套路由

注意下图两块高亮的地方:

image-20201220130240934

为什么点击了 “Message”按钮 “Home”还是高亮呢?

首先Message是在Home下的二级路由,我们点击Message,触发的路由路径是:

image-20201220130352263

路由的匹配是按照注册顺序来的

所以他会去父亲那里找/home/message,由于是模糊匹配,父亲的/home被匹配到了:

image-20201220130545685

这次匹配就是下面这两块东西没丢的原因:

image-20201220130633845

然后由于/home匹配到了,react挂载了Home组件,又会开启Home下的路由匹配:

image-20201220131106790

这回匹配到的就是/home/message了,因此Message会高亮

精准匹配的问题(嵌套的二级路由失效)

这就引出了精准匹配存在的问题:

如果是精准匹配的话在第一次匹配的时候由于路径都不符合,相当于匹配不到,就不会再往下匹配了,导致了这种效果:

点击Message之后页面变这样了:

image-20201220131337050

跳转到Redirect的页面上去了,这里我们设置的Redirect中to指向的是"/about",因此他由于精准匹配的原因什么都没匹配到就走到/about页面去了

路由组件传递参数

首先route自动传递了一些props:

image-20201220134542551

路由组件传递参数的三种方法:

  • params(match对象)
  • search(location对象)
  • state(location对象)

具体的去看代码总结

要注意的是只有state传的参数不会体现在url后面

那就有一个问题了:刷新页面state传的参数会消失吗?

答案是不会,因为我们用的是BrowserRouter,他有某个API :history.xxx会维护history,而state是保存在location中的,而location又是保存在history中的,因此刷新页面参数不会丢

但是当我们把历史记录清了,再次刷新页面他就不行了:

image-20201220135126273

那至少让他不报错呗:

image-20201220135320574

withRouter

哪个一般组件需要使用路由组件函数就在哪个组件下引入并使用withRouter

image-20201220153810382

withRouter包裹一般组件,往一般组件身上加上了history、location、match这三个属性

注意,被withRouter包裹之后返回的是一个新组件

编程式路由导航

image-20201220151228404

利用history完成路由跳转

注意上图这边这么写之后最好看看Link和Route那边需不需要对应的修改,尤其是Route

那么怎么用编程式路由导航实现state传参呢?

image-20201220151835430

其实history上的API都已经给我们准备好了,如上图

image-20201220151934264

注意:

只有路由组件的props里面才有history、location、match这几个对象,一般组件中是没有的

所以在一般组件中无法调用诸如replace、go、push等只有在history中才有的函数

vue中就没有这些顾虑了,没有什么一般组件和路由组件之分,因此都是可以使用history中的东西的

那react中如何使用路由组件才有的函数呢?

用react-router-dom中的withRouter

link如何开启history的replace模式

link跳转的时候默认是history的push模式

那怎么开始replace模式呢?

添加replace={true}

image-20201220140004940

也可以直接写replace:

image-20201220140134558

多级路径下解决样式丢失问题

image-20201220122648732

我们给path加个前缀 /atguigu

第一次访问页面没问题,第二次访问页面bootstrap样式请求错误

原因就在于请求bootstrap资源的路径里面也给加上了/atguigu前缀:

image-20201220122820810

解决方法:

  • 修改index.html中样式link中href的相对路径的写法

    image-20201220122954901

    去掉了前面的 ‘.’

  • 写%PUBLIC_URL%前缀

    image-20201220123132264

  • 我就想写./css/bootstrap.css这种相对路径的写法

    image-20201220123243994

    那就要到入口文件中把BrowserRouter改成HashRouter:

    image-20201220123328737

    原理:

    image-20201220123429970

    hashrouter我们说过会带一个 “#”,而“#”后面的东西都不会发给服务器(自动忽略了,他会认为“#”后面的是前端的资源)

    所以不管传了什么路径,他都无视,直接请求 “#”前面的,在这里也就是localhost:3000

模板字符串在render中报错的原因

因为模板字符串是js的东西,所以要在外面加一个大括号:

image-20201220132900260

暴露核心配置(eject)

在执行了create-react-app命令之后

image-20201220173927958

执行yarn eject

image-20201220173940568

image-20201220174005416

纯函数

image-20201220204018760

纯函数特征之一:传入同样的参数,必定得到同样的输出,比方说函数demo(3)返回2,那下次再传入3,demo(3)的返回一定还是2

纯函数特征之二:不能修改传入参数的值

纯函数特征之三:不能发送网络请求、输入输出设备等不靠谱的东西(返回的东西说没就没的那种)

纯函数特征之四:不能调用第一次调用和第二次调用返回的值不一样的东西,例如Date、random等

redux中的reducers中不可以直接改preState的值,那是因为他底层有个浅比较,比较preState的地址,如果地址一样就不进行更新,所以如果preState是一个数组,而我们直接通过插入元素的方式修改preState:

image-20201220201620692

这个时候preState的地址是没变的,意味着他不会进行更新,但是preState里面的值确实变了

而且上述写法也使得reducers不再是一个纯函数了

所以不要直接修改preState

正确写法:

image-20201220203044395

Redux DevTools

在浏览器下载这个插件之后再使用yarn下载redux-devtools-extension

image-20201220205208362

如何使用:

去redux的store中:

image-20201220205345862

如果我们有其他异步组件就这么写:

image-20201220205420332

在App外面包一层<Provider store={store}><\Provider>的原因

是为了让App的所有后代组件(包括App)都能直接收到redux的store

image-20201220210428086

优化

redux的store里面不单独引入reducers,只单独引入combineReducer,为此我们需要建立一个新的js文件来将所有的reducers汇总成一个combineReducer:

image-20201220211303009

image-20201220211340046

部署

运行npm run build,将生成的build文件放到后端,指定spring的静态资源路径,然后配置nginx即可