在开发小程序展开全文组件时需要用到节点查询API - wx.createSelectorQuery()
来查询全文内容的高度。
wx.createSelectorQuery()
返回一个SelectorQuery
对象实例。SelectorQuery
有五个方法(in
,select
,selectAll
,selectViewport
,exec
),第一个返回SelectorQuery
,后四个返回NodesRef
。NodesRef
有四个方法(fields
,boundingClientRect
,scrollOffset
,context
),第一个返回NodesRef
,后三个返回SelectorQuery
。
对照官方提供的示例代码来看
const query = wx.createSelectorQuery(); // query 是 SelectorQuery 对象 如果在组件内查询请使用this代替wx,否则有可能无法获取节点的查询信息
query.select('#the-id').boundingClientRect(); // select 后是 NodesRef 对象,然后 boundingClientRect 返回 SelectorQuery 对象
query.selectViewport().scrollOffset(); // selectViewport 后是 NodesRef 对象,然后 scrollOffset 返回 SelectorQuery 对象
query.exec(function (res) { // exec 返回 NodesRef 对象
console.log(res[0].top); // #the-id节点的上边界坐标
console.log(res[1].scrollTop); // 显示区域的竖直滚动位置
});
问题:每行执行返回的 SelectorQuery
对象是相同的吗?
答案:是的,都是同一个对象。
问题:直接执行 query.select('#the-id').boundingClientRect().exec
也可以吗?
答案:可以,boundingClientRect()
返回就是 query
。
问题:这样连写 query.select('#the-id').boundingClientRect().selectViewport().scrollOffset()
算两条查询请求吗?
答案:是两条请求。
问题:query.exec
执行后会清空前面的查询请求吗?再次执行还能拿到结果吗?
答案:可以,query
不会清空请求。
问题:boundingClientRect
和 scrollOffset
可以接受 callback
参数,它与 query.exec
执行顺序是怎样,修改 res
结果会影响到后面的 callback
吗?
答案:先执行 boundingClientRect
和 scrollOffset
的 callback
,再执行 query.exec
的 callback
;修改 res
结果会影响到后面 exec
的结果。
上面的问题通过小程序开发者工具中的 WAService.js
源码简单美化还原后可以了解 SelectorQuery
的代码逻辑
SelectorQuery.js
import NodesRef from 'NodesRef';import requestComponentInfo from 'requestComponentInfo';export default function(pluginId) { return class SelectorQuery {
constructor(plugin) { if (plugin && plugin.page) { this._component = this._defaultComponent = plugin.page; this._webviewId = this._defaultComponent.__wxWebviewId__;
} else { var pages = __internalGlobal__.getCurrentPagesByDomain(''); this._defaultComponent = pages[pages.length - 1], this._component = null; this._webviewId = null;
} this._queue = []; this._queueCb = [];
} in(component) { if (!this._webviewId) { this._webviewId = component.__wxWebviewId__; this._component = component;
} else if (this._webviewId !== component.__wxWebviewId__) { console.error('A single SelectorQuery could not work in components in different pages. A SelectorQuery#in call has been ignored.');
} else { this._component = component;
} return this;
}
select(selector) { return new NodesRef(this, this._component, selector, true);
}
selectAll(selector) { return new NodesRef(this, this._component, selector, false);
}
selectViewport() { return new NodesRef(this, 0, '', true);
}
_push(selector, component, single, fields, callback) { if (!this._webviewId) { this._webviewId = this._defaultComponent ? this._defaultComponent.__wxWebviewId__ : undefined;
} const rootNodeId = pluginId ? '' : r.getRootNodeId(this._webviewId); this._queue.push({
component: null != component ? (0 === component ? 0 : component.__wxExparserNodeId__) : rootNodeId,
selector,
single,
fields,
}); this._queueCb.push(callback || null);
}
exec(callback) {
requestComponentInfo(this._webviewId, {
pluginId,
queue: this._queue,
}, (results) => { const queueCb = this._queueCb;
results.forEach((res, index) => { if ('function' == typeof queueCb[index]) {
queueCb[index].call(this, res);
}
}); if ('function' == typeof callback) {
callback.call(this, results);
}
})
}
}
}
requestComponentInfo.js
const subscribe = function(eventType, callback) { const _callback = function(event, webviewId, nativeInfo = {}) { const { data = {}, options } = event; const startTime = options && options.timestamp || 0; const endTime = Date.now(); if ('function' == typeof callback) {
callback(data, webviewId);
Reporter.speedReport({
key: 'webview2AppService',
data,
timeMark: {
startTime,
endTime,
nativeTime: nativeInfo.nativeTime || 0,
}
});
}
};
__safeway__.bridge.subscribe(eventType, _callback);
}const publish = function(eventType, data, webviewIds) { const event = {
data,
options: {
timestamp: Date.now(),
}
};
__safeway__.bridge.publish(eventType, event, webviewIds);
}const requestCb = {};let requestId = 1;
subscribe('responseComponentInfo', function(data) { const reqId = data.reqId; const callback = requestCb[reqId]; if (callback) { delete requestCb[reqId];
callback(data.res);
}
});export default function requestComponentInfo(webviewId, reqs, callback) { const reqId = requestId++; if (!webviewId) { console.warn('An SelectorQuery call is ignored because no proper page or component is found. Please considering using `SelectorQuery.in` to specify a proper one.'); return;
}
requestCb[reqId] = callback,
publish('requestComponentInfo',
{
reqId,
reqs,
},
[webviewId],
);
}
NodesRef.js
export default class NodesRef {
constructor(selectorQuery, component, selector, single) { this._selectorQuery = selectorQuery; this._component = component; this._selector = selector; this._single = single;
}
fields(fields, callback) { this._selectorQuery._push(this._selector, this._component, this._single,
fields,
callback,
); return this._selectorQuery;
}
boundingClientRect(callback) { this._selectorQuery._push( this._selector, this._component, this._single,
{
id: true,
dataset: true,
rect: true,
size: true,
},
callback,
); return this._selectorQuery;
}
scrollOffset(callback) { this._selectorQuery._push( this._selector, this._component, this._single,
{
id: true,
dataset: true,
scrollOffset: true,
},
callback,
); return this._selectorQuery;
}
}
微信小程序里面,根据不同的页面跳转需要,有不同的跳转方法,总结如下:(1)保留当前页面,跳转到应用内的某个页面: wx.navigateTo(OBJECT) 这种情况下保存原先打开的页面,可以...
getWxAddress:function(){varthat=this;wx.authorize({scope:'scope.address',success:function(re...