最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    正文概述 掘金(工业聚)   2020-12-14   427

    前言

    这是一篇纯技巧向的文章,跟一年多之前的《揭秘 Vue-3.0 最具潜力的 API》一样_[0]_,更少的背景铺垫,更多的代码,更多的 demo,更快的节奏。

    让我们直接进入主题。

    背景

    前一阵子,有开发者发推特表示用 Svelte 实现跟 React 一样的功能_[1]_,代码简洁很多。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    他给出的 React 代码如下:

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    而 Svelte 代码如下:

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    用了复杂的 react 处理,佐证 svelte 的优越,这不是一个很公平的对比。

    然后,Vue 也加入了试炼 [2],尤小右给出了他的代码:

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    跟上面 Svelte 版本一样简洁。

    而后,小右又提供了 ref-sugar 版本,更为精炼。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    据说,在推特上:

    一、渲染友好的 React-Hooks 公式

    每当 React hooks 被拉出来溜的时候,一般都是负面的。确实很容易写出性能有问题的代码。

    关键是,大家太轻易使用 useState 了。

    在 vue-composition-api 中,reactivity 数据都有 wrapper,比如是 reactive 和 ref。

    useHeight 等 custom-vca 里不管产生多少个 reactivity 对象,不会直接产生 re-render。

    只有那些被 return 到最外部,跟 template 绑定的部分,会触发视图渲染。

    而 react 的 reactivity 就是通过 re-render 实现的,useState 没有 wrapper,一用就得到一个触发渲染的函数。

    在这种 reactivity 机制下,需要特殊的心智模型去编写代码—— State/Effect 分层。

    useHeight 如果是下面这样:

    let [ref, height] = useHeight()
    
    
    

    高度变化时,被动 re-render,难以过滤,难以转换,难以合并。

    大部分情况下,不是提供 state,而是提供 effect,可能更好。像下面这样:

    let [height, setHeight] = useState(0)
    
    let ref = useHeight((height) => {
      // do something with height
      setHeight(height)
    })
    
    
    

    由使用者去外部声明 state ,然后在 useHeight 的 effect callback 里按需 setHeight。

    更新权反转。

    使用者可能结合其它状态,做 dispatch 到 reducer 里的整体一次性更新,而不是被动 re-render。

    可惜目前 React 社区还没建立清晰的 State/Effect 区分。

    我们可以根据 State/Effect 分层理念,尝试给出 React hooks 渲染友好的公式:

    let handler = useProducer(consumer, options)
    
    
    

    producer 生产者接收 consumer callback 消费者回调作为参数,返回 handler 控制权函数,用以绑定到事件或者其它 consumer 位置。

    以 useHeight 作为案例,验证上述公式的效用。

    二、React 实现

    我们要实现的功能如下:

    给定一个 textarea,它是 resizable 的;我们追踪它的尺寸变化,并展示到文本里,同时我们增加一个 checkbox 控制,可以切换监听 / 不监听。同时,我们只监听一定范围内的尺寸变化,而忽略超出范围的。

    即更精细的控制:

    1)控制监听 / 非监听;

    2)控制监听的范围,只响应符合要求的渲染需求。

    功能如下:

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    我的代码实现,按照从 low-level 到 high-level 层次迭代上来。

    首先实现 useResizeObserver,一个对 dom api 的 low-level 适配。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    返回 3 个控制函数:

    1)trigger 决定监听哪个元素;

    2)enable 启动监听;

    3)disable 禁用监听;

    useResizeObserver 的实现思路,跟大家之前惯常做法不同,它不返回 state 出去,不会引起 re-render,而是调用 callback(target),将 resize effect 暴露出来。

    然后,我们基于 useResizeObserver 实现 useHeight,将 observe 的关注点缩小到 offsetHeight 里,并提供 getCurrentHeight 的新方法。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    同样的思路,我们还可以实现 useWidth 等其它监听。

    使用时,在 useHeight(onHeightResizeEffect) 里,按需将 currentHeight 同步到 setHeight 里。

    通过 observer.trigger 决定监听哪个元素,并提供 enable/disable/getCurrentHeight 等控制函数,通过 useEffect 监听,在合适的实际调度不同的控制。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    以上,渲染友好、控制精细的 React-Hooks 版本完成。

    react-resize-observer 的 demo 地址

    代码或许比 Svelte 和 Vue 的版本长一点,但 State/Effect 的这种实现思路是通用的,在 Vue 里也适用。

    下面演示,基于相同理念,分别用 Rxjs,Vue 和原生 JS 实现相同功能。

    三、Rxjs 实现

    实现 rxjs 版本的 use-height。也要支持:1)切换 trigger;2)切换 enable/disable;3)在 enable 状态下,filter 部分 height 不做响应。

    功能如下:

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    先实现 resizable,适配 low-level 的 dom api 为 rxjs 的 observable 版本,对应之前 react-hooks 适配的 useResizeObserver。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    然后实现 onHeightChange,通过 subject 反转控制,switchMap 实现切换,返回 state$, trigger, enable, disable 四个字段。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    这里返回 state而不像reacthooks版本接受consumer是因为,state 而不像 react-hooks 版本接受 consumer 是因为,state而不像react−hooks版本接受consumer是因为,state 不是 state,它的 subscribe(consumer) 里就带一个 consumer。性质上是一样的。

    通过 subscribe(state$) 去消费数据,通过 rxjs operators 过滤 / 转换 / 转接数据流。在其它 observable 数据流里,调用 handler 里的控制方法,disable/enable/trigger 即可。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    rxjs-resize-observer 的 demo 地址

    接下来,实现 vue/reactivity 版本的 use-height。思路跟 react-hooks 版本和 rxjs 版本也是一样。

    四、Vue 实现

    先实现一个将 resize-observer 这个 low-level api 适配为 vue reactive state 的函数。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    定义一个 shallowReactive,监听 state.trigger 的变化,实现切换 resize-observer 的功能,并将 target 发送给 onResize。

    再实现一个 onHeightChange 函数,拓展 ResizableState 为 HeightChangeState,增加 status 字段提供 enable/disable 切换,增加 height 字段。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    上面做了两个 effect 的组合:

    1)将 onResize effect 根据 option.fiter 映射到 state.height;

    2)将 state.trigger 同步给 resizeState.trigger

    onHeightChange 使用方式如下:

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    onHeightChange 定义了一个 state-effect bindings,但没有指定监听哪个元素,也没有指定如何消费数据。

    通过 state.trigger 赋值决定监听谁。

    通过 effect 决定如何消费。

    vue-resize-observer 的 demo 地址

    功能如下图:

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    细心的同学可能已经发现了,上述 Vue 代码,只用到了 @vue/reactivity 包,而没有用 vue 视图相关的代码。

    没错。

    vue/reactivity 自身已经足够强大,可以独立实现很多功能。在《揭秘 Vue-3.0 最具潜力的 API》有更多解读。

    这里提供一个新的应用潜能方向。

    五、Vue 的 State-Effect-Bindings 模式

    vue/reactivity 可以实现一种 reactive effect bindings 模式,可以概括为 reactive({args, returned}) & effect。

    所有 function,都有 args 参数和 return value;将它们映射到一个 reactive state 中去,可以做到,state.args= 设置时,重新执行 effect,将结果映射回 state.returned。

    这个模式对前端开发者非常友好,因为 dom api 大部分就是这样的。

    elem.style.color = 'red',触发渲染 effect,将结果同步给 computedStyle(elem).style。

    回到 vue-resize-observer,上述模式对应的是:

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    resizable 的 state.trigger 其实是 ResizeObserver.observe 的参数,它的返回值被定义为 elem 本身,不符合 vue watch 的条件,因此,从 state.target 字段,变成 onresize effect callback。

    当 effect 可以同步到 state 时,我们采用 state;当 effect 难以同步到 state 时,我们采用 effect callback。

    onHeightChange 的 on height change effect 可以同步到 state.height,因此我们取消了 effect callback。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    可以拓展 filter 等增强操作,去增强 effect -> state 同步的细节控制,拓展 state.status 等附加信息等。

    当整个 vue app 由 state-effects-bindings 模式来构建,理想情况下,最后将自动形成一个大的 app state,整个应用状态自动可配置化。

    appState.menu.status = 'on' 就打开了菜单。

    appState.loading.text = 'xxx' 就展示了 loading。

    effect(() => appState.menu.status) 就监听了菜单变化。

    并且这个构建过程,不是自上而下的,不是先定义一个 global app state 然后在这个约束下去分配 state 给 sub-component/model。

    可以是自下而上的,每一部分只处理自己关心的部分,并将 state/effect 的能力暴露出去,由更大的部分去将 state/effect 通过 bindings 映射到更大的 state/effects,如此不断整合,最后形成了一个 app state

    如 resizable 只提供了 state.target,而 onHeightChange 在此基础上拓展了 {height, status} 等状态。

    六、原生 JS 实现:手中无框架,心中有框架

    当我们认识到,问题无非是 state-effect-bindings 的不断冒泡时,有没有 react-hooks,有没有 rxjs,有没有 vue-reactivity,不是必要的。

    先实现 Resizable,接受一个对象参数,返回一个对象。对象参数是 visitor/consumer,返回对象是 handler。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    基于 Resizable 实现 HeightChange,接受更多字段的对象参数,返回更多方法的对象。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    然后在 consumer 参数中,指定如何消费数据,如何转换数据,如何过滤数据。

    在 event listener 等 bindings 中,指定如何调度 handler 方法。

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    vanilla-resize-observer 的 demo 地址

    功能如下图:

    纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    尽管用原生 JS 也行,但有 react-hooks, rxjs, vue-reactivity 也是有帮助的,此处主要展示 State/Effect 分层理念在 UI 开发里的通用性。

    总结

    如上,我们演示了 react, rxjs, vue, vanilla-js 在 State/Effect 分层理念下,实现相同功能的代码实现。

    我们可以在 State Management 概念的基础上,再增加一个 Effect Management 的概念。

    正如我之前在某个知乎问答里所说的:

    1)当你的项目数据复杂度很低,用 react 自带的 component-state 就可以

    2)当你的项目数据复杂度一般,lift state 到 root component,然后通过 props 传递来管理

    3)当你的项目数据复杂度较高,mobx + react 是好的选择

    4)当你的项目数据复杂度很高,redux + react 可以帮助你维持可预测性和可维护性的下降曲线不那么陡。所有 state 变化都由 action 规范化。

    5)当你的项目数据复杂度很高且数据来源很杂,rxjs 可以帮助你把所有 input 规范化为 observable/stream,可以用统一的方式处理。

    思路其实很简单:

    1)当 UI 变化很复杂时,用 component 归一化处理,即 View-Management

    2)当 state 变化很复杂时,用 action/state 归一化处理, 即 State-Management

    3)当 data-input 很复杂时,用 rxjs/observable 等概念归一化处理,即 Effect-Management。

    任意问题,只要足够普遍和复杂,就值得抽象出专门化的机制。

    Effect Management 在前端开源生态里,还不是很繁荣,是一个大家可以努力的方向。不仅 rxjs 可以做 effect 管理,vue/reactivity 和 react-hooks 乃至原生 JS 裸写都能做,有待大家的发掘。

    更具体地说,前端社区可以往下面几个方向努力:

    1)基于 react-hooks 和 State/Effect 分层理念重新梳理哪些应该暴露为 effect,哪些应该暴露为 state,提供一个性能更加良好的版本(只能用在 react 体系)。

    2)基于 vue/reactivity 和 State-Effect-Bindings 理念,实现的 state/effect 管理框架(脱离 vue 视图框架也能使用)。

    3)基于 rxjs 和 State-Effect-Observeable 模式,实现 state/effect 管理框架(可以配合任意视图框架使用)。

    期待有同学能在上述方向上产生成功案例~

    引用来源:

    [0] 《揭秘 Vue-3.0 最具潜力的 API》

    mp.weixin.qq.com/s?__biz=MzA…

    [1] react 和 svelte 实现 use-height 的对比

    twitter.com/AdamRackis/…

    [2] vue 作者的 use-height 实现

    twitter.com/youyuxi/sta…


    起源地下载网 » 纯技巧向:React, Vue, Rxjs 和原生 JS 代码大乱斗

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元