Closed leifeng closed 2 years ago
这个是由于useEffect
的触发时机导致的,因为小程序中的taro
的useEffect
与h5中的行为稍微有点不一样
触发useEffect
时小程序节点其实并没有挂载完成,所以导致获取不到对应的元素,自然而然也拿不到元素的属性。
推荐使用useReady
代替useEffect
,等待元素真正装载完毕后就能获取到其属性啦
function A() {
const id = "abc"
useReady(() => {
const node = $('#' + id);
node.height().then((res) => {
console.log('useReady',res);
});
});
return <View>
<View id={id}>
<Text>一堆文字</Text>
</View>
</View>
}
@Barrierml 不行,我传了示例代码,帮忙给看看
@Barrierml 不行,我传了示例代码,帮忙给看看
hello呀,很抱歉之前没有注意到你issue里写的是在插件内,我根据你的示例仓库又查看了一下,发现这其实是一个小程序本身的问题
@tarojs/extend
提供的 $
在内部调用的是 wx.createSelectorQuery().select('some id').boundingClientRect
来获取元素的属性
在2.0时期,也有人提过类似问题了可以查看这里
而在小程序的插件内部使用wx.createSelectorQuery()
创建一个查询器直接去查询时,是会查询不到当前插件页面内的元素的,这其实与taro
本身无关,是在插件内必须使用.in
来手动圈定查询域。
因为我发现你是在子组件内使用的,大概不能用社区推荐的解决方案 通过 this.props.$scope 手动圈定范围
因为这个参数只会在页面组件内传入。
在任何子组件内都可以使用的解决方案
// 获取当前页面实例
const pageInstance = getCurrentPages()[getCurrentPages().length - 1];
// 获取元素尺寸相关信息
Taro
.createSelectorQuery()
.in(pageInstance)
.select('#' + id)
.boundingClientRect((res) => { console.log('onReady', res) })
.exec();
请注意这些代码一定要在
useReady
内使用哦 感觉@tarojs/extend
如果在插件环境下也应该手动指向一下,周末我试着提个PR😊
接下来要说的可能和本次issue无关,写出来单纯的因为我在查看这个问题的过程中碰到的有趣(弯路)事
我开始查这个问题的思路大概是这样的 |检查在小程序内的行为。 => 👌 能获取到 |检查组件生命周期 => 👌 完全正常 |尝试控制台手动获取实例 => 👌能获取到 到这里我就开始怀疑是不是wx环境的问题了,检查了一下,果然插件内的wx实例与小程序内部的wx实例是相隔离开来的
于是我的思路从taro
本身放在了小程序上,经过查看wx.createSelectorQuery
的行为与代码分析
最终发现了一段代码
_selectorQuery._push = function (t, n, o, r, a) {
// 如果没有用 in 手动圈选过范围的话就用默认的组件,也就是当前页面
null === this._webviewId && (this._webviewId = this._defaultComponent ? this._defaultComponent.__wxWebviewId__ : void 0);
// 这里的e变量,如果存在,则 i 为空,所以在查找时就没有可查元素
var i = e ? "" : __virtualDOM__.getRootNodeId(this._webviewId);
// 任务队列
this._queue.push({
// 押入栈的component就是要查找的根元素id
component: null != n ? 0 === n ? 0 : n.__wxExparserNodeId__ : i,
selector: t,
single: o,
fields: r
}), this._queueCb.push(a || null)
}
这段是在2.2.0
基础库内调取wx.createSelectorQuery().select('#some')._selectorQuery._push
函数的代码,而这个方法恰恰是调用boundingClientRect
时会执行的函数
boundingClientRect = function (e) {
// 利用 _selectorQuery._push 将查询压入栈,后续再用exce执行
return this._selectorQuery._push(this._selector, this._component, this._single, {
id: !0,
dataset: !0,
rect: !0,
size: !0
}, e), this._selectorQuery
}
接下来就比较清晰了,看一下这个e
这个变量是什么值就可以了,打开函数闭包查看下,就会发现其实就是把当前插件的providerId
传入了进来,而当我在微信开发者控制台和主程序内调用时,这个e
变量就会是空了。
查到这里我才意识到,这小程序压根不想让你在插件内能直接查元素啊,必须要手动去指定查找范围。
不过想一下插件所提供的内容大概也就明白了,因为会侵入其他小程序,所以必须要手动进行环境隔离,防止一些意外
然后我就去google了一下,转了一圈又回到了社区看到了解决方案,或许我最开始查到插件内无法使用createSelectorQuery的时候,直接去google一下就不会有这么多事情了,笑
感谢 @Barrierml 的热心回复!
首先,$(id)
获取到的是 Taro 虚拟 DOM。要获取元素宽高必须使用小程序的 createSelectorQuery
API。
再者,的确插件可以看作是一个小程序自定义组件,使用 createSelectorQuery().select().boundingClientRect().in(scope).exec()
时需要调用 .in()
并传入插件对象自身。
Taro 把 scope
对象作为插件页面的 props 传入给开发者,开发者在插件页面使用 this.props.$scope
则可获取,也可以传递给子组件使用,文档位置:
相关平台
微信小程序
复现仓库
示例代码
小程序基础库: 2.24.6 使用框架: React
复现步骤
在插件的自定义组件中
期望结果
能获取元素高度
实际结果
offset error: #abc query fail
环境信息