從原生web組件到框架組件源碼(三)
- 2020 年 11 月 3 日
- 筆記
快樂的時光都是這麼短暫,轉眼間,web原生組件的知識點已經學完了,這個雖然暫時不一定有用,但是隨着時間的積累,一步一個腳印的積累,你會有相應的收穫,希望能變得更強,比如兩年前我也會想有現成的東西不用,幹嘛要自己寫呢?但是你確定一直用上層的東西,你的收穫有自己寫快嗎? 在開發的過程過能節約下來的時間,我們可以用這個時間拿來學習,這樣隨着時間的積累我們會變得更強,也會慢慢有更多的時間投入生活,進行正向循環
css問題
自定義元素然後是普通的HTML元素,也可以使用css設置樣式
在我們沒有設置shadow DOM
的組件,進行全局樣式設置
<app-element></app-element>
<style>
/* CSS Global */
app-element {
display: inline-block;
padding: 6px 20px;
background: steelblue;
color: white;
}
app-element span {
font-weight: bold;
vertical-align: super;
font-size: small;
color: gold;
}
</style>
<script>
customElements.define("app-element", class extends HTMLElement {
connectedCallback() {
this.innerHTML = `<div class="element">AppElement <span>New!</span></div>`;
}
});
</script>
- 無論文檔是否具有Shadow DOM,都可以從文檔的全局CSS(從組件外部)對組件本身進行樣式設置。
- 只要沒有Shadow DOM可以「保護」組件,就可以對組件中的元素進行全局樣式設置
HTML 外部引入
<app-element></app-element>
<script>
customElements.define("app-element", class extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<link rel="stylesheet" href="/components/AppElement.css">
<style>
@import "/components/AppElement.css";
</style>
<div class="element">
AppElement <span>New!</span>
</div>
`;
}
});
</script>
程序化動態的方法
<app-element></app-element>
<script>
import css from "./AppElement.css";
customElements.define("app-element", class extends HTMLElement {
connectedCallback() {
document.adoptedStyleSheets = [...document.adoptedStyleSheets, css];
this.innerHTML = `
<div class="element">
AppElement <span>New!</span>
</div>
`;
}
});
</script>
this.shadowRoot.adoptedStyleSheets = [...document.adoptedStyleSheets, css];
我們通過import
加載css,在這種情況下,它是組件的相對路徑,在加載css內容中並生成一個對象cssStyleSheet
,然後通過.adoptedStyleSheets
導入css
這種方法直接使用是錯誤的,需要引入插件css-loader
,應該要藉助webpack ,原生不能直接使用
import css from "./AppElement.css"
css自定義屬性
var(--color,red)
// 第一個變量不存在,用第二個
全局設置,穿透到裏面
<app-element></app-element>
<app-element></app-element>
<app-element></app-element>
<style>
/* CSS Global */
app-element:first-of-type {
--color: orangered;
}
</style>
<script>
customElements.define("app-element", class extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<style>
/* CSS Local */
.element {
display: inline-block;
padding: 6px 20px;
background: var(--color, steelblue);
color: white;
}
span {
font-weight: bold;
vertical-align: super;
font-size: small;
color: gold;
}
</style>
<div class="element">
AppElement <span>New!</span>
</div>
`;
}
});
</script>
css 作用域
css 偽類,僅在定義了shadow DOM
有效
偽類 | 描述 |
---|---|
:host |
它允許我們設置自定義元素(組件自己的容器)的樣式。 |
:host(``css) |
同上一個,但前提是它與中定義的選擇器匹配css 。 |
:host-context(``css) |
同上,但前提是您有與選擇器匹配的父母css 。 |
<app-element></app-element>
<app-element disabled></app-element>
<div class="box">
<app-element></app-element>
</div>
<script>
customElements.define("app-element", class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
connectedCallback() {
this.shadowRoot.innerHTML = `
<style>
:host {
display: inline-block;
padding: 6px 20px;
background: steelblue;
color: white;
}
:host([disabled]) {
background: #aaa;
}
:host-context(.box) {
background: red;
}
span {
font-weight: bold;
vertical-align: super;
font-size: small;
color: gold;
}
</style>
<div class="element">
AppElement <span>New!</span>
</div>
`;
}
});
</script>
修改最外層的盒子的css
影子DOM操作事件
<app-element></app-element>
<script>
customElements.define("app-element", class AppElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
sendMessage() {
alert("Hello!");
}
connectedCallback() {
this.shadowRoot.innerHTML = "<button>點我!</button>";
this.button = this.shadowRoot.querySelector("button");
this.button.addEventListener("click", () => this.sendMessage());
}
// 離開頁面刪除事件
disconnectedCallback() {
this.button.removeEventListener("click", () => this.sendMessage());
}
});
</script>
第二種方法
不用addEventListener
this.button.onclick=()=>this.sendMessage()
// 離開頁面刪除事件
disconnectedCallback() {
this.button.onclick=null;
}
第三種方法
神奇的handleEvent函數
<app-element></app-element>
<script>
customElements.define("app-element", class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
handleEvent(event) {
if (event.type === "click"){
console.log(3)
}
}
connectedCallback() {
this.shadowRoot.innerHTML = "<button>👀 Press me!</button>";
this.button = this.shadowRoot.querySelector("button");
this.button.addEventListener("click", this);
}
disconnectedCallback() {
this.button.removeEvenetListener("click", this);
}
});
</script>
我們發現當我們簡單的放置this
瀏覽器會奇特找到.handleEvent
函數,存在就進行處理,這種方法我們可以通過檢測event.type
,我們可以通過這種方法進行集中處理
自定義事件
選擇項 | 值 | 描述 |
---|---|---|
detail |
假 | 包含我們要傳輸的所有信息的對象。 |
bubbles |
假 | 指示該事件是否應氣泡在DOM「到表面」或沒有。 |
composed |
假 | 指示傳播是否可以遍歷Shadow DOM。 |
cancelable |
假 | 指示是否可以使用取消行為.preventDefault() 。 |
事件傳遞冒泡
<div class="box1">
<div class="box2"></div>
</div>
<script>
let box2 = document.querySelector('.box2');
let box1 = document.querySelector('.box1');
box2.addEventListener('click',()=>{
box2.dispatchEvent(
new CustomEvent('messages', {
detail: {
message: 'hello'
},
bubbles:true,
})
)
})
box1.addEventListener('messages',(e)=>{
console.log(333);
console.log(e.detail);
})
</script>
bubbles=true
通過冒泡傳遞給父級,event.target
拿到dom元素,event.detail
拿到創建事件的數據
默認情況下
<div class="box1">
<div class="box2">
<div class="box3"></div>
</div>
</div>
<script>
let box1 = document.querySelector('.box1');
let box2 = document.querySelector('.box2');
let box3 = document.querySelector('.box3');
box3.addEventListener('click',()=>{
console.log(1);
box3.dispatchEvent(
new CustomEvent('messages', {
detail: {
message: 'hello'
},
bubbles:true,
})
)
})
box2.addEventListener('click',()=>{
console.log(2);
})
box1.addEventListener('click',()=>{
console.log(3);
})
</script>
我們發現默認情況下冒泡是從裡到外1,2,3
當我們在最外層添加
box3.addEventListener("messages", (event) => {
console.log(4);
},{capture:true});
我們發現執行的順序為1,4,2,3
跨組件的通信
組件1發送數據
<first-element></first-element>
customElements.define("first-element", class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
handleEvent(event) {
if (event.type === "click") {
const MessageEvent = new CustomEvent("messages", {
detail: {from: "Manz", message: ++this.i},
bubbles: true,
composed: true // 影子
});
this.dispatchEvent(MessageEvent);
}
}
connectedCallback() {
this.shadowRoot.innerHTML = `<button>點我</button>`;
this.shadowRoot.querySelector("button").addEventListener("click", this);
}
});
接受傳遞來的數據
customElements.define("second-element", class extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
}
handleEvent(event) {
if (event.type === "messages") {
event.detail.from = "Robot";
const data = event.detail;
this.shadowRoot.innerHTML = `
<div>
From ${data.from}:
<span style="color:red">${data.message}</span>
</div>
`;
}
}
connectedCallback() {
this.shadowRoot.innerHTML = `<div>No messages</button>`;
document.addEventListener("messages", this);
}
});
這樣想不想兩個異步組件之間的通信