“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><inputtype="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感兴趣的话,请您点赞收藏、分享本系列文章,也欢迎留言或者私信作者提出问题和建议,您的关注是对我最大的支持和鼓励。感谢您的阅读,祝工作学习顺利!