- 深入理解小程序——生命周期
生命周期类型 不像其他框架,小程序是有页面(page) 和组件(Component) 两个概念,所以可以理解有两种生命周期。不过,你也可以使用组件也注册页面,然后在 [代码]lifetimes[代码] 字段里声明组件的生命周期,在 [代码]pageLifetimes[代码] 里声明页面的生命周期。 页面的生命周期有: onLoad onShow onReady onHide onUnload 而组件的生命周期有: created attached ready moved detached error 这里其实还有一个 linked 生命周期,存在父子关系时会触发 条件渲染的影响 组件可以通过 [代码]wx:if[代码] 和 [代码]hidden[代码] 来控制渲染的,这里对生命周期的触发也有影响。 如定义这么一个组件 [代码]log[代码]: [代码]Component({ lifetimes: { attached() { console.log('log attached') } } }) [代码] 使用 wx:if 然后在页面中使用 [代码]wx:if[代码] 条件渲染: [代码]<view wx:if="{{false}}"> <log /> </view> [代码] 此时不会触发 [代码]attached[代码],因此控制台没有输出。 使用 hidden 反而如果使用 [代码]hidden[代码] 条件渲染: [代码]<view hidden="{{true}}"> <log /> </view> [代码] 此时反而控制台会输出 [代码]log attached[代码]。 两者差异 其实两者的差异在于,[代码]hidden[代码] 会正常渲染 DOM,而 [代码]wx:if[代码] 则不会渲染。 如果组件的父元素使用 [代码]hidden[代码] 进行隐藏,那么此时 [代码]created[代码]、[代码]attached[代码]、[代码]ready[代码] 生命周期均会正常触发。如果在这些生命周期里获取子元素的尺寸,则所有值均返回 0。 如 TDesign 里面的 [代码]swipe-cell[代码] 需要计算 left 和 right 区域的大小;[代码]tabs[代码] 需要计算下划线的位置。 解决方案 比较简单的处理方式:建议用户使用 [代码]wx:if[代码] 而不是 [代码]hidden[代码],不过这明显是治标不治本的方案。 前文也提到了,问题的根本是没有正确地获取到元素的尺寸,因此可以在获取元素尺寸的地方做兼容处理。异常触发的条件则是 [代码]width == 0 && right == 0[代码] 知道在哪里需要兼容处理之后,需要解决的则是:如何在可以获取到正确的尺寸的时候重新获取尺寸呢? 此时可以使用 [代码]wx.createIntersectionObserver[代码] 这个 API。当 [代码]hidden = false[代码] 的时候,组件会重新出现在视图里,Observer 就会被触发,此时重新获取尺寸就能得到正确的尺寸信息了。以下是简易的封装: [代码]const getObserver = (context, selector) => { return new Promise((resolve, reject) => { wx.createIntersectionObserver(context) .relativeToViewport() .observe(selector, (res) => { resolve(res); }); }); }; const getRect = function (context:, selector) { return new Promise((resolve, reject) => { wx.createSelectorQuery() .in(context) .select(selector) .boundingClientRect((rect) => { if (rect) { resolve(rect); } else { reject(rect); } }) .exec(); }); }; export const getRectFinally = (context, selector) => { return new Promise((resolve, reject) => { getRect(context, selector).then(rect => { if (rect.width === 0 && rect.height === 0) { getObserver(context, selector).then(res => { resolve(res) }).catch(reject) } else { resolve(rect) } }).catch(reject) }) } [代码] 父子组件的影响 当存在父子组件的时候,可能很多人根本不知道各种生命周期的触发顺序。 之前 TDesign 的 [代码]cell-group[代码] 有个错误的实现,在 linked 生命周期里获取子元素进行操作: [代码]Component({ relations: { '../cell/cell': { type: 'child', linked() { this.updateLastChid(); }, }, }, updateLastChid() { const items = this.$children; items.forEach((child, index) => child.setData({ isLastChild: index === items.length - 1 })); }, }) [代码] 其实,存在父子组件的时候,生命周期是这么触发的: 父组件 created 子组件 created 父组件 attached 子组件 attached 父组件 linked(触发多次,次数 = 子组件数量) 子组件 linked 父组件 ready 子组件 ready 因此如果是这么使用 [代码]t-cell-group[代码]: [代码]<t-cell-group> <t-cell title="cell1" /> <t-cell title="cell2" /> <t-cell title="cell3" /> </t-cell-group> [代码] 那么子组件 cell 的 [代码]setData[代码] 触发次数为:1 + 2 + 3 = 6 次。 但其实开发者的预期应该是 1 次,所以 [代码]updateLastChid[代码] 应该放在父组件的 ready 方法里才符合预期。 总结 以上是在小程序开发的过程中,常遇到的问题。但如果没有像 TDesign 组件库这样深入开发小程序,可能并不会去深入钻研生命周期的细节。但在日常的业务开发当中,如果开发者能够清晰地理解各种生命周期的本质,在遇到其他问题的时候,也能比较快速地定位问题的关键点。 如果能到达这样的效果,也是笔者写下这篇文章的初心。 更多内容关注:https://github.com/LeeJim
2023-08-18 - 小程序选择本地文件上传
小程序选择本地文件上传,如何选择相应的文件进行上传?????不是图片!!!
2018-08-20