用纯前端写一个可拖拽任务看板:原生 DnD、localStorage 与发布上架全流程
一、为什么手写而不是上框架
需求很简单:三列看板、拖拽换列、按分类筛选、刷新不丢数据。为这点东西引入构建链和依赖,收益为负。所以直接用原生 HTML + CSS + 一份闭包里的 JS,双击 index.html 就能跑,部署时也只需要一个静态目录。
二、数据模型:一个数组 + 三个状态
整个应用的状态只有三样:任务数组、当前筛选分类、正在拖拽的任务 id。任务本身是 { id, title, desc, category, status, createdAt },status 取值 todo / doing / done。渲染函数每次全量重建三列的卡片列表——在这个数据量级下,全量重绘比做增量 diff 简单得多,也不会出现状态不同步。
三、原生拖拽:四个事件 + 一个兜底
HTML5 Drag and Drop 的核心就四个事件:卡片上 dragstart,列容器上 dragenter/dragover(必须 preventDefault(),否则不会触发 drop),最后 drop。我踩的坑是:如果只在 dragstart 里记住「正在拖谁」,一旦中途 dragend 提前触发,这个变量就被清空,drop 时拿不到 id。
解决办法是双保险——dragstart 时同时把 id 写进 dataTransfer,drop 里优先读状态变量、读不到再读 dataTransfer:
card.addEventListener("dragstart", (e) => {
draggingId = task.id;
e.dataTransfer.effectAllowed = "move";
e.dataTransfer.setData("text/plain", task.id);
});
zone.addEventListener("drop", (e) => {
e.preventDefault();
const id = draggingId || e.dataTransfer.getData("text/plain");
if (id) moveTask(id, status);
});
另外 dragover 里只加 class 做高亮,不要在 dragover 里改数据——它触发频率很高,改数据会导致重绘抖动。
四、localStorage 要当成可能失败的东西
隐私模式、存储配额满、被浏览器策略禁用,都可能让 setItem 直接抛异常。读写都包 try/catch,存不进去最多是刷新后丢数据,不会白屏:
function save() {
try { localStorage.setItem(KEY, JSON.stringify(state.tasks)); }
catch (e) { /* 存不下就算了,不影响使用 */ }
}
五、发布到作品展览馆时要注意的几点
把作品提交到华为云开发者作品展览馆,流程上踩到几个值得记的点:
- 封面按 16:9 准备,1280×720 最稳;比例不对会在门禁那一步被打回。
- 线上体验地址 envUrl 是可选的,默认空串也能发布。作品只有静态页面、暂时没有公网地址时不必卡在这。
- 代码仓库要能公开访问,并且分支名要填对;详情包(zip)里建议放一份 README + 架构图。
- 提交前先在本地起服务验证一遍(
python3 -m http.server),确认页面能开、资源 200,再走投稿。
六、小结
这类小工具的正确做法是「少即是多」:不打包、不引框架、状态只有一个数组,天然好调试;真正花时间的是拖拽的事件时序和发布时的材料准备,而不是功能本身。代码已经公开在 Code 仓,按需取用。
- 点赞
- 收藏
- 关注作者
评论(0)