render函数必须存在
所有的生命周期函数都可以没有,但render函数必须存在,否则报错。
原因:
组件时继承自react的Component的,其中内置了其他所有的生命周期函数,唯独没有render函数。
所以我们的render函数是必须自己定义的。
React生命周期函数的使用场景
在http://localhost:3000/中**设置 highlight,那么页面上被 重新渲染的 组件 就会被框起来:
可以发现输入input框时不仅父组件被渲染,子组件也被渲染** :
利用它做性能优化
需优化原因
需优化原因:父组件数据更新 子组件render也被执行
例子:子组件TodoItem
在子组件TodoItem的render函数中加一个打印语句:
1 | console.log("child render"); |
提交11时第一次执行Todoltem的render函数,在父组件的输入框中输入9个数字时不仅执行父组件的render函数9次,还执行了子组件Todoltem的render函数9次:
这就是我们之前讲的“当父组件的render函数被运行时,它的子组件的render都将被重新运行一次”。
可实际上并不需要执行子组件的render,所以我们可以使用生命周期函数来做性能优化。
使用shouldComponentUpdate
注意: shouldComponentUpdate 应该需要自带两个参数(nextProps, nextState),他们代表即将更新的两个数据。
在上一篇笔记 React高级内容(2) 的“React的生命周期函数”中我们提到过,不管state还是props更新都要先通过shouldComponentUpdate来判断是否需要更新。
在子组件TodoItem中使用 shouldComponentUpdate 做性能优化,避免content无变化时被父组件更新带动执行子组件render:
1 | shouldComponentUpdate(nextProps, nextState) { |
可以看到子组件的render仅在提交1时执行了一次,后面父组件的10次改动都并未执行子组件的render:
总结:我们使用 shouldComponentUpdate 函数减少了虚拟DOM的比对,实现了性能优化。
发生 AJAX请求 列表数据
如果我们希望通过AJAX请求得到输入框下面的 列表数据 ,我们需要明确:
首先,不能把AJAX请求放在render函数中,因为数据更新时render会被再次执行。
所以我们需要放在组件中一个 只会被执行一次的函数 中,那么我们可以在父组件中添加 componentDidMount 函数,在页面渲染好(render函数执行后)触发 AJAX请求。
使用componentDidMount函数
为什么
- 我们需要放在组件中一个 只会被执行一次的函数 中。
- componentWillMount函数 也只执行一次,但是到了react native或者更深的时候放在componentWillMount函数中就会报错,所以我们默认将AJAX请求 放在 componentDidMount里。
- 虽然constructor构造函数也只执行一次,但还是建议将AJAX请求 放在 componentDidMount里。
例子:TodoList中发送AJAX请求
借助axios模块
- 注意:axios可自动转换 JSON 数据,从后端获取的JSON数据并不需要通过JSON方法去转换为JS对象,直接就可使用JS对象。
- React并不像jquery那样封装了AJAX发送的内置功能,所以我们需要借助 第三方模块axios。
- 客户端支持防御 XSRF
安装第三方模块axios:
在程序终端输入yarn add axios
(npm与yarn的区别),加载完成后输入npm start
重启服务器:
在父组件TodoList中引用axios:
1 | import axios from "axios"; |
通过axios发送AJAX请求获取接口内容:
1 | // render执行后发生AJAX请求 |
(AJAX是用JavaScript执行异步网络请求。Promise实例 里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,Promise实例拥有then()和catch()。)
当然并没有这个接口,所以最后会显示:
但是我们点开network可以发现确实发送了一个 http://localhost:3000/api/todolist 的请求,只不过404了:
回顾学过的性能优化(面试)
- 在constructor函数里绑定 事件函数 的 this指向(bind),而不是在事件函数内绑定,这样可以保证整个程序里只绑定一次,且可以避免 子组件 的无谓渲染。(后面会细讲)
- React的底层内置的setState是异步的,可以把多次的数据改变结合成一次来做,降低了 虚拟DOM 的比对频率。
- React的底层用了虚拟DOM的概念,替代了之前的 真实的DOM(JS对象)。
- React的 diff算法: 同层比对、key值的运用 来提升 虚拟DOM 比对的速度。
- 借助生命周期函数shouldComponentUpdate来提高组件性能,避免不必要的子组件render函数的渲染更新。
使用Charles实现本地接口数据mock
在上面我们调用接口获取数据时因为接口不存在所以爆出404,但是前端开发和后端是分离的,所以我们就需要在没有接口的情况下 使用 Charles 进行接口数据模拟 。
Charles 是一个抓包工具,他可以抓住我们的浏览器向外发送的请求并进行处理。
工作原理:
我们可以在tools里的map local中进行设置,Charles抓取到“map from”中的地址请求就会返回“map to”中选择的文件的数据。
Charles就是一个中间代理服务器。
比如:他看到我们请求的接口地址是“http://http://localhost.charlesproxy.com:3000/api/todolist”,那么通过Charles我们可以设置 当接收这个地址的请求时就返回我们桌面的todolist.json
文件内的数据,这也就起到了接口模拟数据的作用。
下载安装 Charles
- 下载地址:https://www.charlesproxy.com/latest-release/download.do
- 安装:一路next,然后选择路径。
- 破解:https://www.zzzmode.com/mytools/charles/(替换本地lib中的jar包)
- 重启,就不会显示需要破解的相关信息了
Charles代理Chrome
Charles在Windows下,默认只代理IE浏览器,对 Chrome 需要设置后才能抓包。
SwitchySharp工具下载地址
Chrome设置教程
Charles抓localhost的包
弄好之后可以发现确实可以抓取chrome的包,但并不能抓取localhost的包,
Charles官方对不能捕获localhost本地网页的说明,以及解决方法。全文大致意思如下:
Localhost流量不会出现在Charles中
某些系统被硬编码为不使用代理进行本地主机流量,因此当您连接到http:// localhost /时,它不会显示在Charles中。
解决方法是连接到http://localhost.charlesproxy.com/。这指向IP地址127.0.0.1,因此它应该与localhost完全相同,但它的优势在于它将通过Charles。无论Charles是在跑还是你在使用Charles,这都会有效。如果您使用其他端口,例如8080,只需像往常一样添加它,例如localhost.charlesproxy.com:8080。
您还可以在该域前放置任何内容,例如myapp.localhost.charlesproxy.com,它也将始终解析为127.0.0.1。
或者,您可以尝试添加.
在localhost之后,或用本机名称替换localhost,或使用本地链接IP地址(例如192.168.1.2)。
如果Charles正在运行并且您使用Charles作为代理,那么您也可以使用local.charles作为localhost的替代方案。请注意,这仅在您使用Charles作为代理时才有效,因此上述方法是首选方法,除非您特别希望请求在不使用Charles时失败。
解决方法:
原本的网址:http://localhost:3000/
现在换成:http://localhost.charlesproxy.com:3000/
成功抓包(例子可看下方)
例子(TodoList)
需要实现的功能:在TodoList中发送的AJAX请求(接口)获取到桌面上todolist.json
文件内的内容并显示在输入框下作为默认的无序列表。
获取到todolist.json
内数据
桌面右键新建文件todolist.json
并放入数据(这就是接口的模拟数据):
1 | ["你好呀","你叫什么名字?","我是胡萝卜"] |
在http://localhost.charlesproxy.com:3000/中我们可以看到**AJAX请求的接口地址**:
希望在 TodoList.js
组件中发送AJAX请求时将桌面上的todolist.json
文件内的数据返回过来,则需要借助 Charles
进行设置。
打开 Charles,进行设置,抓取到请求的接口地址是“http://localhost.charlesproxy.com:3000/api/todolist”时就返回`todolist.json`文件的数据:
发送的AJAX请求 数据获取成功:
可以看到获取到的数据:
显示在页面上
我们先修改TodoList.js的代码,打印看看成功接收到的数据res:
1 | componentDidMount() { |
可以注意到res里面有一个 数组data 包含我们想要的数据:
修改then函数中的打印语句:
1 | console.log(res.data); |
可以看到data就是我们想要显示在页面上的数据:
我们知道,在React中,修改state数据页面就会重新渲染,而state中的列表是保存在list: []
中的,所以我们可以使用setState改变state中的list数据使data数组成为无序列表显示在输入框下。
复习:setState()中最好传入一个函数作为参数,记得return:
1 | componentDidMount() { |
结果:todolist.json文件中的内容显示在输入框下面,并且可以实现点击删除,也可以提交新的数据
思路:
state中的list更新后, 父组件TodoList 就会重新执行 render函数 ,那么 render函数 中的 getTodoItem函数 也会被重新执行,在getTodoItem函数中会遍历list中的每一个数据并把他们依次传入 子组件TodoItem 中,在 子组件TodoItem 中每一项都会实现 点击删除 的功能,返回的子组件们就会在父组件的ul标签中形成无序列表。
优化代码
当函数中返回的代码只有一句时,我们可以使用()
包裹代码从而代替函数的{}
return:
(相关原因可看笔记”函数相关知识点补充“中的 箭头函数)
我们之前把data放到list中list: res.data
,但**最好使用扩展运算符将其复制到list中: list: [...res.data]
**,以免因为数据的改动产生不好的影响。