如何实现外部HTML文件中原生Web Component关联JS的动态加载执行
可行性说明
你要的实时加载类Vue单文件的原生Web Component方案完全可行,不需要构建步骤即可实现。
关于eval的顾虑
你担心eval不安全/污染全局是对的,但我们可以用new Function替代,它的作用域完全隔离,不会污染全局上下文,只要你加载的组件代码是自身可控的,就不存在安全问题。
实现思路
你遇到的script未激活的状态刚好是我们需要的,不需要它全局执行,只要按以下步骤处理即可:
- 从模板节点中提取script标签内容,同时把script节点从模板中移除,避免意外执行
- 通过
new Function执行script内容,拿到你定义的methods等组件配置 - 把methods里的方法逐个挂载到自定义元素的原型上
- 额外处理模板内的行内事件(比如你写的
onclick="speak"),原生行内事件默认找全局方法,我们可以把这类属性转成绑定到组件实例方法的事件监听
代码修改示例
无需修改components.html,直接调整你的模板创建逻辑即可:
//(skipping the part where I fetch components.html) //(and inject them into the page) //(and then iterate over each template until...) templates.forEach(x=>{createTemplate(x.id, x.content)}) function createTemplate(elementName, templateContent) { // 提取模板中的script配置 const scriptEl = templateContent.querySelector('script'); let componentConfig = {methods: {}}; if (scriptEl) { const scriptContent = scriptEl.textContent.trim(); templateContent.removeChild(scriptEl); // 隔离执行script获取配置,不会污染全局 const getComponentConfig = new Function(` ${scriptContent} return { methods: methods ?? {} } `); componentConfig = getComponentConfig(); } const elemClass = class extends HTMLElement { constructor() { super(); const shadowRoot = this.attachShadow({mode: 'open'}) shadowRoot.appendChild(templateContent.cloneNode(true)); // 处理行内事件绑定,绑定到组件实例方法 shadowRoot.querySelectorAll('*').forEach(el => { Array.from(el.attributes).forEach(attr => { if (attr.name.startsWith('on')) { const eventName = attr.name.slice(2); const methodName = attr.value.trim(); el.removeAttribute(attr.name); if (typeof this[methodName] === 'function') { el.addEventListener(eventName, e => this[methodName](e)); } } }) }) } } // 把所有methods挂载到组件原型 Object.entries(componentConfig.methods).forEach(([methodName, methodFn]) => { elemClass.prototype[methodName] = methodFn; }) // 原有自定义原型方法保留 elemClass.prototype['test'] = ()=>{console.log("This is a test")} customElements.define(elementName, elemClass); }
扩展说明
如果你后续要加组件data、生命周期钩子、props之类的配置,都可以用同样的思路从模板的script里提取,挂载到组件实例或者原型上,完全可以实现接近Vue单文件的开发体验,不需要任何构建工具。
内容的提问来源于stack exchange,提问作者Moss
相关产品推荐
相关产品推荐

