惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tor Project blog
月光博客
月光博客
P
Proofpoint News Feed
大猫的无限游戏
大猫的无限游戏
N
News and Events Feed by Topic
The Cloudflare Blog
博客园_首页
NISL@THU
NISL@THU
量子位
A
Arctic Wolf
Y
Y Combinator Blog
Spread Privacy
Spread Privacy
Engineering at Meta
Engineering at Meta
F
Fortinet All Blogs
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyberwarzone
Cyberwarzone
The GitHub Blog
The GitHub Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
F
Full Disclosure
C
Cisco Blogs
Security Latest
Security Latest
T
The Exploit Database - CXSecurity.com
T
Tenable Blog
PCI Perspectives
PCI Perspectives
S
Security Affairs
Forbes - Security
Forbes - Security
Hugging Face - Blog
Hugging Face - Blog
C
CERT Recently Published Vulnerability Notes
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 聂微东
H
Hacker News: Front Page
S
Securelist
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
D
Darknet – Hacking Tools, Hacker News & Cyber Security
罗磊的独立博客
S
SegmentFault 最新的问题
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
Security @ Cisco Blogs
The Last Watchdog
The Last Watchdog
小众软件
小众软件
Hacker News - Newest:
Hacker News - Newest: "LLM"
Google DeepMind News
Google DeepMind News
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
IT之家
IT之家
Last Week in AI
Last Week in AI
爱范儿
爱范儿
AWS News Blog
AWS News Blog
MongoDB | Blog
MongoDB | Blog

Surmon.me

一人有限集团 你就是不敢 创造力是温柔的谎言 人类正在退出人类 AI 代替不了这样的你 脉冲点火背后的架构设计 基于 Cloudflare 生态的 AI Agent 实现 NodePress 支持用户登录了 从统计学习到通用智能 2025 投资报告:走慢的路 无依之地 会杀人的菩萨 无我不是共识 文化的积重与偏见 当下即安 科学的尽头是态度 无我不是 Egoless 信仰不因恐惧而存在 世间无解的矛与盾 先别急着做些什么 佛不需要你的皈依 真理的幻觉 两扇大门 造心里的浮屠 自胜者强 逻辑与智慧 真的相 快乐的秘密 只需愿意 是名体验 最深沉的梦 森林里倒下的树 现象与相 庸人自扰 React 与 Vue 的完美融合 开心就好吗 与原生家庭和解 两支毒箭 怎么自净其意 一尘不染 一片白云 让风穿膛而过 喝下去,然后闭嘴 活在当下 当纠结发生时 唯「我」独尊 一场把戏(时间) 一场把戏(二元对立) “我” 和 “我” 谁是众生? 船里没有人 玫瑰不需要说话 没有人能渡任何人 你我为轮,使之不再 当我们在谈论独立时 自我约束就是最好的自我接纳 从善与恶的表象出发 佛法不是心理学 别人眼中的你并不是你 高高山上走 祝你心想事成 祝你的噩梦早日发生 道是无情却有情 裙子只为自己而穿 不要盲目地评估自己 先别急着 “爱自己” 让生命欣然舞动 你无法复制乔布斯 符号不是目的 如果一件事发生了 每当我起心动念时 爱出者爱返 理解不是尊重的基础 在没有解决自己的问题之前 “知足常乐” 不是比较 逻辑是无法成为信仰的 买不到的自由 成为诵经者 伤口听不懂大道理 是立场让你不自由 - Unfreedom 天雨大不润无根之草 做自己的神 当心诱惑本身 牛洞冥思录 修罗启示录 让大脑自举 我更喜欢叫它无梦寺 存在主义也许不是解药 出南洋记 2022 的所有答复 没意义的表达 思考的记录 故宫暂行之幻想 极端的比较 解构的终极 舍利子是如何炼成的 心要野 佛教的偏见 文艺青年 我是动物
Vue 和 React 的异同
Surmon · 2018-09-03 · via Surmon.me

真正的学习通常发生在实践的过程中,为了了解而去进行的学习往往容易脱离真实环境,不够彻底。

新工作新业务的驱动,导致我需要重新学习 React 和 TypeScript,所以,这是我对 Vue 和 React 异同点的一次总结,我希望不仅仅局限于使用层面。

叙述结构按照组件化进行描述,即:异同点会在同一个话题进行总结。

数据流

Vue

Vue 是基于 Object.defineproperty 及观察者模式实现的双向绑定,包括对所有 “类 DOM 原生元素” 的数据流包装,各种双向控件的 v-model 就是一种表现。 前端框架里 Directive 的实现最早来自于 angular.js,但在以 angular.js 为代表的 MVC 时代,Controller 往往才是主角,相当于 Vue 中的组件的 Script 部分。

至于数据流的单双向问题,更多取决于开发者的业务实现,在 Vue2.0 之后,官方提倡优先以单向数据流为设计原则。

由于基于 Object.defineproperty 所以 Vue.js 有条件在核心就支持 computed 等相关数据动态计算功能,而 React 中是不存在的。

React

React 本身不存在整循环的数据绑定,它更专注于 State > View 的最优更新这件事。

所以,任何事件产生的数据 Input 需通过我们自组织的业务逻辑来完成 State 的更新,继而 Diff 计算后进行 View 更新; 并且,React 依然提供了允许我们手动管理 View 更新与否的能力,而在 Vue 中这一点是被内部自动优化的。

小结

Vue 靠 Object.defineproperty 对象的拦截知道何时更新视图,React 靠 setState 的代理模式来做到同样的事。 假设丢弃或不使用 Vue 中 v-model 类似的 API,你会发现单事件流来说和 React 其实没什么区别。

Vue 的组件系统中,我们往往假设数据是可变的,所以也往往都是直接改变数据,正因为如此,我们可以得到最细粒度更新的效果。

React 提倡数据不可变,每一次我们应该创建一个新的 State 覆盖之前的,至于最终更新所需的资源靠 Diff 算法来优化。

两种拦截模式背后进行了怎样复杂的计算?

Vue 在不同环境下会降级为不同的异步 API 作为一个数据捕获周期,如 Promise.resolve().then MutationObserver setTimeout

React 根据版本不同,有稍微不同的更新策略,大致是维护一个状态队列,将所有状态的更改推进队列,按照不同的事务类型进行不同周期的批量更新。

So

  • React 中的 state 等价于 Vue 中的 data
  • React 中的 setState 等价于 Vue 中的 直接赋值
  • React 中的 setState.callback 等价于 Vue 中的 $nextTick
  • React 中的 render 等价于 Vue 中的 render/template

更详细可以参考官方文档。

        
        

123456789101112131415161718192021222324252627282930313233343536373839404142434445

// Vue 中的双向绑定 export default { name: 'some-component', template: `<input name="count" v-model="count" />`, data() { return { count: 1 } } } // Vue 中实现单向数据流 export default { name: 'some-component', template: `<input name="count" :value="count" @input="handleChange" />`, data() { return { count: 1 } }, methods: { handleChange(event) { this.count = event.target.value } } } // React 中实现单向数据流 class SomeComponent extends React.Component { constructor() { this.state = { count: 1 } } handleChange(event) { this.setState({ count: event.target.value }) } render() { return ( <div> <input value={this.state.count} onChange={this.handleChange} /> </div> ) } }

事件流

Vue

按照我的理解,Vue 对于原生 DOM 元素的原生事件都有进行一层包装,简单说就是将所有 dispatchEvent 转化为基于中介者模式的 $emit, 最终导致了事件传播形式的统一,开发者只需要关注框架的事件派发标准就可以。

并且,Vue 中对于各 DOM 元素绑定的事件会被转换为真实 DOM 的事件绑定,顺序为:

TemplateEvent > DomEvent + Decorate > CompoentEvent

各种修饰符,就是在 “包装转化的这一层” 对事件进行了不同程度的:冒泡处理、拦截处理等等...

比如一个例子:

        
        

1234567891011121314

<!-- 事件捕获 + 停止传播 --> <div class="box" @click.capture.stop="handleClick"></div> <!-- 转换后的伪代码 --> <script> box.addEventListener( "click", (event) => { event.stopPropagation(); handleClick(event); }, false ); </script>

React

在 React 中,为了兼顾浏览器兼容性、事件统一机制的高性能,React 内置了一套 SyntheticEvent 用于统一事件管理的系统。

简单说,从头到尾,React 只为最顶级元素绑定了一大套事件,我们为某某元素绑定的事件都相当于在 React 的 SyntheticEvent 里 “注册”; 当事件发生时,我们注册的事件会相应地被触发。

我们为 “类原生 DOM” 组件/元素 绑定的事件并不会真正地转化为真正的 Dom 事件,而是被挂载在 SyntheticEvent 系统的不同周期内进行触发和处理; 这些挂载的事件中得到的 Event 对象是经过 SyntheticEvent 包装后的复合对象,由原生事件产生,由 SyntheticEvent 加工得到的。

如果一定要得到原生事件属性,可通过 nativeEvent 访问得到;SyntheticEvent 支持大部分 Dom 原生事件,但不是全部; 如果需要支持 SyntheticEvent 之外的事件需要在 React 的生命周期中挂载和卸载事件,也就是原生的 window.addEventListener

由于 SyntheticEvent 是共享的。意味着在调用事件回调之后,SyntheticEvent 对象将会被重用,并且所有属性会被置空。这是出于性能因素考虑的。 因此,您无法以异步方式访问事件。

小结

从使用层面看,这俩也没啥区别,hahaha~

        
        

12345

<!-- Vue 中事件捕获 + 停止传播 --> <div class="box" @click.capture.stop="handleClick"></div> <!-- React 中事件捕获 + 停止传播 --> <div class="box" onClickCapture={event => event.stopPropagation() && handleClick()}></div>

组件通信

两者的通信机制基本类似。

  • “父 > 子”:数据不可变,单向数据流
  • “子 > 父”:事件派发(Vue),回调派发(React)

复杂的跨组件通信,两者都有对应的 MessageBus/Store 的解决方案。

Vue

Vue 内置了基于中介者模式事件派发机制,使用 $emit$on 作为事件额派发器和接收器。

React

在组件通信里,React 的事件系统并不能为其进行服务,所以往往是将 父组件的事件处理 以 props 的形式传递给子组件直接调用。

小结

这两者的区别更多体现在事件传递,感觉 React 回调传递会导致组件间不能彻底解耦,当你为宿主组件编写处理逻辑时还需要了解子组件的 API、内部逻辑。

        
        

12345678910111213141516171819202122232425262728293031323334

<!-- Vue 父子通信 --> <template> <div class="child" @click="$emit('click-children', 'i am child 1 event data')" /> </template> <template> <div class="parent"> <children @click-children="handleChildrenClick"></children> </div> </template> <!-- React 父子通信 --> <script> function Child(props) { return <div className="child" onClick={props.onChildClick}></div>; } class Parent extends React.Component { constructor(props) { super(props); } onChildClick(event) { console.log("onChildClick", event); } render() { return <Child onChildClick={this.onChildClick} />; } } </script>

Diff 算法

没仔细研究过,研究过也没看懂,所以,不知道。

模板语法

Vue

数据 IO 的类型采用不同前缀体现,当然本质上也都是内置的 Directive。 Directive 提供了大部分 View 层所需的展示逻辑功能。

        
        

1234567891011121314151617181920212223242526272829303132

<!-- 数据传递: --> <some-compnent :attrdata="someData" /> <some-compnent strattr="someStr" /> <!-- 事件绑定: --> <some-compnent @event.decorate="eventHandle" /> <!-- 指令绑定: --> <some-compnent v-some-directive:name="directiveAttr" /> <!-- 渲染逻辑: --> <a-compnent v-if="isTrue" /> <b-compnent v-else /> <list-item-component v-for="item in list" :key="item.id" >{{ item.name }}</list-item-component > <!-- 插值实例: --> <div class="component-a"> <p>这是一个 A 组件</p> <slot></slot> </div> <component-a> <p>我是给 A 组件的内容</p> </component-a> <!-- output --> <div class="component-a"> <p>这是一个 A 组件</p> <p>我是给 A 组件的内容</p> </div>

React

数据 IO 的类型统一采用花括号 {} 或属性值 "" 体现。 默认并不存在 Directive,View 层所需的展示逻辑功能都需要自行业务编码。

        
        

12345678910111213141516171819202122232425262728293031323334353637383940414243444546

<!-- 数据传递: --> <Compoent data="{someData}" title="stringTitle" /> <!-- 事件绑定: --> <Compoent onEvent="{eventHandle}" /> <!-- 渲染逻辑:三目运算输出值、三目运算输出不同组件、输出循环列表 --> <script> render() { return ( <div>User {isLoggedIn ? 'username logged in' : 'not login'}</div> { isLoggedIn ? ( <LogoutButton onClick={this.handleLogoutClick} /> ) : ( <LoginButton onClick={this.handleLoginClick} /> ) } <ul> { numbers.map((number) => <li key={number.toString()}>{number}</li> )} </ul> ) } </script> <!-- 插值逻辑: --> <script> function Box(props) { return ( <div className="box"> <div className="box-inner">{props.children}</div> </div> ); } function App() { return ( <Box> <div className="login-box"> <p>登陆弹窗</p> </div> </Box> ); } </script>

CSS 方案

Vue

Vue 使用 loader 来抽离和打包 CSS,使用 CSS3 属性选择器实现作用域隔离, 但 CSS3 隔离属性在编译期生成,所以会有一些不可逆的弊端,比如无法根据动态组件结构来实现动态的 deep scope。

如 Angular 就有大概 4-5 种方案来实现作用域隔离。

React

貌似 React 的 CSS 作用域方案都通过 All in js 实现。

其他待续...


2019-07-29 补充:

就 template 来说:

  • Vue == declarative programming
  • React == imperative programming

设计结果背后的设计目的

  • Vue 需要 DSL,且需要关注 DSL,DSL 是其灵魂,实际上 Vue 的 DSL 很简单,且满足并约束了大部分开发场景,弱化了开发者的自由度,使开发者仅需关注业务(组件)单元的关系。
  • React 没有 DSL,React 只有 JSX,JSX 的本质就是 JS,开发者需要关注「使用 JS 渲染数据」的最优解,同时需要关注额外的数据单元组织,抽象能力对 React 项目的组织有很大影响。

在 Vue 中,绝大部分抽象在 Vue 被表现为 filter、directive、event... 在 React 中,大部分抽象可能都会是函数,而对函数的设计和运用就至关重要了。

完。