Vue 2x 中使用 render 和 jsx 的最佳实践 (2)
JSX 书写规范
JSX 支持换行
JSX 的顶层只能有一个根元素,我们很多时候会在最外层包裹一个
div
(后续 React 推出了不占据 Dom 结构的Fragment
,同时,<></>
空标签有同样的效果,但是测试后发现这个在 vue 中不生效,)为了方便阅读,我们通常在 jsx 的外层包裹一个小括号
()
,这样可以方便阅读,并且 jsx 可以进行换行书写;JSX 中的标签可以使单标签,也可以是双标签
如果是单标签,必须以/>结尾
JSX 注释都要用花括号{}包起来
JSX 插入变量
JSX 嵌入表达式
运算表达式
三元表达式
函数调用
JSX 绑定属性
绑定普通属性 Attrs
绑定 class
在 jsx 中,class 属性需要指定为 className,因为 class 在 JavaScript 中是保留字段
绑定 style
在 jsx 中,windows 风格的命名方式(属性、style、方法、event)都需要转换成驼峰式的写法,比如,正常写一个 style 指定左边的外边距:margin-left:‘10px’,
这里需要改成:marginLeft:‘10px’
JSX 绑定事件
JSX 中绑定事件类似在 HTML 原生中绑定事件,只不过 React 中事件命名采用小驼峰(camelCase),而不是纯小写;
但是我们会发现在我们绑定的回调事件中访问我们对应的 this 会是 undefined,这是因为对应的回调函数是 React 内部帮我们去进行调用的,React 无法确定对应的 this 所以采用的是 callback.apply(undefined,[])方式调用,改变了 this 的指向为 undefined。[
这条规则不适用于Vue,因为在Vue中对this做了特殊处理
]
如果我们需要在事件中通过this
来访问React
组件本身属性和方法,有以下几条解决方案:
通过 bind 绑定 this(显示绑定)
使用 bind 绑定的方式除了可以非常简单的获取到事件对象 event 之外,还可以传递我们想要传递的参数
除了显示绑定之外,我们可以使用匿名函数(箭头函数)的方式
同理,在声明函数的时候我们使用箭头函数的方式也可以达到同样效果[
如果想要传递我们自己的参数,还是需要用到bind
]
这里高阶的同学要注意!
如果是在 JSX 中使用事件绑定,请不要使用箭头函数的方式去声明方法甚至直接在 JSX 中使用箭头函数绑定事件。
因为根据 VR 的 render 渲染机制,如果使用箭头函数,那么每当组件的 state 发生改变,推动 render 渲染执行的时候,如果存在箭头函数,每次浏览器都会分配新的内存和额外的开销来执行事件的绑定,组件绑定的层级越深,额外开销越大。
所以,为了最优的性能考虑,请在constructor
构造函数中对需要绑定的事件做显示绑定
JSX 条件渲染
在
jsx
中,不允许使用if
、if-else
,请使用三元运算符
或者逻辑与&&
同样,也允许使用
for
循环,请使用 JS 中的高阶函数map
、filter
……
createElement
要更透彻的了解和学习 JSX,浅析其本质,那么一定要先了解createElement
因为提到JSX
,不可避免的需要提到createElement
,所以,是不是奇奇怪怪的知识又增加了 : )
从React中看createElement
JSX 实际上仅仅是 React.createElement(type, config, children)方法的语法糖,该方法接收三个参数:
type
当前 ReactElement 的类型,如果是标签元素,那么使用字符串表示“div”,如果是组件元素直接使用组件的名称就可以。
config
我们在 JSX 中绑定的属性会在 config 对象中以键值对的形式存在。
children
存放标签中的内容,以 children 数组的形式存储
我们都知道,JSX 是通过babel
进行解析的,而我们编写 JSX 的时候必须依赖babel
我们可以再 babel 的官网查看 JSX 的转换过程:传送门
如果我们直接使用 React.createElement()来编写代码,就不需要以来 bable 进行解析也可以正常的渲染显示
我们通过 React.createElement()方法最后返回得到的是一个ReactElement
对象,
这个ReactElement
对象作用是什么?
其实 React 利用ReactElement
对象组成了一个 JavaScript 对象树,这个对象树就是我们经常讲的一个概念--虚拟DOM(VR DOM)
,我们可以将之前 jsx 返回的结果进行打印来查看对应的 ReactElemnt 对象:
我们编写的 JSX 代码经过 bable 编译解析成对应的React.createElement()
方法形式,
经过React.createElement()
方法调用返回我们对应的ReactElement对象树
(虚拟 DOM 树),对应的ReactElement对象树
经过ReactDOM.render()
方法转换为真正的 DOM 在我们的浏览器进行渲染。
JSX -> VR DOM -> DOM
为什么要用VR DOM
很难跟踪状态发生的改变:原有的开发模式,我们很难跟踪到状态发生的改变,不方便针对我们应用程序进行调试;
操作真实 DOM 性能较低:传统的开发模式会进行频繁的 DOM 操作,而这一的做法性能非常的低;
DOM 操作非常耗费性能
document.createElement 本身创建出来的就是一个非常复杂的对象:传送门
DOM 操作会引起浏览器的回流和重绘,所以在开发中应该避免频繁的 DOM 操作
不是用了VR DOM性能就一定会变好
React 从来没有说过 “React 比原生操作 DOM 快”。
React 的基本思维模式是每次有变动就整个重新渲染整个应用。
如果没有 Virtual DOM,简单来想就是直接重置 innerHTML。
很多人都没有意识到,在一个大型列表所有数据都变了的情况下,重置 innerHTML 其实是一个还算合理的操作... 真正的问题是在 “全部重新渲染” 的思维模式下,即使只有一行数据变了,它也需要重置整个 innerHTML,这时候显然就有大量的浪费。
我们可以比较一下 innerHTML
vs. Virtual DOM
的重绘性能消耗:
innerHTML: render html string O(template size) + 重新创建所有 DOM 元素 O(DOM size)
Virtual DOM: render Virtual DOM + diff O(template size) + 必要的 DOM 更新 O(DOM change)
Virtual DOM render + diff 显然比渲染 html 字符串要慢。
但是!它依然是纯 js 层面的计算,比起后面的 DOM 操作来说,依然便宜了太多。
可以看到,innerHTML
的总计算量不管是 js 计算还是 DOM 操作都是和整个界面的大小相关,但 Virtual DOM
的计算量里面,只有 js 计算和界面大小相关,DOM 操作是和数据的变动量相关的。
前面说了,和 DOM
操作比起来,js
计算是极其便宜的。这才是为什么要有 Virtual DOM
:
它保证了:
不管你的数据变化多少,每次重绘的性能都可以接受;
你依然可以用类似
innerHTML
的思路去写你的应用。
版权声明: 本文为 InfoQ 作者【默默的成长】的原创文章。
原文链接:【http://xie.infoq.cn/article/d0cc53309282cf042288b6dde】。未经作者许可,禁止转载。
评论