红叶何时落水
组件化开发有利于代码的复用
这里是原生组件的思想与构建
首先,代码复用,就意味着会有一个模板,这个模板已经实现了基础功能,我们只需要调用它即可;那么一个组件由两部分构成,视图与逻辑。
视图模板
视图的模板原生提供的方法为<template>标签,里面的内容可以被其他页面使用;
<template id="new_message">
<style>
.main {
width: 300px;
height: 100px;
background-color: aqua;
display: flex;
flex-direction: column;
position: absolute;
top: 40%;
left: 40%;
}
.title {
width: 100%;
height: 30px;
text-align: center;
line-height: 30px;
}
input {
width: 80%;
height: 20px;
margin-left: 10%;
}
.button {
width: 100%;
height: 50px;
display: flex;
flex-direction: row;
justify-content: space-around;
}
button {
margin-top: 10px;
align-items: center;
justify-items: center;
height: 60%;
width: 30%;
}
</style>
<div class="main">
<slot name = 'title' class="title">old_title</slot>
<input type="text">
<div class="button">
<button id="Yes" >确定</button>
<button id="No">放弃</button>
</div>
</div>
</template>
注意两个点
1.<template>需要一个id,方便逻辑层来获取他;
2.<slot>插槽的使用
上面的模板有一个插槽<slot name = 'title' class="title">old_title</slot>
这个插槽代表一个坑位,如果我们在使用组件的时候不向这个坑位添加内容时,他会显示默认内容;
然后,就是逻辑的模板
逻辑模板是一段可复用的代码,并且多次调用,不会相互干扰。
那么符合以上条件的是什么? 最简单的例子,函数。
我们定义一个函数,并且使函数内部的变量都使局部变量。那么,我们不管什么时候调用他,都会有一套全新的变量;
那么我们的逻辑模板,同样是一个函数;先来学会构建简单的模板--类--。
function Dad (name) {
this.name = name;
// console.log(this.name);
}
Dad.prototype.Dad_method1 = function () {
console.log(this.name)
}
function son (name) {
Dad.call(this, name);// 调用Dad(),同时将Dad的方法映射到son绑定的this对象,并且将Dad的this指向从¥---->son的this指向地址(对象a的地址)
// this.Dad_method1();// 这里等同于外面的a.Dad_method1();
}
son.prototype = new Dad('Init');// 这里Dad实例化,this指向实例化Dad的地址¥。
son.prototype.constructor = son;
let a = new son('Init2')
// a.Dad_method1();
class Dad {
constructor (name) {
this.name = name;
}
Dad_method1 () {
console.log(this.name)
}
}
class son extends Dad {
constructor (name) {
super(name);// 等同于 Dad.call(this, name);// 调用Dad(),同时将Dad的方法映射到son绑定的this对象
this.Dad_method1();
}
}
let a = new son ('Init2');
// a.Dad_method1();
只提几个关键概念
1.this指向的是第一个调用函数的对象的地址。
2.call()调用一下函数,并且把被调用函数的this指向修改为我们第一个参数传入的地址;
3.super() 作用等同于 fn().call(this);
4.ES6之前我们需要手动设置constructor指向
son.prototype.constructor = son;
5.son继承Dad的方法,使之变成自己的方法,思想感觉有点像vue的代理
/** 将 某一个对象的属性 访问 映射到 对象的某一个属性成员上 */
function proxy( target, prop, key ) {
Object.defineProperty( target, key, {
enumerable: true,
configurable: true,
get () {
return target[ prop ][ key ];
},
set ( newVal ) {
target[ prop ][ key ] = newVal;
}
} );
}
明白的类的使用之后,我们就可以构建组件的逻辑模板了。
class newElement extends HTMLElement {
constructor() {
super();// 不需要向HTMLElement的构造函数传参,只需要改变this指向,让我们可以使用HTMLElement的方法与属性
this.Init();
}
Init() {
const shadowRoot = this.attachShadow({ mode: 'open' });// 建立一个ShadowDom用于存放我们的组件,与非组件分开
var temp_ele = document.getElementById('new_message');
var content = temp_ele.content.cloneNode(true);// 去除template这个外壳
shadowRoot.appendChild(content);
// 绑定事件
shadowRoot.querySelector('#Yes')
.addEventListener('click', (e) => {
console.log('Yes');
})
shadowRoot.querySelector('#No')
.addEventListener('click', (e) => {
console.log('No');
})
this.attrs = this.getAttribute("attrs");
}
}
window.customElements.define('new-ele-name', newElement);
再说关键几点
我们需要继承HTMLElement的某些方法,以便我们可以使用一些基本的方法。
class newElement extends HTMLElement {
constructor() {
super();// 不需要向HTMLElement的构造函数传参,只需要改变this指向,让我们可以使用HTMLElement的方法与属性
this.Init();
}
之后,我们建立一个ShadowDom与页面的其他元素分开,以保证组件的独立,以及不受到公共样式的干扰。
Init() {
const shadowRoot = this.attachShadow({ mode: 'open' });// 建立一个ShadowDom用于存放我们的组件,与非组件分开
我们获取模板内容,将其添加到ShadowDom上 ;
var temp_ele = document.getElementById('new_message');
var content = temp_ele.content.cloneNode(true);// 去除template这个外壳
shadowRoot.appendChild(content);
然后,对页面添加一些交互事件
shadowRoot.querySelector('#Yes')
.addEventListener('click', (e) => {
console.log('Yes');
})
shadowRoot.querySelector('#No')
.addEventListener('click', (e) => {
console.log('No');
})
我们可以利用属性作为对外开放的接口,以此实现通信
设置属性attrs
<new-ele-name attrs = '2'>
<text slot="title">new_title</text>
</new-ele-name>
获取属性
this.attrs = this.getAttribute("attrs");
}
}
关于插槽,有一个点,插槽与ShadowDom同级,ShadowDom里的插槽指向外面的插槽。自己试一下就明白了。
最后,注册组件(组件名字必须有"-"连接)
window.customElements.define('new-ele-name', newElement);
使用组件,再body里写入;
<new-ele-name attrs = '2'>
<text slot="title">new_title</text>
</new-ele-name>
完整代码
<html>
<body>
<new-ele-name attrs = '2'>
<text slot="title">new_title</text>
</new-ele-name>
<template id="new_message">
<style>
.main {
width: 300px;
height: 100px;
background-color: aqua;
display: flex;
flex-direction: column;
position: absolute;
top: 40%;
left: 40%;
}
.title {
width: 100%;
height: 30px;
text-align: center;
line-height: 30px;
}
input {
width: 80%;
height: 20px;
margin-left: 10%;
}
.button {
width: 100%;
height: 50px;
display: flex;
flex-direction: row;
justify-content: space-around;
}
button {
margin-top: 10px;
align-items: center;
justify-items: center;
height: 60%;
width: 30%;
}
</style>
<div class="main">
<slot name = 'title' class="title">old_title</slot>
<input type="text">
<div class="button">
<button id="Yes" >确定</button>
<button id="No">放弃</button>
</div>
</div>
</template>
<!-- <button onclick="a.open()">启动组件</button> -->
</body>
<!-- <script src="../前端笔记/组件js.js"></script> -->
<script>
class newElement extends HTMLElement {
constructor() {
super();// 不需要向HTMLElement的构造函数传参,只需要改变this指向,让我们可以使用HTMLElement的方法与属性
this.Init();
}
Init() {
const shadowRoot = this.attachShadow({ mode: 'open' });// 建立一个ShadowDom用于存放我们的组件,与非组件分开
var temp_ele = document.getElementById('new_message');
var content = temp_ele.content.cloneNode(true);// 去除template这个外壳
shadowRoot.appendChild(content);
// 绑定事件
shadowRoot.querySelector('#Yes')
.addEventListener('click', (e) => {
console.log('Yes');
})
shadowRoot.querySelector('#No')
.addEventListener('click', (e) => {
console.log('No');
})
this.attrs = this.getAttribute("attrs");
}
}
window.customElements.define('new-ele-name', newElement);
// var a = new use_message();
</script>
</html>
当然,我们可以向高阶函数那样,加个高阶类,来修饰一下基础组件
这里使用第二种调用组件的方法
如果理解了类,就可以看懂
<html>
<body>
<!-- <new-ele-name attrs = '2'>
<text slot="title">new_title</text>
</new-ele-name> -->
<template id="new_message">
<style>
.main {
width: 300px;
height: 100px;
background-color: aqua;
display: flex;
flex-direction: column;
position: absolute;
top: 40%;
left: 40%;
}
.title {
width: 100%;
height: 30px;
text-align: center;
line-height: 30px;
}
input {
width: 80%;
height: 20px;
margin-left: 10%;
}
.button {
width: 100%;
height: 50px;
display: flex;
flex-direction: row;
justify-content: space-around;
}
button {
margin-top: 10px;
align-items: center;
justify-items: center;
height: 60%;
width: 30%;
}
</style>
<div class="main">
<slot name='title' class="title">old_title</slot>
<input type="text">
<div class="button">
<button id="Yes">确定</button>
<button id="No">放弃</button>
</div>
</div>
</template>
<button onclick="a.open()">启动组件</button>
</body>
<!-- <script src="../前端笔记/组件js.js"></script> -->
<script>
class newElement extends HTMLElement {
constructor() {
super();// 不需要向HTMLElement的构造函数传参,只需要改变this指向,让我们可以使用HTMLElement的方法与属性
this.Init();
}
Init() {
const shadowRoot = this.attachShadow({ mode: 'open' });// 建立一个ShadowDom用于存放我们的组件,与非组件分开
var temp_ele = document.getElementById('new_message');
var content = temp_ele.content.cloneNode(true);// 去除template这个外壳
shadowRoot.appendChild(content);
// 绑定事件
shadowRoot.querySelector('#Yes')
.addEventListener('click', (e) => {
console.log('Yes');
})
shadowRoot.querySelector('#No')
.addEventListener('click', (e) => {
console.log('No');
})
this.attrs = this.getAttribute("attrs");
}
}
window.customElements.define('new-ele-name', newElement);
class use_message {
constructor() {
this.el = document.createElement('new-ele-name');
this.Init();
}
Init() {
this.el.shadowRoot.querySelector('#Yes')
.addEventListener('click', (e) => {
this.close();
})
this.el.shadowRoot.querySelector('#No')
.addEventListener('click', (e) => {
this.close();
})
}
open() {
document.body.appendChild(this.el);
}
close() {
document.body.removeChild(this.el);
}
}
var a = new use_message();
</script>
</html>