---
title: "Open-WC 筆記"
path: "Open-WC 筆記"
---
{%hackmd @RintarouTW/About %}
# Open-WC 筆記
Open Web Component
- open-wc depend on LitElement(successor of Polymer).
- Light weight.
- shadow dom : 看來終於解決 naming polution 的問題了
- customElement
- customEvent (finally...)
## Shadow DOM
### LitElement 實現方式
```javascript
const header = document.createElement('header');
const shadowRoot = header.attachShadow({mode: 'open'});
shadowRoot.innerHTML = '<h1>Hello Shadow DOM</h1>'; // Could also use appendChild().
// header.shadowRoot === shadowRoot
// shadowRoot.host === header
```
<center>
```graphviz
digraph {
host ->shadowRoot
}
```
</center>
shadowRoot 即 Shadow DOM 之 document, 支援 getElementById(), querySelector(),...。
### Custom Element 使用 Shadow DOM
```javascript
// Use custom elements API v1 to register a new HTML tag and define its JS behavior
// using an ES6 class. Every instance of <fancy-tab> will have this same prototype.
customElements.define('fancy-tabs', class extends HTMLElement {
function Object() { [native code] }() {
super(); // always call super() first in the constructor.
// Attach a shadow root to <fancy-tabs>.
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<style>#tabs { ... }</style> <!-- styles are scoped to fancy-tabs! -->
<div id="tabs">...</div>
<div id="panels">...</div>
`;
}
...
});
```
## `<slot></slot>`
Light DOM
組件用戶編寫的標記。該 DOM 不在組件 shadow DOM 之內。 它是元素實際的子項。
```javascript
<button is="better-button">
<!-- the image and span are better-button's light DOM -->
<img src="gear.svg" slot="icon">
<span>Settings</span>
</button>
```
Shadow DOM
該 DOM 是由組件的作者編寫。Shadow DOM 對於組件而言是本地的,它定義內部結構、作用域 CSS 並封裝實現詳情。它還可定義如何渲染由組件使用者編寫的標記。
```javascript
#shadow-root
<style>...</style>
<slot name="icon"></slot>
<span id="wrapper">
<slot>Button</slot>
</span>
```
Browser Render 會通過 `<slot></slot>` 來組合 Light DOM 和 Shadow DOM
```javascript
<button is="better-button">
#shadow-root
<style>...</style>
<slot name="icon">
<img src="gear.svg" slot="icon">
</slot>
<slot>
<span>Settings</span>
</slot>
</button>
```
Shadow DOM 使用 `<slot >` 元素將不同的 DOM 樹組合在一起。Slot 是組件內部的佔位符,用戶可以使用自己的標記來填充。
通過定義一個或多個 slot,您可將外部標記引入到組件的 shadow DOM 中進行渲染。 這相當於您在說“在此處渲染用戶的標記”。
如果 `<slot>` 引入了元素,則這些元素可“跨越” shadow DOM 的邊界。 這些元素稱爲分佈式節點。從概念上來看,分佈式節點似乎有點奇怪。 Slot 實際上並不移動 DOM;它們在 shadow DOM 內部的其他位置進行渲染。
組件可在其 shadow DOM 中定義零個或多個 slot。Slot 可以爲空,或者提供回退內容。 如果用戶不提供 light DOM 內容,slot 將對其備用內容進行渲染。
[深入 Shadow DOM](https://developers.google.com/web/fundamentals/web-components/shadowdom?hl=zh-tw)
## Open WC Custom Element
```javascript
import { LitElement, html } from "lit-element";
class UpdateArraysAndObjects extends LitElement {
static get properties() {
return {
myArray: { type: Array },
myObject: { type: Object }
};
}
constructor() {
super();
this.myObject = { id: 1, text: "foo" };
this.myArray = [{ id: 1 }, { id: 2 }];
}
render() {
}
}
customElements.define("update-arrays-and-objects", UpdateArraysAndObjects);
```
where customElements.define(custom-tag-name, element_class_name);
## Attribute and Property
- Attribute: html tag attribute ex: `<div id="id">`
- Property: javascript object property
## Render Style
```javascript
import { LitElement, html, css } from "lit-element";
class RenderStyles extends LitElement {
/**
* Styles should be added as a static getter. They are evaluated once, and then added
* in the element's shadow dom.
*
* Shadow dom takes care of scoping the CSS of your element to only affect your
* element's template, and not the element outside. For an in-depth explanation
* of shadow dom, see: https://github.com/praveenpuglia/shadow-dom-in-depth
*/
static get styles() {
return css`
:host {
display: block;
}
.message {
color: blue;
}
`;
}
render() {
return html`
<div class="message">Hello world!</div>
`;
}
}
customElements.define("render-styles", RenderStyles);
```
## LitElement Oberserve Scope
LitElement only watch on Array and Object changes, not it's content.
Use this.requestUpdate() instead after mutating the content directly.
```javascript
// this.myArray.push(newItem);
// this.myObject.id = newId;
// this.requestUpdate();
```
```javascript
import { LitElement, html } from "lit-element";
class UpdateArraysAndObjects extends LitElement {
static get properties() {
return {
myArray: { type: Array },
myObject: { type: Object }
};
}
constructor() {
super();
this.myObject = { id: 1, text: "foo" };
this.myArray = [{ id: 1 }, { id: 2 }];
}
render() {
return html`
<h3>Array items</h3>
<ul>
${this.myArray.map(
item => html`
<li>${item.id}</li>
`
)}
</ul>
<button @click=${this._addArrayItem}>Add array item</button>
<h3>Object</h3>
<div><strong>${this.myObject.id}</strong>: ${this.myObject.text}</div>
<button @click=${this._updateObjectId}>Add object item</button>
`;
}
/**
* If you mutate an array directly, LitElement will not detect
* the change automatically.
*
* The recommended approach is to use immutable data patterns.
* You can easily append an array item using array spread syntax:
*/
_addArrayItem() {
const newId = Math.round(Math.random() * 100);
const newItem = { id: newId };
this.myArray = [...this.myArray, newItem];
/**
* An alternative method is to mutate the array and then call
* requestUpdate() to notify LitElement the property changed.
*/
// this.myArray.push(newItem);
// this.requestUpdate();
}
/**
* If you mutate an object directly, LitElement will not detect
* the change automatically.
*
* The recommended approach is to use immutable data patterns.
* You can easily update an object's property using the object
* spread syntax:
*/
_updateObjectId() {
const newId = Math.round(Math.random() * 100);
this.myObject = {
...this.myObject,
id: newId
};
/**
* An alternative method is to mutate the object and then call
* requestUpdate() to notify LitElement the property changed.
*/
// this.myObject.id = newId;
// this.requestUpdate();
}
}
customElements.define("update-arrays-and-objects", UpdateArraysAndObjects);
```
###### tags: `html` `node` `open-wc` `lit-element`