The content of this article is about how to render html content (code examples) in WeChat mini programs. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you. .
The rich text content of most web applications is stored in the form of HTML strings. There is no problem in displaying HTML content through HTML documents. However, how should this part of the content be rendered in the WeChat mini program (hereinafter referred to as "mini program")?
When the applet was first launched, it was impossible to directly render HTML content, so a library called "wxParse" was born. Its principle is to parse HTML code into tree-structured data, and then render the data through the template of the mini program.
Later, the mini program added the "rich-text" component to display rich text content. However, this component has a huge limitation: Events of all nodes are blocked within the component. In other words, in this component, even a simple function such as "preview image" cannot be implemented.
Later, the mini program allowed web pages to be nested through the "web-view" component. Displaying HTML content through web pages was the most compatible solution. However, because one more page needs to be loaded, performance is poor.
Based on user experience and functional interaction considerations, we abandoned the two native components "rich-text" and "web-view" , selected "wxParse". However, after using it, I found that "wxParse" cannot meet the needs well:
Our small program is developed based on the "WePY" framework, and "wxParse" is based on Written by native applet. To make the two compatible, the source code of "wxParse" must be modified.
"wxParse" simply displays and previews the image of the original img element through the image component. In actual use, the cloud storage interface may be used to shrink the image to achieve the purpose of "Display with small image and preview with original image".
"wxParse" directly uses the video component of the mini program to display the video, but the hierarchy problem of the video component often leads to UI abnormalities (such as replacing a fixedly positioned element blocked).
In addition, if you take a look at the code repository of "wxParse", you will find that it has not been iterated for two years. So the idea of rewriting a rich text component based on the "WePY" component model came up, and the result was the "WePY HTML" project.
The first step is to parse the HTML string into tree-structured data. I use the "special character separation method". The special characters in HTML are "", the former is the start character and the latter is the end character.
•If the content to be parsed begins with a start character, the content between the start character and the end character will be intercepted and parsed as nodes.
•If the content to be parsed does not start with a start character, the content from the beginning to before the start character (or the end if the start character does not exist) will be intercepted and parsed as plain text.
•The remaining content will enter the next round of analysis until there is no remaining content.
As shown in the figure below:
#In order to form a tree structure, a context node (the root node by default) must be maintained during the parsing process:
•If the intercepted content is a start tag, a child node will be created under the current context node based on the matched tag name and attributes. If the tag is not a self-closing tag (br, img, etc.), the context node is set to the new node.
•If the intercepted content is an end tag, close the current context node according to the tag name (set the context node as its parent node).
•If it is plain text, a text node is created under the current context node, and the context node remains unchanged.
The process is as shown in the table below:
After the above process, the HTML string is parsed into a node tree.
Comparison
Compare the above algorithm with other similar parsing algorithms (performance is measured by "parsing 10,000-length HTML code"):
可见,在不考虑容错性(产生错误的结果,而非抛出异常)的情况下,本组件的算法与其余两者相比有压倒性的优势,符合小程序「 小而快 」的需要。而一般情况下,富文本编辑器所生成的代码也不会出现语法错误。因此,即使容错性较差,问题也不大(但这是需要改进的)。
树结构的渲染,必然会涉及到子节点的 递归 处理。然而,小程序的模板并不支持递归,这下仿佛掉入了一个大坑。
看了一下「wxParse」模板的实现,它采用简单粗暴的方式解决这个问题:通过13个长得几乎一模一样的模板进行嵌套调用(1调用2,2调用3,……,12调用13),也就是说最多可以支持12次嵌套。一般来说,这个深度也足够了。
由于「WePY」框架本身是有构建机制的,所以不必手写十来个几乎一模一样的模板,通过一个构建的插件去生成即可。
以下为需要重复嵌套的模板(精简过),在其代码的开始前和结束后分别插入特殊注释进行标识,并在需要嵌入下一层模板的地方以另一段特殊注释(「」)标识:
<!-- wepyhtml-repeat start --> <template> <block> <block> <view> <!-- next template --> </view> </block> <block>{{ item.text }}</block> </block> </template> <!-- wepyhtml-repeat end -->
以下是对应的构建代码(需要安装「 wepy-plugin-replace 」):
// wepy.config.js { plugins: { replace: { filter: /\.wxml$/, config: { find: /([\W\w]+?)/, replace(match, tpl) { let result = ''; // 反正不要钱,直接写个20层嵌套 for (let i = 0; i /g, () => { return i === 20 ? '' : `<template></template>`; }); } return result; } } } } }
然而,运行起来后发现,第二层及更深层级的节点都没有渲染出来,说明嵌套失败了。再看一下dist目录下生成的wxml文件可以发现,变量名与组件源代码的并不相同:
<block></block>
「WePY」在生成组件代码时,为了避免组件数据与页面数据的变量名冲突,会 根据一定的规则给组件的变量名增加前缀 (如上面代码中的「$htmlContent$wepyHtml$」)。所以在生成嵌套模板时,也必须使用带前缀的变量名。
先在组件代码中增加一个变量「thisIsMe」用于识别前缀:
<!-- wepyhtml-repeat start --> <template> {{ thisIsMe }} <block> <block> <view> <!-- next template --> </view> </block> <block>{{ item.text }}</block> </block> </template> <!-- wepyhtml-repeat end -->
然后修改构建代码:
replace(match, tpl) { let result = ''; let prefix = ''; // 匹配 thisIsMe 的前缀 tpl = tpl.replace(/\{\{\s*(\$.*?\$)thisIsMe\s*\}\}/, (match, p) => { prefix = p; return ''; }); for (let i = 0; i /g, () => { return i === 20 ? '' : `<template></template>`; }); } return result; }
至此,渲染问题就解决了。
图片
为了节省流量和提高加载速度,展示富文本内容时,一般都会按照所需尺寸对里面的图片进行缩小,点击小图进行预览时才展示原图。这主要涉及节点属性的修改:
•把图片原路径(src属性值)存到自定义属性(例如「src」)中,并将其添加到预览图数组。
•把图片的src属性值修改为缩小后的图片URL(一般云服务商都有提供此类URL规则)。
•点击图片时,使用自定义属性的值进行预览。
为了实现这个需求,本组件在解析节点时提供了一个钩子( onNodeCreate ):
onNodeCreate(name, attrs) { if (name === 'img') { attrs['src'] = attrs.src; // 预览图数组 this.previewImgs.push(attrs.src); // 缩图 attrs.src = resizeImg(attrs.src, 640); } }
对应的模板和事件处理逻辑如下:
<template> <image></image> </template>
// 点击小图看大图 imgTap(e) { wepy.previewImage({ current: e.currentTarget.dataset.src, urls: this.previewImgs }); }
在小程序中,video组件的层级是较高的(且无法降低)。如果页面设计上存在着可能挡住视频的元素,处理起来就需要一些技巧了:
•隐藏video组件,用image组件(视频封面)占位;
•点击图片时,让视频全屏播放;
•如果退出了全屏,则暂停播放。
相关代码如下:
<template> <view> <!-- 视频封面 --> <image></image> <!-- 播放图标 --> <image></image> <!-- 视频组件 --> <video></video> </view> </template>
{ // 点击封面图,播放视频 videoTap(e) { const nodeId = e.currentTarget.dataset.nodeid; const context = wepy.createVideoContext('wepyhtml-video-' + nodeId); context.play(); // 在安卓微信下,如果视频不可见,则调用play()也无法播放 // 需要再调用全屏方法 if (wepy.getSystemInfoSync().platform === 'android') { context.requestFullScreen(); } }, // 视频层级较高,为防止遮挡其他特殊定位元素,造成界面异常, // 强制全屏播放 videoPlay(e) { wepy.createVideoContext(e.currentTarget.id).requestFullScreen(); }, // 退出全屏则暂停 videoFullscreenChange(e) { if (!e.detail.fullScreen) { wepy.createVideoContext(e.currentTarget.id).pause(); } } }
The above is the detailed content of How to render html content in WeChat applet (code example). For more information, please follow other related articles on the PHP Chinese website!