Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >什么是 ”无渲染组件“ ?

什么是 ”无渲染组件“ ?

作者头像
winty
发布于 2023-08-23 01:18:32
发布于 2023-08-23 01:18:32
22800
代码可运行
举报
文章被收录于专栏:前端Q前端Q
运行总次数:0
代码可运行

无头用户界面组件是一种不提供任何接口而提供最大视觉灵活性的组件。“等等,你是在提倡没有用户界面的用户界面模式么?”

是的,这正是我所提倡的。

掷硬币组件

假设你现在需要实现一个掷硬币的功能,当组件渲染时模拟一次掷硬币!一半的时间组件应该渲染 “正面”,一半的时间应该渲染 “反面”。你对你的产品经理说 “这需要多年的研究!” 然后你继续工作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 const CoinFlip = () =>
  Math.random() < 0.5 ? <div>Heads</div> : <div>Tails</div>;

事实证明,模仿掷硬币比你想象的要容易得多,所以你可以自豪地分享成果。你得到了回复,“这真的是太棒了!请更新那些显示很酷的硬币的图片好么?” 没问题!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 const CoinFlip = () =>
  Math.random() < 0.5 ? (
    <div>
      <img src=/heads.svg” alt=”Heads” />
    </div>
  ) : (
    <div>
      <img src=/tails.svg” alt=”Tails” />
    </div>
  );

很快,他们会在营销材料中使用你的 <CoinFlip /> 组件,来向人们演示你的新功能有多么炫酷。“我们想在博客上发表文章,但是我们需要标签 'Heads' 和 'Tails',用于 SEO 和其他事情。” 哦,天啊,或许我们需要在商城网站中添加一个标志?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 const CoinFlip = (
  // We’ll default to false to avoid breaking the applications
  // current usage.
  { showLabels = false }
 ) =>
  Math.random() < 0.5 ? (
    <div>
      <img src=/heads.svg” alt=”Heads” />

      {/* Add these labels for the marketing site. */}
      {showLabels && <span>Heads</span>}
    </div>
  ) : (
    <div>
      <img src=/tails.svg” alt=”Tails” />

      {/* Add these labels for the marketing site. */}
      {showLabels && <span>Tails</span>}
    </div>
  );

后来,出现了一个需求。“我们想知道你能否只给 APP 里的 <CoinFlip /> 添加一个重掷硬币的按钮?” 事情开始变得糟糕,以致于我不敢再直视 Kent C. Dodds 的眼睛。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 const flip = () => ({
   flipResults: Math.random()
 });

 class CoinFlip extends React.Component {
   static defaultProps = {
     showLabels: false,
     // We don’t repurpose `showLabels`, we aren’t animals, after all.
     showButton: false
   };

   state = flip();

   handleClick = () => {
     this.setState(flip);
   };

   render() {
    return (
      // Use fragments so people take me seriously.
      <>
      {this.state.showButton && (
        <button onClick={this.handleClick}>Reflip</button>
      )}
      {this.state.flipResults < 0.5 ? (
        <div>
          <img src=/heads.svg” alt=”Heads” />
          {showLabels && <span>Heads</span>}
        </div>
      ) : (
        <div>
          <img src=/tails.svg” alt=”Tails” />
          {showLabels && <span>Tails</span>}
        </div>
      )}
      </>
    );
  }
 }

很快就有同事找到你。“嗨,你的 <CoinFlip /> 性能太棒了!我们刚接到任务要开发新的 <DiceRoll /> 特性,我们希望可以重用你的代码!” 新骰子的功能:

  • 想要 “重新掷骰子” 的 onClick。
  • 希望在 APP 和商城网站中都显示。
  • 有完全不同的界面。
  • 有不同的随机性。

你现在有两个选项,回复 “对不起,我们不一样。” 或着你一边向 CoinFlip 中添加 DiceRoll 的复杂功能,一边看着组件无法承受过多职责而崩溃。(是否有一个给忧郁的程序员诗人的市场?我喜欢追求这种技术。)

无头组件了解一下

无头用户界面组件将组件的逻辑和行为与其视觉表现分离。当组件的逻辑足够复杂并与它的视觉表现解耦时,这种模式非常有效。实现 <CoinFlip/> 的无头将作为函数子组件或渲染属性,就像这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 const flip = () => ({
   flipResults: Math.random()
 });
 class CoinFlip extends React.Component {
   state = flip();
   handleClick = () => {
     this.setState(flip);
   };
   render() {
     return this.props.children({
       rerun: this.handleClick,
       isHeads: this.state.flipResults < 0.5
     });
   }
 }

这个组件是无头的,因为它没有渲染任何东西,它期望当它在处理逻辑的时,各种 consumers 完成视觉表现。因此 APP 代码看起来应该是这样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 <CoinFlip>
   {({ rerun, isHeads }) => (
    <>
      <button onClick={rerun}>Reflip</button>
      {isHeads ? (
        <div>
          <img src=/heads.svg” alt=”Heads” />
        </div>
      ) : (
        <div>
          <img src=/tails.svg” alt=”Tails” />
        </div>
      )}
    </>
   )}
 </CoinFlip>

商场站点代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 <CoinFlip>
  {({ isHeads }) => (
    <>
      {isHeads ? (
        <div>
          <img src=/heads.svg” alt=”Heads” />
          <span>Heads</span>
        </div>
      ) : (
        <div>
          <img src=/tails.svg” alt=”Tails” />
          <span>Tails</span>
        </div>
      )}
    </>
  )}
 </CoinFlip>

这很好不是么!我们把逻辑与视觉表现完全解耦!这给我们视觉上带来了很大的灵活性!我知道你正在思考什么......

你这小笨蛋,这不就是一个渲染属性么?

这个无头组件恰好是作为渲染工具实现的,是的!它也可以作为一个高阶组件来实现。即使是简单的实现,也可以到达我们的要求。它甚至可以作为 ViewController 来实现。或者是 ViewModel 和 View。这里的重点是将翻转硬币的机制和该机制的 “界面” 分离。

<DiceRoll /> 呢?

这种分离的巧妙之处在于,推广我们的无头组件以及支持我们同事的新的 <DiceRoll /> 的特性会很容易。拿着我的 Diet Coke™:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 const run = () => ({
   random: Math.random()
 });

 class Probability extends React.Component {
   state = run();

   handleClick = () => {
     this.setState(run);
   };

   render() {
     return this.props.children({
       rerun: this.handleClick,

       // By taking in a threshold property we can support
       // different odds!
       result: this.state.random < this.props.threshold
     });
   }
 }

利用这个无头组件,我们在没有对 consumer 进行任何更改对情况下,交换的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 const CoinFlip = ({ children }) => (
  <Probability threshold={0.5}>
    {({ rerun, result }) =>
      children({
        isHeads: result,
        rerun
    })}
  </Probability>
 );

现在我们的同事可以分享我们的 <Probability /> 模拟程序机制了!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 const RollDice = ({ children }) => (
   // Six Sided Dice
   <Probability threshold={1 / 6}>
     {({ rerun, result }) => (
       <div>
         {/* She was able to use a different event! */}
         <span onMouseOver={rerun}>Roll the dice!</span>
         {/* Totally different interface! */}
         {result ? (
           <div>Big winner!</div>
         ) : (
           <div>You win some, you lose most.</div>
         )}
       </div>
     )}
  </Probability>
 );

非常干净,不是么?

分离原则 —— Unix 哲学

这表达了一个存在很长时间对普遍基本原则,“Unix 基础哲学第四条”:

分离原则:将策略与机制分离,将接口和引擎分离 —— Eric S. Raymond。

我想借用书中的部分,并且用 “接口” 来替换 “策略” 一词。

接口和机制都倾向于在不同时间范围内变化,但接口的变化比机制要快得多。GUI 工具包那时尚的外观和体验会变,但是操作和组合却不会。 因此,将接口和机制结合在一起有两个不好的影响:它使得接口变的生硬,更难响应用户的需求,这意味着试图更改接口具有很强的不稳定性。 另一方面,通过将这两者分开,我们可以在没有中断机制的情况下试验新的接口。我们还可以更容易地为该机制编写好的测试(接口,因为它们太新了,难以证明这样的投资是合理的)。

我喜欢这里的真知灼见!这也让我们对何时使用无头组件模式有了一些了解。

  • 这个组件会持续多长时间?除了界面外,是否值得刻意保留这个机制?也许在另一个外观和体验不同的项目中可以使用这种机制?
  • 我们的界面改变的频率多快?同一机制会有多个接口么?

当你将 “机制” 和 “策略” 分离时,就会产生间接的成本。你需要确保分离的价值大于它的间接成本。我认为这在很大程度上是过去许多 MV* 模式出问题的地方,它们从这样一个公理开始,即所有的东西都应该以这种方式分开;而在现实中,机制和策略往往是紧密耦合的,或分离的成本并没有超过分离的好处。

开源无头组件和非平凡引用

要获取一个真正的示例性非平凡无头组件,可以了解一下我朋友 Kent C. Dodds 在 Paypal 上的项目:downshift 的文章。事实上,正是 downshift 给了这篇文章一些灵感。在不提供任何用户界面的情况下,downshift 提供了复杂的自动完成、下拉、选择体验,这些体验都是可以访问的。在这里看看它所有可用的方法。

我希望随着时间的推移,会出现更多类似的项目。我无法计算有多少次我想使用一个特定的开源 UI 组件,但却无法这样做,因为在满足设计要求的方式上,它并不是 “主题化的” 或 “可剥离的”。无头组件完全通过 “自带接口” 的要求来解决这个问题。

在一个设计系统和用户界面库都是无头的世界里,你的界面可以有一种高端定制的感觉,以及优秀开源库的持久性和可访问性。你仅需要将时间花费在你所需要的部分 —— 一个独特的,外观及体验都只属于你 APP 的部分。

我可以继续讨论从国际化到 E2E 测试集成的好处,但我建议你最好自己去体验。

最后

  • 译者:@Starrier
  • 译文:https://github.com/xitu/gold-miner/blob/master/TODO1/headless-user-interface-components.md
  • 作者:@Merrick Christensen
  • 原文:https://medium.com/merrickchristensen/headless-user-interface-components-565b0c0f2e18
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-05-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端Q 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
回望过去,展望未来- 2024 React 生态一览表
React 最初是由 Facebook(Meta) 内部开发的,然后于 2013 年 5 月 29 日在 Facebook 的 F8 开发者会议上首次公开宣布,并「于同一天开源发布」。不知不觉中,React已经开源 10 年了。
前端柒八九
2023/11/13
8590
回望过去,展望未来- 2024 React 生态一览表
滴滴前端高频react面试题总结
当调用 setState时, React做的第一件事是将传递给setState的对象合并到组件的当前状态,这将启动一个称为和解( reconciliation)的过程。
flyzz177
2022/09/14
4K0
前端框架与库 - React基础:组件、Props、State
React 是一个用于构建用户界面的 JavaScript 库,特别擅长创建可复用的组件。掌握组件、Props 和 State 的概念是学习 React 的基石。本文将深入浅出地探讨这些核心概念,包括常见问题、易错点以及如何避免这些问题,并附带代码示例。
Jimaks
2024/07/12
1550
Next.js 实战 (二):搭建 Layouts 基础排版布局
等了许久,Next.js 终于迎来了 v15.x 版本,刚好 Github 上面的旧项目重构完,终于可以放心大胆地去研究 Next.js了。
白雾茫茫丶
2024/12/05
2440
Next.js 实战 (二):搭建 Layouts 基础排版布局
React源码分析与实现(一):组件的初始化与渲染
阅读源码的方式有很多种,广度优先法、调用栈调试法等等,此系列文章,采用基线法,顾名思义,就是以低版本为基线,逐渐了解源码的演进过程和思路。
Nealyang
2019/09/29
1.6K0
React源码分析与实现(一):组件的初始化与渲染
ThinkPhp5开发实战2:后台管理登录设计
本文主讲:ThinkPhp5开发实战系列续集:设计登录界面,完成登录操作 对于没有配置开发环境或者TP5框架的同学,请参考文章 [第一章 ThinkPhp5开发实战1:搭建环境配置TP5框架(持续更新收藏关注)]
德宏大魔王
2023/08/08
4980
ThinkPhp5开发实战2:后台管理登录设计
可视化拖拽组件库一些技术要点原理分析(四)
本文是可视化拖拽系列的第四篇,比起之前的三篇文章,这篇功能点要稍微少一点,总共有五点:
谭光志
2022/09/20
1.3K0
可视化拖拽组件库一些技术要点原理分析(四)
前端代码层面优化的一些想法
这样连续使用三元选择符并不利于理解,并且如果有更多的类型,会导致过长的三元判断,可以使用map替换:
HenryYang
2022/08/30
1.2K0
浅谈 React 组件设计
前端组件化一直是老生常谈的话题,在前面介绍 React 的时候我们已经提到过 React 的一些优势,今天则是带大家了解一下组件设计原则。
玖柒的小窝
2021/10/22
6870
react 学习(12)实现 cloneElement
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该方法和他的实现原理。
测不准
2022/06/17
9720
react 学习(12)实现 cloneElement
从零开始学 Web 之 BOM(二)定时器
多次点击“摇起来”按钮的时候,timeId 的值会有多个,而停止的时候,只会清理最后一个值,其他的值对应的定时器没有清理。
Daotin
2018/08/31
1.4K0
如何在React项目中,创建令人惊叹的动画翻转卡片效果
为了实现翻转卡片,我们将使用React-Card-Flip库。在本教程中,我们将逐步介绍创建动态卡片组件并在交互时翻转的过程。
前端达人
2023/11/06
9870
如何在React项目中,创建令人惊叹的动画翻转卡片效果
「React 手册 」如何创建函数组件?
大家好,在前面的几篇相关文章里,我们一起学习了如何使用类的方式声明组件,以及如何属性传值和处理本地数据状态,本篇文章我们一起学习如何使用函数的方式进行声明组件。
前端达人
2019/12/30
2.8K0
「React 手册 」如何创建函数组件?
Vue 中可重用组件的 3 个主要问题
当我们谈论或讨论在 Vue 中创建用户界面组件时,经常会提到可重用性。没错,Vue 的关键原则之一就是其基于组件的架构,这促进了可重用性和模块化。但这到底意味着什么呢?
前端小智@大迁世界
2024/02/12
2050
Vue 中可重用组件的 3 个主要问题
Vue开发仿京东商场app
vue3-jd-h5是一个电商H5页面前端项目,基于Vue 3.0.0 + Vant 3.0.0 实现,主要包括首页、分类页面、我的页面、购物车等,部分效果如下图。
xiangzhihong
2021/01/15
9660
父组件中vuex方法更新state,子组件不能及时更新并渲染的解决方法
场景: 我实际用到的是这样的,我父组件引用子组件related,父组件调用获取页面详情的方法,更新了state值related,子组件根据该related来渲染相关新闻内容,但是页面打开的时候总是先加载子组件,子组件在渲染的时候还没有获取到更新之后的related值,即使在子组件中watch该值的变化依然不能渲染出来子组件的相关新闻内容。 我的解决办法: 父组件像子组件传值,当父组件执行了获取页面详情的方法之后,state值related更新,然后传给子组件,子组件再进行渲染,可以正常获取到。 父组件代码:
蓓蕾心晴
2018/06/13
2.3K0
Vue.js 基础知识内容(前端开发必备)
Vue.js 是一个渐进式的 JavaScript 框架,主要用于构建用户界面。Vue 的核心库只关注视图层,便于与其他库或现有项目集成。与其他框架相比,Vue 更易于学习和使用,适合构建单页应用(SPA)。
繁依Fanyi
2024/06/13
1830
Typecho Cuteen 主题美化
前往 https://www.alapi.cn/ 注册一个账号 复制自己的Token替换下边的 你的密钥内容
氢云
2023/03/31
2.1K0
Typecho Cuteen 主题美化
原生HTML+CSS+JS制作自己的导航主页(前端大作业,源码+步骤详解)
咕咕了好久啦,今天使用原生HTML+CSS+JS做一个很简单的个人定制的导航主页吧!
全栈程序员站长
2022/09/15
7K2
原生HTML+CSS+JS制作自己的导航主页(前端大作业,源码+步骤详解)
【wiki知识库】05.分类管理实现--前端Vue模块
除了分类管理,我们的首页也变动了一下。首页的导航栏加载的是我们已经有的分类,同时还加上了一个欢迎页面。
哈__
2024/06/06
1240
【wiki知识库】05.分类管理实现--前端Vue模块
推荐阅读
相关推荐
回望过去,展望未来- 2024 React 生态一览表
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验