实用示例。实时演示。
选择一个示例全屏打开,实时预览与源码并排显示。
组件
4带静态与动态参数、子节点、按对象引用匹配的列表和嵌套数据的普通函数。每个预览都统计自己的挂载次数。
静态与动态参数
值参数只在组件构建时读取一次。函数参数在每次 update() 时读取。两种情况下组件本身都只运行一次,挂载计数可以证明。
子节点与修饰器
组件用展开语法转发它的子节点:构建器、文本、属性对象和修饰器。条件部分放进 when(),这样包裹它们的组件永远不会被重建。
列表项的对象引用
只要元素还在数组中,list() 就保留它的行:重排、插入和重命名都不会丢失输入框或焦点。一个开关会在每次更新时克隆对象,展示替换对象引用的代价。
嵌套数据
一个团队,成员各有任务:每层一个组件,嵌套的 list() 调用,派生计数是作用于同一批对象的函数。深层数据变化只更新对应的 DOM 节点。
API
13每个基础功能都有一个小演示,与文档中的嵌入示例一致。
标签构建函数
构建器接受属性、静态子节点和动态文本。重命名会改变状态,然后调用 update()。
组件
组件就是一个普通函数。每次 Counter() 调用都保有自己的计数。
属性
属性值可以是函数。第二个按钮翻转 busy 并调用 update();Save 的 disabled 和 aria-busy 随之变化。
事件
onInput 和 onKeyDown 是普通属性。输入文字,然后按 Escape 清空。
生命周期
onMount 启动一个定时器并返回其清理函数。卸载时运行清理,所以计数会暂停,直到计时器再次挂载。
显式更新
count++ 只改变变量。文本在下一次 update() 时才跟上,无论它来自按钮还是页面上的其他任何地方。
动态值
编辑名字。静态文本在元素构建时就固定了;动态文本在每次 update() 时重新运行。forceUpdate() 重建整棵树,所以静态文本也会跟上。
update()
每次 update() 都重新计算所有动态值,即使其数据没有变化。页面上的其他演示也会推高这个计数。
scope()
两个按钮都添加一项并重命名用户。update('cart') 只刷新带作用域的面板。
when()
第一个为真的条件生效;否则渲染 .else()。新分支从头构建。
list()
行按对象引用匹配。勾选一行,然后点 Reverse:同一个 <li> 连同复选框一起移动。
region() 与 into()
一个布局,带有堆叠的主区域和侧边区域,从外部填充。在 Home 中输入,打开对话框再关闭:文本仍在。Article 用一次 into() 填充两个区域。
forceUpdate()
update() 只刷新动态的 lang 行。forceUpdate() 再次调用 App 并更新原有的 <h1>。显式形式只重建这棵树,而非整个页面。
样式
3创建和组合 CSS 类,并使用简单的主题。
SVG
27用 SVG 标签构建函数做的图表、动画、工具和游戏,由普通数据、指针事件和 update() 驱动。
柱状图
由数组生成 rect。它们的 y 和 height 是数据的函数,所以 update() 会移动柱子。
折线图
一条 path,其 d 由各点构建,带渐变面积和每点一个圆。
环形图
每个扇区一个圆。stroke-dasharray 给出各自在环上的份额,stroke-dashoffset 给出起点。
SVG 动画
以构建器形式写 SMIL:animateSvg 让太阳脉动,animateTransformSvg 让行星绕行。一个动态的 dur 设定速度。
仪表
一段用 stroke-dashoffset 填充的弧,和一根用 CSS transform 旋转的指针。两者都是同一个数的函数。
雷达图
五个轴上的五个值。形状是一条 path,其 d 由数据算出,所以 update() 会让它变形。
热力图
一组 rect,其 fill-opacity 跟随一个等级。点击格子提升它;一个 titleSvg 子节点是它的提示。
悬停提示
SVG 元素上的指针事件。onPointerEnter 选出活动点,一个提示组滑到它那里。
曲线编辑器
拖动三次贝塞尔曲线的控制柄。onPointerDown 选中一个点,svg 上的 onPointerMove 移动它,path 随之变化。
时钟
表针是 line,其 transform 是时间的函数。onMount 启动走时并返回清理函数。
图标切换
一个用 CSS 过渡变形为叉号的菜单图标,和一个点击时以 SMIL 动画弹跳的心形。
实时流
两条序列随定时器向左滚动。一段与一拍等长的 CSS 过渡让滑动平滑,悬停可读取任意采样。
K 线图
四十个位置各自读取对应的 K 线数据。切换时间周期,开关均线,悬停查看数值,或让实时 K 线持续更新。
柱状竞赛图
十根柱子从不离开 DOM。排名设定 translateY,所以数值变化时一个过渡把每根柱子滑到它的行。
堆叠面积图
四个带状区域在堆叠、流式和百分比布局之间变换。隐藏一条序列只是让对应区域的厚度为零,所以每条 path 的命令结构保持不变。
矩形树图
可以逐级下钻的方化布局。切换指标时每个格子滑到新位置,点击分组则换入它的子项。
实时直方图
样本从四种分布中涌入。每次变化时重新计数分箱,一条拟合的钟形曲线跟随均值,滑块设定分箱数。
画板
指针事件绘制折线。list() 为每一笔渲染一条,正在画的那一笔在每次 update() 时延长。
旋钮
一个自定义滑块:指针的角度设定值,方向键步进,pathLength="100" 把值变成一段虚线。
色轮
按角度选色相,按距离选饱和度。九十个楔形、一个径向渐变和一个深色圆盘组成色轮。
万花尺
一个轮子在环内滚动,一支笔描绘轨迹。每帧延长一段轨迹,只更新它自己的 scope()。
弹跳球
requestAnimationFrame 循环中的重力、墙壁和碰撞。拉回再放手,发射一个球。
粘稠团块
一个模糊和一个颜色矩阵滤镜把团块融在一起。一串圆跟随指针。
力导向图
节点互斥,连线牵引,布局逐渐冷却直至静止。拖动节点,或点击一个节点长出子节点。
刷选与缩放
在概览图上刷选一个范围,放大上方的图表。一个跟随刷选的 clipPath 为选区上色。
井字棋
和电脑对弈。when() 添加每个标记,其虚线上的 CSS 动画把它画出来。
环形文字
输入徽章上的文字。textPath 让文字沿圆弯曲,textLength 让它恰好绕满一圈。
路由
6路由表、匹配、当前路由、层和加载器。nuclo-router 是独立的包:npm install nuclo nuclo-router。
带路由的应用
一个在内嵌框架中运行、拥有自己路由器的真实应用:路由表、一个 outlet()、普通链接、等待状态和 404。随意点击,再用返回。
嵌套路由
Docs 是一个带子路由的布局。主题渲染在它的 outlet() 里,./topic 链接相对于布局的路由解析。在笔记中输入,再切换主题:布局永不重建。
路由加载器
文章页导出一个 load(),在页面构建前运行。等待时观察 router.pending,加载异常文章时抛出错误后则看到带重试的错误状态。
层
订单表单把客户页作为一层推入并等待其结果。表单不会重新渲染,所以它的备注得以保留。保存时,push() 返回的 Promise 会以保存结果完成,取消或返回则将其关闭。
多路由器与加载
两个路由器,两个加载指示。商店布局和它的分区都有 load():它们在同一个 router.pending 下并行运行。侧边栏有自己的路由器,已创建但从未启动,其加载标志由 onNavigate 刷新。
模式匹配
静态、:param 和 *rest 模式,以及挂在两个父级下的分区表。输入路径并观察 match(),它不加载任何东西即可解析。
应用
15更大的程序,全屏运行,可在代码标签页中查看完整源码:带窗口的桌面、数据表格、聊天、游戏、编辑器,以及拥有自己状态的库。
页面构建器
直接在页面上书写,拖动把手重排块或把它们移入布局,按 / 插入,右键打开操作。设置网格和弹性布局样式,撤销编辑,以手机尺寸预览并导出 HTML。
算法步进器
引擎是普通 JavaScript:修改数组并在每一步后 yield 的生成器。播放器调用 next() 和 update();柱子是作用于同一批对象的 list(),所以一次交换只移动两个元素。
看板
一个团队看板:添加任务、分派给成员、在对话框中编辑、按人员或搜索过滤、在列间拖动,以及撤销删除。列和卡片是同一批对象的普通数组;每次移动都是一次 splice()。行按对象引用匹配,所以拖动、排序、分派或编辑卡片都会复用它的元素。屏幕上的挂载计数可以证明。
three.js 检视器
一个拥有自己状态的库。面板直接从 three.js 对象读取位置、颜色和标志,并把编辑写回去。渲染循环每帧调用一次 update();无需维护一份同步副本。
无限滚动
一个没有尽头的信息流:一个快速的模拟 fetch() 把每一页即时生成到同一个普通数组里。只有视口附近的行在 DOM 中:list() 在两个占位元素之间渲染 posts.slice(start, end),所以到十万条帖子时页面依然轻快。真正末尾处的哨兵在进入视野前就请求下一页。
桌面
一个功能完整的桌面,带文件、便笺、终端、计算器、计时器、看板、财务和日历。在一个保存于浏览器中的共享模拟磁盘上创建和整理文件夹、编辑便笺、使用 shell 命令。直接用键盘在计算器中输入,点击终端任意位置聚焦其输入行,在看板的列间拖动卡片,或在财务电子表格中记账并看图表随之变化——账本保存在 IndexedDB 中。移动、缩放和切换应用时窗口保持自己的状态;运行中的计时器用 into() 填充任务栏托盘。
节点图
把数字、文本和开关接入数学、比较和 If 节点,每个值随你的输入实时更新。函数也是图:它们的 Input 和 Output 节点就是每个 Call 的端口,而 Call 可以指向自己所在的函数,所以 factorial 和 sum 通过 If 设置递归终止条件。引擎是普通 JavaScript:每个节点是一个生成器,通过 yield 请求输入,一个循环用自己的栈驱动它们,所以 sum(50000) 嵌套五万层调用约只需十分之一秒,且不触碰 JavaScript 调用栈。对于缺少终止条件的递归,只能通过步数上限将其停止。拖动写入 x 和 y 并只更新画布作用域;运行计数表明移动从不触发图的运行。
数据表格
一张作用于 10,000 个普通行对象的工作表。搜索、过滤、排序和分页都是作用于数组的普通函数,表体是作用于当前页的 list()。行按对象引用匹配,所以排序只移动留在本页的行,编辑过的行在任何地方都是同一个对象。已构建行数可以证明。
聊天
一个有三个频道的聊天,每个频道是一个普通对象:历史、游标、正在输入、草稿和滚动位置,所以切换时每个频道都保持原样,update("chat-sidebar") 只刷新未读角标。历史用 unshift() 无限向上加载,scrollTop 按新增高度增长。消息在服务器收到之前就会显示在界面上,send() 更新原有消息对象,重试、编辑、删除和表情回应从不重建它所在的行。消息支持渲染代码、粗体、链接和 @提及。
实时仪表盘
以不同频率刷新的卡片,从每秒 20 次到每秒 1 次。每张卡片是一个 scope(),它的定时器带着卡片 id 调用 update(),所以每次定时触发只重新计算对应的卡片。每个动态值都在屏幕上计数:换成普通的 update(),时钟每秒会被求值约一百次,却只变化一次。
像素编辑器
图片是一个含 1024 个颜色字符串的普通数组,每个格子从中读取颜色。一笔写入数组并调用 update();结束时编辑器用 slice() 保留一份副本。撤销、重做和历史 list() 中的每一步都把旧数组放回去:无需状态存储库,也无需不可变数据结构。
贪吃蛇
游戏循环是一个定时器,在普通对象上调用 step(),然后每拍调用一次 update()。蛇是其格子对象的 list(),每一拍尾部对象变成新的头部,所以只有一个元素移动,没有任何元素被构建:已构建段数只在蛇进食时增加。食物以 keyframes() 动画弹出。
步进音序器
一个拥有自己时钟的库。Web Audio 在自己的时间线上提前调度每个音符,节奏模式是一个普通的二维布尔数组,网格切换其中的值,调度器读取它,所以点击会在下一轮被听到。播放头只在当前播放的步进位置变化时更新自己的 scope()。音频在第一次播放时启动,并在卸载时通过清理函数关闭,所以离开示例就会停止声音。
语言切换
每个标签都是静态值,写法与单语言应用一样,所以 update() 从不再读它们:购物车点击只刷新数量和总计。切换语言在这棵树上调用 forceUpdate(),它再次运行组件并更新原有元素,包括 lang 和 dir。货币、复数、日期和列表来自 Intl,购物车和已输入的优惠码在切换后仍然保留。
主题编辑器
一个小型组件库:createCss() 命名 token,variants() 用它们构建按钮、徽章、卡片、输入框、警告和开关。token 是 CSS 变量,所以每个类只编译一次。每个控件写入一个普通的主题对象并调用 update(),它重写一个 style 元素的文本。