1、开发react 项目,需要安装的基本模块 npm install --save-dev babel-plugin-transform-decorators-legacy 高阶组件用@修饰符使用时需要安装的插件 npm install antd --save npm install react-app-rewired@2.0.2-next.0 babel-plugin-import --save 这个是对antd配置按需加载的模块,安装react-app-rewired取代react-scripts可以扩展webpack,具体配置在config.overrides.js页面里类似于vue的vue.config.js,最后在package.json里修改为 "scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-app-rewired eject" } npm install redux --save npm install react-redux --save npm install redux-logger --save npm install redux-thunk --save npm install react-router-dom --save 2、export default 每次只能导出一个文件,import对应导入时不需要写{} export 每次可以导出多个文件,import对应导入时需要写{} *注意导入时的名称要和导出时的名称一样 export function render(vnode, container) {} export default function render2(vnode, container) {} import {render} from './kreact-dom' import render2 from './kreact-dom' render(jsx, document.querySelector("#root")); render2(jsx, document.querySelector("#root")); *注意如果function render(vnode, container) {} export default {render} export default单独写一行,后面方法名有{},那么 导入时的名称可以随意,比如 import ReactDom from './kreact-dom' ReactDom.render() *注意如果function render(vnode, container) {} export default render export default单独写一行,后面方法名没有{},那么 导入时的名称也可以随意,比如 import ReactDom from './kreact-dom' ReactDom() 3、实现异步操作队列的方法 // 使用Generator顺序执行两次异步操作 function* r(num) { const r1 = yield compute(num); yield compute(r1); } // compute为异步操作,结合Promise使用可以轻松实现异步操作队列 function compute(num) { return new Promise(resolve => { setTimeout(() => { const ret = num * num; console.log(ret); // 输出处理结果 resolve(ret); // 操作成功 }, 1000); }); } let it = r(2); // 修改为可处理Promise的next function next(data) { let { value, done } = it.next(data); // 启动 if (!done) { value.then(num => { next(num); }); } } next(); 4、1. 什么是JSX?为什么需要JSX? 怎么用?原理?JSX是对js语法扩展,使我们可以用类似xml方式描述视图 执行快、类型安全、简单快速 ### 总结: 1.webpack+babel-loader编译时,将JSX传到React.createElement(...)里,作为函数的一系列参数(type,props,children) 2.所有React.createElement(...)执行结束会得到一个JS对象树,他能完整描述dom结构,称之为虚拟DOM 3.React-DOM.render(vdom,container)可以将vdom转换为dom追加至container中 通过遍历vdom树,根据vtype不同,执行不同逻辑:vtype为1生成原生标签,vtype为2实例化class组件并将其render返回的vdom初始化,vtype为3直接执行函数将结果初始化 5、如果更新的值依赖于之前状态,或者使用props this.setState({ num: this.state.num + this.props.incream }) 那么,要这样写 this.setState((prevState,props) => { num: prevState.num +props.incream; }) 6、js对象的浅拷贝和深拷贝的研究 js中对象之间的赋值属于引用计数赋值,两个对象的内存地址指向在一起。将arr2=arr,那么我改变arr2的一个属性值,arr的这个属性值也一起改变了,这显然不是我们所期望的结果。 var arr = [1,2,3]; var arr2 = arr; arr2.push(4); console.log(arr);//输出[1,2,3,4] 对象的浅拷贝举例: var obj ={a:1,b:2,c:[1,2]}; var shallowCopy = shallow(obj); function shallow(obj){ var shallowObj = {}; for(var name in obj){ if(obj.hasOwnProperty(name)){ shallowObj[name] = obj[name] } } return shallowObj } console.log(shallowCopy);//输出的就是这个对象,我们实现了简单的浅复制; 浅复制:只会将对象的各个属性进行依次复制,并不会进行递归复制,而js存储对象都是存地址的,所以浅复制会导致obj.c和shallowCopy.c 指向同一块内存地址;会导致引用。 对象的深拷贝举例: function deepClone(obj){ var newObj = obj instanceof Array ? [] : {}; if(typeof obj !== 'object'){ return }else{ for(var i in obj){ if(obj.hasOwnProperty(i)){ newObj[i] = typeof obj[i] === 'object'?deepClone(obj[i]):obj[i]; } } } return newObj } 深复制:它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。这就不会存在上面obj和shallowCopy的c属性指向同一个对象的问题。 7、父组件render()会导致子组件更新,如果父组件传递给子组件的值没有发生变化,我们不希望子组件更新,加重系统开销。可以使用PureComponent,自动对比父组件传递的值是否变化,如果没有就不会更新 8、state发生改变时会导致组件更新,从 shouldComponentUpdate()开始。 props发生改变时会导致组件更新,从componentWillReceiveProps() 开始,不过现在已经弃用这个钩子函数了 9、区别__proto__跟prototype class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } var point = new Point(2, 3); point.hasOwnProperty('x') // true point.hasOwnProperty('y') // true point.hasOwnProperty('toString') // false point.__proto__.hasOwnProperty('toString') // true 结论: 1、class里定义的方法toString()实际上是定义在原型对象上 2、hasOwnProperty方法中属性必须是对象自身的,原型对象上继承的属性不算 3、point.__proto__===Point.prototype 10、this.props包括什么? 第一部分:父组件传递给子组件的值,比如<son data={list}></son> 第二部分:如果这个组件属于路由组件,还包括location match history三个路由属性 第三部分:如果这个组件通过connect()方法把dispatch跟store里的state映射到了props里,那么还包括dispatch和state(各个reducer的返回值组成的对象树) 11、react-router-dom路由信息怎么获取呢? 一个路由条目:/login/:id?redirect=to.path 需要这样的匹配:/login/123?redirect=to.path this.props.history.push({ pathname:'/login', state:{from:props.location.pathname}, params:{id:123}, query:{redirect:to.path} }) 路由跳转 this.props.location.pathname获取的是/login this.props.location.search获取的是?redirect=to.path this.props.match.params获取的是{id:123} this.props.location.state获取的是{from:props.location.pathname} this.props.location.query获取的是{redirect:to.path} 12、公共数据流的概念,他必须包括一个动作,dispatch action,导致reducer产生一个新的返回值,更新state,这里又通过mapStateToProps把state里的值映射到了props里,所以props值发生了改变,那么就会引发一系列的更新,从 componentWillReceiveProps开始,一直到render、componentDidUpdate, 公共数据流的里值(state)在项目中的任意地方都可以获取。 13、公共数据的概念,由react本身提供的一个类方法叫createContext,包括Provider、 Consumer,通过<Provider value="1"> <Parent /> </Provider>把公共数据传进<Parent />以及<Parent />的所有子组件里,那么孙子组件就可以通过Consumer,获取value里的值 function Child (props) { return ( <Consumer> {value => <div> value: {value} </div>} </Consumer> ) } 14、umi可以像平常一样写CSS的方法: npm install classnames --save 作用是可以让react的 className='a b c' 拥有多个值 import './index.less' index.less 必须这样设置一下 :global{} 把样式放到这个里面就可以了 15、umi中的配置式路由-心得 { path: '/', component: '../layouts', routes: [ { path: '/', component: './home' }, { path: '/class', component: './class' }, { path: '/my', component: './my', Routes: ['./routes/PrivateRoute.js'] }, { component: './404' } ] } // 如果包含子路由的话,path可以不精确匹配,反之必须精确匹配 16、多行溢出省略 .text_ellipsis{ text-align: center; font-size: .28rem; color: #333; text-overflow: ellipsis; word-break: break-all; display: -webkit-box; -webkit-line-clamp: 2; /*! autoprefixer: off */ -moz-box-orient: vertical; -webkit-box-orient: vertical; /*! autoprefixer: on */ overflow: hidden; height: 0.86rem; } 17、 react兼容IE11 yarn add react-app-polyfill 在src/index.js的最顶部引入 import 'react-app-polyfill/ie11' import 'react-app-polyfill/stable' 在packge.json文件下 browserlist 添加ie11