首先我们来简单实现以下我们 Div 类中的 mountTo 方法,这里我们还需要给他加入 setAttribute 和 appendChild 方法,因为在我们的 createElement 中有挂載属性子元素的逻辑,如果没有这两个方法就会报错。但是这个时候我们先不去实现这两个方法的逻辑,方法内容留空即可。
class Div { setAttribute() {} appendChild() {} mountTo(parent) { this.root = document.createElement('div'); parent.appendChild(this.root); } }
这里面其实很简单首先给类中的 root 属性创建成一个 div 元素节点,然后把这个节点挂載到这个元素的父级。这个 parent 是以参数传入进来的。
然后我们就可以把我们原来的 body.appendChild 的代码改为使用 mountTo 方法来挂載我们的自定义元素类。
// document.body.appendChild(a); a.mountTo(document.body);
用现在的代码,我们 webpack 打包看一下效果:
我们可以看到我们的 Div 自定义元素是有正确的被挂載到 body 之上。但是 Div 中的 span 标签都是没有被挂載上去的。如果我们想它与普通的 div 一样去工作的话,我们就需要去实现我们的 setAttribute 和 appendChild 逻辑。
接下来我们就一起来尝试完成剩余的实现逻辑。在开始写 setAttribute 和 appendChild 之前,我们需要先给我们的 Div 类加入一个构造函数 constructor。在这里个里面我们就可以把元素创建好,并且代理到 root 上。
constructor() { this.root = document.createElement('div'); }
然后的 setAttribute 方法其实也很简单,就是直接使用 this.root 然后调用 DOM API 中的 setAttribute 就可以了。而 appendChild 也是同理。最后我们的代码就是如下:
class Div { // 构造函数 // 创建 DOM 节点 constructor() { this.root = document.createElement('div'); } // 挂載元素的属性 setAttribute(name, attribute) { this.root.setAttribute(name, attribute); } // 挂載元素子元素 appendChild(child) { this.root.appendChild(child); } // 挂載当前元素 mountTo(parent) { parent.appendChild(this.root); } }
我们 webpack 打包一下看看效果:
我们可以看到,div 和 span 都被成功挂載到 body 上。也证明我们自制的 div 也能正常工作了。
这里还有一个问题,因为我们最后调用的是 a.mountTo(),如果我们的变量 a 不是一个自定义的元素,而是我们普通的 HTML 元素,这个时候他们身上是不会有 mountTo 这个方法的。
所以这里我们还需要给普通的元素加上一个 Wrapper 类,让他们可以保持我们元素类的标准格式。也是所谓的标准接口。
我们先写一个 ElementWrapper 类,这个类的内容其实与我们的 Div 是基本一致的。唯有两个区别
在创建 DOM 节点的时候,可以通过传当前元素名 type 到我们的构造函数,并且用这个 type 去建立我们的 DOM 节点
appendChild 就不能直接使用 this.root.appendChild,因为所有普通的标签都被改为我们的自定义类,所以 appendChild 的逻辑需要改为 child.mountTo(this.root)
class ElementWrapper { // 构造函数 // 创建 DOM 节点 constructor(type) { this.root = document.createElement(type); } // 挂載元素的属性 setAttribute(name, attribute) { this.root.setAttribute(name, attribute); } // 挂載元素子元素 appendChild(child) { child.mountTo(this.root); } // 挂載当前元素 mountTo(parent) { parent.appendChild(this.root); } } class Div { // 构造函数 // 创建 DOM 节点 constructor() { this.root = document.createElement('div'); } // 挂載元素的属性 setAttribute(name, attribute) { this.root.setAttribute(name, attribute); } // 挂載元素子元素 appendChild(child) { child.mountTo(this.root); } // 挂載当前元素 mountTo(parent) { parent.appendChild(this.root); } }
这里我们还有一个问题,就是遇到文本节点的时候,是没有转换成我们的自定义类的。所以我们还需要写一个给文本节点,叫做 TextWrapper。
class TextWrapper { // 构造函数 // 创建 DOM 节点 constructor(content) { this.root = document.createTextNode(content); } // 挂載元素的属性 setAttribute(name, attribute) { this.root.setAttribute(name, attribute); } // 挂載元素子元素 appendChild(child) { child.mountTo(this.root); } // 挂載当前元素 mountTo(parent) { parent.appendChild(this.root); } }