社交媒体 照片由 Andre Taissin 在 Unsplash 上拍摄
增强任何 DOM 内置元素的最简单方法:
无需填充,所有现代浏览器都可以工作™️
可以毫无问题地扩展HTML 、 SVG或任何其他自定义命名空间,例如MathML
元素可以从头开始创建,也可以根据需要升级以实现优雅的水合作用
适合313 字节(核心)或774 字节,包含自定义元素生命周期回调(没有什么新东西需要学习)
这个示例可以进行现场测试,它只是触及了这个极其微小但功能强大的模块的皮毛。
// const createRegistry = require('nonchalance');import createRegistry from 'nonchalance/core';// 对于所有没有原生 DOM 的环境,默认导出可选择接受类似 `globalThis` 的上下文。/ / 这样的注册表可以公开限定的类名称以及将用于创建元素的“文档”引用。 const {HTML} = createRegistry(); // 扩展任何元素以创建或升级自定义元素 // 从与全局上下文类密码不共享任何内容的注册表扩展了 HTML.Input { // 将继承静态标签 field = "input" 构造函数(...args) {super(...args);this.type = '密码'; } // 避免恶意脚本轻松检索任何密码 获取值(){返回'********'; } // 演示案例:设置密码时仍然使用原生 `value` 设置值(秘密){super.value =秘密; }}document.body.innerHTML = ` <form> <input type="text" name="user" placeholder="user"> <input type="password" name="password" placeholder="password"> <input type="submit"> </form>`;// 通过 new ClassTypenew Password(document.querySelector('[type="password"]')) 升级所需的元素;// 或创建一个新实例 const Secret = Object.分配(新密码,{ 名称:“通过”, 值:Math.random(), placeholder: '运行时密码'});const form = document.querySelector('form');form.insertBefore(secret, form.lastElementChild);
使用自定义功能模块,可以升级任何类型的元素,而不会遇到非法构造函数错误,当自然class extends HTMLSomethingElement {}
意图时,该错误会在任何时候出现,当此类未在全局范围内定义为条目时customElements
注册表。
不仅没有通过该模块在全局上下文中全局共享任何内容,而且完全不需要让任何内置扩展工作的每一项尴尬的额外工作:
新的或传递的元素总是保留其原型根链
不会发生额外的属性或冲突的名称
最重要的是,因为可以为每个模块或项目创建任何HTML 注册表以在其组件之间共享,所以还可以将任何虚假或模拟的globalThis
环境传递给此类注册表创建,至少有一个公开createElementNS(namespace, tagName)
的document
字段createElementNS(namespace, tagName)
方法,以及项目要测试的一个或多个类,例如HTMLElement
和/或此类项目成功所需的任何其他类。
然而,由于该模块的主要目标是DOM ,因此globalThis
引用被用作合理的默认值,但这仍然并不意味着通过默认导出创建的注册表共享任何内容。
不。 custom-function
工作方式可以总结如下:
# a native <p> protoype chain HTMLParagraphElement -> HTMLElement -> Element -> Node # a <p> passed to new (class CustomP extends HTML.P {}) CustomP -> HTMLParagraphElement -> HTMLElement -> Element -> Node # a <p> passed to class AnotherP extends CustomP {} AnotherP -> CustomP -> HTMLParagraphElement -> HTMLElement -> Element -> Node
简而言之,通过new AnotherP
创建元素或通过new AnotherP(liveParagraph)
升级元素只需更新原型链,而不需要元素离开 DOM 或更改其本机性质,因为它保留在原型继承链中。
摘要:无关紧要的注册表只是升级元素而不改变其性质,这与本机内置扩展底层工作的方式完全相同。
是的,通过/jsx
导出或通过/ref
导出。
关于 /jsx请参阅/jsx 导出是什么?部分。
关于 /ref请参阅/ref 导出是什么?部分。
/ce
导出以与类的connectedCallback
、 disconnectedCallback
和attributeChangedCallback
方法及其静态observedAttributes
字段兼容的方式自动升级元素。
该模块使用已经运行良好的自定义元素模块的微调版本。
请参阅 codepen 上的现场演示,了解其工作原理。
不。打个比方来说, HTML元素既具有语义含义,又具有明确定义的、期望的实用性,就像JS函数将永远是JS函数一样,即使Object.setPrototypeOf(() => {}, Number.prototype)
发生了......你能看到,或者同意,这有多么错误?
该模块不想(也可能无法)防止其功能的误用,因此请确保每当元素升级时,它都会在幕后保留其本机原型链,否则您将独自对抗DOM 。 ..如果你问我的话,这很不方便?
简而言之,同样的方式customElements.define('my-link', class extends HTMLDivElement {}, {extends: 'a'})
没有意义,这个模块相信它的用户无意义的类将有望被避免。
目前,该模块的默认 / main 导出指向完全相同的/core
导出。
因为这个模块以其简单性和汽件代码大小打开了潘多拉魔盒,而且主要是因为它仍然落后于0.
版本,所以我正在尝试考虑索引中应该包含哪些内容,这里是我的一些想法:
如果有一个基于 ESX 的模块能够理解以这种方式定义的组件,这不是很酷吗?
拥有一个通过该模块创建组件的JSX pragma 函数不是很酷吗?
拥有...(这里是您的占位符)...不是很酷吗?
是的,这会很酷,如果我能决定如何命名默认导出,我很乐意将该名称与其他优点一起作为该模块的默认条目...请继续关注,或者请给我关于如何做到这一点的想法和提示
但在那之前,请使用显式导出来确保将来的更新不会扰乱您的逻辑,如果最近的更改给您带来了麻烦,我深表歉意,但我很确定您可以轻松关联或理解这是永远的!
当元素远距离升级时,这些元素可能附加了一些没有机会通过其访问器的属性。
这个帮助器只是确保继承的属性作为自己的元素键被删除,然后立即作为访问器触发。
import createRegistry from 'nonchalance/ce';导入访问器 from 'nonchalance/accessors';const {HTML} = createRegistry();class WithAccessors extends HTML.Div { 构造函数(...args){访问器(super(...args)); } 获取值() {console.log('获取值', this._value);返回this._value; } 设置值(_value) {this._value = _value;console.log('设置值', this._value); }}// 原生div元素const div = document.createElement('div');div.value = 123;//升级新WithAccessors(div);//重新检查console.log(div.value);
请观看现场直播以进行更多测试。
/builtin
导出(248 字节)与/core
完全相同,只是它不在幕后使用custom-function
,这意味着:
不可能使用new BuiltIn()
或new BuiltIn(element)
因为这会引发错误,除非尚未注册为customElementbuiltinextend
它可用于自动化组件注册,如 CodePen 上的现场演示所示
关于此导出的唯一主要警告是,因为它基于真正的标准自定义元素,所以 Safari 或 WebKit 可能需要内置的 polyfill,例如:
<!-- 仅适用于 Safari 的最顶层页面脚本 --><script>self.chrome ||self.netscape ||document.write('<script src="//unpkg.com/@webreflection/custom-elements -builtin"><x2fscript>');</script>
请注意,虽然默认情况下允许HTML
和SVG
命名空间作为内置扩展,但自定义元素不接受SVG扩展,因此当前/builtin
导出实际上只能进行HTML扩展。
./dummy
导出主要用于SSR ,提供完全相同的实用程序来扩展仅携带静态tag
字段的类。
结合/tag
可以在远处进行 100% SSR 和水合物。
从'https://unpkg.com/nonchalance/dummy'导入createRegistry;从'https://unpkg.com/nonchalance/tag'导入createTag;const {HTML} = createRegistry();class HelloDiv扩展HTML.Div { connectCallback() {console.log('我在这里'); }}// 创建一个可重用的命名空间 const nmsp = {HelloDiv};// 创建一个标签 Transformerconst tag = createTag(nmsp);// 想象一个服务器响应 // 注意:此代码仅用于演示console.log(标签`<!doctype html><script type="module">从 'https://unpkg.com/nonchalance/ce' 导入 createRegistry;const {HTML} = createRegistry();const nmsp = {};for (const el of document.querySelectorAll('[data-comp]')) { const {comp} = el.dataset; 删除 new nmsp[comp]; (el);}</script><HelloDiv></HelloDiv>` 。加入('') 。修剪() .replace('const nmsp = {};',`const nmsp = { ${[...Object.entries(nmsp)].map( ([key, value]) => `${key}: ${值}` ).join(',n')} };` ));
/jsx
导出(976 字节)接受额外的createElement
选项并返回一个jsx
函数,该函数可用作@jsx pragma进行转换,以及React或Preact中默认已工作的所有其他内容,以及通过HTML或SVG注册表扩展的类,包括/ce
为这些类带来的所有功能:自定义元素,例如生命周期,上面有一些香料:
类将在其构造函数中接收沿元素传递的props ,启用信号、其他功能或处理默认JSX组件可能处理的任何内容。
当调用构造函数时,该元素将已经填充其子元素,从而避免在解析文档之前定义/注册类时标准自定义元素可能出现的恶作剧。
与/builtin
extends 类似, new Component(props)
是不可能的,但<Component {...props} />
总是可以的。
在 CodePen 上查看它在React实践中的实际应用。
DOM就是DOM ,无论中间有多少间接。根据框架功能的不同,您的 DX 可能会有所不同,但如果React是您所追求的,那么有一种微小而优雅且基于ref
的方法可以使用nonchalance/core或nonchalance/ce来提升常规 JSX 节点:
import referenced from 'nonchalance/ref';// 指示组件将作为引用传递 // 注意:这只是一个授予类完整性的轻量代理 // 无论其在 Wild 中的用法如何 const Component = referenced(class extends HTML.分区{ 构造函数(...args) {super(...args);this.addEventListener('click', console.log); }});ReactDOM.render( <div ref={Component}>点击我</div>, 文档.正文);
ref
实用程序还可以用作装饰器,并且不会影响常规nonchalance类的任何功能。另外,每个元素仅升级一次,因此可以安全地在构造函数中添加侦听器或逻辑。
请在 codepen 上观看此演示并进行操作。
/selector
导出(796 字节)允许实时定义任何 CSS 选择器,以便相关类将自动升级与该选择器匹配的任何元素。
请注意,此类选择器应尽可能唯一,否则可能会发生意外。使用特定的类或data-
属性来最好地描述您的选择器。
import createRegistry from 'nonchalance/core';import { Define } from 'nonchalance/selector';const { HTML } = createRegistry();const Special = Define('[data-comp="special"]', class extends HTML.分区{ constructor(...args) {super(...args);this.textContent = '我很特别!'; }});// 将包含“我很特别!”文本一次 livedocument.body.innerHTML = `<div data-comp="special"></div>`;
./tag
导出(188 字节)允许以水合友好的方式进行模板转换。
它可以用作功能齐全的模板文字标签背后的中间值,并且一旦这些元素登陆DOM就会发生水合作用。
从 'nonchalance/ce' 导入 createRegistry;从 'nonchalance/tag' 导入 createTag;const {HTML} = createRegistry();class HelloDiv 扩展 HTML.Div { connectCallback() {console.log('我在这里'); }}// 创建一个可重用的命名空间 const nmsp = {HelloDiv};// 创建一个标签 Transformerconst tag = createTag(nmsp);// 快速而肮脏的 demodocument.body.innerHTML = tag`<HelloDiv />`.join( '');// 水合示例for (const el of document.querySelectorAll('[data-comp]')) { const {comp} = el.dataset; 删除 el.dataset.comp; // 升级元素一次 新 nmsp[comp](el);}
在 CodePen 上观看直播。
是的。默认情况下, /core
和/ce
导出都可以创建HTML和SVG注册表:
import createRegistry from 'nonchalance/core';const {HTML, SVG} = createRegistry();class Circle 扩展 SVG.Circle { 构造函数(options) {Object .assign(super(), options) .setAttribute('fill', 'gold'); } 设置 cx(value) { this.setAttribute('cx', value) } 设置 cy(value) { this.setAttribute('cy', value) } 设置 r(value) { this.setAttribute('r', value) }}document.querySelector('svg').append( 新圆({cx: 100, cy: 100, r: 50}));
在 codepen 上观看直播。
还可以将任何命名空间传递给createRegistry(options)
,以{MathML: "http://www.w3.org/1998/Math/MathML"}
为例。
任何对document.createElementNS
有意义的命名空间都是允许的,我们可以升级的DOM元素类型没有限制。
我之前对此有过很长的回答,但总结是该模块使用W3C 、 WHATWG或ECMAScript提供的标准,并且它需要不到 1KB 的空间即可在任何地方工作。
这不是polyfill,它是一个实用程序,可以帮助您在 JS 世界中编写组件,并且不用担心这些组件会发生冲突,需要工具,或者不能跨您喜欢/需要/喜欢的任何目标项目移植。
简而言之,如果您可以添加少于 1K 字节来为前端和后端世界提供通用组件,那么您就选择了正确的模块?
没有什么比成为一个粗心的孩子,在泥地里与所有“不要那样做! ”的思想家玩耍更自由的了。
这个模块在某种程度上代表了现代 JS 功能提供的自由的感觉,展示了一种优雅、可移植和超轻量级的替代方案,以替代浏览器供应商和现代规范所提供的不断增加的复杂性,所有这些都迫使开发人员解决简单扩展的能力内置并保留 Web 闻名的简单性和良好的可访问性。