“Add Tags” 技术方案并行对比:React Hooks vs dagger.js(含核心 JS 代码)
源码:
- React Hooks:https://codepen.io/prvnbist/pen/jJzROe?editors=1010
- dagger.js:https://codepen.io/dagger8224/pen/ZErjzwm
一、对比总览(表格)
维度 | React Hooks 实现(prvnbist) | dagger.js 实现(dagger8224) | 结论 |
---|---|---|---|
框架/依赖 | 依赖 React/ReactDOM + Babel/JSX;ReactDOM.render 挂载 |
原生 DOM + 指令(+load /*each /*value#trim /+click /+keyup ),零构建可跑 |
dagger 依赖更轻;React 生态更全 |
状态管理 | useState (不可变更新) |
作用域对象可变更新(push /splice ) |
React 可预测性强;dagger 上手直观 |
事件/输入 | onKeyUp 手写回调读取/清空输入 |
*value#trim 双向绑定,+keyup#every:-Enter 声明式触发 |
dagger 更少样板 |
列表渲染/删除 | tags.map(...) + onClick |
*each="tags" + +click="removeTags(...)" |
均直观;dagger 更贴 HTML |
对外通信 | props.selectedTags([...]) 向父级冒泡 |
示例未展示(可拓展自定义事件/全局 store) | React 更现成 |
A11y | 删除控件是 span “x”,无按钮角色/ARIA(两边均是) |
同左 | 建议两者都改 <button aria-label="Remove tag"> |
性能(本例) | VDOM diff 开销极小 | 直达 DOM 绑定 | 小组件差异可忽略 |
适用场景 | 工程化/组件生态/团队协作 | 免构建/轻量嵌入/快速原型 | 视项目体量取舍 |
二、代码量差异(粗略非空行统计)
注:仅为量级参考;CSS 两边大致一致。
面板 | React Hooks | dagger.js |
---|---|---|
HTML | ~1 行(<div id="root"> ) |
~10 行(含指令模板) |
JS | ~33 行(TagsInput + App + render ) |
~12 行(loading /removeTags /addTags ) |
CSS | ~80+ 行(两者近似) | ~80+ 行 |
合计(HTML+JS) | ~34 行 | ~22 行 |
结论:dagger.js 显著减少 JS 体量,把交互“下沉”到 HTML 指令;React JS 更多但 HTML 更薄,符合组件化/状态提升的常规模式。
三、核心 JS 代码对照
1) React Hooks(核心 JS)
const TagsInput = props => {
const [tags, setTags] = React.useState(props.tags);
const removeTags = indexToRemove => {
setTags([...tags.filter((_, index) => index !== indexToRemove)]);
};
const addTags = event => {
if (event.target.value !== "") {
setTags([...tags, event.target.value]);
props.selectedTags([...tags, event.target.value]);
event.target.value = "";
}
};
return (
<div className="tags-input">
<ul id="tags">
{tags.map((tag, index) => (
<li key={index} className="tag">
<span className='tag-title'>{tag}</span>
<span className='tag-close-icon'
onClick={() => removeTags(index)}
>
x
</span>
</li>
))}
</ul>
<input
type="text"
onKeyUp={event => event.key === "Enter" ? addTags(event) : null}
placeholder="Press enter to add tags"
/>
</div>
);
};
const App = () => {
const selectedTags = tags => {
console.log(tags);
};
return (
<div className="App">
<TagsInput selectedTags={selectedTags} tags={['Nodejs', 'MongoDB']}/>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
2) dagger.js(核心 JS)
const loading = () => ({
tag: '',
tags: ['Nodejs', 'MongoDB']
});
const removeTags = (index, tags) => tags.splice(index, 1);
const addTags = $scope => {
const { tag, tags } = $scope;
if (tag) {
tags.push(tag);
$scope.tag = '';
}
};
dagger 对应模板(片段),可见通过指令表达列表、事件与双向绑定:
<div dg-cloak class="App" +load="loading()">
<div class="tags-input">
<ul id="tags">
<li class="tag" *each="tags">
<span class='tag-title'>${ item }</span>
<span class='tag-close-icon' +click="removeTags(index, tags)">x</span>
</li>
</ul>
<input type="text" placeholder="Press enter to add tags"
*value#trim="tag" +keyup#every:-Enter="addTags($scope)">
</div>
</div>
四、与代码对应的实现分析
A. 初始化与挂载
- React:
const [tags, setTags] = useState(props.tags);
为局部状态;通过ReactDOM.render(<App/>, #root)
挂载。组件化边界清晰,便于复用与组合。 - dagger:
+load="loading()"
在容器上注入初始作用域(tag
/tags
),无需显式渲染入口,适合在任意静态页“就地”增强。
B. 输入与校验
- React:
addTags(event)
里手动读取/清空event.target.value
;默认不trim
,可在函数中补充校验与去重。 - dagger:
*value#trim="tag"
天然裁剪空白,结合+keyup#every:-Enter
让“回车新增”无需手写键值判断。
C. 列表渲染与删除
- React:
tags.map(...)
渲染项,onClick={() => removeTags(index)}
删除。不可变更新(filter
)便于可预测渲染、状态回溯。 - dagger:
*each="tags"
迭代;+click="removeTags(index, tags)"
直接调用,常配合可变更新(splice
)写法简洁。
D. 对外通信
- React:
props.selectedTags([...])
将内部变更上报给父组件或外层应用(常见于表单控件封装)。 - dagger:示例未展示;可通过自定义事件(
dispatchEvent
)或外层监听指令来完成数据冒泡。
E. 可访问性(A11y)
- 两边删除控件均为
span
,建议替换为:
并配合键盘支持(Enter/Space)和更明确的焦点样式。<button type="button" class="tag-close-icon" aria-label="Remove tag">x</button>
五、改进建议(两边通用)
- 输入规则:去重、最大标签数、禁止全空白;支持粘贴多标签(按逗号/空格分割)。
- 错误反馈:同名标签时给出可视化提示(如 shake 动画/辅助文本)。
- 测试:React 侧可配合 Testing Library;dagger 侧可通过 DOM 测试/端到端测试保障交互。
- 样式与主题:抽离 tokens/vars,支持暗色模式与尺寸变体。
六、选型参考
- 需要工程化、强生态、多人协作 —— 倾向 React:组件化边界清晰、工具链与第三方库成熟。
- 追求零构建、轻量嵌入、快速上线 —— 倾向 dagger:指令式绑定 + 原生 DOM,代码量更小,上手极快。
本文内容就到这里,后续文章将为大家带来更多案例和讲解。
如果对dagger.js感兴趣的话,请您点赞收藏、分享本系列文章,也欢迎留言或者私信作者提出问题和建议,您的关注是对我最大的支持和鼓励。感谢您的阅读,祝工作学习顺利!