- 主题:对比 vue,reactjs 就是个笑话!
你这都是老掉牙的吵架话题了。。。都吵了四五年了,不新鲜了。。。
【 在 hgoldfish (老鱼) 的大作中提到: 】
: 最近有调查说 vue 在国内的使用率不断降低,reactjs 不断提升。对此本青表示痛心疾首!这是一种历史的倒退!
: 想当初 java 程序员费了九牛二虎之力,从 html 和 java 代码混合的 jsp 进化到了 struts,现在又进化到 spring boot,终于做到做业务逻辑和页面设计分开,方便设计师和程序员分工合作。从此前后端分离,终于发展出 js 前端社区。php, python, ruby 等社区也发展出类似的
:
http://blog.itpub.net/69981092/viewspace-2710742/: ...................
--
FROM 123.120.182.*
一方面,rxjs不是每个人都学得懂。。。
另一方面,重度依赖decorator,而这个decorator是早年间ng团队和ts团队勾兑以后加上的语法功能,现在js tc39委员会早已废弃了这版本的decorator,新版本遥遥无期,即使出来了也和现在这个版本非常不同。所以大家普遍担心今后ts的当前版本decorator会成为一个大坑。所以react和vue团队都规避使用decorator是有合理性的。mobx新版本也不再使用decorator了
【 在 libgcc (乞讨积分,求施舍,长期有效) 的大作中提到: 】
: 是啊,我觉得angular那套挺好的,decorator+class+typescript+rxjs+httpclient
: 基础设施齐全了,稳定在一种风格,也没那么多条条框框的,没啥太明显的硬伤,干就完了
: 除了angular那个自己的module我觉得有点麻烦和冗余,其它的都挺好的,有些高级货比如响应式表单你不用就得了
: ...................
--
FROM 123.120.168.*
class组件,和hooks组件,各有各的复杂,都很麻烦。本质上是因为react采取了一个奇葩的响应式方案,就是对状态根对象做简单的引用比较。为了这个,弄出一堆fp不可变的东西来,不管咋弄心智负担都不小。
这方面我还是觉得vue mobx这种proxy式的响应式方案比较靠谱,也切合现在业务逻辑一般都用class oop来写的实际情况。proxy式的响应方案,虽然也要注意不能丢失引用,但是规则还是比较简单的,只要记住永远要用a.b就可以,不能没有中间那个点。
前面有人提到被toRef toRaw这些搞晕了,其实这些都是处理特殊情况的边缘api。正常只要记住上面的规则,会用ref reactive,知道computed返回的是个ref,就可以干活了。
今天刚在知乎上写了个相关文章,贴出来吧
https://www.zhihu.com/question/468249924/answer/1968728853
【 在 eGust (十年) 的大作中提到: 】
: react 官方文档 hooks 的介绍部分阐述了引入 hooks api 的理由:
:
https://reactjs.org/docs/hooks-intro.html#motivation: - It’s hard to reuse stateful logic between components
: ...................
--
FROM 123.120.168.*
useState这个hook的原理,真是进阶学习js工作原理的绝好案例。
【 在 eGust (十年) 的大作中提到: 】
: 所以你只是因为已经习惯 class、生命周期的模型了,但实际上 hooks 对初学者来说更友好。自始至终都是 fp,跟用什么语言关系不大,不需要再掺进来 oo;hooks 是更加抽象的封装,只要记使用场景就好了,不需要了解内部是怎样实现的。
--
FROM 123.120.168.*
useState的具体实现,充分利用了闭包,而且又巧妙地隐藏了闭包的外表
【 在 tgfbeta (右旋肉碱) 的大作中提到: 】
: 这个只是react自己架构的一个construct,算不得js的工作原理
--
FROM 123.120.168.*
【 在 eGust (十年) 的大作中提到: 】
: 标 题: Re: 对比 vue,reactjs 就是个笑话!
: 发信站: 水木社区 (Wed Jun 30 09:19:44 2021), 站内
:
下面这一段,watcher的复杂性,和useEffect有何不同呢?useEffect如何解决“触发顺序”之类的问题呢?
: ng1/vue 的双向数据绑定表面上看起来非常直观,vue 的 data 看起来也是跟 state 一样的概念。但这种设计却不得不引入 watch,而它又其实引发了许多问题。首先 watcher 里面可能会再更新 data,而又可能触发其它的 watcher,这就导致了数据的更新流程非常复杂,不像 newState = reduce(oldState, action) 那么简单。其次,多个 watchers 监视同一个数据的变动也是很难避免的,那么它们触发的顺序就变成了一件非常微妙的事情,很可能由于触发顺序的不同而产生不同的结果。另外,watcher 里面依赖了哪些外部的变量也是非常难预测的,导致的结果就完全相同 data 初始结果,同样的事件可能导致不同的最终值。所以如果没设计好的话,很容易把 unittest 的依赖关系搞的非常复杂,很难实现。
:
: 总体上,我是更倾向 react + hooks 的,因为 fp 的条条框框在那里绕不过去,迫使项目的设计差不了,用起来是个先苦后甜的过程。vue 容易上手,副作用就是项目上设计起来非常容易放飞自我,最后的坑不比 jquery 的项目少。
:
: 【 在 beep (菜M.喵星耗子) 的大作中提到: 】
: : class组件,和hooks组件,各有各的复杂,都很麻烦。本质上是因为react采取了一个奇葩的响应式方案,就是对状态根对象做简单的引用比较。为了这个,弄出一堆fp不可变的东西来,不管咋弄心智负担都不小。
: : 这方面我还是觉得vue mobx这种proxy式的响应式方案比较靠谱,也切合现在业务逻辑一般都用class oop来写的实际情况。proxy式的响应方案,虽然也要注意不能丢失引用,但是规则还是比较简单的,只要记住永远要用a.b就可以,不能没有中间那个点。
: : 前面有人提到被toRef toRaw这些搞晕了,其实这些都是处理特殊情况的边缘api。正常只要记住上面的规则,会用ref reactive,知道computed返回的是个ref,就可以干活了。
: : ...................
:
: --
:
: ※ 来源:·水木社区 mysmth.net·[FROM: 115.188.133.*]
--
FROM 123.120.168.*
因为现在前端越来越重了,前端项目越来越大了
【 在 chaobill (若我离去,后会无期) 的大作中提到: 】
: 我想起那句话, java 善于解决 java 发明的问题
: 要切图仔理解这些高深名词,真是太难了
: 问题来了,为什么前端要搞那么复杂
: ...................
--
FROM 123.120.168.*
我咋觉得react也一样呢?复刻一下这个例子:
const [{ value, step }, setCounter] = useState(counter);
useEffect(() => {
console.debug("watcher A", { ...counter });
setCounter(old => ({ value: old.value + old.step, step: old.step })) // 1
}, [step]);
useEffect(() => {
console.debug("watcher B", { ...counter });
setCounter(old => ({ value: old.value + old.step, step: old.step })) // 2
}, [step]);
跑的时候 1 和 2 两个 setCounter 也是顺序运行的,它们各自取到的 old.value 也不一样吧?
我个人理解,useState useEffect 本质就是为了造成纯组件函数的“假象”,把生存周期是跨越多次渲染而一直存在的 state 变量、effect 函数,都存到了用户看不见的闭包里,因为 react 函数组件只有一层 render 函数,正常来看的话没有造闭包的地儿:
...; // state 和 effect 真实创建于框架代码里,也就是组件 render 函数体之外
function Component () {
...; // render 通过 useState useEffect 取出作用域外的闭包变量
}
而 vue 是把这种变量显式地放在 setup 函数形成的闭包里。
setup() {
...; // 构造跨多次渲染的闭包变量
return () => {
// render 直接通过原生js作用域机制获取外面的闭包变量
}
}
所以本质都是闭包变量,只是闭包的位置不同而已吧?
除非说 react 内部在上述同一批次内多次调用 setCount 的时候,故意都是传入初始的
count,而不是前面 setCount 修改过的 count。。。如果真是这样,我觉得反而不符合用户意图啊?
稍后有空试一下。
【 在 eGust (十年) 的大作中提到: 】
: 标 题: Re: 对比 vue,reactjs 就是个笑话!
: 发信站: 水木社区 (Wed Jun 30 16:51:15 2021), 站内
:
: 重点并不在 watch 上,declarative ui 是 model -> view 的映射。理论上只要给定了
一个确定的状态,state 也好 data 也好,都能生成一个确定的 ui。
:
: react 里面 newState = reduce(oldState, action),这是一个非常确定的状态。举一个
counter.value += Number(counter.step);
}, [step]);
【 在 eGust (十年) 的大作中提到: 】
: 标 题: Re: 对比 vue,reactjs 就是个笑话!
: 发信站: 水木社区 (Wed Jun 30 16:51:15 2021), 站内
:
: 重点并不在 watch 上,declarative ui 是 model -> view 的映射。理论上只要给定了一个确定的状态,state 也好 data 也好,都能生成一个确定的 ui。
:
: react 里面 newState = reduce(oldState, action),这是一个非常确定的状态。举一个不太恰当的例子:
: import { reactive, watch, toRef } from "vue";
:
: export const counter = reactive({
: value: 1,
: step: 1
: });
:
: const step = toRef(counter, "step");
:
: watch(step, () => {
: console.debug("watcher A", { ...counter });
: counter.value += Number(counter.step);
: });
:
: watch(step, () => {
: console.debug("watcher B", { ...counter });
: counter.value += Number(counter.step);
: });
:
: 如果 step 的值改变了:1 -> 2,那么你会看到的输出是
: watcher A { value: 1, step: 2 }
: watcher B { value: 3, step: 2 }
:
: 如果把这个实现换成 react 的话,由于 value 是从
: const [{ value, step }, setCounter] = useState(counter);
:
: 里取出来的,所以不管有几个 useEffect,调用了几次 setCounter,在所有的 effect 函数里面,value 的值都会是 1。
:
: 这个例子本身很不好,实际中的情况是,多个 watchers 监听各自的 props/computed,但它们都会根据变化来和 data 本身的值来更新 data,于是就产生了不确定性。但是一时间不太容易构造一个合适的例子,临时只能想出这个示意一下,不知道解释明白了没……
:
: 【 在 beep (菜M.喵星耗子) 的大作中提到: 】
: : 下面这一段,watcher的复杂性,和useEffect有何不同呢?useEffect如何解决“触发顺序”之类的问题呢?
:
:
: --
:
: ※ 修改:·eGust 于 Jun 30 17:00:22 2021 修改本文·[FROM: 101.98.83.*]
: ※ 来源:·水木社区 mysmth.net·[FROM: 101.98.83.*]
--
修改:eGust FROM 101.98.83.*
FROM 123.120.168.*
useState useEffect useMemo 的原理非常重要,不是实现细节。不懂具体怎么实现的,只背api,分分钟掉到沟里,基本没法干活
【 在 tgfbeta (右旋肉碱) 的大作中提到: 】
: 这是实现细节,不重要
: 正所谓闭包是穷人的对象,对象是穷人的闭包
: Closures are poor man's objects. Objects are poor man's closures.
: ...................
--
FROM 123.120.168.*
@eGust 试了一下,有意思。
const [counter, setCounter] = useState({ value: 1, step: 1 })
useEffect(() => {
console.log('A', { ...counter })
setCounter((old) => {
console.log('AA', { ...old })
return { value: old.value + old.step, step: old.step }
})
}, [counter.step])
useEffect(() => {
console.log('B', { ...counter })
setCounter((old) => {
console.log('BB', { ...old })
return { value: old.value + old.step, step: old.step }
})
}, [counter.step])
step 1->2 时,外层 A B 处拿到的两个 counter 是一样的,如你所说。
但是,setCounter内层 AA BB 处拿到的两个 old 是不一样的,反而和 vue 类似,拿到的一个是没加过 step 的,一个是加过的。
所以 react 最终页面效果还是value加了2,和vue的最终效果一样。
看来一个渲染批次之内的多个 setCount 传入多个 old => new 这样的 reducer 函数的时候,是 lazy evaluation,把两个 setCount 压到最后顺序执行了。这个最终效果和你说的有偏差,react 的这种写法,一样有你说的调用顺序的“微妙”问题。也就是说,那两个
setCount,和 vue 的两个 watch 一样,都有先后次序的问题。
想想也应该是这样啊,用户写了对同一个内存对象的两次累加修改,在单线程模式下,最后的本质肯定还是一先一后执行的啊,最终肯定得加两次啊。哪怕并发模式,将来有了
cocurrent,正确的逻辑也得是加锁,最终还是要加两次呀。
只要加两次,就肯定无法避免先后次序问题。咱们这个例子只是加一,顺序无所谓。碰上顺序有关系的,反而是 cocurrent 比较麻烦,vue 和 non-cocurrent react 还好,不管怎么 lazy evalute,最终应该还是按照代码里的真实顺序来调用的,次序虽然不一定直观,但应该是稳定的。
【 在 beep (菜M.喵星耗子) 的大作中提到: 】
: 标 题: Re: 对比 vue,reactjs 就是个笑话!
: 发信站: 水木社区 (Wed Jun 30 21:52:25 2021), 站内
:
: 我咋觉得react也一样呢?复刻一下这个例子:
:
: const [{ value, step }, setCounter] = useState(counter);
:
: useEffect(() => {
: console.debug("watcher A", { ...counter });
: setCounter(old => ({ value: old.value + old.step, step: old.step })) // 1
: }, [step]);
:
: useEffect(() => {
: console.debug("watcher B", { ...counter });
: setCounter(old => ({ value: old.value + old.step, step: old.step })) // 2
: }, [step]);
:
: 跑的时候 1 和 2 两个 setCounter 也是顺序运行的,它们各自取到的 old.value 也不一样吧?
:
: 我个人理解,useState useEffect 本质就是为了造成纯组件函数的“假象”,把生存周期是跨越多次渲染而一直存在的 state 变量、effect 函数,都存到了用户看不见的闭包里,因为 react 函数组件只有一层 render 函数,正常来看的话没有造闭包的地儿:
:
: ...; // state 和 effect 真实创建于框架代码里,也就是组件 render 函数体之外
:
: function Component () {
: ...; // render 通过 useState useEffect 取出作用域外的闭包变量
: }
:
: 而 vue 是把这种变量显式地放在 setup 函数形成的闭包里。
:
: setup() {
: ...; // 构造跨多次渲染的闭包变量
: return () => {
: // render 直接通过原生js作用域机制获取外面的闭包变量
: }
: }
:
: 所以本质都是闭包变量,只是闭包的位置不同而已吧?
:
: 除非说 react 内部在上述同一批次内多次调用 setCount 的时候,故意都是传入初始的
: count,而不是前面 setCount 修改过的 count。。。如果真是这样,我觉得反而不符合用户意图啊?
:
: 稍后有空试一下。
:
:
:
:
: 【 在 eGust (十年) 的大作中提到: 】
: : 标 题: Re: 对比 vue,reactjs 就是个笑话!
: : 发信站: 水木社区 (Wed Jun 30 16:51:15 2021), 站内
: :
: : 重点并不在 watch 上,declarative ui 是 model -> view 的映射。理论上只要给定了
: 一个确定的状态,state 也好 data 也好,都能生成一个确定的 ui。
: :
: : react 里面 newState = reduce(oldState, action),这是一个非常确定的状态。举一个
: counter.value += Number(counter.step);
: }, [step]);
:
:
: 【 在 eGust (十年) 的大作中提到: 】
: : 标 题: Re: 对比 vue,reactjs 就是个笑话!
: : 发信站: 水木社区 (Wed Jun 30 16:51:15 2021), 站内
: :
: : 重点并不在 watch 上,declarative ui 是 model -> view 的映射。理论上只要给定了一个确定的状态,state 也好 data 也好,都能生成一个确定的 ui。
: :
: : react 里面 newState = reduce(oldState, action),这是一个非常确定的状态。举一个不太恰当的例子:
: : import { reactive, watch, toRef } from "vue";
: :
: : export const counter = reactive({
: : value: 1,
: : step: 1
: : });
: :
: : const step = toRef(counter, "step");
: :
: : watch(step, () => {
: : console.debug("watcher A", { ...counter });
: : counter.value += Number(counter.step);
: : });
: :
: : watch(step, () => {
: : console.debug("watcher B", { ...counter });
: : counter.value += Number(counter.step);
: : });
: :
: : 如果 step 的值改变了:1 -> 2,那么你会看到的输出是
: : watcher A { value: 1, step: 2 }
: : watcher B { value: 3, step: 2 }
: :
: : 如果把这个实现换成 react 的话,由于 value 是从
: : const [{ value, step }, setCounter] = useState(counter);
: :
: : 里取出来的,所以不管有几个 useEffect,调用了几次 setCounter,在所有的 effect 函数里面,value 的值都会是 1。
: :
: : 这个例子本身很不好,实际中的情况是,多个 watchers 监听各自的 props/computed,但它们都会根据变化来和 data 本身的值来更新 data,于是就产生了不确定性。但是一时间不太容易构造一个合适的例子,临时只能想出这个示意一下,不知道解释明白了没……
: :
: : 【 在 beep (菜M.喵星耗子) 的大作中提到: 】
: : : 下面这一段,watcher的复杂性,和useEffect有何不同呢?useEffect如何解决“触发顺序”之类的问题呢?
: :
: :
: : --
: :
: : ※ 修改:·eGust 于 Jun 30 17:00:22 2021 修改本文·[FROM: 101.98.83.*]
: : ※ 来源:·水木社区 mysmth.net·[FROM: 101.98.83.*]
:
:
: --
:
: ※ 修改:·beep 于 Jun 30 22:18:46 2021 修改本文·[FROM: 123.120.168.*]
: ※ 来源:·水木社区 mysmth.net·[FROM: 123.120.168.*]
--
修改:eGust FROM 101.98.83.*
FROM 123.120.168.*