官方教程地址
一. webComponent - customElements
创建的自定义元素,因为它不仅提供一个元素容器,还自带了生命周期函数,我们可以在这些钩子函数中进行加载渲染等操作,从而简化步骤。
// /src/element.js
// 自定义元素
class MyElement extends HTMLElement {
// 声明需要监听的属性名,只有这些属性变化时才会触发attributeChangedCallback
static get observedAttributes () {
return ['name', 'url']
}
constructor() {
super();
}
connectedCallback() {
// 元素被插入到DOM时执行,此时去加载子应用的静态资源并渲染
console.log('micro-app is connected')
}
disconnectedCallback () {
// 元素从DOM中删除时执行,此时进行一些卸载操作
console.log('micro-app has disconnected')
}
attributeChangedCallback (attr, oldVal, newVal) {
// 元素属性发生变化时执行,可以获取name、url等属性的值
console.log(`attribute ${attrName}: ${newVal}`)
}
}
/**
* 注册元素
* 注册后,就可以像普通元素一样使用micro-app,当micro-app元素被插入或删除DOM时即可触发相应的生命周期函数。
*/
window.customElements.define('micro-app', MyElement)
二. shadowDom
1. 基本理解
Shadow DOM 允许将隐藏的 DOM 树附加到常规的 DOM 树中——它以 shadow root 节点为起始根节点,在这个根节点的下方,可以是任意元素,和普通的 DOM 元素一样。
这里,有一些 Shadow DOM 特有的术语需要我们了解:
- Shadow host:一个常规 DOM 节点,Shadow DOM 会被附加到这个节点上。
- Shadow tree:Shadow DOM 内部的 DOM 树。
- Shadow boundary:Shadow DOM 结束的地方,也是常规 DOM 开始的地方。
- Shadow root: Shadow tree 的根节点。
你可以使用同样的方式来操作 Shadow DOM,就和操作常规 DOM 一样——例如添加子节点、设置属性,以及为节点添加自己的样式(例如通过
element.style属性),或者为整个 Shadow DOM 添加样式(例如在<style>元素内添加样式)。不同的是,Shadow DOM 内部的元素始终不会影响到它外部的元素(除了:focus-within),这为封装提供了便利。
2. 进行css隔离的原因
Shadow DOM 内部的样式不会影响外部, 外部的样式不会影响shadow Dom的元素的样式, 例如下面案例中同样class 的标签,不会相互影响样式
3. 基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.haha {
background-color: pink;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="inner"></div>
<div class="haha">哈哈哈哈</div>
</div>
</body>
<script>
const inner = document.querySelector('.inner');
const innerShadow = inner.attachShadow({mode: 'open'});
const div = document.createElement('div');
div.innerHTML = "haha";
div.className = 'haha'
const style = document.createElement('style');
style.textContent = `
.haha {
color: #ff0000;
}
`;
innerShadow.appendChild(style)
innerShadow.appendChild(div)
</script>
</html>
三.Proxy 代理
1.基本定义
- Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
- Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
2.基本用法
const handler = {
get: function(obj, prop) {
return prop in obj ? obj[prop] : 37;
}
};
const p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;
console.log(p.a, p.b); // 1, undefined
console.log('c' in p, p.c); // false, 37
3. 一个完整的 traps 列表示例
/*
var docCookies = ... get the "docCookies" object here:
https://developer.mozilla.org/zh-CN/docs/DOM/document.cookie#A_little_framework.3A_a_complete_cookies_reader.2Fwriter_with_full_unicode_support
*/
var docCookies = new Proxy(docCookies, {
"get": function (oTarget, sKey) {
return oTarget[sKey] || oTarget.getItem(sKey) || undefined;
},
"set": function (oTarget, sKey, vValue) {
if (sKey in oTarget) { return false; }
return oTarget.setItem(sKey, vValue);
},
"deleteProperty": function (oTarget, sKey) {
if (sKey in oTarget) { return false; }
return oTarget.removeItem(sKey);
},
"enumerate": function (oTarget, sKey) {
return oTarget.keys();
},
"ownKeys": function (oTarget, sKey) {
return oTarget.keys();
},
"has": function (oTarget, sKey) {
return sKey in oTarget || oTarget.hasItem(sKey);
},
"defineProperty": function (oTarget, sKey, oDesc) {
if (oDesc && "value" in oDesc) { oTarget.setItem(sKey, oDesc.value); }
return oTarget;
},
"getOwnPropertyDescriptor": function (oTarget, sKey) {
var vValue = oTarget.getItem(sKey);
return vValue ? {
"value": vValue,
"writable": true,
"enumerable": true,
"configurable": false
} : undefined;
},
});
/* Cookies 测试 */
alert(docCookies.my_cookie1 = "First value");
alert(docCookies.getItem("my_cookie1"));
docCookies.setItem("my_cookie1", "Changed value");
alert(docCookies.my_cookie1);
四.正则表达式
先看micro-app里面的正则
/^[\s\S]+{/
/^((html[\s>~,]+body)|(html|body|:root))/
1.定义
是用来描述字符串内容格式,使用它通常用于匹配一个字符串的内容是否符合格式要求。
2.字符串的正则方法
- split:根据匹配字符串切割父字符串
- match:使用正则表达式与字符串相比较,返回一个包含匹配结果的数组
- search:使用正则表达式或指定字符串进行搜索,返回第一个出现的匹配项的下角标
- replace:使用正则表达式和字符串比较,然后用新的子串来替换被匹配的子串
3.正则表达式方法
- exec:在目标字符串中执行一次正则匹配操作
在全局模式下,当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把正则表达式对象的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。这就是说,您可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。 - test:测试当前正则是否能匹配目标字符串
4.[] 字符集
简单类:就是任意多个字符进行集合书写,多个字符连续书写。
比如:[abc]
范围类:有时匹配的东西过多,而且类型又相同,全部输入太麻烦,我们可以在中间加了个横线。
比如:[0-9] [A-Z] [a-z]
组合类:允许用中括号匹配不同类型的单个字符,或者是一类及多类,或者一些单一的符号。
比如:[0-9a-z]
5.修饰符 g i
g 修饰符用于执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
i 修饰符用于执行对大小写不敏感的匹配。
6.边界符 ^ $
^ 开头 表示字符串必须以^后面的内容作为开头。
前面匹配的结果必须位于字符串最后
7.预定义类 \d \D \s \S \w \W
\d [0-9] \d可以匹配一个任意的数字字符
\D [^0-9] \D 匹配任意一个不是数字的字符
\s 可以匹配任意的空白。
\S 可以匹配任意的不是空白的字符。
\w 可以匹配任意的字母、数字或下划线。
\W 可以匹配任意的字母、数字、下划线以外的内容。
量词
如果需要将正则的某个符号连续匹配多次,可以使用量词对符号进行修饰。量词的写法:{},内部书写对应的数字,表示量词是多少。
| 词 | 软硬 | 出现次数 |
|---|---|---|
| {n} | 硬性量词 | 对应零次或者n次 |
| {n,m} | 软性量词 | 至少出现n次但不超过m次(中间不能有空格) |
| {n,} | 软性量词 | 至少出现n次 |
| ? | 软性量词 | 出现零次或一次 |
| * | 软性量词 | 出现零次或多次(任意次) |
| + | 软性量词 | 出现一次或多次(至少一次) |
中文匹配
固定字符集写法:[\u4e00-\u9fa5]
可以匹配一个任意的中文汉字。
分组
正则中使用()表示分组,内部的内容会作为一个整体进行操作。
后期如果小括号后面有量词,表示小括号内部整体重复匹配多少次。
/(byebye){2}/.test(“byebye”) true
正则表达式不包含某些字符
^: 以·····开头的字符串
$:以·····结尾的字符串
如:
只包含数字字母: [0-9a-zA-Z]
以数字开头: ^[0-9]
以字母结尾:[a-zA-Z]$
而不包含数字字母:
[^0-9a-zA-A] :那个开始符是写在中括号里面的!!
五.观察者模式
观察者模式定义了观察者和被观察者的一对多的依赖关系,它们之间存在直接的联系;当被观察者发布通知时,所有依赖于它的观察者都将得到通知。
╭─────────────╮ Fire Event ╭──────────────╮
│ │─────────────>│ │
│ Subject │ │ Observer │
│ │<─────────────│ │
╰─────────────╯ Subscribe ╰──────────────╯
六.发布订阅模式
发布订阅模式中,发布者发布消息时不会将消息直接发送给订阅者,发布者和订阅者之间不存在直接的联系;在发布者和订阅者之间存在第三方平台,称为消息代理或调度中心或中间件,它维持着发布者和订阅者之间的联系,可以处理所有发布者发布的消息并将它们分发给对应的订阅者,实现了发布者与订阅者之间的解耦。
╭─────────────╮ ╭───────────────╮ Fire Event ╭──────────────╮
│ │ Publish Event │ │───────────────>│ │
│ Publisher │────────────────>│ Event Channel │ │ Subscriber │
│ │ │ │<───────────────│ │
╰─────────────╯ ╰───────────────╯ Subscribe ╰──────────────╯
七.观察者模式 和 发布订阅模式的差异
- 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
- 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
- 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。
- 观察者 模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。
尽管它们之间有区别,但有些人可能会说发布-订阅模式是观察者模式的变异,因为它们概念上是相似的。
八.setAttribute
增加一个指定名称和治的新属性,或者把一个现有属性设定为指定的值 elementNode.setAttribute(name,value)
注意:
把指定的属性设置为指定的值。如果不存在具有指定名称的属性,该方法将创建一个新属性。
九.eval('this')和(0,eval)('this')的区别是
1.在ecma规范中,eval存在直接调用和间接调用两种方式,而直接调用时上下文为当前执行环境,间接调用时上下文为全局环境
2.直接调用eval时,为直接调用,而使用表达式计算得到的eval是间接调用
这样就很明了了,eval('this')和(0,eval)('this')的区别是,一个是在当前执行环境下,一个是在全局执行环境下,后面的调用方式才可百分百确定指向的是全局宿主对象