web开发框架带来的新思考
随着 astro、qwik、nextjs13 等框架的出现,人们逐渐开始思考究竟有多少代码需要被发送到客户端中,简单的 html + js + css 能够使TTI(time to interactive 可交互时间)的效率大幅提升。 solidjs、svelet 等框架抛弃了虚拟DOM,他们选择了一种能够直接查找到变化的元素,并进行局部渲染的策略,这种策略也在 react 的虚拟DOM的性能优化框架 million 中被使用。
于是我们现在要讨论这样一种情况:当离开了 react、vue 带给我们的响应式渲染功能,在简单的 html + js + css 中,怎样给页面带来一些简单的交互效果——比如切换暗黑模式。
可以看到,随着切换模式,图片的样式也随之点亮,如果我们使用的是 svg 图片,我们可以很容易在 react 简单封装一个组件:
function Svg({color}){
return (
<svg ...>
<path d="..." fill={color}></path>
</svg>
)
}
当我们在切换模式时,给这个组件传入对应的 color ,就可以实现图中的点亮效果。但如果没有了 react 的帮助,可能我们就需要通过 document.querySelector(svg path) 选中里层的元素,并对其修改 fill 属性的颜色。
但我们举的是一个极简例子,如果元素层级更深,并且要维护多个 svg 元素,则这种 js 代码很快会变得繁琐起来。
使用currentColor
这时有一个 css 属性可以帮上我们忙: currentColor 。它表示取该元素上的 color 属性作为颜色。这是一个使用示例:
<div color="red">
<svg>
<path fill="currentColor" />
</svg>
</div>
因为 color 是一个 可继承属性 ,因此 svg 、 path 会一层一层继承最外层 div 的红色 color 。而我们又为 path 的 fill 设置了 currentColor 值,于是最终我们得到了 <path fill="red" /> .
因此我们用 js 修改最外层的属性名,来实现暗黑模式的切换效果,以下是简单的示例代码:
<html class="dark">
<body>
<!--... -->
<svg id="light_icon"><path d="..." fill="currentColor" /></svg>
<svg color="#FF7D33" id="dark_icon"><path d="..." fill="currentColor" /></svg>
<!--... -->
</body>
<script>
const darkBtn = document.querySelector("#dark_icon");
const lightBtn = document.querySelector("#light_icon");
const html = document.querySelector("html");
darkBtn?.addEventListener("click", () => {
html?.classList.add("dark");
darkBtn.setAttribute('color' , '#FF7D33')
lightBtn?.setAttribute('color' , '#2c2c2c')
});
lightBtn?.addEventListener("click", () => {
html?.classList.remove("dark");
darkBtn?.setAttribute('color' , '#2c2c2c')
lightBtn.setAttribute('color' , '#FF7D33')
});
</script>
</html>
总结
currentColor 给原生 html + js + css 开发带来了一定便利。混合渲染模式的场景下,还有多少组件应该放在客户端渲染依然是一个值得讨论的问题。