⚡ 30 秒速记

  • 组件接收输入,组织状态并产出界面描述
  • 渲染器把描述落实到宿主环境
  • 组件描述与内部渲染子树不是同一层
  • 有状态逻辑需要跨更新保存上下文
  • 函数式外观不等于所有框架都没有内部实例

组件是一块有明确输入和职责的界面单元,渲染结果交给框架落到实际页面。 外层组件节点描述要创建哪个组件、传入什么,组件内部再根据状态产出自己的子树。这样业务可以按组件组合,不必自己维护每一步节点操作。有状态组件还需要保存状态和生命周期上下文;不同框架如何表示实例属于实现选择,不能仅看函数写法就断言没有状态。

版本校准: 本文出现 Object.defineProperty、Dep、Watcher 和双端 Diff 时,主要描述 Vue 2 实现;Vue 3 使用 Proxy、effect 与新的渲染器路径。Vue 2 已于 2023-12-31 结束维护,新项目应以 Vue 3 为基线,旧项目参考 Vue 2 EOL 官方说明 制定迁移与安全策略。

当我们使用 Vue 或 React 时,往往会将页面拆分为各种组件,通过拼装组件来形成页面和应用,就像搭积木一样。

那么,大家有没有思考过:"组件的产出是什么?"

# 组件的产出是什么

在 JQuery 盛行的年代,比起组件,“模板引擎”的概念要更加流行。

我们可以使用 lodash.template 函数来回忆一下当年是如何用模板开发一个页面的:

import { template } from 'lodash'

const compiler = template('<h1><%= title %></h1>')
const html = compiler({ title: 'My Component' })

document.getElementById('app').innerHTML = html

模板引擎的概念是:字符串 + 数据 => html。

lodash.template 函数虽然称不上是“引擎”,但足以说明问题。

我们将模板字符串传递给 template 函数,该函数返回一个编译器 compiler,只要把数据传入 compiler 函数,便能得到最终想要渲染的内容。

当数据发生变化时,我们需要使用新的数据重新编译模板:

const newHtml = compiler({ title: 'New Component' })

如果把上面的逻辑封装成一个函数,那么一个组件就诞生了:

const MyComponent = props => {
  const compiler = MyComponent.cache || (MyComponent.cache = template('<h1><%= title %></h1>'))
  return compiler(props)
}
MyComponent.cache = null

我们可以这样使用它:

webapp
公众号
开发者导航
切换夜间模式
点击侧边栏上一篇
点击侧边栏下一篇
折叠侧边栏
收起全部