# Exparser设计原理 本章主要包含以下内容: - WebComponent原理 - Custom Element原理 - ShadowDOM思想 - Exparser原理 ## 什么是WebComponent? `WebComponent` 汉语直译过来第一感觉是web组件的意思,但是它只是一套规则、一套API。你可以通过这些API创建自定义的新的组件,并且组件是可以重复使用的,封装好的组件可以在网页和Web应用程序中进行使用。 当前的前端开发环境,Vue、React等都基于组件化开发的形式,但是他们的组件生态并不互通,如果你有过两个框架的开发经验的话,你应该知道最烦恼的就是两个框架的UI组件表现不一致的问题。 我们抽离组件为了提高复用率,提升开发效率。但是脱离了像`Vue、React`这样的框架后,你会发现,原生JS难道就不能开发自定义组件吗?`WebComponent`就是为了解决这个问题。 换一个角度来说,并不是所有的业务场景都需要`Vue\React`这样的框架进行开发、也并是都需要工程化。很多业务场景我们需要原生JS、HTML。 言归正传,`WebComponent`实现的组件可以和HTML原生标签一起使用,有了这个概念之后,我们看一下它的具体表现形式是怎样的。 ```html ``` 上面我们看到``标签还是我们熟悉的标签,但是``标签就是自定义组件的标签了,它不属于html语义化标签中的任何一个,是自定义的。 接下来我们从这个简单的DEMO入手,对`WebComponent`进行了解。首先就是三大规范: - Custom Elements规范 - Template规范 - Shadow DOM规范 > MDN:https://developer.mozilla.org/zh-CN/docs/Web/Web_Components ### Custom Element 所谓自定义元素,即当内置元素无法为问题提供解决方案时,自己动手来创建一个自定义元素来解决,上方的``就是我们手动创建的自定义元素。 元素的状态是指定义该元素(或者叫做升级该元素)时元素状态的改变,升级过程是异步的。 元素内部的状态有: - `undefined` 未升级:即自定义元素还未被define。 - `failed` 升级失败:即define过了也实例化过了,但失败了。会自动按HTMLUnknownElement类来实例化。 - `uncustomized` 未定制化:没有define过但却被实例化了,会自动按HTMLUnknownElement类来实例化。 - `custom` 升级成功:define过并且实例化成功了。 接下来我们来看一个示例: ```html ``` ```css .custom-style{ display: inline-block; padding: 15px; border: 1px solid red; border-radius: 4px; color: red; } ``` ```js // 定义自定义组件 class CustomComponent extends HTMLElement { constructor() { super(); const box = document.createElement("div"); box.className = "custom-style"; const text = document.createElement("p"); text.innerText = "这是一个自定义组件"; box.appendChild(text); this.appendChild(box); } } window.customElements.define("custom-component", CustomComponent); ``` 效果如下: image-20230215094800286 首先可以看出,需要有个类的概念。自定义元素类必须继承自window内置的`HTMLElement`类。 然后在`constructor`中定义类一些标记模版,定义模板后,执行`this.appendChild`,其中`this`指向了当前类实例。 最后将自定义组件挂载到`customElements`上,通过`window.customElements.define`方法。这个时候注意了,需要给自定义组件起一个名字,可以看到上面例子中我起的名字为`custom-component`。起名字是有规则的,规则如下: - 自定义元素的名称,**必须**包含短横线(-)。它可以确保html解析器能够区分常规元素和自定义元素,还能确保html标记的兼容性。 - 自定义元素只能一次定义一个,一旦定义无法撤回。 - 自定义元素不能单标记封闭。比如``,必须写一对开闭标记。比如 ``。 对于自定义组件挂载的相关API: - `window.customElement.define('custom-component', CustomComponent, extendsInit)` // 定义一个自定义元素 - `window.customElement.get('custom-component')` // 返回已定义的自定义元素的构造函数 - `window.customElement.whenDefined('custom-component')` // 接收一个promise对象,是当定义自定义元素时返回的,可监听元素状态变化但无法捕捉内部状态值。 其中`window.customElement.whenDefined`方法监听的元素状态为上述讲解的四种元素状态中的: `failed`升级失败和`custom`升级成功。 这里有个问题,我们demo里的dom结构比较简单,所以我们通过`document.createElement`、`appendChild`方法进行构建还不算复杂,如果dom结构很复杂的组件怎么办呢?一顿使用createElement也不是个办法。这个时候我们就要引入`