51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 476|回复: 1
打印 上一主题 下一主题

[原创] 浅谈容易混淆的前端框架概念

[复制链接]
  • TA的每日心情
    擦汗
    昨天 09:02
  • 签到天数: 1042 天

    连续签到: 4 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2023-3-15 13:12:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    有3个容易混淆的前端框架概念:
      ·响应式更新
      · 单向数据流
      · 双向数据绑定
      在继续阅读本文前,读者可以思考下是否明确知道三者的含义。
      这三者之所以容易混淆,是因为他们虽然同属前端框架范畴内的概念,但又不是同一抽象层级的概念,不好直接比较。
      本文会从3个抽象层级入手讲解这三者的区别。
      响应式更新
      「响应式更新」也叫「细粒度更新」。同时,最近前端圈比较火的??Signal??这一概念描述的也是「响应式更新」。
      笼统的讲,「响应式更新」描述的是「状态与UI之间的关系」,即「状态变化如何映射到UI变化」。
      考虑如下例子(例子来自what are signals[1]一文):
      function TodoApp() {
          const [todos, setTodos] = useState(
            [{ text: 'sleep', completed: false }]
          )

          const [showCompleted, setShowCompleted] = useState(false)

          const filteredTodos = useMemo(() => {
              return todos.filter((todo) => !todo.completed || showCompleted)
          }, [todos, showCompleted])
          return (
              <TodoList todos={filteredTodos} />
          )
      }


      在TodoApp组件中,定义了两个状态:
      ·待办事项todos
      · 是否展示完成的事项showCompleted
      以及根据上述状态派生出的状态filteredTodos?。最终,返回<TodoList/>组件。
      如果todos?状态变化,UI该如何变化?即「我们该如何知道状态变化的影响范围」?这时,有两个思路:
      · 推(push)
      · 拉(pull)
      推的原理
      我们可以从变化的状态(例子中为todos)出发,根据状态的派生关系,一路推下去。

    图片来自what are signals一文

      在例子中:
      1. todos变化
      2. filteredTodos?由todos派生而来,变化传导到他这里
      3. <TodoList/>?组件依赖了filteredTodos,变化传导到他这里
      4. 确定了todos变化的最终影响范围后,更新对应UI
      这就建立了「状态与UI之间的关系」。
      除了「推」之外,还有一种被称为「拉」的方式。
      拉的原理
      同样的例子,我们也能建立「状态与可能的UI变化的关系」,再反过来推导??UI??变化的范围。

    图片来自what are signals一文

      在例子中:
      1. todos变化。
      2. 可能有UI变化(因为建立了「状态与可能的UI变化的关系」)。
      3. UI与<TodoList/>组件相关,判断他是否变化。
      4. <TodoList/>?组件依赖filteredTodos,filteredTodos由todos派生而来,所以filteredTodos是变化的。
      5. 既然filteredTodos变化了,那么<TodoList/>组件可能变化。
      6. 计算变化的影响范围,更新UI。
      在主流框架中,React?的更新以「推」为主,Vue?、Preact?、Solid.js等更多框架使用「拉」的方式。
      本文聊的「响应式更新」就是「拉」这种方式的一种实现。
      单向数据流
      我们可以发现,不管是「推」还是「拉」,他们都需要计算变化的影响范围,即「一个状态变化后,究竟有多少组件会受影响」。
      那么,从框架作者的角度出发,是希望增加一些约束,来减少「计算影响范围」这一过程的复杂度。
      同样,从框架使用者的角度出发,也希望增加一些约束,当「计算影响范围」出bug后,更容易排查问题。
      这就有了「单向数据流」。
      「单向数据流」是一条约定,他规定了「当状态变化后,变化产生的影响只会从上往下传递」。
      考虑如下例子:
      function Parent() {
        const [num] = useState(0);
        return <Child data={num}/>;
      }
      function Child({data}) {
        const isEven = data % 2 === 0;
        return <GrandChild data={isEven}/>;
      }
      function GrandChild({data}) {
        return <p>{data}</p>;
      }


      <Parent/> 组件的状态num 作为props?传给<Child/> 组件,再作为props 传给<GrandChild/>组件,整个过程只能自上而下。
      「单向数据流」并不是实现前端框架必须遵循的原则,他的存在主要是为了减少开发者的心智负担,让「状态变化后,计算影响范围」这一过程更可控。
      双向数据绑定
      当本文开篇聊「响应式更新」时,讨论的是「状态与UI的关系」,这是将框架作为一个整体来讨论,抽象层级比较高。
      当我们继续聊到「单向数据流」时,讨论的是「状态变化的影响范围在组件间单向扩散」,这是「组件与组件之间的关系」,抽象层级下降了一级。
      接下来我们要讨论的「双向数据绑定」,讨论的是单个组件内发生的事。
      「双向数据绑定」是「状态+改变状态后触发的回调」相结合的语法糖。
      这里不讨论框架语境下「语法糖」一词是否完全准确
      比较知名的「双向数据绑定」实现,比如 Vue 中的 v-model 语法:
      <input v-model=‘data’/>

      相当于如下状态+事件回调的组合:
      <input @input='onInput' :value=‘data’ />

      实际上早期React中也有类似实现,名叫LinkedStateMixin,只是早已被废弃。

    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-8 14:49 , Processed in 0.065328 second(s), 22 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表