微信小程序开发问题集
setData: invokeWebviewMethod 数据传输长度为 *** 已经超过最大长度 1048576
在小程序中执行 setData
时,如果数据内容过大的话,会出现数据传输长度超过最大长度的问题。其原因在于 setData
实际上是在 webview 上执行了 stringByEvaluatingJavaScriptFromString
这类方法,如果传入数据过大的话,就会使 webview 的内存开销过大,JavaScript 代码执行失败。
开发中,在存在大量数据的情况下,可以对数据执行 map
之类的操作,仅留下需要展示的数据以及对应的 key,这样每次 setData
的数据就会小很多。
rich-text 数据来源
由于小程序不支持 iframe
这样的组件,所以在需要展示 html 页面的时候,只能使用 rich-text
。
欲显示的 html 内容最好在服务器端转换成数组的形式,有以下几个原因:
- 根据小程序开发文档所言,
nodes
属性推荐使用 Array 类型,由于组件会将 String 类型转换为 Array 类型,因而性能会有所下降 - rich-text 仅支持有限的 html 标签。对于不支持的标签,一部分可以替换成
span
或者tag
这类它支持的标签。剩下的也可以根据实际情况直接去除或者替换成提示文字。 - 小程序中的
wxss
样式仅能根据class
生效,而原 html 文档的 css 文件中会有使用类型选择器,为了能使用原有的这些样式,为每一个元素增加类似__tag__span
这样的类名,同时对原 css 执行正则表达式将其中的span {...}
替换成.__tag__span {...}
的形式 - 剔除 rich-text 不支持的 attr
以上这些操作适合在服务器端执行,代码如下:
const parse5 = require('parse5');
const entities = require('entities');
const AVAILABLE_ATTRS = [
'class',
'style',
'span',
'width',
'alt',
'src',
'height',
'start',
'type',
'colspan',
'rowspan',
];
const AVAILABLE_TAGS = [
'a', 'abbr', 'b', 'blockquote',
'br', 'code', 'col', 'colgroup',
'dd', 'del', 'div', 'dl', 'dt',
'em', 'fieldset', 'h1', 'h2', 'h3',
'h4', 'h5', 'h6', 'hr', 'i', 'img',
'ins', 'label', 'legend', 'li',
'ol', 'p', 'q', 'span', 'strong',
'sub', 'sup', 'table', 'tbody',
'td', 'tfoot', 'th', 'thead',
'tr', 'ul',
];
const REPLACEABLE_TAGS = [
'pre', 'small', 'var', 'button', 'font', 'details',
'summary', 'caption', 'figure', 'figcaption', 'dfn',
'string', 's',
];
const REPLACE_MAP = {
pre: 'div',
small: 'span',
var: 'span',
button: 'span',
font: 'span',
details: 'div',
summary: 'div',
caption: 'div',
figure: 'div',
figcaption: 'div',
dfn: 'span',
string: 'span',
s: 'span',
};
const IGNOREABLE_TAGS = [
'meta',
];
function parseNode(ctx, node) {
const { childNodes, tagName, attrs: _attrs } = node;
let needOverrideTagName = false;
if (node.nodeName === '#text') {
return {
type: 'text',
text: entities.decodeHTML(node.value),
};
}
if (AVAILABLE_TAGS.indexOf(tagName) === -1) {
if (IGNOREABLE_TAGS.indexOf(tagName) > -1) {
return null;
} else if (REPLACEABLE_TAGS.indexOf(tagName) > -1) {
needOverrideTagName = true;
} else {
if (tagName !== 'iframe') {
ctx.logger.info(`UNSUPPORT TAG: ${tagName}`);
}
return {
name: 'div',
attrs: {
class: 'content-unavailable',
},
children: [
{
type: 'text',
text: '此处内容无法在当前环境中显示',
},
],
};
}
}
let children;
if (childNodes && childNodes.length > 0) {
children = childNodes.map(node => parseNode(ctx, node)).filter(child => !!child);
}
const attrs = _attrs
.filter(attr => AVAILABLE_ATTRS.indexOf(attr.name) > -1)
.reduce((obj, attr) => {
obj[attr.name] = attr.value;
return obj;
}, {});
const classNames = attrs.class ? attrs.class.split(' ') : [];
classNames.push(`__tag_${tagName}`, '__univ');
attrs.class = classNames.join(' ');
const nodeObj = {
name: needOverrideTagName ? REPLACE_MAP[tagName] : tagName,
attrs,
children,
};
return nodeObj;
}
function parseHtml(ctx, html) {
const documentFragment = parse5.parseFragment(html);
const nodes = documentFragment.childNodes.map(node => parseNode(ctx, node));
return nodes;
}
rich-text setData
在使用 setData
对 rich-text
的 nodes
属性赋值时,如果元素过多,则可能出现页面卡顿或者数据传输长度超过最大长度的问题。此时,可以将 nodes 数组分成适量片段,分段 setData
。
<rich-text wx:for="{{ nodesList }}" wx:for-index="index" wx:for-item="nodes" nodes="{{nodes}}"></rich-text>
const nodes = ***;
const nodesList = [];
const lastNodes = nodes.reduce((arr, cur) => {
const curArr = [cur];
const newArr = arr.concat(curArr);
if (JSON.stringify(newArr).length > (1048576 - 1024 * 1000)) {
nodesList.push(arr);
return curArr;
} else {
return newArr;
}
}, []);
nodesList.push(lastNodes);
this.setData({
'nodesList': []
});
nodesList.forEach((nodes, idx) => {
if (idx === 0) {
this.setData({
[`nodesList[${idx}]`]: nodes,
loading: false
});
} else {
this.setData({
[`nodesList[${idx}]`]: nodes
});
}
});