💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[toc] ## 转换过程:jsx->React.createElement->{虚拟dom} ``` import React from 'react'; //这个名字不能随便改 因为编译后就叫这个名字 看77行 import ReactDOM,{render} from 'react-dom'; //jsx语法时facebook自己发明的 babel-preset-react let ele = <h1 className="read"><span>zfpx</span>hello,world</h1>; //以上等同于以下 // type,props,children React.createElement( "h1" //type ,{ className:'read' } ,React.createElement( "span" ,null ,"zfpx" ) ,"hello,world" ); // -> // let obj = { // type:'h1' // ,props:{ // className:'red' // ,children:[ // {type:'span',props:{children:'zfpx'}} // ,'hello,world' // ] // } // } ``` ## prop-types和属性验证 ``` import React from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; let person = { name:'ahhh' ,age:111 ,gender:'男' ,hobby:['sleeping'] ,salary:100 } class Person extends Component{ // Person.propTypes = static propTypes = { name:PropTypes.number.isRequired ,age:PropTypes.number ,gender:PropTypes.oneOf(['男','女']) ,hobby:PropTypes.array ,salary:function(props,keyName,componentName){ if(props[keyName]<1000){ throw new Error(`${componentName}error${props[keyName]}is too low`) //Person error 100 is too low } } ,position:PropTypes.shape({ x:PropTypes.number ,y:PropTypes.number }) } constructor(props){ super(); } render(){ let{name,age,gender,hobby,salary,position} = this.props; return( <div> {this.props.name} </div> ) } } ``` ## React.createElement和ReactDOM.render 原理 ``` class Element{ constructor(type,props){ this.type = type; this.props = props; } } let React = { createElement(type,props,...children){ if(children.length === 1)children = children[0]; return new Element(type,{...props,children}); } }; function render(eleObj,container){ let {type,props} = eleObj; let ele = document.createElement(type); for(let key in props){ if(key!=='children'){ if(key === 'className'){ ele.setAttribute('class',props[key]); }else{ ele.setAttribute(key,props[key]); } } else{ // 是我们的儿子节点 let children = props[key]; if(Array.isArray(children)){ // 是数组 children.forEach(child=>{ if(child instanceof Element){ render(child,ele); }else{ ele.appendChild(document.createTextNode(child)); } }); }else{ if(children instanceof Element){ render(children,ele); }else{ ele.appendChild(document.createTextNode(children)); } } } } container.appendChild(ele); } // render(ele,window,root); ``` ## JSX ``` import React from 'react'; //这个名字不能随便改 因为抽象语法树编译后就是React React.createElement import ReactDOM,{render} from 'react-dom'; //jsx和html写法不完全一样 // className ->class // htmlFor -> label的for // jsx元素可以嵌套 //相邻的react元素 不能不被包裹使用 必须加一层标签 //jsx里面可以放js 里面区分是不是js根据的是{} let name = '' let ele = ( <React.Fragment> <label htmlFor="a"></label> <input type="text" id='a'/> <h1> {/*注释*/} {name}{age} {function name(params) {return 100}()} {1+3}{1==1?'2':'3'} </h1> <h1 style={{background:'red'}}>hello</h1> <div dangerouslySetInnerHTML={{__html:'<h1>hello ahhh</h1>'}}></div> {/*相当于innerHtml*/} </React.Fragment> ); render(ele,window.root); ``` ## render第一个参数放函数 ``` //渲染函数 import React from 'react'; import ReactDOM from 'react-dom'; //1) function school(name,age){ return <h1>{name}{age}</h1> } ReactDOM.render(school('ahhh',111), document.getElementById('root')); //2) let el = ( <ul>{school('ahhh',111)}</ul> ) ReactDOM.render(el,window.root) //3) let dinner = ['汉堡','可乐','薯条']; let eleObj = dinner.map((item,index)=>( <li key={index}>{item}</li> )); ReactDOM.render(eleObj,window.root); ``` ## 组件 ``` //组件分两种 函数组件(函数)function 类组件 class //函数组件中没有this 没有声明周期 没有状态 import React from 'react'; import ReactDOM from 'react-dom'; //怎么区分是组件还是jsx元素 //如果名字是大写就是组件,小写就是jsx元素 //组件必须要有返回值 也可以返回null null也是合法的 function School(props){ // return null return <h1>{props.name}{props.age}</h1> } //School({name:'ahhh',age:111}); //组件可以通过属性传递数据 ReactDOM.render(<School name='ahhh' age='111'/>, document.getElementById('root')); ``` ## 函数组件和类组件与时钟示例 ``` import React from 'react'; import ReactDOM from 'react-dom'; function Clock(props){ return ( <div> <span>当前时间:</span> {props.date.toLocaleString()} </div> ) } //不会一直渲染 只渲染一次 ReactDOM.render(<Clock date={new Date()}/>, document.getElementById('root')); //组件的特点 就是复用 多次使用 setInterval(function(){ ReactDOM.render(<Clock date={new Date()}/>, document.getElementById('root')); },1000); ``` ``` import React from 'react'; import ReactDOM from 'react-dom'; class Clock extends React.Component{ constructor(){ super(); this.state = {date:new Date().toLocaleString(),name:'ahhh'} } // state = {date:new Date().toLocaleString()} //如果用类组件需要提供一个render方法 // 组件渲染完成后会调用这个生命周期 componentDidMount(){ setInterval(()=>{ this.setState({date:new Date.toLocaleString()}); },1000); } render(){ return ( <span>时间是:{this.state}</span> ) } } ReactDOM.render(<Clock/>,window.root); //组件有两个数据源 一个是属性 外界传递的 还有一个叫状态是自己的 //都会导致页面刷新 // 你传递的属性可能不是我预期的 ``` ## 组件通信 ![](https://box.kancloud.cn/86d0db46d19ab323795317e46ed96df1_744x111.png) ## 关于React.createElement ![](https://box.kancloud.cn/1928509184762f9f6e89383ad0bc6d92_668x75.png) ``` //编辑后就是 React.createElement //大写的React ``` ## 父组件会在子组件DidMount后才DidMount(包括父组件重新Mount,子组件也会重新Mount,然后父组件才DidMount) ## 16.3新特性 ### React.createRef() ``` export default class xxx extends React.Component{ constructor(){ super(); this.text = React.createRef(); } componentDidMount(){ // this.text.focus(); this.text.current.focus(); } render(){ return ( <div> {/*<input type="text" ref={(input)=>{this.text=input}}/>*/} {/*会自动的将当前输入框 放在this.text.current*/} <input type="text" ref={this.text}/> </div> ) } } ``` ### getDerivedStateFromProps ![](https://box.kancloud.cn/209e765566e1c1b426b91f04d6093722_586x366.png) ### getSnapshotBeforeUpdate ![](https://box.kancloud.cn/99fee69979284c8fc86226b3a4cb772b_476x237.png) ![](https://box.kancloud.cn/08bc9a257cc90acfe3726b788c16c014_844x155.png) ## react-redux 如果老的状态和新的状态时同一个(地址) ![](https://box.kancloud.cn/221c7aed8422289df2b6931321bc4b59_663x311.png) `react-redux`是不会允许更新视图的 ![](https://box.kancloud.cn/f99e02d6e8aba9fe74f589c9a1939503_561x288.png) ## immutable.js ``` let {Map} = require('immutable'); // 导入immuatable //1.只操作对象 let obj = {a:1}; //返回的m1 就是不可变对象,永远不会变 let m1 = Map(obj); ``` set ``` // let m2 = m1.set('a',100); //调用set后永远返回的都是一个新的不可变对象 ``` update ``` let m2 = m1.update('a',(a)=>a+100); console.log(m1.get('a')); console.log(m2.get('a')); ``` Map 只处理一层 ``` let obj = {a:{a:1},b:2}; let m1 = Map(obj); console.log(m1); ``` 可以发现下面第二层的数据不大对 ![](https://box.kancloud.cn/6188d073bbd4a29c473e5aafd8fe5d5d_456x92.png) 多层使用`fromJS` ![](https://box.kancloud.cn/d7a7bc1f40aa96433df150c5c05dffce_556x213.png) ![](https://box.kancloud.cn/5087b133a8a5e0c3f4dcb07d8dbdfffe_556x206.png) 嵌套多层时 改变深层的属性 ``` let {Map,fromJS} = require('immutable'); let obj = {a:{b:{c:{d:1},d:100},m:100}}; let m1 = fromJS{obj}; m1.setIn(['a','b','c','d'],2); // ->a.b.c.d = 2 并且返回新对象 ``` getIn ``` console.log(m1.getIn(['a','b','c','d'])); ``` immutable 支持对数组操作 List -> 相当于操作对象时的Map 多层级时仍然使用`fromJS` ``` let m1 = List([[1,2,3],[4,5,6]]) let m1 = fromJS([[1,2,3],[4,5,6]]) ``` `immutable`对象不可直接访问其属性 ``` let m1 = fromJS({a:1}); console.log(m1); ``` ![](https://box.kancloud.cn/35705e59f5137c6cbb5f6d229f9f803c_243x88.png) ``` console.log(m1.a) <<< undefined ``` 可以使用`toJS`方法将immutable对象转换为普通js对象 ![](https://box.kancloud.cn/df02396d7529850c20e7fb9f4764fabd_377x340.png) 两个`immutable`对象一定是不相同的,但如果我们想要比较两个对象中的值一不一样,可以使用`is()`方法 ![](https://box.kancloud.cn/53c06437469862602b7b1d0aad215e78_352x120.png) ### immutable在react中的应用 ![](https://box.kancloud.cn/e7e30186b9295c66a1ee44c8e4b471cc_643x528.png) ### redux-immutable ![](https://box.kancloud.cn/b05954dccfe61182b422db194beec291_545x60.png) 不再用redux里的cmobineReducers ![](https://box.kancloud.cn/d2136d6920c1873a1010e9f07cfddbc8_641x207.png) ![](https://box.kancloud.cn/37e4bf9032d6a73aa3681521fdace493_666x232.png)