前言
框架之间的对比老生常谈,但是关于咸豆腐还是甜豆腐好吃的争论属实没什么营养(小孩子才争来争去,大人们当然都直接拍手称好)。一直都觉得每个得到广大开发者肯定且流行起来的框架,都有自己的优势和短板,在于使用者的使用场景和使用体验,特别是最近系统的接触了React之后,这样的感受更甚。技术就是这样,永远在不断的迭代和更替。喊着"Rethinking Best Practices"诞生的React(燃起来了,兄弟们),也着实从开发模式的层面上取得了突破性的成绩,而作为独立开发者的尤大搞出来的Vue,也一样很酷。
废话不多说,先三连为敬。(图片来自网络)
鉴于自己对距离React深度使用者还差一个深度的距离,本文主要从一些使用和语法上进行一些对比,记录一些自己的感悟,如有纰漏,感谢指出。
结构
框架哲学
其实是比较一些框架核心思想上的异同,为什么叫哲学这么装逼的名字呢,因为React先这么叫的(Thinking in React)...
作为主流的前端开发框架,他们在设计思想上还是有很多相通之处的:
- 推崇模块化的组织方式,以组件划分页面
- 都使用单向数据流,强调数据流向的清晰、可控
- 抛弃繁琐的DOM操作,数据驱动
- Virtual DOM
- 强调逻辑关注点的抽离、组合,为此Vue3增加了composition API,react提供了Hooks
- ...... 他们都为很多的开发行为如组件化、数据管理等提供了很方便的实现,这些后面会进行详细的比较,从这些层面可以很清晰的看出:
- Vue更注重结构(Html)、逻辑(js)、表现(css)的分离,所以.vue文件自然而然的出现了。而React强调"all in js",认为渲染逻辑本质上与其他 UI 逻辑内在耦合,催生出了jsx。
- Vue提倡模板语法,并有很多指令以实现各种功能。React这些功能均在js中实现(或jsx)。
- Vue提供很多的options API,配合模板语法方便快捷的实现可交互的UI(当然这也是为什么会被诟病方言过多的原因之一,引用尤大的话,用着舒服就完事儿了)。React则都通过js来实现,更贴近原生,往往更加灵活("all in js",again)。
- ......
Hook VS composition API
Vue3最大的改动便是新增了composition API(组合式API),尝过鲜之后简直感动到哭晕在厕所,简直不要太好用!简直是强迫症患者的福音(看不得超过200行的代码,又膈应Mixins),提供了新的代码组织方式。很快啊,很快网上就出现了composition API抄袭React Hooks的言论,在同时接触了两者之后,发现他们确实有很多的共性(比如vue官网对于组合式API的示例中就用了useXXX的命名,乍一看还真有点React自定义Hook内味儿),但这样就扣上抄袭的帽子是很野蛮的,开源的世界相互借鉴是很正常的现象,一种新模式的出现时值得大家去借鉴和学习的,这样才能更好的促进技术的发展和前进不是。
动机
有趣的是二者出现的动机是很一致的,这里引用React官网Hook动机一节中的部分内容:
- 在组件之间复用状态逻辑很难
- 复杂组件变得难以理解
在组件之间复用状态逻辑很难:
通过创建组件,我们可以将界面中重复的部分连同其功能一起提取为可重用的代码段。仅此一项就可以使我们的应用在可维护性和灵活性方面走得相当远。
二者的组件系统都可以完成功能模块的抽象和复用,但是都缺乏一种更好的聚合逻辑关注点(React称为状态逻辑)的方案。
- Vue:composition API出现之前,vue的options API导致同一块逻辑的代码会分散在组件的各个选项之中,比如分别散布在data、watch、methods选项和生命周期钩子中,这就使得阅读和编写这一块代码时需要在其中反复横跳。当然也不是没有方案处理,比如Mixin,但是这种方案有诸多弊端(可自行查阅相关内容)。
- React:与上述情况类似,'React 没有提供将可复用性行为“附加”到组件的途径',同一块的状态逻辑可能分散在组件的各个部分(如生命周期),React在Hooks之前的解决方案是render props 和 高阶组件。但也都有各自的弊端。
复杂组件变得难以理解: 组件之间的状态逻辑难以复用,也就自然会催生很多难以理解的复杂组件,复杂组件中往往散布了一些不同的状态逻辑,而又不适合单独提取出独立组件。这也是很多万恶的大文件的由来。
用法
(详细用法参照官网和上述结构图)
- Vue:新增了一个setup()生命周期钩子,在里面可以使用vue提供的一些新的api。
- React:在函数组件里“钩入” React state 及生命周期等特性。也可以自定义Hook来在不同组件之间复用状态逻辑。
UI渲染
模板
- Vue:提倡结构、逻辑、表现分离,使用自己的模板语法。可以在.vue中的下编写DOM结构,也可以在实例对象(组件)的template选项中编写,也支持使用jsx动态生成。个人更喜欢Vue的模板,更贴近原始的DOM结构。
- React:使用jsx,'实现了一套独立于浏览器的 DOM 系统,兼顾了性能和跨浏览器的兼容性'。
列表渲染
- Vue:一般使用v-for指令在模板中完成,也可以使用jsx动态的渲染。
- React:使用Array.prototype.map()等数组上的方法在jsx中渲染,或者React.Children上的map、forEach方法遍历。
条件渲染
- Vue:模板语法v-if,也可使用jsx。
- React:使用元素变量;jsx中使用条件表达式(如&&、三目运算符)。
插槽
- Vue:内置Slot组件,有具名插槽,作用域插槽。
- React:使用props.children。
样式
- Vue:一般在.vue文件中使用,作为独立模块存在。支持scoped、deep、global等伪类来定义作用域。内联样式同Html。
- React:可自行选择支持css module。内联样式需遵循React的DOM系统,给style属性赋值,值一般为js对象。
innerHTML
- Vue:指令v-html、v-text。
- React:React中DOM系统的dangerouslySetInnerHTML属性。
异步组件
- Vue:Vue.component() 允许以工厂函数的方式定义异步组件,Vue3中使用Vue.defineAsyncComponent()来显式定义异步组件。
- React:使用React.lazy()定义一个懒加载组件。
suspense
- Vue:内置Suspense组件。
- React:React.Suspense类。 Suspense 使得组件可以“等待”某些操作结束后,再进行渲染。这对一些按需加载、服务端渲染、loading等场景特别有用。 Vue和React中都可创建异步组件配合使用(见5.7节)。
fragment
- Vue:Vue3中新增的特性,template可以支持多个根标签(原来只能支持一个)。这将出现多个根节点如何继承组件Attributes的问题,Vue3给出的解决方案是使用$attr动态绑定到需要继承Attributes的根节点上
- React:React.Fragment类,支持多个根节点,可简写<></>。
DOM传送门
- Vue:使用内置组件Teleport。
- React:使用ReactDOM.createPortal(child, container)。
Virtual DOM
数据管理
render数据
- Vue:props和data。以props向下传递的单向数据流。提供v-model指令(props、event $emit的语法糖)来实现双向绑定。
- React:props和state。以props向下传递的单向数据流。需自己添加反向数据流。
响应性
- Vue:使用computed和watch选项来实现响应性,composition API中使用computed()、watch()、watchEffect()方法。(详细请参考官网)
- React:使用Hook:useMemo()、useCallback()。 computed和useMemo都返回依赖某些值的计算值。computed可自动收集所依赖的所有值,useMemo需在参数中指定。
组件
组织形式
- Vue:分为函数组件(无状态)和有状态组件。函数组件Vue2中使用选项式声明创建,Vue3支持函数创建(详细)。
- React:分为函数组件(在Hook之前也可称为无状态组件)和class组件。分别使用函数和ES6的class语法创建。
由上不难看出,其实二者的组件模型是极其相似的。
都可划分为无状态组件和有状态组件;都支持异步组件;都不建议使用继承,而是提倡组合的方式组织组件 ......
正是因为具有极其相似的组件模型,导致面临了一些相同的问题。如前面提到同样相似的Hook和composition API的诞生。
Vue认为关注点分离不等于文件类型分离。使用单文件(.vue)把css、js、html划分为松散耦合的组件组织起来。需配合vue-loader使用。
生命周期
这里官网都比较详细,这里不做过多赘述。
Vue组件生命周期
React组件生命周期
二者都在组件初始化、创建、挂载、更新、销毁流程中提供相应的钩子函数,供使用者进行渲染控制和逻辑处理。同时也都提供了一些处理错误情况的钩子(可参照上述结构图或官网)。
组件通信
- Vue:
props向下传递;
event emit方式向上传递;
跨组件Provider/Inject;
event bus。 - React:
props向下传递;
event emit方式向上传递;
跨组件使用useContext传递。
事件处理
- Vue:在模板中使用指令v-on绑定,针对不同事件类型提供相应事件修饰符。返回原生事件对象。
- React:向事件处理函数中传递合成事件对象--SyntheticEvent实例。它是浏览器的原生事件的包装器,具有与浏览器原生事件相同的接口。且是跨浏览器的。原生事件保存在SyntheticEvent实例中的nativeEvent属性中。
总结
本文主要从思想、语法层面对Vue和React进行了简单的比较和梳理。涉及的内容较浅,随着React的深入使用和实践,相信也会有很多不一样的感悟。后续会对文章持续更新,同时对于React原理、生态(如router、redux等)与Vue的比较也会逐步补充上来。