样式化 Web 组件
影子 DOM
文档对象模型 (DOM) 是 HTML 结构、样式和内容的面向对象表示。它将文档公开为树状结构的节点和对象,以便 JavaScript 等编程语言进行操作。Shadow DOM 允许将隐藏的DOM 树添加到文档 DOM 树中。这些隐藏的 DOM 树与父 DOM 树隔离,将 CSS 的范围限制在 Web 组件中,允许在父级中找到重复的类和 ID,而无需交互。这就是实现样式封装和 Web 组件创建的方式。
在 Angular 中使用ShadowDom封装时,应用程序使用浏览器的本机实现。使用Emulated时,它会模拟浏览器的本机功能以克服当前有限的支持,但会应用类似的样式原则和规则。(有关 Angular 中封装类型的更多详细信息,请参阅Angular 中的 CSS 封装)
让我们仔细看看它是如何工作的。
使用 Vanilla JavaScript 的自定义组件
<html>
<head>
<meta charset="utf-8">
<title>Vanilla JS</title>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<h1>Vanilla JS</h1>
<section>
</section>
<script src="main.js"></script>
</body>
</html>
CSS
body {
font-family: sans-serif;
font-size: 16px;
padding: 1rem;
}
section {
background: ghostwhite;
border: solid 1px lightgray;
padding: 1rem;
}
我们的页面看起来应该是这样的:
现在创建自定义组件。我们的 JS 文件目前是空白的,所以让我们开始吧。我们需要做的第一件事是定义一个扩展HTMLElement并定义我们的元素的类。这是我们的影子主机,它将包含我们的影子根和影子树。
JS
class CustomChip extends HTMLElement {
constructor() {
super();
// Define web component here
}
}
// customElements.define(name, constructor, options);
customElements.define('custom-chip', CustomChip);
请注意,自定义元素名称必须包含连字符。
在我们的 HTML 中:
HTML(部分)
<section>
<custom-chip></custom-chip>
</section>
我们将在构造函数中定义 Web 组件。我们要做的第一件事是将影子根附加到影子主机。
JS(部分)
class CustomChip extends HTMLElement {
constructor() {
super();
// Create a shadow root
const shadow = this.attachShadow({ mode: 'open' });
}
}
我们使用open模式,因为它允许我们使用 JavaScript 访问 shadow DOM。如果我们使用closed模式,myCustomElem.shadowRoot将返回null,我们将无法从 shadow DOM 外部访问它。
正如我们在开发工具中看到的,我们现在有一个带有影子 DOM 的新创建组件的实例。
因为我们正在创建一个芯片,所以我们要添加一个标签和一个头像。在构造函数内部,我们创建 HTML 元素,然后将它们附加到影子 DOM。这些元素构成了影子树。
JS(部分)
// Create container
const wrapper = document.createElement('div');
// Add avatar
const avatar = document.createElement('img');
let imgUrl;
if (this.hasAttribute('img')) {
imgUrl = this.getAttribute('img');
} else {
imgUrl = 'assets/default.png';
}
avatar.src = imgUrl;
// Add text
const info = document.createElement('span');
info.setAttribute('class', 'info');
// Take attribute content and put it inside the info span
const text = this.getAttribute('label');
info.textContent = text;
// attach the created elements to the shadow dom
shadow.appendChild(wrapper);
wrapper.appendChild(avatar);
wrapper.appendChild(info);
因为我们从标签属性中提取了芯片的文本,所以我们继续将其添加到 HTML 中。图像路径也一样。
HTML(部分)
<section>
<custom-chip label="Foo" img="assets/purple.png"></custom-chip>
<custom-chip label="Bar"></custom-chip>
</section>
现在容器内有一个头像和一些文本,让我们来设置它的样式。在构造函数中,我们仍然添加样式,然后将它们附加到 shadow DOM。
JS(部分)
// Style the component
const style = document.createElement('style');
console.log(style.isConnected);
style.textContent =
`
:host {
background: lightgray;
border-radius: 40px;
display: inline-block;
font-family: sans-serif;
padding: .5rem 1rem;
}
img {
height: 1rem;
margin-right: .5rem;
vertical-align: baseline;
}
`
// attach styles to shadow DOM
shadow.appendChild(style);
console.log(style.isConnected);
我们新创建的组件已定义范围、设置样式并可供使用。
Angular 模拟 DOM
Angular 的工作方式类似。如果我们创建一个CustomChip组件并为其设置相同的样式,我们会得到类似的结果。但在这里 Angular 为我们做了很多繁重的工作。让我们来看看。
我们将使用带有模拟封装的 Angular 6(Angular 的默认封装方法)。
使用 CLI 我们将生成一个应用程序和一个CustomChip组件。
$ ng new angular
$ cd angular
$ ng generate component CustomChip
然后,我们添加所有相同的 CSS 和 HTML,并创建 Typescript 来传递标签和图像源。
应用程序.html
<h1>Angular</h1>
<!-- Same as the vanilla version -->
<section>
<custom-chip label="foo" img="/assets/purple.png"></custom-chip>
<custom-chip label="bar"></custom-chip>
</section>
样式.css
body {
font-family: sans-serif;
font-size: 16px;
padding: 1rem;
}
section {
background: ghostwhite;
border: solid 1px lightgray;
padding: 1rem;
}
定制芯片.html
<div>
<img [src]="img ? img : 'assets/default.png'">
<span class="info">{{ label }}</span>
</div>
自定义芯片.css
/* Same as the vanilla version */
:host {
background: lightgray;
border-radius: 40px;
display: inline-block;
font-family: sans-serif;
padding: .5rem 1rem;
}
img {
height: 1rem;
margin-right: .5rem;
vertical-align: baseline;
}
自定义芯片.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'custom-chip',
templateUrl: './custom-chip.component.html',
styleUrls: ['./custom-chip.component.css']
})
export class CustomChipComponent {
@Input() label;
@Input() img;
}
我们现在以 Angular 方式拥有相同的组件。
造型
为独立组件使用 CSS 的优点在于,如果我们在页面的其他地方添加图像,它不会受到组件中头像样式的影响。让我们添加另一个结构类似的芯片,但这次是纯 HTML。
HTML(部分)
<section>
<custom-chip label="foo" img="/assets/purple.png"></custom-chip>
<custom-chip label="bar"></custom-chip>
</section>
<div>
<img src="assets/default.png">
<span>I am also a chip</span>
</div>
我们的新元素完全不受组件样式的影响。组件样式仅限于组件本身。
使用styles.css从组件外部设置样式
要在 Angular 中从组件本身外部编辑组件的样式,我们可以将样式包含在项目根目录的style.css文件中。此处放置的样式将全局应用于应用程序,并影响所有元素,甚至组件内部的元素。这是一个很好的解决方案,允许组件继承诸如font-family之类的基本样式。
这与我们的 Vanilla JS 版本不同,在 Vanilla JS 版本中,我们的组件不会继承样式,因为我们的组件与分配给文档 DOM 的样式是隔离的。
style.css (部分)
img {
background: palegoldenrod;
border: solid 1px gray;
border-radius: 50%;
display: inline-block;
margin-top: 12px;
padding: 2px 10px;
}
div {
background: yellow;
border-radius: 0;
margin-top: 1rem;
padding: 1rem;
}
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~