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

> 
> 不要做重复的工作；基于浏览器开发 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/) 荣誉推出


OK
Web components are a collection of open source technologies such as JavaScript and HTML that allow you to create custom elements that you can use and reuse in web apps. The components you create are independent of the rest of your code, so they're easy to reuse across many projects.


Best of all, it's a platform standard supported by all major modern browsers.

## What's in a web component?

**Custom elements:**This JavaScript API allows you to define new types of HTML elements.**Shadow DOM:**This JavaScript API provides a way to attach a hidden separate[Document Object Model](https://en.wikipedia.org/wiki/Document_Object_Model)(DOM) to an element. This encapsulates your web component by keeping the styling, markup structure, and behavior isolated from other code on the page. It ensures that styles are not overridden by external styles or, conversely, that a style from your web component doesn't "leak" into the rest of the page**.****HTML templates:**The element allows you to define reusable DOM elements. The element and its contents are not rendered in the DOM but can still be referenced using JavaScript.

## Write your first web component

You can write a simple web component with your favorite text editor and JavaScript. This how-to uses bootstrap to generate simple stylings then creates a simple card web component to display the temperature of a location passed to it as an attribute. The component uses the [Open Weather API](https://openweathermap.org/api), which requires you to generate an APPID/APIKey by signing in.

The syntax of calling this web component requires the location's longitude and latitude:

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


Create a file named **weather-card.js** that will contain all the code for your web component. Start by defining your component. This can be done by creating a template element and adding some simple HTML elements into it:

```
const template = document.createElement('template');
template.innerHTML = `
<div class="card">
<div class="card-body"></div>
</div>
`
```


Start defining the WebComponent class and its constructor:

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


The constructor attaches the shadowRoot and sets it to open mode. Then the template is cloned to shadowRoot.

Next, access the attributes. These are the longitude and latitude, and you need them to make a GET request to the Open Weather API. This needs to be done in the `connectedCallback`

function. You can use the `getAttribute`

method to access the attributes or define getters to bind them to this object:

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


Now define the `connectedCallBack`

method that fetches weather data whenever it is mounted:

```
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)} °C`
this._shadowRoot.appendChild($temperature);
}
```


Once the weather data is retrieved, additional HTML elements are added to the template. Now, your class is defined.

Finally, define and register a new custom element by using the method `window.customElements.define`

:

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


The first argument is the name of the custom element, and the second argument is the defined class. Here's a [link to the entire component](https://gist.github.com/rkpattnaik780/acc683d3796102c26c1abb03369e31f8).

You've written your first web component! Now it's time to bring it to the DOM. To do that, you must load the JavaScript file with your web component definition in your HTML file (name it **index.html**):

```
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<weather-card longitude='85.8245' latitude='20.296'></weather-card>
<script src='./weather-card.js'></script>
</body>
</html>
```


Here's your web component in a browser:

![Web component displayed in a browser Web component displayed in a browser](https://opensource.com/sites/default/files/uploads/webcomponent.png)

(Ramakrishna Pattnaik, CC BY-SA 4.0)

Because web components need only HTML, CSS, and JavaScript, they are natively supported by browsers and can be used seamlessly with frontend frameworks, including React and Vue. The following simple code snippet shows how to use web components with a simple React App bootstrapped with [Create React App](https://create-react-app.dev/docs/getting-started/). For this, you need to import the **weather-card.js** file you defined earlier and use it as a component:

```
import './App.css';
import './weather-card';
function App() {
return (
<weather-card longitude='85.8245' latitude='20.296'></weather-card>
);
}
export default App;
```


## Lifecycle of a web component

All components follow a lifecycle from initialization to removal from the DOM (i.e., unmount). Methods are associated with each lifecycle event so that you can control the components better. The various lifecycle events of a web component include:

**Constructor:**The constructor for a web component is called before it is mounted, meaning it's created before the element is attached to the document. It's used for initializing local state, binding event handlers, and creating the shadow DOM. The constructor must make a call to`super()`

to call the class the Web Component class extends.**ConnectedCallBack:**This is called when an element is mounted (that is, inserted into the DOM tree). It deals with initializations creating DOM nodes and is used mostly for operations like instantiating network requests. React developers can relate it to`componentDidMount`

.**attributeChangedCallback:**This method accepts three arguments:`name`

,`oldValue`

, and`newValue`

. It is called whenever one of the component's observed attributes gets changed. Attributes are declared observed attributes using a static`observedAttributes`

getter:

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

`attributeChangedCallback`

will be called whenever the attribute name or`_id`

is changed.**DisconnectedCallBack:**This is called when an element is removed from the DOM tree (i.e., unmounted). It is equivalent to React's`componentWillUnmount`

. It is used to free resources that won't be garbage-collected automatically, like unsubscribing from DOM events, stopping interval timers, or unregistering all registered callbacks.**AdoptedCallback:**It is called each time the custom element is moved to a new document. It only occurs when dealing with IFrames.

## Modular open source

Web components can be a powerful way to develop web apps. Whether you're comfortable with JavaScript or just getting started with it, it's easy to create reusable code with this great open standard, no matter what browser your target audience uses.

## Comments are closed.