一、真实DOM和虚拟DOM|缺点和优点|子组件向父组件传值|React组件3个部分|ref可以设置回调函数|为什么不能用index做key|状态提声|跨多级组件传参原理 1、真实DOM和虚拟DOM (1)真实DOM,用户每次操作DOM(文档对象模型),都会引起BOM(浏览器对象模型)重新渲染 (2)虚拟DOM,用户每次操作DOM(文档对象模型),都会改变虚拟DOM(堆内存的一个对象),最后一次性地渲染到BOM上。 2、React的缺点和优点 (1)缺点: (A)React本身只是一个V,不是一个完整的框架,不是一套完整的框架 (B)需要加上React-Router和React-Redux才能成为一套完整的框架 (2)优点: (A)单向数据流动; (B)虚拟DOM取代物理DOM作为操作对象; (C)用JSX语法取代HTML模板,在JavaScript里声明式地描述UI。 3、react子组件向父组件传值 (1)react父组件向子组件传值:属性传参 (2)react子组件向父组件传值:在父组件定义一个函数并绑定父组件的this,在函数体里,将函数的参数赋值给父组件this的一个属性;通过属性传参的方式把这个函数传给子组件。在子组件里,给这个函数传参并执行,实现子组件向父组件传值! 4、React组件3个部分 (1)React组件基本上由3个部分组成——属性(props)、状态(state)以及生命周期方法 (2)React组件一旦接收到的参数(即props)或自身状态有所改变,React组件就会执行相应的生命周期方法,最后渲染。 (3)整个过程完全符合传统组件所定义的组件职责(“属性更新”与“状态改变”)。 (4)以上内容来自《深入React技术栈》第18和30页。 5、ref可以设置回调函数 (1)<input type="text" ref="myInput"/> this.refs.myInput.value ="22"; //this.$refs.myInput.value ="22" 减少获取dom节点的消耗 (2)ref属性可以设置为一个回调函数,这也是官方强烈推荐的用法;这个函数执行的时机为: (3)组件被挂载后,回调函数被立即执行,回调函数的参数为该组件的具体实例。 (4)组件被卸载或者原有的ref属性本身发生变化时,回调也会被立即执行,此时回调函数参数为null,以确保内存泄露。 6、为什么不能用index做key? (1)react会根据key来决定是否重新构建该组件 (2)删除和添加操作,会使一个组件使用另一个的组件的index,进而key,进而data 7、状态提声(父组件的函数作为属性传给子组件) (1)在父组件的constructor中定义状态 (2)在父组件的方法中执行this.setState({}) (3)把父组件的方法作为属性fromParent传给子组件 (4)在子组件的方法中加上this.props.fromParent(e.target.value); (5)触发子组件的事件,执行子组件的方法,改变父组件的状态 8、跨多级组件传参原理 //祖父组件声明自己支持 context static childContextTypes = { color:PropTypes.string, callback:PropTypes.func, } //祖父组件提供一个函数,用来返回相应的 context 对象 getChildContext(){ return{ color:"red", callback:this.callback.bind(this) } } //孙子组件声明自己需要使用 context static contextTypes = { color:PropTypes.string, callback:PropTypes.func, } 二、约束性组件和非约束性组件(受控组件和非受控组件) 1、约束性组件 <input type="text" value={this.state.name} onChange={this.handleChange} /> handleChange: function(e) { this.setState({name: e.target.value}); } //用户输入内容A>触发onChange事件>handleChange中设置state.name="A"渲染input使他的value变成A 2、非约束性组件 <input type="text" defaultValue="a" />//用户输入A -> input 中显示A 3、React 把 input,textarea 和 select 三个组件做了抽象和封装,他们有统一的 value 属性 和 onChange 事件。 <input type='text' name='intro' id='intro' value={this.state.email} onChange={this.handleEmail} /> <textarea type='text' name='intro' id='intro' value={this.state.intro} onChange={this.handleIntro} /> <textarea type='text' name='intro' id='intro' value={this.state.intro} onChange={this.handleIntro} /> 4、checkbox改变的不是 value ,而是 checked 状态。 <input type='radio' name='gender' checked={this.state.male==='MALE'} onChange={this.handleGender} value='MALE' /> <input type='radio' name='gender' checked={this.state.male==='FEMALE'} onChange={this.handleGender} value='FEMALE' /> handleGender(e){ this.setState({ male:e.target.value }) } 三、setState 1、this.setState接收两种参数 (1)对象+函数(可选):传入的对象浅层合并到新的state中 (2)函数+函数(可选):第一个函数接受两个参数,第一个是当前state,第二个是当前props,该函数返回一个对象,和直接传递对象参数是一样的,就是要修改的state;第二个函数参数是state改变后触发的回调。 2、this.setState何时同步何时异步? (1)异步,由react控制的事件处理程序 (2)同步,react控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval等。 (3)大部分开发中用到的都是react封装的事件,比如onChange、onClick、onTouchMove等,这些事件处理程序中的setState都是异步处理的。 3、this.setState何时渲染 (1)死循环:this.setState执行时,会根据_compositeLifeCycleState是否为null,来决定是否重新渲染,因此在有的生命周期里,会产生死循环。 (2)只生效一次:this.setState最后执行队列时,先用变量获取并存储state,后来直接修改变量,最后把变量赋值给state,页面渲染。 handleClick() { //const count = this.state.count 下面 this.setState 多次执行,但只生效一次。因为似乎存在此行代码。 this.setState({ count: count + 1 }) this.setState({ count: count + 1 }) } 四、React生命周期 1、React15、React16完整生命周期比较 A、实例化 (1)React15之ES5写法:propTypes = {}; getDefaultProps(){return{}}; //React16之ES6写法:static propTypes = {}; static defaultProps = {}; (2)React15之ES5写法:getInitialState(){return{}}; //React16之ES6写法:constructor (3)React15之ES5写法:componentWillMount//执行setState会合并到初始化状态中;获取从属性生成的状态;此后生命状态会被重置为null; //React16之ES6写法:static getDerivedStateFromProps//执行setState会合并到初始化状态中;获取从属性生成的状态 (4)render//执行setState会发起updateComponent导致-死循环 (5)componentDidMount//执行setState会导致更新;这是发起异步请求去API获取数据的绝佳时期 B、存在期 (1)React15之ES5写法:componentWillReceiveProps//执行setState会合并到状态中;此后生命状态会被重置为null //React16之ES6写法:static getDerivedStateFromProps//执行setState会合并到状态中; (2)shouldComponentUpdate//执行setState会发起updateComponent导致-死循环 (3)React15之ES5写法:componentWillUpdate//执行setState会发起updateComponent导致-死循环 //React16之ES6写法:此处没有componentWillUpdate (4)render//执行setState会发起updateComponent导致-死循环 //React16之ES6写法:此处新增getSnapshotBeforeUpdate//执行setState会发起updateComponent导致-死循环 (5)componentDidUpdate//可以有条件地执行setState C、销毁期 (1)componentWillUnmount//等待页面卸载,改变state没意义。 2、React16生命周期(过渡) (1)沿用了3个componentWillMount,componentWillReceiveProps,componentWillUpdate (2)新增了2个getDerivedStateFromProps,getSnapshotBeforeUpdate (3)在实际项目中,沿用方案和新增方案只能二选一,不能混用 3、React17生命周期(全新) (1)弃用了3个componentWillMount,componentWillReceiveProps,componentWillUpdate (2)启用了2个getDerivedStateFromProps,getSnapshotBeforeUpdate (3)新增了1个componentDidCatch(处理错误) 来源:https://blog.csdn.net/liuqiao0327/article/details/107297106 五、React组件写法 1、React15.6.1版组件之ES5写法 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>React实例</title> <script src="https://lib.baomitu.com/react/15.6.1/react.js"></script> <script src="https://lib.baomitu.com/react/15.6.1/react-dom.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Button = React.createClass({ setNewNumber(number,event) { this.setState({number: this.state.number + 1}) }, getDefaultProps() { return { name: "计数器" }; }, getInitialState() { return{number: 0}; }, render() { return ( <div> <button onClick = {this.setNewNumber.bind(null,this.state.number,event)}>点击{this.props.name}</button> <Text myNumber = {this.state.number}></Text> </div> ); } }) var Text = React.createClass({ //一、以下实例化时期 getDefaultProps() { console.log("1.getDefaultProps 获取默认属性"); return { }; }, getInitialState() { console.log("2.getInitialState 获取初始状态"); return { }; }, componentWillMount() { console.log("3.componentWillMount 此组件将要被渲染到目标组件内部"); }, componentDidMount() { console.log("5.componentWillMount 此组件已经被渲染到目标组件内部"); }, //二、以下存在时期 componentWillReceiveProps() { console.log("6.componentWillReceiveProps 此组件将要收到属性"); }, shouldComponentUpdate(newProps, newState) { console.log("7.shouldComponentUpdate 组件是否应该被更新"); return true; }, componentWillUpdate() { console.log("8.componentWillUpdate 组件将要被更新"); }, componentDidUpdate() { console.log("10.componentDidUpdate 组件已经更新完成"); }, //三、以下销毁时期 componentWillUnmount() { console.log("11.componentWillUnmount 组件将要销毁"); }, render() { console.log("4和9.render 组件将要渲染"); return ( <div> <h3>{this.props.myNumber}</h3> </div> ); } }) ReactDOM.render( <div> <Button /> </div>, document.getElementById('example') ); </script> </body> </html> 2、React16.4.0版组件之ES6写法 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>React实例</title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> class Button extends React.Component { //name="计算器";state = {number: 0}; //上下写法,二选一 constructor(props) { super(props); this.name="计算器"; this.state = {number: 0}; }; setNewNumber(number,event) { this.setState({number: this.state.number + 1}) }; render() { return ( <div> <button onClick = {this.setNewNumber.bind(this,this.state.number,event)}>点击{this.name}</button> <Text myNumber = {this.state.number}></Text> </div> ); } } class Text extends React.Component { //一、以下实例化时期 constructor(props) { super(props); console.log("2.constructor 获取初始状态"); } componentWillMount() { console.log("3.componentWillMount 此组件将要被渲染到目标组件内部"); } componentDidMount() { console.log("5.componentWillMount 此组件已经被渲染到目标组件内部"); } //二、以下存在时期 componentWillReceiveProps() { console.log("6.componentWillReceiveProps 此组件将要收到属性"); } shouldComponentUpdate(newProps, newState) { console.log("7.shouldComponentUpdate 组件是否应该被更新"); return true; } componentWillUpdate() { console.log("8.componentWillUpdate 组件将要被更新"); } componentDidUpdate() { console.log("10.componentDidUpdate 组件已经更新完成"); } //三、以下销毁时期 componentWillUnmount() { console.log("11.componentWillUnmount 组件将要销毁"); } render() { console.log("4和9.render 组件将要渲染"); return ( <div> <h3>{this.props.myNumber}</h3> </div> ); } } ReactDOM.render( <div> <Button /> </div>, document.getElementById('example') ); </script> </body> </html> 来源 https://www.runoob.com/try/try.php?filename=try_react_life_cycle2 六、插槽 1、portal插槽 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>React插槽实例</title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="container"></div> <div id="outer"></div> </body> <script type="text/babel"> const container = document.getElementById('container'); const outer = document.getElementById('outer'); class Modal extends React.Component { constructor(props) { super(props); this.div = document.createElement('div'); } componentDidMount() { outer.appendChild(this.div); } componentWillUnmount() { outer.removeChild(this.div); } render() { return ReactDOM.createPortal( this.props.children, this.div ); } } class Parent extends React.Component { constructor(props) { super(props); this.state = {clicks: 0}; this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(state => ({ clicks: state.clicks + 1 })); } render() { return ( <div onClick={this.handleClick}> <div>{this.state.clicks}</div> <div><button>Click</button></div> <Modal> <div> <button className="modal">Click</button> </div> </Modal> </div> ); } } ReactDOM.render(<Parent/>, container); </script> 2、React.Children插槽 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>React插槽实例</title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="container"></div> <div id="outer"></div> </body> <script type="text/babel"> class Parent extends React.Component { constructor(props) { super(props); this.state = {clicks: 0}; this.handleClick = this.handleClick.bind(this); } handleClick(event) { console.log(event) this.setState(state => ({ clicks: state.clicks + 1 })); } render() { var that = this; return ( <div> <div>{this.state.clicks}</div> <div><button onClick={this.handleClick}>clicks</button></div> <ul> { React.Children.map(this.props.children,function(item,index){ if(index !=1){ return <li onClick={that.handleClick}>{item}</li> }else{ return <li onClick={that.handleClick}>{item}---{index+1}</li> } }) } </ul> </div> ); } } ReactDOM.render(<Parent> <span style={{cursor:'pointer',userSelect: 'none'}}>插槽一</span> <span style={{cursor:'pointer',userSelect: 'none'}}>插槽二</span> <span style={{cursor:'pointer',userSelect: 'none'}}>插槽三</span> </Parent>, document.getElementById('container')); </script> </html> 七、React.15.6.0源码外框 /** * React v15.6.0 */ (function (allFn) { if (typeof exports === "object" && typeof module !== "undefined") { module.exports = allFn() } else if (typeof define === "function" && define.amd) { define([], allFn) } else { var tempGlobal; if (typeof window !== "undefined") { tempGlobal = window } else if (typeof global !== "undefined") { tempGlobal = global } else if (typeof self !== "undefined") { tempGlobal = self } else { tempGlobal = this } tempGlobal.React = allFn() } })(function () { var define, module, exports; return (function outerFn(first, second, third) { function recursion(number) { if (!second[number]) { if (!first[number]) { var error = new Error("Cannot find module '" + number + "'"); throw error.code = "MODULE_NOT_FOUND", error } var module = second[number] = { exports: {} }; first[number][0].call(module.exports, function (key) { var value = first[number][1][key]; return recursion(value ? value : key) }, module, module.exports, outerFn, first, second, third) } return second[number].exports//在react实例化的过程中,这行代码不但因获取依赖而多次执行,而且还因获取react实例而最后执行。 } for (var number = 0; number < third.length; number++) recursion(third[number]);//fn(16)第1次执行,执行结果没有变量接收 return recursion //执行到这,整个逻辑就快结束了。前两行可以合并为一行:return recursion(third[0]),同时下面的"(48)"应当删掉。 })({ 2: [function (_dereq_, module, exports) { var thisVar = _dereq_(138) }, { "25": 25, "30": 30 }], }, {}, [16])(16)// fn(16)第2次执行,因为n[num]为真,所以直接返回n[num].exports并被挂在g.React上 }); 八、ant-design-pro脚手架的构成 Pro的底座是umi,umi是一个(基于)webpack之上的(自动化)整合工具。 Pro的核心是umi,umi的核心是webpack。 1、web 技术 2、Umi-前端应用框架(可整个或部分复用的软件) (1)Node.js 前端开发基础环境 (2)Webpack 前端必学必会的打包工具 (3)React Router 路由库,被dva内置 (4)proxy 反向代理工具 (5)dva 轻量级的应用框架(可整个或部分复用的软件) (6)fabric 严格但是不严苛的 lint 规则集 (7)TypeScript 带类型的 JavaScript 3、Ant Design 前端组件库 4、ProComponents 模板组件 5、useModel 简易数据流 6、编译时和运行时 (1)编译时,环境是node环境,可以使用fs,path等功能;没有使用webpack,不能使用jsx,不能引入图片 (2)运行时,编译完成,开始在浏览器环境运行,不能使用fs、path,有跨域的问题;这个环境被webpack编译过,可以写jsx,导入图片 7、Umi的插件 (1)plugin-access,权限管理 (2)plugin-analytics,统计管理 (3)plugin-antd,整合 antd UI 组件 (4)plugin-initial-state,初始化数据管理 (5)plugin-layout,配置启用 ant-design-pro 的布局 (6)plugin-locale,国际化能力 (7)plugin-model,基于 hooks 的简易数据流 (8)plugin-request,基于 umi-request 和 umi-hooks 的请求方案 8、Umi其它 (1)配置:开发配置和(浏览器)运行配置 (2)路由:配置路由和约定式路由 (3)插件:id和key,每个插件都会对应一个id和一个key,id是路径的简写,key是进一步简化后用于配置的唯一值。 九、前端路由:url有变化,但不向后台发送请求, 1、它的作用是 (1)记录当前页面的状态; (2)可以使用浏览器的前进后退功能; 2、hash模式帮我们实现这两个功能,因为它能做到: (1)改变url且不让浏览器向服务器发出请求; (2)监测 url 的变化; (3)截获 url 地址,并解析出需要的信息来匹配路由规则。 3、history 模式改变 url 的方式会导致浏览器向服务器发送请求。 4、react前端路由触发条件 (1)<Link to={item.url}>{item.location}</Link> (2)browserHistory.push('/aaa') 暂时留存 四、React组件生命周期 1、ES5写法 ES5写法之实例化时期 (1)propTypes = {}; getDefaultProps(){return{}}; (2)getInitialState(){return{}}; (3)componentWillMount//执行setState会合并到初始化状态中;获取从属性生成的状态 -----执行到这里,生命状态会被重置为null (4)render//执行setState会发起updateComponent导致-死循环 (5)componentDidMount//执行setState会导致更新;这是发起异步请求去API获取数据的绝佳时期 ES5写法之存在期 (1)componentWillReceiveProps//执行setState会合并到状态中; -----执行到这里,生命状态会被重置为null (2)shouldComponentUpdate//执行setState会发起updateComponent导致-死循环 (3)componentWillUpdate//执行setState会发起updateComponent导致-死循环 (4)render//执行setState会发起updateComponent导致-死循环 (5)componentDidUpdate//可以有条件地执行setState ES5写法之销毁时期 (1)componentWillUnmount//等待页面卸载,改变state没意义。 2、ES6写法(旧,React v16.3-) ES6写法之实例化时期 (1)static propTypes = {}; static defaultProps = {}; (2)constructor (3)componentWillMount//执行setState会合并到初始化状态中;获取从属性生成的状态 -----执行到这里,生命状态会被重置为null (4)render//执行setState会发起updateComponent导致-死循环 (5)componentDidMount//执行setState会导致更新;这是发起异步请求去API获取数据的绝佳时期 ES6写法之存在期 (1)componentWillReceiveProps//执行setState会合并到状态中;----将有变动 -----执行到这里,生命状态会被重置为null (2)shouldComponentUpdate//执行setState会发起updateComponent导致-死循环 (3)componentWillUpdate//执行setState会发起updateComponent导致-死循环 (4)render//执行setState会发起updateComponent导致-死循环 (5)componentDidUpdate//可以有条件地执行setState ES6写法之销毁时期 (1)componentWillUnmount//等待页面卸载,改变state没意义。 3、ES6写法(新,React v16.4+) ES6写法之实例化时期 (1)static propTypes = {}; static defaultProps = {}; (2)constructor (3)static getDerivedStateFromProps//执行setState会合并到初始化状态中;获取从属性生成的状态 -----执行到这里,生命状态会被重置为null (4)render//执行setState会发起updateComponent导致-死循环 (5)componentDidMount//执行setState会导致更新;这是发起异步请求去API获取数据的绝佳时期 ES6写法之存在期 (1)static getDerivedStateFromProps//执行setState会合并到状态中;----已有变动 -----执行到这里,生命状态会被重置为null (2)shouldComponentUpdate//执行setState会发起updateComponent导致-死循环 //此处没有componentWillUpdate (3)render//执行setState会发起updateComponent导致-死循环 //下(4)为新增 (4)getSnapshotBeforeUpdate//执行setState会发起updateComponent导致-死循环 (5)componentDidUpdate//可以有条件地执行setState 销毁时期 (1)componentWillUnmount//等待页面卸载,改变state没意义。 另外==== (1)getDerivedStateFromProps//返回一个对象来更新state,如果返回 null 则不更新任何内容。