ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

GrapesJS 组件脚本(Components JS)完全指南:为组件绑定 JavaScript 与外部依赖

GrapesJS 组件脚本(Components  JS)完全指南:为组件绑定 JavaScript 与外部依赖 GrapesJS 组件脚本Components JS完全指南为组件绑定 JavaScript 与外部依赖【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs本指南围绕 GrapesJS 的「组件脚本」能力展开讲解如何为自定义组件挂载交互脚本、如何在导出的 HTML 中正确生成可独立运行的 JS 代码以及如何处理组件对第三方库计数器、轮播、画廊等的依赖。读完本文你将掌握script/script-props等核心配置的完整用法、editor.getHtml()导出行为与两种依赖注入方案的取舍能够开发出既能在画布内实时预览、又能无缝嵌入最终模板的带 JS 组件。::: warning 版本前提 本指南内容针对GrapesJS v0.16.34 及以上版本。阅读前建议先掌握 Components 模块 与 Traits 模块 的基础概念。 :::基础脚本给组件附加 JavaScript最简单的场景是给组件挂一段脚本让它在组件被渲染拖入画布或出现在导出的 HTML 中时自动执行。// 自定义脚本注意避免使用箭头函数 const script function () { alert(Hi); // this 被绑定到组件对应的 DOM 元素 console.log(the element, this); }; // 注册一个新的自定义组件类型 editor.Components.addType(comp-with-js, { model: { defaults: { script, // 加一点样式让组件在画布中可见 style: { width: 100px, height: 100px, background: red, }, }, }, }); // 为组件创建块Block便于直接拖拽使用 editor.Blocks.add(test-block, { label: Test block, attributes: { class: fa fa-text }, content: { type: comp-with-js }, });把新建的块拖入画布后你会立刻看到弹窗与控制台输出。值得注意的关键点是this上下文被绑定到组件元素本身因此如果你需要在脚本中操作组件元素可以直接写this.innerHTML inner content。在 Component 类型定义 中可以看到script属性同时支持函数与字符串两种形态默认值为。导出 HTML 时脚本如何生成一个容易忽略的事实是脚本并非内联写在组件元素上而是由编辑器集中收集、统一输出。当你在编辑器中导出代码Export 按钮或editor.getHtml()时comp-with-js组件会生成类似下面的结构div idc764/div script var items document.querySelectorAll(#c764); for (var i 0, len items.length; i len; i) { (function () { // START component code alert(Hi); console.log(the element, this); // END component code }).bind(items[i])(); } /script可以看到编辑器给所有带脚本的组件自动附加了唯一 ID如c764并通过querySelectorAll统一检索执行。再拖入一个test-block时ID 会被合并进选择器脚本只生成一份div idc764/div div idc765/div script var items document.querySelectorAll(#c764, #c765); for (var i 0, len items.length; i len; i) { (function () { // START component code alert(Hi); console.log(the element, this); // END component code }).bind(items[i])(); } /script这一「同类组件合并脚本、按 ID 逐个执行」的生成策略在源码中由 JsGenerator 负责mapModel会为带脚本的组件记录其 IDJsGenerator.ts#L26-L32并将 ID 写回组件 attributesbuild则把同类型组件的 ID 用querySelectorAll(# ids)聚合成一段脚本JsGenerator.ts#L89-L97。对应地HtmlGenerator 在cleanId: true时会清理无用的自动 ID但只要组件带script或script-export其 ID 就必须保留否则脚本将失去查找目标——这一点在 CodeModels 测试 中有直接验证。重要警告脚本运行在隔离的 iframe 中::: danger 请仔细阅读本节 :::所有组件脚本都在画布 iframe 内执行与最终模板的运行环境一致处于隔离状态因此它们不属于编辑器当前页面的document。编辑器页面中加载的外部库例如随编辑器一起引入的 jQuery 等在组件脚本里并不存在——后续会介绍如何处理依赖。这意味着你不能使用函数作用域之外的任何东西。看下面的反例const myVar John; const script function () { alert(Hi myVar); console.log(the element, this); };这段代码不会生效运行时将报myVar未定义的错误。导出的 HTML 能更清楚地说明原因div idc764/div script var items document.querySelectorAll(#c764); for (var i 0, len items.length; i len; i) { (function () { alert(Hi myVar); // - ERROR: undefined myVar console.log(the element, this); }).bind(items[i])(); } /script原因一目了然script函数的源码被原样序列化进一段独立script中执行闭包外部的myVar根本不在执行上下文里。凡是脚本要引用的数据都必须显式地通过参数或属性传入。向脚本传递属性script-props当脚本需要依据组件属性产生不同行为时——例如你想根据某个属性甚至通过 Traits 面板动态修改用不同参数初始化一个第三方库——可以使用script-props属性。// props 参数只包含你在 script-props 中声明的属性 const script function (props) { const myLibOpts { prop1: props.myprop1, prop2: props.myprop2, }; alert(My lib options: JSON.stringify(myLibOpts)); }; editor.Components.addType(comp-with-js, { model: { defaults: { script, // 为自定义属性定义默认值 myprop1: value1, myprop2: 10, // 定义 traits用于在面板中修改属性 traits: [ { type: select, name: myprop1, changeProp: true, options: [ { value: value1, name: Value 1 }, { value: value2, name: Value 2 }, ], }, { type: number, name: myprop2, changeProp: true, }, ], // 声明需要传给脚本的属性 // 这些属性一旦变化脚本会被重置并重新执行 script-props: [myprop1, myprop2], // ... }, }, });此时在 Traits 面板中修改myprop1或myprop2脚本会带着更新后的属性值重新触发。script-props 的底层机制从源码看script-props的生效链路非常清晰在 Component 默认属性 中script-props与script均为空字符串默认值类型定义在 types.ts#L263 中声明为string[]。initScriptProps()会监听script-props的变更并对其列出的每个属性挂上change:xxx监听任一属性变化都会触发rerender事件从而重渲染组件并重新执行脚本Component.ts#L1095-L1113。__getScriptProps()负责按script-props声明的键名从组件属性中筛选出实际传给脚本的对象Component.ts#L1801-L1808。导出时JsGenerator 会把每个组件的props序列化为 JSON 对象通过(fn.bind(el))(props[el.id])将属性对象作为首个参数传给脚本JsGenerator.ts#L76-L88生成的最终代码形如var props {c764:{myprop1:value1,myprop2:10},c765:{myprop1:value2,myprop2:20}}; var ids Object.keys(props).map(function(id) { return #id }).join(,); var els document.querySelectorAll(ids); for (var i 0, len els.length; i len; i) { var el els[i]; (function (props) { /* ... */ }.bind(el))(props[el.id]); }画布内的实时执行路径则由 CanvasView.updateScript 完成它把脚本塞进 iframe 的 JS 容器并通过script-props是否存在来决定是直接使用函数体还是包一层function(){...}包装CanvasView.ts#L612。提示在旧版本中曾用tagVarStart/tagVarEnd默认{[ 与 ]}在脚本字符串中做变量替换这套机制已被标记为deprecated官方明确建议改用script-props见 编辑器配置。依赖处理组件如何加载外部库正如上文所述组件脚本在画布 iframe 中独立执行、没有任何外部依赖——这与编辑器最终导出的 HTML 完全一致。如果组件需要第三方库有两种方案组件级依赖与模板级依赖。组件级依赖推荐组件级方案是更优的选择依赖会在组件存在时动态加载并只在组件存在于画布时才被打印进最终 HTML。做法是在初始化脚本执行前先加载依赖const script function (props) { const initLib function () { const el this; const myLibOpts { prop1: props.myprop1, prop2: props.myprop2, }; someExtLib(el, myLibOpts); }; if (typeof someExtLib undefined) { const script document.createElement(script); script.onload initLib; script.src https://.../somelib.min.js; document.body.appendChild(script); } else { initLib(); } };这段脚本的容错逻辑是若someExtLib尚未加载首次渲染则动态插入script并在onload后再初始化若已加载例如第二次渲染则直接初始化避免重复加载外部库。模板级依赖当某个依赖被所有组件共享例如 jQuery时与其在每个脚本里重复加载不如把依赖直接注入画布 iframeconst editor grapesjs.init({ // ... canvas: { // 注入 iframe head 的外部脚本 scripts: [https://.../somelib.min.js], // 外部样式同理 styles: [https://.../ext-style.min.css], } });在 Canvas 配置 中可以看到scripts与styles均支持字符串 URL 或带属性的对象形式scripts: [ https://...1.js, { src: /file.js, someattr: value } ], styles: [ https://...1.css, { href: /style.css, someattr: value } ],请务必注意编辑器不会把canvas.scripts/canvas.styles渲染进导出的 HTML例如editor.getHtml()的结果所以最终页面即渲染导出 HTML 的那个页面如何引入这些依赖需要你自行处理。这类依赖注入只服务于画布内的实时预览与交互。总结与实践建议场景推荐方案关键点组件需要一段独立交互逻辑script属性函数体内的this即组件元素不要引用外部作用域变量脚本需要依据组件属性变化script-propschangeProptraits声明的属性变化会触发脚本重新执行单个组件需要第三方库组件级动态加载依赖按需加载仅在组件存在时输出所有组件共享的依赖canvas.scripts/canvas.styles仅注入画布 iframe不进入导出 HTML核心要点回顾脚本是函数体序列化而非闭包捕获组件脚本在导出/执行时被转成字符串闭包外的变量一律不可用所有数据必须经script-props显式传入ID 是脚本与元素的桥梁带脚本的组件会保留自动生成的 ID可参考 HtmlGenerator 与相关测试导出代码时不要手动移除这些 ID依赖注入有明确边界组件级依赖与模板级依赖Canvas 配置各司其职最终页面仍需自行引入模板级资源Traits 联动配合changeProp: true的 traits可以让用户在编辑界面直接改属性、实时看到脚本行为变化这也是构建可配置组件的常见套路。更多相关能力可继续阅读 Components 模块、Traits 模块 与 组件模型源码深入理解组件模型的完整属性体系。【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表