---
author: Ramakrishna Pattnaik
category: 软件开发
comments_data: []
count:
  commentnum: 0
  favtimes: 0
  likes: 0
  sharetimes: 0
  viewnum: 2480
date: '2022-10-17 10:11:36'
editorchoice: false
excerpt: 不要做重复的工作；基于浏览器开发 Web App 时，需要制作一些可重用的模块。
fromurl: https://opensource.com/article/21/7/web-components
id: 15148
islctt: true
largepic: /images/p/202210/17/101134uzsiis8xsu9wqibi.jpg
permalink: /article-15148-1.html
pic: /data/attachment/album/202210/17/101134uzsiis8xsu9wqibi.jpg.thumb.jpg
related: []
reviewer: wxy
selector: lujun9972
summary: 不要做重复的工作；基于浏览器开发 Web App 时，需要制作一些可重用的模块。
tags:
- Web 组件
thumb: false
title: 开发你的第一个 Web 组件
titlepic: true
translator: cool-summer-021
updated: '2022-10-17 10:11:36'
---


> 
> 不要做重复的工作；基于浏览器开发 Web App 时，需要制作一些可重用的模块。
> 
> 
> 


![](/data/attachment/album/202210/17/101134uzsiis8xsu9wqibi.jpg "Digital creative of a browser on the internet")


Web 组件是一系列开源技术（例如 JavaScript 和 HTML）的集合，你可以用它们创建一些 Web App 中可重用的自定义元素。你创建的组件是独立于其他代码的，所以这些组件可以方便地在多个项目中重用。


首先，它是一个平台标准，所有主流的浏览器都支持它。


### Web 组件中包含什么？


* **定制元素**：JavaScript API 支持定义 HTML 元素的新类别。
* **影子 DOM**：JavaScript API 提供了一种将一个隐藏的、独立的 [文档对象模型](https://en.wikipedia.org/wiki/Document_Object_Model)（DOM）附加到一个元素的方法。它通过保留从页面的其他代码分离出来的样式、标记结构和行为特征对 Web 组件进行了封装。它会确保 Web 组件内样式不会被外部样式覆盖，反之亦然，Web 组件内样式也不会“泄露”到页面的其他部分。
* **HTML 模板**：该元素支持定义可重用的 DOM 元素。可重用 DOM 元素和它的内容不会呈现在 DOM 内，但仍然可以通过 JavaScript 被引用。


### 开发你的第一个 Web 组件


你可以借助你最喜欢的文本编辑器和 JavaScript 写一个简单的 Web 组件。本指南使用 Bootstrap 生成简单的样式，并创建一个简易的卡片式的 Web 组件，给定了位置信息，该组件就能显示该位置的温度。该组件使用了 [Open Weather API](https://openweathermap.org/api)，你需要先注册，然后创建 APPID/APIKey，才能正常使用。


调用该组件，需要给出位置的经度和纬度：



```
<weather-card longitude='85.8245' latitude='20.296' />

```

创建一个名为 `weather-card.js` 的文件，这个文件包含 Web 组件的所有代码。首先，需要定义你的组件，创建一个模板元素，并在其中加入一些简单的 HTML 标签：



```
const template = document.createElement('template');

template.innerHTML = `
  <div class="card">
    <div class="card-body"></div>
  </div>
`

```

定义 Web 组件的类及其构造函数：



```
class WeatherCard extends HTMLElement {
  constructor() {
    super();
    this._shadowRoot = this.attachShadow({ 'mode': 'open' });
    this._shadowRoot.appendChild(template.content.cloneNode(true));
  }
  ......
}

```

构造函数中，附加了 `shadowRoot` 属性，并将它设置为开启模式。然后这个模板就包含了 shadowRoot 属性。


接着，编写获取属性的函数。对于经度和纬度，你需要向 Open Weather API 发送 GET 请求。这些功能需要在 `connectedCallback` 函数中完成。你可以使用 `getAttribute` 方法访问相应的属性，或定义读取属性的方法，把它们绑定到本对象中。



```
get longitude() {
  return this.getAttribute('longitude');
}

get latitude() {
  return this.getAttribute('latitude');
}

```

现在定义 `connectedCallBack` 方法，它的功能是在需要时获取天气数据：



```
connectedCallback() {
  var xmlHttp = new XMLHttpRequest();
  const url = `http://api.openweathermap.org/data/2.5/weather?lat=${this.latitude}&lon=${this.longitude}&appid=API_KEY`
  xmlHttp.open("GET", url, false);
  xmlHttp.send(null);
  this.$card = this._shadowRoot.querySelector('.card-body');
  let responseObj = JSON.parse(xmlHttp.responseText);
  let $townName = document.createElement('p');
  $townName.innerHTML = `Town: ${responseObj.name}`;
  this._shadowRoot.appendChild($townName);
  let $temperature = document.createElement('p');
  $temperature.innerHTML = `${parseInt(responseObj.main.temp - 273)} &deg;C`
  this._shadowRoot.appendChild($temperature);
}

```

一旦获取到天气数据，附加的 HTML 元素就添加进了模板。至此，完成了类的定义。


最后，使用 `window.customElements.define` 方法定义并注册一个新的自定义元素：



```
window.customElements.define('weather-card', WeatherCard);

```

其中，第一个参数是自定义元素的名称，第二个参数是所定义的类。这里是 [整个组件代码的链接](https://gist.github.com/rkpattnaik780/acc683d3796102c26c1abb03369e31f8)。


你的第一个 Web 组件的代码已完成！现在应该把它放入 DOM。为了把它放入 DOM，你需要在 HTML 文件（`index.html`）中载入指向 Web 组件的 JavaScript 脚本。



```
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
</head>

<body>
  <weather-card longitude='85.8245' latitude='20.296'/>
  <script src='./weather-card.js'></script>
</body>

</html>

```

这就是显示在浏览器中的 Web 组件：


![Web component displayed in a browser](/data/attachment/album/202210/17/101137ki5ra5qigrepep16.png "Web component displayed in a browser")


由于 Web 组件中只包含 HTML、CSS 和 JavaScript，它们本来就是浏览器所支持的，并且可以无瑕疵地跟前端框架（例如 React 和 Vue）一同使用。下面这段简单的代码展现的是它跟一个由 [Create React App](https://create-react-app.dev/docs/getting-started/) 引导的一个简单的 React App 的整合方法。如果你需要，可以引入前面定义的 `weather-card.js`，把它作为一个组件使用：



```
import './App.css';
import './weather-card';

function App() {
  return (
  <weather-card longitude='85.8245' latitude='20.296'></weather-card>
  );
}

export default App;

```

### Web 组件的生命周期


一切组件都遵循从初始化到移除的生命周期法则。每个生命周期事件都有相应的方法，你可以借助这些方法令组件更好地工作。Web 组件的生命周期事件包括：


* `Constructor`：Web 组件的构造函数在它被挂载前调用，意味着在元素附加到文档对象前被创建。它用于初始化本地状态、绑定事件处理器以及创建影子 DOM。在构造函数中，必须调用 `super()`，执行父类的构造函数。
* `ConnectedCallBack`：当一个元素被挂载（即，插入 DOM 树）时调用。该函数处理创建 DOM 节点的初始化过程中的相关事宜，大多数情况下用于类似于网络请求的操作。React 开发者可以将它与 `componentDidMount` 相关联。
* `attributeChangedCallback`：这个方法接收三个参数：`name`, `oldValue` 和 `newValue`。组件的任一属性发生变化，就会执行这个方法。属性由静态 `observedAttributes` 方法声明：

```
static get observedAttributes() {
  return ['name', '_id'];
} 

```

一旦属性名或 `_id` 改变，就会调用 `attributeChangedCallback` 方法。
* `DisconnectedCallBack`：当一个元素从 DOM 树移除，会执行这个方法。它相当于 React 中的 `componentWillUnmount`。它可以用于释放不能由垃圾回收机制自动清除的资源，比如 DOM 事件的取消订阅、停用计时器或取消所有已注册的回调方法。
* `AdoptedCallback`：每次自定义元素移动到一个新文档时调用。只有在处理 IFrame 时会发生这种情况。


### 模块化开源


Web 组件对于开发 Web App 很有用。无论你是熟练使用 JavaScript 的老手，还是初学者，无论你的目标客户使用哪种浏览器，借助这种开源标准创建可重用的代码都是一件可以轻松完成的事。


*插图：Ramakrishna Pattnaik, [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)*




---


via: <https://opensource.com/article/21/7/web-components>


作者：[Ramakrishna Pattnaik](https://opensource.com/users/rkpattnaik780) 选题：[lujun9972](https://github.com/lujun9972) 译者：[cool-summer-021](https://github.com/cool-summer-021) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出