(1)JSX语法深入理解 简介:从根本上来说,JSX语法提供了一种创建React元素的语法糖,JSX语句可以编译成: React.createElement(component, props, …children)的形式。
1 2 3 4 5 6 7 8 9 10 11 12 13 <MyButton color="blue" shadowSize={2}> Click Me </MyButton> //编译结果: React.createElement( MyButton, {color: 'blue', shadowSize: 2}, 'Click Me' ) //使用自闭和的形式: <div className="sidebar" />
1.指定React元素的类型
JSX标签的头部,决定了React元素的类型,大写的标签,意味着JSX的标签与React的组件一一对应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <Foo/>标签就对应了Foo组件 //(1)必须包裹在一定的范围内 import React from 'react'; import CustomButton from './CustomButton'; function WarningButton() { // return React.createElement(CustomButton, {color: 'red'}, null); return <CustomButton color="red" />; } 引入了2个组件,构成了一个新的组件WarningButton,组件的返回值的元素,必须包含在一定范围内,这里通过函数的’{ ‘, ’ } ‘实现包裹的效果。 //(2)用户定义的组件必须大写 function Hello(){ return <h2>Hello,World</h2> } //定义过程 <Hello/> //使用过程 //(3)不能在运行期间,动态的选择类型 import React from 'react'; import { PhotoStory, VideoStory } from './stories'; const components = { photo: PhotoStory, video: VideoStory }; function Story(props) { return <components[props.storyType] story={props.story} />; //这样写是不对的,我们在返回的组件中,动态定义了组件,这种动态的定义是无效的 } //应该改写为: import React from 'react'; import { PhotoStory, VideoStory } from './stories'; const components = { photo: PhotoStory, video: VideoStory }; function Story(props) { const SpecificStory = components[props.storyType]; return < SpecificStory story={props.story} />; //这样就是正确的,我们不要在JSX的标签中使用动态定义 }
2.JSX中的Props属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 //(1)JS表达式 //通过{},包裹js的语法来使用 <MyComponent foo={1 + 2 + 3 + 4} /> //(2)Props属性的默认值 <MyTextBox autocomplete /> <MyTextBox autocomplete={true} /> //两个式子是等价的,但是不推荐使用默认值,因为在ES6的语法中{foo}代表的意思是:{foo:foo}的意思,并不是{foo:true}。 //(3)扩展属性 function App1() { return <Greeting firstName="Ben" lastName="Hector" />; } function App2() { const props = {firstName: 'Ben', lastName: 'Hector'}; return <Greeting {...props} />; }
3.JSX中的children
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 //(1)children中的function function ListOfTenThings() { return ( <Repeat numTimes={10}> {(index) => <div key={index}>This is item {index} in the list</div>} </Repeat> ); } function Repeat(props) { let items = []; for (let i = 0; i < props.numTimes; i++) { items.push(props.children(i)); } return <div>{items}</div>; } //Repeat组件的定义中可以看出来,children中的方法按此定义会一直执行10次 //(2)忽略Boolean,Null以及Undefined //false,null,undefined以及true是不能通过render()方法,呈现在页面上的,下面的这些div块的样式 相同,都是空白块: <div /> <div></div> <div>{false}</div> <div>{null}</div> <div>{true}</div> <div> {showHeader && <Header />} <Content /> </div> //逻辑是,只有showHeader==true,在会在页面呈现Header组件,否则为null,即为不显示任何东西,这相当于一个if的判断了。 <div> {props.messages.length && <MessageList messages={props.messages} /> } </div> //即使元素为0,0是能够呈现在页面中的。也就是说上述代码中,只要 props.messages数组存在,不管长度是否为0都是存在的。 //(3)如何显示Null,Undefined和Boolean <div> My JavaScript variable is {String(myVariable)}. </div> //其先转化为字符串之后再显示
(2)对于Refs最新变动的理解 1.什么是ReactJS中的refs React中组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,都先在虚拟 DO上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。 如果我们想在虚拟DOM时(此时DOM还没有转化为真是DOM),取到某一个元素,此时不能通过JS的getElementByXXX这种形式。
2.ReactJS前期的做法 在虚拟DOM阶段取元素,语法如下:
1 2 3 4 5 6 function Hello(){ handleClick:function(){ this.refs.myinput.fucus(); } return <input ref="myinput" /> }
可以通过refs.[refsName]来取得虚拟DOM中的元素
3.ReactJS最新版本,对于refs的定义 组件中的refs是一个回调函数。这个函数,在组件生成期(mounted)会自动执行,这个回调函数的参数是元素本身:
1 2 3 4 5 6 function Hello(){ handleClick:function(){ } return <input ref={(input)=>{this.myinput=input;}}/> }
ref的函数,传入了input元素本身,并且将this.myinput赋值为input元素本身。
在元素的销毁期(unmounted)也会执行,但是在销毁期,无论如何只会返回null。
ref 属性:React 支持一种非常特殊的属性,你可以用来绑定到 render() 输出的任何组件上去。这个特殊的属性允许你引用 render() 返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例。
1 2 3 4 5 6 7 <input type="text" ref="username" /> //下面4种方式都可以通过ref获取真实DOM节点 var usernameDOM = this.refs.username.getDOMNode(); var usernameDOM = React.findDOMNode(this.refs.username); var usernameDOM = this.refs['username'].getDOMNode(); var usernameDOM = React.findDOMNode(this.refs['username']);
(3)React中的Context 简介:在React中,数据可以以流的形式自上而下的传递,每当你使用一个组件的时候,你可以看到组件的props属性会自上而下的传递。但是,在某些情况下,我们不想通过父组件的props属性一级一级的往下传递,我们希望在某一级子组件中,直接得到上N级父组件中props中的值。
1.一般情况下通过props传值的情况 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Button extends React.Component { render() { return ( <button style={{background: this.props.color}}> {this.props.children} </button> ); } } class Message extends React.Component { render() { return ( <div> {this.props.text} <Button color={this.props.color}>Delete</Button> </div> ); } } class MessageList extends React.Component { render() { const color = "purple"; const children = this.props.messages.map((message) => <Message text={message.text} color={color} /> ); return <div>{children}</div>; } }
分析一下这段代码,大致的组件分为3级:顶层MessageLists——>Message一级子类——>Button底层子类,我们来看从父组件到子组件的值的传递情况:
(1)text:我们可以看到,在顶层组件MessageLists中的值,传递到一级子组件Message中,并在此组件中被使用。
(2)color:再看props中的color的传递情况,在顶层组件MessageLists中的值,先传递到一级子组件Message中,在传递到二级子组件Button中,最后在二级子组件中被使用。
综上:这就是一般在React中,所使用的通过props属性,在父组件与子组件中进行值传递。
2.如何利用React中的Context来进行值的越级传递。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Button extends React.Component { render() { return ( <button style={{background: this.context.color}}> {this.props.children} </button> ); } } Button.contextTypes = { color: React.PropTypes.string }; class Message extends React.Component { render() { return ( <div> {this.props.text} <Button>Delete</Button> </div> ); } } class MessageList extends React.Component { getChildContext() { return {color: "purple"}; } render() { const children = this.props.messages.map((message) => <Message text={message.text} /> ); return <div>{children}</div>; } } MessageList.childContextTypes = { color: React.PropTypes.string };
上述代码,我们实现了通过React的Context实现了值——color的越级传递。我们来分析一下上述的方法。
(1)首先在顶层组件中:
1 2 3 MessageList.childContextTypes = { color: React.PropTypes.string };
定义了顶层组件所拥有的子类context对象——该顶层组件所拥有的的子类context对象为color,且必须为字符串。
然后通过getChildText方法,来给子context对象的属性赋值:
1 2 3 4 getChildContext() { return {color: "purple"}; }
这样就完成了顶层组件中,context对象的赋值。
(2)越级传递,因为color属性只在最底层使用
我们来看color属性的越级传递,因为color属性,在一级子组件Message中并没有直接用到,因此我们可以直接传递到最底层(越级),在Button组件中使用。
首先Button组件中,再次声明了所接受到的context的子组件color的类型,声明必须为字符串:
1 2 3 Button.contextTypes = { color: React.PropTypes.string };
然后可以通过this.context.color这种方式调用:
1 2 3 <button style={{background: this.context.color}}> {this.props.children} </button>
(4)ShouldComponentUpdate的用法 简介:ShouldCompleteUpdate,指明什么时候component(组件)需要进行更新。
1.常见的SCU的用法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 //(1)比如在下面的例子中,组件中只有2个值,props.color和state.count可以发生改变 class CounterButton extends React.Component { constructor(props) { super(props); this.state = {count: 1}; } shouldComponentUpdate(nextProps, nextState) { if (this.props.color !== nextProps.color) { return true; } if (this.state.count !== nextState.count) { return true; } return false; } render() { <button color={this.props.color} onClick={() => this.setState(state => ({count: state.count + 1}))}> Count: {this.state.count} </button> } } //组件仅仅会校验prop.color和state.count,如果这些值都不会改变,那么组件就不会有更新。 //(2)如果组件更加复杂,拥有的状态变量更多 //当组件复杂化,拥有状态变多时,我们需要设计一种模式,对所有的props变量和state变量,做一个“shallow comparison(浅比较)”,这样会使得SCU函数冗杂化,为了解决该问题,React给了我们提供了另一个继承方法——React.PureComponent: class CounterButton extends React.PureComponent { constructor(props) { super(props); this.state = {count: 1}; } render() { <button color={this.props.color} onClick={() => this.setState(state => ({count: state.count + 1}))}> Count: {this.state.count} </button> } } //在大部门情况下我们可以用React.PureComponent来代替SCU,但是当props和state中的变量发生突变的情况下, //“shallow comparison”会失效,因此在props和state的变量发生突变的情况下, //不能通过React.PureComponent来更新组件。 var x=[1,2]; var y=x; x.push(3); console.log(x==y)//输出true var x={a:1}; var y=x; x.b=2; console.log(x==y)//输出true //shallow comparison不能进行深层比较的原因是,js中数组和对象的本质都是Object, //一旦赋值y=x后,无论x如何变化,x,y都会只想的是同一个对象。
3.如何解决shallow comparison失效的问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 //失效状态1: handleClick() { // This section is bad style and causes a bug const words = this.state.words; words.push('marklar'); this.setState({words: words}); } //解决方法: handleClick() { this.setState(prevState => ({ words: prevState.words.concat(['marklar']) })); } //失效状态2: function updateColorMap(colormap) { colormap.right = 'blue'; } //解决方法: function updateColorMap(colormap) { return Object.assign({}, colormap, {right: 'blue'}); } //本质:解决方法的本质是生成了一个新的对象,新对象与原对象比较一定返回的是false。
为正常使用来必力评论功能请激活JavaScript