react 翻书效果_手翻书

本文通过一个生动的手翻书例子,解释了React如何通过声明式编程实现翻书效果。每次调用组件,React都会返回新的一帧,组合起来形成完整的交互界面。文章强调在React中,程序员只需关注最终结果,而无需关注具体DOM操作,这种编程方式有助于提高代码可读性和业务逻辑的聚焦。
摘要由CSDN通过智能技术生成
本文是《React 思维模式》的预览连载之一,这是我最近在写的一本小说体裁的 React 书。想知道我创作背后的心酸故事吗?点这里从头看

我惊慌失措地奔回,想拿上观察者窗口去修改那怪物的代码,总之就算救不回艾伦也要把那害人的怪物除掉!这时,屏幕上显示接收到一条新消息,我打开一看,消息竟然是艾伦发的!

靠!这破墙竟然是怪物伪装的!我被吞进去了。不过先别哭,我没死,在这个世界我不需要呼吸,也不会被消化。就是被关在这里太无聊了。快来救我吧,只需要 !FEag#2gsadg#@

消息以一堆乱码收尾,可能是肚子里信号不好?不过,我还是松了一口长气,这吓死人的 React 星。那么,怎么放艾伦出来呢?划开怪物的肚皮?我从衣袋里摸出刚刚捡回来的相片,试图找出一点线索。那个相机倒是很给力,把整个事发过程都一帧一帧记录下来了。我忽然想起了小时候玩过的手翻书,一时童心大起,把一张张相片按时间戳排列好,摞整齐,手指捏着页边一翻,艾伦的悲壮经历跃然眼前。

08005accb2da301a2185205775931aa3.png

也许是冥冥中自有安排,没想到我的无心之举竟然跟 React 有关,我玩了一遍手翻书以后,又有一行蓝字出现在我的视野:

React 思维模型:React 爱看手翻书

手翻书的代码例子

React 的工作过程就像播放手翻书动画。每调用组件一次,组件就返回手翻书的一页,调用多次,装订起来,快速一翻就形成了完整的交互界面。

我们来回顾一下当时的情况:

const wallActions = ['静候','变身','吞咽','静候']
const alanReactions = ['卖萌', '惊恐', '挣扎', '受困']

function App() {
  console.log("新一帧") // 加了这一句方便跟踪程序运行状况
  const [timeline, setTimeline] = useState(0)
  return (
    <div>
      <div>拍照墙:{wallActions[timeline]}</div>
      <div>艾伦:{alanReactions[timeline]}</div>
      <button onClick={() => setTimeline(timeline+1)}>下一动作</button>
    </div>
  )
}

运行这段程序并观察 JavaScript 控制台,一开始就会出现一行提示: 新一帧。这说明函数 App 被调用了一次。第一次调用的返回值如下(为了易读,我用了 JSX,实际上应该是一个对象):

<div>
  <div>拍照墙:静候</div>
  <div>艾伦:卖萌</div>
  <button onClick={function ...}>下一动作</button>
</div>

咔嚓……注意,一张相片拍好并打印出来了。

当用户点击“下一个动作”按钮后,按钮的事件处理器函数就会执行:() => setTimeline(timeline+1),这将把 timeline state 设置为 1。这时,控制台上会再次出现提示:新一帧,函数 App 又被调用了一次。这一次的调用返回值为:

<div>
  <div>拍照墙:变身</div>
  <div>艾伦:惊恐</div>
  <button onClick={function ...}>下一动作</button>
</div>

咔嚓……又打印出一张!这次,拍照墙和艾伦的动作都跟上一张上不同。

如果现在又点“下一动作”,情况类似,咔嚓……又会打出第三张相片。

你注意到了吗?每次调用 setTimeline 函数后,组件函数 App 都会被执行一次,会打印出新的一张相片(返回一组新的 React 元素)。这个过程被称为“渲染”(render)。

前面说过,组件函数返回的是 React 元素,其实就是一个 JavaScript 对象,并不是 DOM 元素。我们写组件时,只管正确地打印这些相片(也就是手翻书动画的每一帧)就够了,至于具体怎么更改 DOM 元素、怎么在浏览器里呈现出交互效果,全权由 React 代劳。

如果你并不关心艾伦的生死,而是想看一个更接近实际应用的例子,请看如下观察者窗口控制界面代码的简化版,其中包含了一个消息对话框:

function App() {
  console.log("新一帧") // 加了这一句方便跟踪程序运行状况
  const [isDialogVisible, setIsDialogVisible] = React.useState(false)
  const dialog = (
    <div style={styles.dialogBackdrop}>
      <div style={styles.dialogContainer}>
        <div>艾伦:靠!这破墙竟然是怪物伪装的!我被吞进去了。不过先别哭,我没死,在这个世界我不需要呼吸,也不会被消化。就是被关在这里太无聊了。快来救我吧,只需要!FEag#2gsadg#@ </div>
        <button onClick={() => { setIsDialogVisible(false) }}>关闭</button>
      </div>
    </div>
  );
  return (
    <div style={styles.app}>
      <div>观察者窗口主控界面</div>
      <button onClick={() => { setIsDialogVisible(true) }}>打开消息对话框</button>
      { isDialogVisible && dialog }
    </div>
  );
}

如果用户点击打开消息对话框一次,并点击关闭,JavaScript 控制台上会出现几行 新一帧

声明式和命令式

从前面的例子可以看到,组件的作用似乎仅限于输出不同状态下的相片,并不直接修改浏览器中的 DOM,换句话说,我们只关注最后结果,而不是如何得到这个结果的中间过程。这是写 React 程序需要时刻铭记的一个重要思维模式:声明式编程(declarative programming)。

与之相对照的编程方式是“命令式”(imperative),我们写程序时需要描述生成结果的具体步骤,也就是说需要给计算机下达具体的行动命令,例如需要调用浏览器的哪个 API。传统的 Web 编程方式(比如 jQuery 或浏览器 API)是命令式,而 React、Vue.js、Angular、Svelte 等新生代 Web 框架都是声明式。

除了需要记忆繁琐的 API 细节,命令式编程最主要的问题是在写程序时需要在脑中一直跟踪系统状态,并根据不同状态写出相应处理代码。以 openDialog 函数的实现方式为例,命令式的代码如下所示:

function openDialog() {
  const div = document.getElementById('container')
  // 1. 需要跟踪系统状态:dialog 是否已经存在?
  // 2. 根据当前系统状态下达行动命令:如果不存在,就创建 dialog;否则,什么也不做
  if (!document.getElementById('dialog')) {
    const dialog = document.createElement('div')
    dialog.appendChild('div')
    dialog.appendChild('button')
    ...
    div.appendChild(dialog)
  }
}

想象一下,在一个真实的系统里,一个页面上动辄有几十个甚至更多的动态元素,这些元素之间甚至还相互依赖,要跟踪每一个元素的当前状态是多么的困难,相应的处理代码会有多么的晦涩难懂。

相比之下,使用声明式编程就轻松很多:

// 描述出所期望的最终结果:
<div style={styles.app}>
  <div>观察者窗口主控界面</div>
  { isDialogVisible && dialog }
</div>

// 要打开对话框?一行代码搞定!
function openDialog() {
  setIsDialogVisible(true)
}

React 将大部分繁琐的 API 细节和状态跟踪都处理妥当,让我们聚焦于业务逻辑,而不是一次又一次地重新发明轮子、费劲地跟浏览器较真。

再举两个例子来帮助你理解声明式编程。例如,SQL 是一种声明式编程语言:

SELECT * FROM snacks

上面这一句 SQL 只是定义了需要从哪个表取出数据、需要取出什么数据,这是跟业务逻辑息息相关的信息。而具体的实现细节就由数据库引擎代劳了,比如判断数据库到底是存在本地还是远程服务器、如何读写数据库文件、如何使用索引等等。

再比如,CSS 也是一种声明式语言:

body {
  font-size: 16px;
  padding: 8px;
  background: #FEFEFE
}

书写 CSS 的时候,我们只会去关注我们想定义的样式,而至于如何将这些样式应用到 DOM 树上、如何优化浏览器渲染等具体实现步骤,都可以放心地交给浏览器来完成。

当然,从根本上来讲,计算机是命令式的,我们需要给它下达具体的行动指令(读写哪个寄存器、加法、移位等等)。所以,声明式是命令式的一种抽象,也就是说,所有的声明式语言或框架都是以某种命令式的实现为基础的。数据库引擎和 CSS 处理器中肯定有很多命令式的实现,React 的核心代码里也肯定调用了很多命令式的 DOM API(document.createElementelement.removeChild等等)。

好了,这就是声明式和命令式编程,你理解了吗?声明式是 React 中最主要的编程方式,所以,在绝大多情况下只需要用 JSX 标签描述你想要的最终结果就够了,不要直接去修改 DOM。在 React 程序里混写 jQuery 一般来说是个坏主意。

小结

没想到啊,不经意间拿艾伦的写真当做手翻书,居然也玩出一个 React 思维模型。

  • React 的工作过程就像播放手翻书动画。每调用组件一次,组件就返回手翻书的一页,调用多次,装订起来,快速一翻就形成了完整的交互界面。
  • React 中最主要的编程方式是声明式编程。
  • 写组件时,我们的主要任务是正确地用 JSX 描述手翻书动画的每一帧。

艾伦,再挺一会,我来救你了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值