中篇
Web Components 生命周期
关于自定义元素的 生命周期
♻️ 组件的生命周期
让我们看一下自定义元素的生命周期,下面是一份代码备注,方便开始快速开发。
class MyElementLifecycle extends HTMLElement {
// 元素初始化的时候执行
constructor() {
// HTMLElement.prototype.constructor.call(this)
super()
console.log('constructed!')
}
/**
* connectedCallback
* 当元素插入到 DOM 中时,将调用 connectedCallback。
* 这是运行安装代码的好地方,比如获取数据或设置默认属性。
* 可以将其与React的componentDidMount方法进行比较
* vue的mount方法作比较
*/
connectedCallback() {
console.log('connected!')
}
/**
* disconnectedCallback
* 只要从 DOM 中移除元素,就会调用 disconnectedCallback。清理时间到了!
* 我们可以使用 disconnectedCallback 删除事件监听,或取消记时。
* 但是请记住,当用户直接关闭浏览器或浏览器标签时,这个方法将不会被调用。
*
* 可以用window.unload beforeunload或者widow.close 去触发在浏览器关闭是的回调
*
* 可以与 react 中的 componentWillUnmount 的方法进行比较
* vue 中的 destory中是生命周期函数进行对比
*/
disconnectedCallback() {
console.log('disconnected!')
}
static get observedAttributes() {
return ['my-attr']
}
/**
*
* @param {*} name
* @param {*} oldVal
* @param {*} newVal
*
* 每当添加到observedAttributes数组的属性发生变化时,就会调用这个函数。使用属性的名称、旧值和新值调用该方法
* react 中的 static getDerivedStateFromProps(props, state) 有些类似
* 基本上和vue中的watch使用和observedAttributes + attributeChangedCallback使用雷同;
*/
attributeChangedCallback(name, oldVal, newVal) {
console.log(`Attribute: ${name} changed!`)
}
/**
* 每次将自定义元素移动到新文档时,都会调用 adoptedCallback。只有当您的页面中有 < iframe > 元素时,您才会遇到这个用例。
* 通过调用document.adoptnode (element)调用它,基本上用不上
*/
adoptedCallback() {
console.log('adopted!')
}
/**
* 生命周期的执行顺序 挂载的时候 按照react 或者vue中的执行顺序是相同的
* constructor -> attributeChangedCallback -> connectedCallback
*/
}
// 不是生命周期的API 但是非常重要 注册
window.customElements.define('my-element-lifecycle', MyElementLifecycle)
看过上面的的代码注释后,下面关于生命周期的介绍是重复的;
constructor() 初始化
constructor()
方法是元素挂载之前运行的,一般会在这个函数中设置一些元素的初始状态,还有事件的监听 以及创建 Shadow Dom
类比
Vue
中的create()
,React
中class
组件中的constructor()
它们两个是一样的
connectedCallback() 挂载完成
当元素插入到 DOM 中时,将调用 connectedCallback
, 通常,组件的一些样式
或者其他需要获取元素的状态
应该在 connectedCallback
进行设置,因为在这里才能确定元素的所有属性和子元素都是可用的。constructor
一般应该只用于初始化状态和设置shadow DOM
。
constructor
和 connectedCallback
之间的区别
创建元素时调用constructor
,
已经插入到 DOM
元素后调用 connectedCallback
可以将其与
vue
的mount
方法、React
的componentDidMount
方法进行比较
disconnectedCallback 卸载元素
当自定义元素
从DOM
中移除的时候,就会触发该方法;同时也会在这个事件中删除调改元素添加的事件,比如全局的键盘函数
以及延时函数
,
disconnectedCallback 的对应函数是 connectedCallback,
另外有个注意点,当用户关闭浏览器或关闭该标签页时,将不会调用此方法。不过可以用 window.unload beforeunload
或者 widow.close
去触发在浏览器关闭是的回调
react
中的componentWillUnmount
的方法进行比较,vue
中的destory
中是生命周期函数进行对比
attributeChangedCallback 属性监听
使用这个函数需要和 observedAttributes
该方法配合使用,需要将你要监听的属性在该方法中注册;
static get observedAttributes() {
return ['my-attr']
}
上面的函数中,就是对my-attr
属性进行了监听;当该属性的值发生改变的时候,就会触发attributeChangedCallback
/**
*
* @param {*} name 属性名称
* @param {*} oldVal 改变前的值
* @param {*} newVal 改变后的值
*
*/
attributeChangedCallback(name, oldVal, newVal) {
console.log(`Attribute: ${name} changed!`)
}
**注意:**只有在 observedAttributes
监听的值才会触发 attributeChangedCallback
react
中的static getDerivedStateFromProps(props, state)
与这个有些类似,以及vue
中的watch
函数
执行顺序
constructor -> attributeChangedCallback -> connectedCallback
;vue 以及 react 中的生命周期也是这样的一个顺序;
这个时候,可能会有些困惑了。
为什么 attributeChangedCallback
要在 connectedCallback
之前执行 ?
回想一下,web 组件上的属性主要用来初始化配置。这意味着当组件被插入DOM
时,这些配置需要可以被访问了。因此attributeChangedCallback
要在connectedCallback
之前执行。 这意味着你需要根据某些属性的值,在Shadow DOM
中配置任何节点,那么你需要在构造函数中引用这些节点,而不是在connectedCallback
中引用它们。
例如,如果你有一个ID
为container
的组件,并且你需要在根据属性的改变来决定是否给这个元素添加一个灰色的背景,那么你可以在构造函数中引用这个元素,以便它可以在attributeChangedCallback
中使用:
constructor() {
this.container = this.shadowRoot.querySelector('#container');
}
attributeChangedCallback(attr, oldVal, newVal) {
if(attr === 'disabled') {
if(this.hasAttribute('disabled') {
this.container.style.background = '#808080';
}
else {
this.container.style.background = '#ffffff';
}
}
}
如果你一直等到connectedCallback
再去创建this.container
。然后在第一时间调用attributeChangedCallback
,它还是不可用的。因此尽管你应该尽可能的延后你组件的connectedCallback
,但在这种情况下是不可能的。
customels.define
另外,在自定义元素未注册时候使用它,它会仅仅作为 HTMLUnknownElement
,一个实例;代表着一个无效的 HTML 元素,派生自 HTMLElement 接口,,但它没有任何可用的附加属性或方法
然后通过 customements.define()
注册时,它会继承HTMLElement
这个类。这个过程称为升级。
通过 customElements.whenDefined
方法,当自定义元素被定义时一个 Promise
返回{jsxref("undefined")}}
. 如果自定义元素已经被定义,则 resolve
立即执行
Promise<> customElements.whenDefined(name);
customElements.whenDefined('my-element').then(() => {
// my-element is now defined
})
浏览器的支持情况
Edge 在发布的 19 版本中开始提供支持。对于旧的浏览器,可以使用 polyfill 来兼容 IE11