react的非DOM操作
- 非dom属性?
dangerouslySetInnerHTML,ref,key
非dom标准属性,也就是说dom标准里面没有规定的属性,react引入了三个非dom属性,如上。
dangerouslySetInnerHTML:字面意思,危险的设置内部html,这个属性的作用就是在jsx中,直接插入html代码。我们为什么用这个属性插入html代码呢?而不是在编写代码的时候直接写入呢?因为有的时候我们在编写代码的时候,无法确实要插入什么代码,也就是说这部分html代码是动态生成的。或者说不是由我们来编写的,为什么这中行为是危险的呢?有一个词语叫做跨站攻击,之所以会产生跨站攻击,就是因为有这样的直接写入代码功能,我们举个例子,如果页面上要显示一个内容,这个内容来自于用户的输入,假设用户的输入中包含了代码,比如说js代码,html代码,如果我们不经过检测就直接使用,把这个dom插入到页面中,那么其他人访问页面的时候,就会执行这个人写入的代码,由于我们无法判断这个用户的意图,所以他有可能写入很危险的代码,比如说添加一个连接,连接到一个木马,那么访问这个页面的用户,就会在不知不觉中中病毒或者下载木马,这是非常危险的,但是有些时候我们确实需要动态的写入代码,所以react还是提供了这个属性,只是明确的在名字里面告诉你这个属性是很危险的,尽量不要去使用。
ref:reference的缩写,父组件引用子组件,我们在编写组件的时候,经常会用到嵌套的情况,父组件如果嵌套了子组件,父组件引用子组件的时候,就需要用ref,在实际的使用中ref其实是在父组件中维护了很多引用,每一个引用都引用到了一个对应的子组件,这样我们在父组件中就可以通过这些引用来操作子组件。为什么我们不能操作父组件的父组件呢?这个其实不是代码问题,而是设计问题,在react中我们使用组件的目的就是使代码模块化,每个组件只需要考虑自己自身的功能,逻辑,不需要关心谁在使用它,因此组件不需要引用他本身的父组件,父组件和组件之间的交互式通过属性的传递来实现的,我们后面会说。但是这样的传递是单向的,也就是说,属性总是由上往下传递,下面的组件不会对上面的组件进行控制,这是为了让逻辑更加清晰,
key:提高渲染性能。react特点之一就是性能好,这是因为他去除了手动的dom操作,完全由自动来实现,但是自动实现就说明他有一套算法,也就说在页面发生变化的时候,react需要应用这套算法来判断如何高效的修改页面,以反映出对应的变化,这个算法通常被称为diff,也就是different的缩写,表示计算两个状态之间的差异。 - react diff算法
react diff算法的流程图
首先需要明确我们现在进行比较的是两个组件,react中所有的东西都是组件,所以我们只要明白了两个组件之间是如何比较的,其他的结构都可以分解成组件进行比较。
最左侧是开始,开始以后第一个判断就是节点是否相同,相同的意思就是div和div是相同的,div和p就是不同的,或者说我们自定义的HelloMessage和HelloMessage是相同的,HellowMessage 和HelloWorld就不是相同的组件,节点如果相同才会进行后面的比较,如果节点不同,react会直接抛弃旧的节点,生成一个全新的节点,在这里react是基于一个假设,如果两个节点不同,那么他们的内容很大程度不同。如果节点不同我们就直接结束比较,生成全新的节点,如果节点相同,下一步需要判断的是,这个几点是自定义的节点还是dom的标准节点,也就是说,他是div还是HelloWorld,如果说不是自定义的节点,是标准的节点,那么react下一步需要做的就是比较两个节点的属性,比如说class,id等,如果属性完全相同,就说明两个节点是相同的,结束比较;如果属性不同,就把不同的属性记下来,并应用改动,比如说多了一个新属性,就加入一个新属性,少了一个属性就删掉一个属性,类似这样的操作。也就是react并不会删掉旧的节点,只会在他上面进行操作。若果是自定义的节点,react会重新渲染他,我们后面会学习属性和状态,一个组件有很多状态,如果是同一个自定义组件,那么新组件可能只是旧组件的一个状态,所说reac会 把新的状态传入到旧的组件中,然后比较组件的渲染结果,并进行改动,也就是说react仍然不会重新生成组件,而是在旧的组件上进行改动,这就是diff算法的整个流程。那么key到底有什么用呢?key作用主要体现在节点的比较,假设我们有一个类似列表的节点,也就是父节点里面有多个子节点,那么如果没有key的时候,我们进行改动,react会比较傻的从左到右进行比较,比如说在改动之前只有节点1,在改动之后我们插入一个节点2,变成了节点2,节点1,那么react执行的操作是,删除节点1,添加节点2,添加节点1,也就是说,react无法判断新状态中的节点1是不是就状态中的节点1,所以他只能把不同的全部删掉,即使他们可能相同,这样就导致性能上的问题,那么引入key的目的就是给每个节点加上唯一的标识,这样的话react通过比较key,就可以知道到底哪些节点是原有节点,哪些节点是新加入的节点,针对我们刚才说的例子, 节点1变成了节点2节点1,这时候,react只需要做一个操作,就是插入节点2,因为节点1的key是一样的,说明他们两个是同一个节点,没有变化。
了解这个原理,对于我们编写react组件有什么启示呢?
第一点就是如果两个组件非常类似,那就尽量把他们写成一个组件,因为我们在流程中看到,两个不相同的组件一定会被重新生成,即使他们的内容非常的相似,。第二个启示就是如果使用类似的列表来展示元素的话,那么元素尽量加上key,这样可以极大地提高效率,避免不必要的性能损耗。 - 如何使用非dom属性?
dangerously的实例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>hello world</title> </head> <body> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/react.js"></script> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/JSXTransformer.js"></script> <script type="text/jsx"> var style={ color:"red", border:"1px solid #f09", }; var rawHTML={ __html:"<h1>I am inner HTML</h1>" }; var HelloWorld=React.createClass({ render: function(){ return <p>Hello,world</p> } }); React.render(<div style={style} dangerouslySetInnerHTML={rawHTML}></div>,document.body); </script> </body> </html>
ref实例,要注意通过引用拿到的并不是这个dom节点本身,也就说我们并不能进行dom之间的操作,比如说设置文本,这样是不行的,我们拿到的只是一个虚拟的dom节点,也就是react展示给我们的dom节点,如果想要拿到真正的dom节点,还需要调用一个方法,后面我们会说,不过react并不会鼓励我们这样去做,除非在特殊的情况下,我们需要操作dom节点,其他情况react会帮助我我们进行操作,
这个例子不全,后面我们会继续讲解。<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>hello world</title> </head> <body> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/react.js"></script> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/JSXTransformer.js"></script> <script type="text/jsx"> var style={ color:"red", border:"1px solid #f09", }; var rawHTML={ __html:"<h1>I am inner HTML</h1>" }; var HelloWorld=React.createClass({ render: function(){ this.refs.childp return <p ref="childp">Hello,world</p> } }); React.render(<div style={style} dangerouslySetInnerHTML={rawHTML}></div>,document.body); </script> </body> </html>
key实例:注意在每个组件的内部,key的值必须是不同的,注意是组件内部!两个组件之间就没有这个限制了。
记住两点:1,内容类似的组件尽量合并称为同一个组件,2列表类型的元素,一定要加上唯一的key,做到这两点,就能避免很多性能问题。<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>hello world</title> </head> <body> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/react.js"></script> <script type="text/javascript" src="http://cdn.bootcss.com/react/0.13.2/JSXTransformer.js"></script> <script type="text/jsx"> var style={ color:"red", border:"1px solid #f09", }; var rawHTML={ __html:"<h1>I am inner HTML</h1>" }; var HelloWorld=React.createClass({ render: function(){ return <ul> <li key="1">1</li> <li key="2">2</li> <li key="3">3</li> </ul> } }); React.render(<div style={style} dangerouslySetInnerHTML={rawHTML}></div>,document.body); </script> </body> </html>