在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Document</title>
</head>
<body>
<div id='app'>
<!-- //3.使用組件 -->
<my-cpn></my-cpn>
</div>
<script src='../js/vue.js'></script>
<script>
//1.創建組件構造器對象(es6 ` 可以換行不用+)
const cpnC=Vue.extend({
template: `
<div>
<h2>我是標題</h2>
<p>我是內容,哈哈哈</p>
<p>我是內容,呵呵呵</p>
</div>`
})
//2.注冊組件
Vue.component('my-cpn',cpnC)
const app=new Vue({
el:'#app', //用于掛載要管理的元素
data:{ //定義數據
message: '你好啊,李銀河!',
name: 'codewhy'
}
})
</script>
</body>
</html>
在這里插入圖片描述
在這里插入圖片描述
注:必須在new Vue掛載的實例之下,才被渲染出來
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Document</title>
</head>
<body>
<div id='app'>
<cpn></cpn>
<cpn></cpn>
</div>
<div id='app2'>
<cpn></cpn>
</div>
<script src='../js/vue.js'></script>
<script>
//1.創建組件構造器對象(es6 ` 可以換行不用+)
const cpnC=Vue.extend({
template: `
<div>
<h2>我是標題</h2>
<p>我是內容,哈哈哈</p>
<p>我是內容,呵呵呵</p>
</div>`
})
//2.注冊組件(全局組件),意味著可以在多個Vue的實例下面使用
//Vue.component('my-cpn',cpnC)
const app=new Vue({
el:'#app', //用于掛載要管理的元素
data:{ //定義數據
message: '你好啊,李銀河!',
name: 'codewhy'
},components: {
//局部組件:
// cpn 使用組件時候的標簽名
cpn:cpnC
}
});
const app2=new Vue({
el:'#app2', //用于掛載要管理的元素
data:{ //定義數據
message: '你好啊,李銀河!',
name: 'codewhy'
}
})
</script>
</body>
</html>
在這里插入圖片描述
在這里插入圖片描述
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Document</title>
</head>
<body>
<div id='app'>
<cpn2></cpn2>
</div>
<div id='app2'>
<cpn></cpn>
</div>
<script src='../js/vue.js'></script>
<script>
//1.創建第一個組件
const cpnC1=Vue.extend({
template: `
<div>
<h2>我是標題1</h2>
<p>我是內容,哈哈哈</p>
</div>`
});
//2.創建第二個組件(父組件)
const cpnC2=Vue.extend({
template: `
<div>
<h2>我是標題2</h2>
<p>我是內容,呵呵呵</p>
<cpn1></cpn1>
</div>`,
components:{
cpn1:cpnC1
}
});
//root組件
const app=new Vue({
el:'#app', //用于掛載要管理的元素
data:{ //定義數據
message: '你好啊,李銀河!',
name: 'codewhy'
},components: {
//2.注冊:局部組件:
// cpn 使用組件時候的標簽名
cpn2:cpnC2
}
});
</script>
</body>
</html>
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Document</title>
</head>
<body>
<div id='app'>
<cpn></cpn>
</div>
<!-- 1.第一種 -->
<!-- <script type="text/x-template" id="cpn">
<div>
<h2>我是標題</h2>
<p>我是內容</p>
</div>
</script> -->
<!-- 2.第二種 -->
<template id="cpn">
<div>
<h2>我是標題</h2>
<p>我是內容</p>
</div>
</template>
<script src='../js/vue.js'></script>
<script>
//1.注冊一個全局組件
Vue.component('cpn',{
template:`#cpn`
})
const app=new Vue({
el:'#app', //用于掛載要管理的元素
data:{ //定義數據
message: '你好啊,李銀河!',
name: 'codewhy'
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Document</title>
</head>
<body>
<div id='app'>
<cpn></cpn>
</div>
<!-- 1.第一種 -->
<!-- <script type="text/x-template" id="cpn">
<div>
<h2>我是標題</h2>
<p>我是內容</p>
</div>
</script> -->
<!-- 2.第二種 -->
<template id="cpn">
<div>
<h2>我是標題</h2>
<p>我是內容</p>
</div>
</template>
<script src='../js/vue.js'></script>
<script>
const app=new Vue({
el:'#app', //用于掛載要管理的元素
data:{ //定義數據
message: '你好啊,李銀河!',
name: 'codewhy'
},
components: {
'cpn':{
template:'#cpn'
}
}
})
</script>
</body>
</html>
在這里插入圖片描述
注:組件內不能訪問data數據,所以Vue組件因該有自己保存數據的地方
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Document</title>
</head>
<body>
<div id='app'>
<cpn :cmovies='movies' :cmessage='message'></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<p> {{cmessage}}</p>
<h2>{{cmovies}}</h2>
</div>
</template>
<script src='../js/vue.js'></script>
<script>
// 父傳子:props
//局部組件
const cpn={
template:'#cpn',
//props: ['cmovies','cmessage']
props:{
//1.類型限制
//cmovies:Array,
//cmessage:String
//2.提供一些默認值
cmessage:{
type:String,
default:"aaaaa",
required:true ///必傳
},
//類型是對象或數組的時候,默認值必須是一個函數
cmovies:{
type:Array,
default(){
return []
}
}
}
}
const app=new Vue({
el:'#app', //用于掛載要管理的元素
data:{ //定義數據
message: '你好啊,李銀河!',
name: 'codewhy',
movies: ['海','王者','夏洛特']
},
components: {
'cpn':cpn
}
})
</script>
</body>
</html>
注:vue不支持直接的駝峰標識,需要進一步的轉換,如:childMyMessage,需要在綁定的時候使用:child-my-message
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Document</title>
</head>
<body>
<div id='app'>
<cpn :cinfo='info' :child-my-message="message"></cpn>
</div>
<template id="cpn">
<!-- 如果有多個標簽,需要外部有個div包起來 -->
<div>
<h2>{{cinfo}}</h2>
<h2>{{childMyMessage}}</h2>
</div>
</template>
<script src='../js/vue.js'></script>
<script>
const cpn={
template: '#cpn',
props:{
cinfo:{
type : Object,
default(){
return {}
}
},
childMyMessage:{
type : String,
default : ''
}
}
}
const app=new Vue({
el:'#app', //用于掛載要管理的元素
data:{ //定義數據
message: '你好啊,李銀河!',
name: 'codewhy',
info:{
name: 'why',
age: 18,
height: 1.88
}
},
components: {
'cpn':cpn
}
})
</script>
</body>
</html>
注:發射事件,自定義事件,子傳父;然后父組件監聽事件,注意名字不要用駝峰標識。
通過前面的學習,對自定義組件的相關概念和知識點也有了一定了解,今天我們就來學習一下給自定義元素及其子元素設置樣式的幾種方法。
index.html:
<style>
my-card{
display: block;
margin: 20px;
width: 200px;
height: 200px;
border: 3px solid #000;
}
</style>
<my-card></my-card>
<script src="./index.js"></script>
index.js:
class MyCard extends HTMLElement {
constructor() {
super();
this.shadow=this.attachShadow({ mode: "open" });
}
}
window.customElements.define("my-card", MyCard);
結果樣式生效:
需要注意的是:繼承自 HTMLElement 的自定義元素,其 style.display 默認為 inline。
由以上結果可以推論出:
在 style 標簽中增加如下樣式:
<style>
my-card {
display: block;
margin: 20px;
width: 200px;
height: 200px;
border: 3px solid #000;
}
.card-header {
padding: 10px;
font-weight: bold;
background-color: yellow;
}
</style>
<my-card></my-card>
<script>
class MyCard extends HTMLElement {
constructor () {
super();
this.shadow=this.attachShadow({mode: "open"});
let headerEle=document.createElement("div");
headerEle.className="card-header";
headerEle.innerText="My Card";
this.shadow.appendChild(headerEle);
}
}
window.customElements.define("my-card", MyCard);
</script>
結果:不生效。
通過前面的學習,我們知道:自定義元素內部實際上是一個 Shadow DOM,它和主 DOM 是相互隔離的,所以,主 DOM 中的樣式是影響不到 Shadow DOM 的。
這里分為兩種場景:在主 DOM 使用 JS 、在 Custom Elements 構造函數中使用 JS。
第一種:在主 DOM 使用 JS 給 Shadow DOM 增加 style 標簽:
<style>
my-card {
display: block;
margin: 20px;
width: 200px;
height: 200px;
border: 3px solid #000;
}
</style>
<my-card>
</my-card>
<script>
class MyCard extends HTMLElement {
constructor () {
super();
this.shadow=this.attachShadow({mode: "open"});
let headerEle=document.createElement("div");
headerEle.className="card-header";
headerEle.innerText="My Card";
this.shadow.appendChild(headerEle);
}
}
window.customElements.define("my-card", MyCard);
// 給 Shadow DOM 增加 style 標簽
let styleEle=document.createElement("style");
styleEle.textContent=`
.card-header{
padding:10px;
background-color: yellow;
font-size: 16px;
font-weight: bold;
}
`;
document.querySelector("my-card").shadowRoot.appendChild(styleEle);
</script>
效果如下:
第二種:在 Custom Elements 構造函數中使用 JS 增加 style 標簽:
<style>
my-card {
display: block;
margin: 20px;
width: 200px;
height: 200px;
border: 3px solid #000;
}
</style>
<my-card>
</my-card>
<script>
class MyCard extends HTMLElement {
constructor () {
super();
this.shadow=this.attachShadow({mode: "open"});
let styleEle=document.createElement("style");
styleEle.textContent=`
.card-header{
padding:10px;
background-color: yellow;
font-size: 16px;
font-weight: bold;
}
`;
this.shadow.appendChild(styleEle);
let headerEle=document.createElement("div");
headerEle.className="card-header";
headerEle.innerText="My Card";
this.shadow.appendChild(headerEle);
}
}
window.customElements.define("my-card", MyCard);
</script>
效果如下:
就以上兩種方式來說,第二種更符合組件化的特征,并且使用第一種方式時要注意,如果將添加 style 標簽的代碼放在定義 Custom Elements 之前會報錯(找不到自定義元素)。
這里使用 JS 創建 link 標簽,然后引入 CSS 文件給自定義元素內部的子元素設置樣式,代碼如下:
<style>
my-card {
display: block;
margin: 20px;
width: 200px;
height: 200px;
border: 3px solid #000;
}
</style>
<my-card>
</my-card>
<script>
class MyCard extends HTMLElement {
constructor () {
super();
this.shadow=this.attachShadow({mode: "open"});
let linkEle=document.createElement("link");
linkEle.rel="stylesheet";
linkEle.href="./my_card.css";
this.shadow.appendChild(linkEle);
let headerEle=document.createElement("div");
headerEle.className="card-header";
headerEle.innerText="My Card";
this.shadow.appendChild(headerEle);
}
}
window.customElements.define("my-card", MyCard);
</script>
my_card.css 代碼如下:
.card-header{
padding:10px;
background-color: yellow;
font-size: 16px;
font-weight: bold;
}
效果如下:
當然,這里也可以在主 DOM 中使用 JS 給 Shadow DOM 引入 CSS 文件,但是,這樣做不符合組件化的特征,所以略過。
以上就是給自定義元素及其子元素進行樣式設置的基本方法總結。
~
~ 本文完,感謝閱讀!
~
學習有趣的知識,結識有趣的朋友,塑造有趣的靈魂!
大家好,我是〖編程三昧〗的作者 隱逸王,我的公眾號是『編程三昧』,歡迎關注,希望大家多多指教!
今前端編程中,利用語義化的 HTML 結合 CSS 來完一個組件并不是一件難事,這也意味著無論在 React、Vue 中都可以插入,不過它倆不是今天的主角,接下來我將用一個例子來介紹如何封裝一個完整的原生 HTML 的 Web Components 組件,讓我們開始吧!
首先我們來了解下 HTML 中的 <details> 元素,它可以用于創建一個小部件,其中包含僅在小部件處于“打開”狀態時才可見的附加信息,<details>元素內可以包含的內容沒有任何限制。
默認情況下,元素創建的小部件<details>處于“關閉”狀態(open標簽可使其打開)。通過單擊小部件在“打開”和“關閉”狀態之間切換,以顯示或隱藏標簽中包含的附加信息,內部標簽 <summary> 元素則可為該部件提供概要。
一個簡單的例子如下:
<details>
<summary> 不能說的秘密 </summary>
藏的這么深,可還是被你發現了
</details>
details {
border: 1px solid #aaa;
border-radius: 4px;
padding: .5em .5em 0;
}
summary {
font-weight: bold;
margin: -.5em -.5em 0;
padding: .5em;
}
details[open] {
padding: .5em;
}
details[open] summary {
border-bottom: 1px solid #aaa;
margin-bottom: .5em;
}
使用語義化 HTML 的優點:頁面內容結構更清晰,方便開發者閱讀,更利于瀏覽器的理解與加載,搜索引擎解析與SEO優化。
原生元素默認的樣式很簡陋,因此我們需要為其定制一下樣式,這塊內容我們簡單帶過,只講解關鍵部分,樣式內容有省略,具體可以在文末的碼上掘金中看到呈現效果。
.ContentWarning > summary {
position: relative;
list-style: none; /** 去除默認樣式 **/
user-select: none;
cursor: pointer;
/** 為其添加一個斜線背景 **/
--stripe-color: rgb(0 0 0 / 0.1);
background-image: repeating-linear-gradient(45deg,
transparent,
transparent 0.5em,
var(--stripe-color) 0.5em,
var(--stripe-color) 1em);
}
/** 通過var變量調整懸停時的顏色樣式 **/
.ContentWarning>summary: hover,
.ContentWarning>summary: focus {
--stripe-color: rgb(150 0 0 / 0.1);
}
現在我們來把它封裝成一個完整的組件,這需要先將 HTML 編寫在模板 template 當中,并設置一個 id,如下所示:
<template id="warning-card">
<details class="ContentWarning">
<summary>
<strong>?? 注意:</strong> 以下為隱藏內容
</summary>
<slot name="desc"> 藏的這么深,可還是被你發現了 </slot>
</details>
</template>
熟悉 Vue 的小伙伴應該很容易理解上面的代碼,結構很相似,不過網頁不會直接渲染它包裹的內容。此外我們還對此模板設置了一個插槽 slot,后面會講到它的作用。
有了上面封裝好的模板,我們就需要在 JS 中定義成可用組件來讓其能夠被使用,調用 window 下的 customElements.define 方法,第一個參數是傳入組件名稱,我們定義組件名為: warning-card ,第二個參數傳入一個繼承了 HTMLElement 的類,在其構造方法當中獲取并克隆一個新的 HTML 節點,它會通過 appendChild 渲染到頁面當中。
window.customElements.define('warning-card',
class extends HTMLElement {
constructor() {
super();
var templateElem=document.getElementById('warning-card');
var content=templateElem.content.cloneNode(true);
this.appendChild(content);
}
})
接著我們就可以在頁面中把它當作組件那樣使用了:
<warning-card> </warning-card>
回頭看看上面我們模板中設置的插槽 slot,此時還是沒有生效的,我們需要稍微改寫一下構造函數中的渲染方式,將 web 組件定義為一個 Shadow DOM,這樣構造的是一個可以將標記結構、樣式和行為隱藏起來,并與頁面上的其他代碼相隔離,保證不同的部分不會混在一起的獨立元素,并在最后使用 Node.cloneNode() 方法添加了模板的拷貝到 Shadow 的根結點上。
window.customElements.define('warning-card',
class extends HTMLElement {
constructor() {
super();
var template=document.getElementById('warning-card').content;
this.attachShadow({ mode: 'open' }).appendChild(template.cloneNode(true));
}
})
現在我們嘗試使用下組件,往其內容添加一個圖片,指向名為 desc 的 slot 插槽中:
<warning-card>
<img slot="desc" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ba825ffee78c4a1b9c0232e5d2f1d048~tplv-k3u1fbpfcp-watermark.image?" />
</warning-card>
這時你會發現,圖片插入到 details 元素的隱藏區域當中了,slot 已經成功生效,但是樣式卻消失了,這時因為組件已經被完全隔離,我們需要將樣式作用在其內部才會生效。
<template id="warning-card">
<style>
<!-- TODO: 組件的樣式 -->
</style>
<details class="ContentWarning">
<summary>
<strong>?? 注意:</strong>
</summary>
<slot name="desc">THE DESCRIPTION</slot>
</details>
</template>
這樣組件就正常了:
除了定制模板中的插槽,我們也可以通過 HTML 標簽屬性來實現一些簡單的傳參,例如在 summary 標簽中顯示一個標題:
<warning-card title="前方高能">
</warning-card>
我們只需要在模板中定義好這個標題的位置:
<template id="warning-card">
<details class="ContentWarning">
<summary>
<!-- TODO: 模板中加入一個span標簽 -->
<strong>?? 注意:</strong> <span id="title"></span>
</summary>
</details>
</template>
最后在構造函數中我們通過 document 的原生方法寫入模板中就可以了:
window.customElements.define('warning-card',
class extends HTMLElement {
constructor() {
super();
var template=document.getElementById('warning-card').content;
// TODO: 找到title標簽,寫入傳入組件的title屬性值
template.querySelector('#title').innerText=this.getAttribute('title');
this.attachShadow({ mode: 'open' }).appendChild(template.cloneNode(true));
}
})
至此,我們通過一個簡單的原生組件學習了如何編寫 Web Components,可以在此代碼片段中查看具體源碼:原生Web Components組件 - 碼上掘金原生Web Components組件 - 碼上掘金。
以上就是文章的全部內容,希望對你有所幫助!如果覺得文章寫的不錯,可以點贊收藏,也歡迎關注,我會持續更新更多前端有用的知識與實用技巧,我是茶無味de一天,希望與你共同成長~
*請認真填寫需求信息,我們會在24小時內與您取得聯系。