一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情
经过前两天的学习,已经基本入门 Web Component ,那么就开始实战,开发第一个组件
不过在此之前,还要介绍另一个特性
slot
slot 由其 name 属性标识,并且允许您在模板中定义占位符,当在标记中使用该元素时,该占位符可以填充所需的任何 HTML 标记片段。--MDN
比如我们的template
是这样的:在需要的地方放置一个指定name
属性的slot
<template id="mHeaderTemplate">
<p><slot name="my-text">My default text</slot></p>
</template>
在使用的时候,可以这样玩:给元素设置一个slot
属性,值为template
里slot
的name
<m-header id="mHeader" title="hello">
<span slot="my-text">Let's have some different text!</span>
</m-header>
这样span
会替换template
里的slot
。使用起来还是挺简单的
特性介绍完毕,接下来进行今天的主题:组件开发实战
Reply 组件
本次来复制 Ant Design 的 Card
组件
先观察组件
-
默认情况下有一个头像、输入框、提交按钮
-
在输入值点击提交按钮后,输入框上方会出现每一条的评论,包括评论人头像、昵称、评论的内容
针对上述功能,简单的实现思路(肯定有更好、更优雅的 😬):
-
m-reply
为受控组件,只负责提交、渲染数据,因此需要一个comments
属性,指代待渲染的评论 -
在输入框输入数据后,点击提交,组件会向父组件发送一个消息(自定义事件),并将输入框的内容传递出去,因此父容器需要监听该事件,父组件在在处理好数据后更新
comments
,m-reply
会自动渲染最新的数据 -
对于
comments
,该属性是一个数组,每一项包含三项数据:评论内容comment
、评论人头像avatar
、评论人昵称nickName
在设计好思路后,开干!
先完成提交评论的功能,在这里,我们选择自定义事件的方法来实现组件间通信,在点击提交按钮时触发一个自定义事件submitComment
,并将评论内容传递出去
connectedCallback() {
const submitBtn = this.shadowRoot.querySelector('#submitBtn');
const commentInput = this.shadowRoot.querySelector('#input');
submitBtn.addEventListener('click', () => {
const res = this.dispatchEvent(new CustomEvent('submitComment', {
detail: commentInput.value,
}));
});
}
有两个注意点注意:
-
触发事件的是
this
,即当前组件 -
数据需要放置在自定义事件的
detail
属性上,直接传递数据或者放在其他属性上会接收不到
🚩
接着是父组件监听该事件并处理数据
const reply = document.querySelector("#reply");
reply.addEventListener("submitComment", (e) => {
comments.push({
comment: e.detail,
nickName,
avatar,
});
console.log("comments", comments);
reply.setAttribute("comments", JSON.stringify(comments));
});
同样有两个注意点:
-
需要先获取当前组件,然后在当前组件上添加监听
-
组件的属性会自动将设置的值转为字符串类型,因此需要先
JSON.stringify
最后,完成渲染每一条评论的代码
renderComments(comments) {
const Comment = ({ avatar, nickName, comment }) => (`
...
`);
const commentsContainer = this.shadowRoot.querySelector('#commentsContainer');
commentsContainer.innerHTML = comments.reduce((acc, cur) => `${acc}${Comment(cur)}`, '');
}
该函数会在attributeChangedCallback
监听到comments
变化时调用,渲染出最新的评论
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'comments') {
this.renderComments(JSON.parse(newValue));
}
}
最后的效果
🎉
ps:该组件还有不少待完善的地方:提交按钮没有loading
、没有校验输入值,数据安全性较低,存在 XSS 攻击的隐患、写死提交时间等,这些都会在后面慢慢完善
完整代码在这里
总结
Web Component 可用来开发跨框架组件,特性基本够用,使用起来也挺方便,只是开发体验有待提高,需要不停地使用原生 api 操作 dom;其次,使用自定义事件来实现组件间通信,代码量有点多(可能还有更好的方法我没找到 🤔)
总体来说,使用 Web Component 来开发一套组件库也是一种新的思路,新的选择,值得尝试