)
低代码前端后端【免费下载链接】plasmicVisual builder for React. Build apps, websites, and content. Integrate with your codebase.项目地址https://gitcode.com/gh_mirrors/pl/plasmic点击查看免费下载本文基于仓库中 examples/react-dnd 示例工程讲解如何在 Plasmic 低代码平台中注册基于 react-dnd 的代码组件Code Components让拖拽排序Drag-and-Drop能力以可视化组件的形式出现在 Plasmic Studio 中供设计师和非开发团队成员直接拖拽使用。读完本文你将掌握示例工程的目录结构与运行方式、DndProvider的全局接入方式、useDrag/useDrop的经典排序实现、DataProvider/repeatedElement/useSelector的 Plasmic 数据联动模式以及如何通过plasmic-init.ts将自定义组件注册进 Plasmic Loader。示例概览把拖拽交互封装成 Plasmic 代码组件examples/react-dnd是一个由create-plasmic-app引导生成的 Next.js 项目见 package.json它的核心目标与 README 开头一致演示如何注册使用了 react-dnd 的代码组件为你的应用提供拖拽交互能力。也就是把可排序卡片列表这类交互完全封装进一个 Plasmic 组件里之后在 Plasmic Studio 中只通过属性面板即可配置初始数据与展示字段无需再写拖拽逻辑。整个示例由三个层次的代码协作完成拖拽引擎层react-dndreact-dnd-html5-backend提供拖拽状态机与 HTML5 拖拽事件适配组件逻辑层Card可拖拽卡片与Container卡片容器等 React 组件实现排序状态管理与 Plasmic 数据上下文注册层plasmic-init.ts 中通过PLASMIC.registerComponent(...)把上述组件注册为 Plasmic 可识别的代码组件。快速开始启动开发服务器并接入 Plasmic Studio按 README 的指引安装依赖后运行yarn dev打开浏览器访问http://localhost:3000即可看到渲染结果。项目采用 Plasmic Loader 的托管渲染模式页面内容由 pages/[[...catchall]].tsx 的 catch-all 路由动态接管通过PLASMIC.maybeFetchComponentData(plasmicPath)拉取 Plasmic 项目中对应路径的组件数据并渲染该页面还通过extractPlasmicQueryData预取查询数据并使用revalidate: 60开启增量静态再生成让新发布的内容无需完整重建即可上线。在 Plasmic Studio 中编辑项目时页面会随编辑自动热更新README 中 The page auto-updates as you edit the project 所述。要做到这一点需要先在 Plasmic 项目中把 App Host 指向本机地址启动开发服务器后访问http://localhost:3000/plasmic-host在 Plasmic Studio 的项目设置中将宿主 URL 配置为上述地址之后 Studio 中的画布会通过 pages/plasmic-host.tsx 加载代码组件的实时预览。plasmic-host.tsx的核心实现非常简短加载preamble.js脚本strategybeforeInteractive然后渲染PlasmicCanvasHost /它负责在 Studio 画布中嵌入真实渲染的 React 组件树。项目结构一图看懂示例的代码布局examples/react-dnd/ ├── components/ │ ├── Card.tsx # 可拖拽卡片useDrag useDrop 组合 │ ├── Container.tsx # 卡片容器AtomicContainer / CustomizableContainer / Field │ ├── ItemTypes.tsx # 拖拽类型常量CARD: card │ └── SimpleCard.tsx # 仅可拖拽、不可排序的极简示例 ├── pages/ │ ├── [[...catchall]].tsx # 接管所有 Plasmic 页面路径的渲染 │ ├── _app.tsx # 全局注入 DndProvider │ ├── plasmic-host.tsx # Studio 画布宿主页面 │ └── api/hello.ts ├── plasmic-init.ts # Plasmic Loader 初始化与代码组件注册 ├── package.json └── next.config.js / tsconfig.json / yarn.lock组件层是本文的主角接下来逐一展开。全局接入在_app.tsx注入DndProviderreact-dnd 要求所有使用useDrag/useDrop的组件必须位于DndProvider内部。示例在 pages/_app.tsx 中全局完成这件事import type { AppProps } from next/app; import { DndProvider } from react-dnd; import { HTML5Backend } from react-dnd-html5-backend; function MyApp({ Component, pageProps }: AppProps) { return ( DndProvider backend{HTML5Backend} Component {...pageProps} / /DndProvider ); } export default MyApp;关键点backend 选择桌面浏览器场景使用HTML5Backend基于 HTML5 Drag and Drop API移动端触屏场景通常需要换成TouchBackend来自react-dnd-touch-backend示例只演示了 HTML5 后端全局包裹的原因DndProvider通过 React Context 向子树提供拖拽管理器DragDropManager而注册进 Plasmic 的代码组件最终也会渲染在这棵子树中因此全局包裹可保证任何页面上的拖拽组件都能正常工作依赖版本见 package.jsonreact-dnd与react-dnd-html5-backend均为^16.0.1React 为18.2.0Next.js 为16.2.1。注册层plasmic-init.ts中的组件注册plasmic-init.ts 首先初始化 Plasmic Loaderexport const PLASMIC initPlasmicLoader({ projects: [ { id: 48urnctFmaYjeHBp3o9yY1, token: JYhhxVfCOcX2e1oSAeDOMMJJuIcWeV59RCRFQwew77lt3qnhL4YMGvaO67cBTr84mAwH81d3162gnybtxdw, }, ], // 默认使用最后一次发布published的版本 // 开发期可设 preview: true 使用未发布草稿更慢仅建议开发使用。 preview: false, });随后注册三个代码组件它们构成了拖拽示例在 Studio 中可用的完整组件面1.AtomicContainer——开箱即用的排序列表PLASMIC.registerComponent(AtomicContainer, { name: AtomicContainer, props: { defaultData: { type: object, defaultValue: DEFAULT_DATA, }, }, });只暴露一个defaultData对象属性默认值为 Container.tsx 中导出的DEFAULT_DATA一组 id/text 卡片数据其中特意包含一条更长的文本以验证拖拽高度差场景。在 Studio 中编辑者可以修改这份初始数据拖拽排序逻辑完全内置。2.CustomizableContainer——插槽slot定制化列表PLASMIC.registerComponent(CustomizableContainer, { name: CustomizableContainer, providesData: true, props: { defaultData: { type: object, defaultValue: DEFAULT_DATA }, children: { type: slot, defaultValue: { type: vbox, children: [{ type: component, name: Field }], }, }, }, });providesData: true声明该组件会向子树提供数据上下文children声明为slot默认插槽内容是一个Field组件。这使得设计者可以在不写代码的情况下自由组合每张卡片内部长什么样——这正是可视化定制的关键设计。3.Field——读取卡片数据的展示字段PLASMIC.registerComponent(Field, { name: Field, props: { field: { type: string, defaultValue: text }, }, });Field通过useSelector(card)从数据上下文读取当前卡片对象再按field属性取值渲染默认渲染text字段。核心实现Card.tsx中的拖拽排序逻辑Card.tsx 是拖拽交互的核心它把useDrag与useDrop组合到同一个 div 上类型定义export interface CardProps { id: any; index: number; moveCard: (dragIndex: number, hoverIndex: number) void; className?: string; children?: ReactNode; } interface DragItem { index: number; id: string; type: string; }Drop 端hover 半程判定const [{ handlerId }, drop] useDropDragItem, void, { handlerId: Identifier | null }({ accept: ItemTypes.CARD, collect(monitor) { return { handlerId: monitor.getHandlerId() }; }, hover(item: DragItem, monitor) { if (!ref.current) return; const dragIndex item.index; const hoverIndex index; if (dragIndex hoverIndex) return; // 自己替换自己忽略 const hoverBoundingRect ref.current.getBoundingClientRect(); const hoverMiddleY (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; const clientOffset monitor.getClientOffset(); const hoverClientY (clientOffset as XYCoord).y - hoverBoundingRect.top; // 向下拖只有越过目标卡片垂直中点才移动 if (dragIndex hoverIndex hoverClientY hoverMiddleY) return; // 向上拖只有越过目标卡片垂直中点才移动 if (dragIndex hoverIndex hoverClientY hoverMiddleY) return; moveCard(dragIndex, hoverIndex); // 注意这里直接修改了 monitor item 的 index。 // 一般应避免 mutation但此处为了性能、避免昂贵的索引查找而刻意使用。 item.index hoverIndex; }, });这段经典逻辑的要点从源码注释可以直接读到用getBoundingClientRect()计算目标卡片的垂直中点hoverMiddleY再结合monitor.getClientOffset()得到鼠标相对卡片顶部的偏移hoverClientY只有鼠标越过 50% 高度线时才真正触发moveCard从而避免在卡片边缘反复抖动item.index hoverIndex的原地修改则省去了每次 hover 都重新查找拖拽项索引的开销。Drag 端拖拽状态收集const [{ isDragging }, drag] useDrag({ type: ItemTypes.CARD, item: () ({ id, index }), collect: (monitor: any) ({ isDragging: monitor.isDragging() }), });type与 Drop 端的accept都使用ItemTypes.CARD见 ItemTypes.tsx值为字符串card两者必须一致才能匹配。渲染与透明度反馈const opacity isDragging ? 0 : 1; drag(drop(ref)); return ( div ref{ref} style{{ ...style, opacity }}>const [cards, setCards] useState(defaultData); const moveCard useCallback((dragIndex: number, hoverIndex: number) { setCards((prevCards: Item[]) update(prevCards, { $splice: [ [dragIndex, 1], [hoverIndex, 0, prevCards[dragIndex] as Item], ], }) ); }, []);这里使用immutability-helper的$splice命令实现先删除、再插入的原地移动语义依赖immutability-helper ^3.1.1见 package.json返回的是新数组符合 React 不可变更新要求。AtomicContainer的简单渲染const renderCard useCallback((card, index) ( Card key{card.id} index{index} id{card.id} moveCard{moveCard} {card.text} /Card ), []);每张卡片直接把card.text作为 children 传入结构固定。CustomizableContainer的插槽渲染const renderCard useCallback((card, index) ( DataProvider key{card.id} namecard data{card} Card index{index} id{card.id} moveCard{moveCard} {repeatedElement(index, children)} /Card /DataProvider ), []);这里有三个 Plasmic Loader 的核心 API 协同DataProvider以namecard向子树提供当前卡片数据注册时声明的providesData: true与之对应repeatedElement(index, children)把用户配置的插槽内容例如默认的Field组件按当前索引展开到每张卡片中——每张卡片渲染同一份插槽设计但数据不同Field内部再通过useSelector(card)取回数据export function Field({ className, field }: { className?: string; field?: string }) { const data useSelector(card); return div className{className}{data[field ?? id]}/div; }配合注册时field的默认值text用户在 Studio 中把Field的field属性改为id即可让卡片显示编号改回text即显示文本——完全可视化、零代码。极简变体SimpleCard.tsxSimpleCard.tsx 展示了只做可拖动无排序的最小写法适合作为理解useDrag的入门样例export default function Card({ isDragging, text }: any) { const [{ opacity }, dragRef] useDrag( () ({ type: card, item: { text }, collect: (monitor) ({ opacity: monitor.isDragging() ? 0.5 : 1, }), }), [] ); return div ref{dragRef} style{{ opacity }}{text}/div; }相比Card.tsx它只使用useDrag暴露dragRef通过collect把拖拽状态映射为透明度无useDrop因而不能成为放置目标其中type: card与ItemTypes.CARD的值一致说明示例内拖拽类型是统一约定为card的。这也提示若要实现跨容器或跨类型拖拽例如卡片拖入回收区只需新增ItemTypes枚举值并在对应accept中声明即可。运行环境与依赖一览运行示例所需的依赖与脚本如下来自 package.json依赖版本用途plasmicapp/loader-nextjs^1.0.158Plasmic Loader注册组件并渲染页面react-dnd^16.0.1拖拽核心库useDrag/useDropreact-dnd-html5-backend^16.0.1HTML5 拖拽后端immutability-helper^3.1.1不可变地执行数组 splice 排序next/react/react-dom16.2.1/18.2.0Next.js 应用框架与 React 运行时可用脚本yarn dev开发、yarn build构建、yarn start生产启动、yarn lint代码检查。总结让拖拽交互进入团队协作流程README 在结尾点明了这个示例的业务价值借助 Plasmic团队中的非开发者可以自行发布页面与内容到网站或应用中。react-dnd示例正是这一理念在交互层面的延伸——把通常需要专门前端开发的拖拽排序能力封装成AtomicContainer/CustomizableContainer/Field三个可视化组件一个开箱即用、一个可自由定制卡片内部结构、一个负责展示字段配合DndProvider的全局接入和plasmic-init.ts的属性注册非开发者在 Studio 中即可完成数据配置与版式调整。若想进一步深入可结合仓库内 docs 目录下的贡献与平台文档、packages/loader-nextjs 中 Loader 的 API 定义以及 packages/react-web 中DataProvider/repeatedElement/useSelector等运行时 API 的实现理解代码组件注册与数据上下文的底层机制官方文档与社区论坛也提供了代码组件、App Hosting 等主题的详细说明。赞分享低代码前端后端【免费下载链接】plasmicVisual builder for React. Build apps, websites, and content. Integrate with your codebase.项目地址https://gitcode.com/gh_mirrors/pl/plasmic点击查看免费下载相关推荐3步轻松解锁Windows远程桌面多用户连接RDPWrap实用配置指南3步轻松解锁Windows远程桌面多用户连接RDPWrap实用配置指南 Windows远程桌面服务RDP的多用户连接限制让许多团队协作场景受阻而RDPW 告别卡顿React拖拽组件终极对决React DnD vs React Beautiful DnD 告别卡顿React拖拽组件终极对决React DnD vs React Beautiful DnD 你是否还在为React项目中的拖拽功能头疼明明添文档知识库终极Plasmic代码组件集成指南如何在可视化构建中拖拽自定义React组件终极Plasmic代码组件集成指南如何在可视化构建中拖拽自定义React组件 Plasmic是一个强大的React可视化构建工具它允许开发者和设计师通过拖拽低代码前端后端上一篇AWS EC2 Security Groups 实战演练用控制台与 AWS CLI 验证入站规则对 Web 应用可达性的影响下一篇Sphinx 远程 Logo 与 Favicon 配置实战从 test-remote-logo 测试夹具理解 html_logo / html_favicon 的 URL 支持创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考