# TodoList **Repository Path**: dirtypool/TodoList ## Basic Information - **Project Name**: TodoList - **Description**: 一个基于React的简单TodoList - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-02-08 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 细节补充 1. 在create-react-app脚手架生成的原生代码中,src/index.js里面,有个: ```jsx import * as serviceWorker from './serviceWorker'; ``` serviceWorker是PWA(progressive web application),当我们把网页放在https服务器上时,用户第一次访问后,断网依旧可以使用该网页。 2. `render()`函数返回的元素必须有一个包裹元素,类似`
`这样的大的标签,如果不想显示`
`这样的大标签,可以使用``,并不会在浏览器中显示。 3. ==class 类==不管是原型方法还是静态方法定义,“this”值在被调用的函数内部将为 undefined,为了避免这个问题只能==强制去绑定this==。 4. this.state.inputValue延迟问题:由于==setState是异步的==,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在"调用"发出后,"被调用者"通过状态、通知来通知调用者,或通过回调函数处理这个调用。 5. ==immutable概念==:当对state数据有直接修改数据的需求时,最好通过复制副本来操作,使其指向新的数组。 6. `dangerouslySetInnerHTML = {{__html: item}}`:使内容不被转义(`

`之类的标签不显示文本,直接成为样式),第一个花括号是由于在标签中使用表达式,第二个是指内容为 JS 对象 7. setState的写法: ```jsx handleItemClick(index){ this.setState((prevState) => { //这个花括号返回的是函数体 //prevState代替this.state const list = prevState.list; list.splice(index,1); //这个花括号返回对象,list: list 可以直接list return {list} }); } ``` 8. 如果标签中的方法太长,可以拆分出来用函数代替: ```jsx
    {this.getItem()}
//拆分 getItem() { return this.state.list.map((item, index) => { return ( ) } ) } ``` ## 知识点 9. 声明式开发和命令式开发的区别: - JQuery是命令式开发,直接操作DOM - React是声明式开发,是面向数据的,React根据数据来构建DOM 10. 子父组件的通信: 父组件向子组件传**只读**的值props(单向数据流),子组件接受,如果子组件要向父组件传值,需要调用父组件传递过来的方法 11. React是视图层框架: 如果出现组件层级多的数据交互,涉及到了父子组件的多层传值问题,太过复杂,所以需要配合类似Redux、Mobx等数据层框架来辅助开发 # 高阶知识点 ## PropTypes和DefaultProps的应用 1. PropTypes是用于强校验的,限制父组件的传值类型: ```jsx import PropTypes from 'prop-types' TodoItem.propTypes = { //isRequired指必须要传递 test: PropTypes.string.isRequired, // 一个对象可以是几种类型中的任意一个类型 content: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]), deleteItem: PropTypes.func.isRequired, index: PropTypes.number } ``` 2. DefaultProps用于设置默认属性 ```jsx TodoItem.defaultProps = { test: 'hello' } ``` PropTypes更多方法在:[React官方文档](https://react.docschina.org/docs/typechecking-with-proptypes.html) ## Props, State与render函数的关系 当组件的Props, State发生变化是render函数会重新执行,当父组件的render函数被运行时,子组件的render也会被重新执行一次。 ## 虚拟DOM 1. state/props 数据 2. JSX 模版 3. 数据 + 模版 生成虚拟DOM ==虚拟DOM就是一个JS对象,用它来描述真实DOM== (生成对象比生成DOM损耗新能低很多) ```jsx React.createElement('div',{id:'abc'},React.createElement.('span',{},'hello')) ``` 4. 用虚拟DOM的结构生成真实的DOM来显示 ```html
hello
``` 5. state/props 发生变化 6. 数据 + 模版 生成新的虚拟DOM ```jsx ['div',{id:'abc'},['span',{},'bye']] ``` 7. 比较原始虚拟DOM和新的虚拟DOM的区别 8. 直接操作DOM,改变相应的内容 > JSX -> createElement -> JS 对象(虚拟DOM)-> 真实DOM 优点: 1. 性能提升 2. 使得跨端应用得以实现,React Native。通过虚拟DOM来生成原生组件 ### Diff算法 setState是异步的,原因:连续调用setState,React会合并连续的多次调用,只调用一次,来提升性能。 1. 同层比对:算法简单,但可能浪费生成虚拟的DOM时间(第一层不同,直接删除原始的虚拟DOM,生成新的) 2. Key值:每个DOM节点有Key值,作比对时快速建立起关联,==Key值不要是index,不能保证新DOM节点和旧DOM节点的Key值保持一致== ## ref 的使用 ref是一种引用,帮助直接获取DOM元素。 下面是几个适合使用 refs 的情况: - 管理焦点,文本选择或媒体播放。 - 触发强制动画。 - 集成第三方 DOM 库。 使用方法:(这种情况不建议使用) ```jsx {this.input = inputText}} onChange={this.handleInputChange} /> handleInputChange() { const value = this.input.value; this.setState(() => ({ inputValue: value }), () => { console.log(this.input.value); }); } ``` 当ref和setState结合时,DOM获取不及时,原因是setState是异步的,可以用==setState的第二个属性,是一个回调函数,setState执行完后才执行== ## React的生命周期函数 ==生命周期函数指在某一时刻会自动调用执行的函数,是针对每一个组件来讲==,类似render()函数 image-20200207234219317 **render函数会被反复执行** **初始化:** 1. **初始化state和props** **挂载:** 1. **componentWillMount:** 在组件即将被挂载到页面的时刻自动执行 2. **render**: 组件渲染 3. **componentDidMount:** > 这两个函数,只在挂载时执行(第一次在页面显示时) **更新:** 1. **componentWillReceiveProps:** 当组件从父组件接受参数,只要父组件的render函数被==重新==执行了就会执行该生命周期函数(如果这个组件第一次存在于父组件中,不会执行,如果之前已经存在于父组件中,才会执行) 2. **shouldComponentUpdate:** 组件更新之前执行,返回布尔值 3. **componentWillUpdate:** 组件被更新之前,shouComponentUpdate之后,取决于shouComponentUpdate返回的布尔值来判断是否执行 4. **render**: 组件更新,重新渲染 5. **componentDidUpdate** 组件更新完成后执行 **卸载**: 1. **componentWillUnmount**: 组件即将被从页面中剔除的时候执行 ### 使用场景 当父组件的render执行时,子组件的render也会被执行,这样会带来性能损耗。 eg. ![image-20200208004609480](res/image-20200208004609480.png) 打开React Developer Tools开启Highlight后,发现在父组件的输入框中输入数据,并没有改变子组件item的值,但是子组件item的render也被更新了 此时,就可以用到`shouldComponentUpdate()`这个生命周期函数: eg. 在子组件中: ```jsx shouldComponentUpdate(nextProps,nextState) { if(nextProps.content !== this.props.content){ return true; }else{ return false; } } ``` eg. ==发送AJAX请求,放在componentDidMount()中==: 由于React中不像JQuery一样有封装AJAX,所以使用axios ```shell npm install axios ``` 完成后 ```jsx import axios from 'axios' componentDidMount(){ axios.get('/api/todolist') .then(() => {alert('success')}) .catch(() => {alert('error')}) } ``` ## 使用Charles实现本地数据mock ```jsx import axios from 'axios' componentDidMount(){ axios.get('http://localhost.charlesproxy.com:3000/api/todolist.json') .then((res) => { this.setState(() => ({ list: [...res.data] })); }) .catch(() => {alert('error')}) } ``` 新建json文件todolist ```json ["dell","lee"] ``` 在Charles中 ![image-20200208014404733](res/image-20200208014404733.png) 在浏览器中输入http://localhost.charlesproxy.com:3000