react
在传统组件架构的基础上增加了创新性的 虚拟 dom
和 diff
算法,减少了传统组件架构的手动局部渲染问题,并使用类似 html 的 jsx
语法来构建组件,配合极简的 api,概念很少,入门很快。
本文将介绍如何从传统的组件架构 KISSY 迁移到 react。示例组件为 menu
以及calendar/date-picker
kissy menu: https://github.com/kissyteam/menu react menu:https://github.com/react-component/menu
kissy date-picker: https://github.com/kissyteam/date-picker react calendar:https://github.com/react-component/calendar
目录结构
以 calendar/date-picker
举例,打开 lib
目录可见,组织基本相同,calendar 包括日选择面板以及弹出的月,年,年代选择面板,由四个组件组成: kissy 对应为date-picker.js
decade-panel.js
month-panel.js
year-panel.js
react 对应为 Calendar.js
DecadePanel.js
MonthPanel.js
YearPanel.js
组件设计
API
kissy:
new DatePicker({
...
}).render(container);
new Menu({
...
children:[{
new MenuItem(…)
}]
}).render(container)
react:
React.render(<Calendar …/>, container);
React.render(<Menu …><MenuItem></MenuItem></Menu>,container)
api 有很大的相似性,都支持属性,组合等特性,但是 react 将 dom 和组件统一了起来,从而比 kissy 达到更强的一致性:
React.render(<div><span>dom</span><Calendar …/></div>, container);
状态与属性
kissy 实际上不区分状态与属性,都是通过 attr
来声明:https://github.com/kissyteam/date-picker/blob/2622151aa29ef0c3f2b7d1c541b91699fe105298/lib/date-picker.js#L492
因而需要文档可说明,哪些只能初始化 config
的,哪些可以后期 set
。
而 react 通过 props
和 state
来区分可以 config
的属性以及后期可以set(setState)
的属性。
模版
kissy 的组件架构为传统的 mvc
架构,v
采用 xtemplate
模版,因此 kissy 有单独的 xtpl
目录用于存放组件的模版,例如
其中又有组件自身实现的一些命令从而打通模版与组件:https://github.com/kissyteam/date-picker/blob/2622151aa29ef0c3f2b7d1c541b91699fe105298/lib/date-picker/xtpl/picker.xtpl#L59
而 react 实际上没有模版的概念,通过 jsx
实现了一个 虚拟 dom
,jsx 可以利用全部的 js 特性,和组件沟通也更顺畅,例如 react calendar 主要渲染 “模版” 的地方:
局部刷新
kissy 本质上是 observer/pub/watcher
模式, 当属性通过 set
方法发生改变,用户需要自行定义改变 dom 的代码来局部刷新:https://github.com/kissyteam/date-picker/blob/2622151aa29ef0c3f2b7d1c541b91699fe105298/lib/date-picker.js#L390 十分繁琐,并且大多使用 innerHTML 造成大量 gc
而 react 通过 setState
,diff
, 以及 批量更新
机制,自动并高效得更新 dom,用户只需要关注 render
方法即可: https://github.com/react-component/calendar/blob/a0d7d4e508ecbee676f56a60efa8b931df4aa098/lib/Calendar.js#L40
生命周期
react 和 kissy 都会有一些生命周期的 hook 函数可以定义,react 更加丰富
react :
new hook: – getDefaultProps() – getInitialState() – componentWillMount() – render() – componentDidMount() – componentWillUnmount()
update hook: – componentWillReceiveProps() – shouldComponentUpdate() – componentWillUpdate() – render() – componentDidUpdate()
kissy:
new hook – initializer -> getInitialState – beforeCreateDom – afterCreateDom – beforeBindUI – afterBindUI – beforeRenderDom – afterRenderDom -> componentDidMount – destructor -> componentWillUnmount
常用的 react 都已经包含.
事件
react 通过 dom 事件全局代理
以及模版内声明的方式达到了更少的原生事件注册,减少了内存占用,而 kissy 则是自动在组件跟节点进行绑定,如果需要内部节点事件则需要自行绑定
在组件方面 react 没有自定义事件的概念,也没有自定义事件的冒泡机制,组件事件实际上是通过 callback
传递来实现,不知道是优点还是缺点:https://github.com/react-component/menu/blob/45816af15fffb0c6e598602c289e39483fb1d6f7/lib/Menu.js#L162
而在 kissy 组件中 children 的自定义事件默认都会传递到 parent 上,触发 parent 的事件绑定,因此很容易就可以实现组件自定义事件的事件委托。
对于用户使用则无明显不同
kissy:
var menu = new Menu({
listeners:{
“select”: function(){
}
},
children:[{
new MenuItem()
}]
}).render(container);
react:
React.render(<Menu onSelect={function(){}}><MenuItem></MenuItem></Menu>)
内部组件调用
react 中由于 render
等都是虚拟 dom,取得组件实例需要通过在虚拟 dom 中配置ref
属性:https://github.com/react-component/menu/blob/45816af15fffb0c6e598602c289e39483fb1d6f7/lib/Menu.js#L152
然后从组件的 refs
map 中得到对应的组件实例后才能调用其方法:https://github.com/react-component/menu/blob/45816af15fffb0c6e598602c289e39483fb1d6f7/lib/Menu.js#L55
而 kissy 由于没有虚拟 dom 这一层,组件方法可以直接调用:https://github.com/kissyteam/menu/blob/af4d3628ff1bd4d262ffc944297af21ea8039cc4/lib/menu/control.js#L118
另外 react 不推荐组件的方法调用,推荐状态。而 kissy 若这么做,由于属性机制太弱,则得不偿失。
生态圈/服务器端渲染
kissy 生态圈为 gallery,一般只能客户端线上 cdn combo 使用。 react 组件依托 npm,可以使用 npm 上的众多模块,配合 browserify/webpack 等工具可以完全离线开发,打包,并且可以在服务器端进行首屏渲染。
总结
react 在吸收传统组件架构的基础上,通过自己的创新,大大提升了开发者的效率和使用体验,如果你仍然在用传统组件模式编码,我强烈建议你尽快切换到 react,人生苦短,活在当下 🙂 而这篇文章我又多么希望一年前就有别人写给我看.