>  기사  >  위챗 애플릿  >  WeChat 애플릿에서 HTML 콘텐츠를 렌더링하는 방법(코드 예)

WeChat 애플릿에서 HTML 콘텐츠를 렌더링하는 방법(코드 예)

不言
不言앞으로
2018-10-25 16:29:075105검색

이 기사의 내용은 WeChat 미니 프로그램에서 HTML 콘텐츠(코드 예제)를 렌더링하는 방법에 대한 것입니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

대부분의 웹 애플리케이션의 서식 있는 텍스트 콘텐츠는 HTML 문자열 형식으로 저장됩니다. HTML 문서를 통해 HTML 콘텐츠를 표시하는 데 문제가 없습니다. 그런데 위챗 미니 프로그램(이하 '미니 프로그램')에서는 이 부분의 콘텐츠를 어떻게 렌더링해야 할까요?

Solution

wxParse

애플릿이 처음 출시되었을 때 HTML 콘텐츠를 직접 렌더링하는 것이 불가능하여 "wxParse"라는 라이브러리가 탄생했습니다. 그 원리는 HTML 코드를 트리 구조의 데이터로 구문 분석한 다음 미니 프로그램의 템플릿을 통해 데이터를 렌더링하는 것입니다.

rich-text

나중에 미니 프로그램은 리치 텍스트 콘텐츠를 표시하기 위해 "리치 텍스트" 구성 요소를 추가했습니다. 그러나 이 구성 요소에는 큰 제한이 있습니다. 모든 노드의 이벤트가 구성 요소 내에서 차단됩니다. 즉, 이 컴포넌트에서는 '미리보기 이미지'와 같은 간단한 기능조차 구현할 수 없습니다.

web-view

나중에 미니 프로그램에서는 "web-view" 구성 요소를 통해 웹 페이지를 중첩할 수 있게 되었으며, 웹 페이지를 통해 HTML 콘텐츠를 표시하는 것이 가장 호환되는 솔루션이었습니다. 하지만 한 페이지를 더 로드해야 하기 때문에 성능이 좋지 않습니다.

"WePY"가 "wxParse"를 만났을 때

사용자 경험과 기능적 상호 작용 고려 사항을 바탕으로 우리는 두 가지 기본 구성 요소인 "rich-text"와 "web-view"를 버리고 "wxParse"를 선택했습니다. 그러나 사용해 본 결과 "wxParse"는 요구 사항을 잘 충족할 수 없다는 것을 알게 되었습니다.

  • 저희 애플릿은 "WePY" 프레임워크를 기반으로 개발된 반면 "wxParse"는 기본 애플릿을 기반으로 작성되었습니다. 두 가지를 호환되게 하려면 "wxParse"의 소스 코드를 수정해야 합니다.

  • "wxParse"는 이미지 구성 요소를 통해 원본 img 요소의 이미지를 단순히 표시하고 미리 봅니다. 실제 사용에서는 "작은 이미지로 표시하고 원본 이미지로 미리보기"라는 목적을 달성하기 위해 클라우드 저장소 인터페이스를 사용하여 이미지를 축소할 수 있습니다.

  • "wxParse"는 미니 프로그램의 비디오 구성 요소를 직접 사용하여 비디오를 표시하지만 비디오 구성 요소의 레벨 문제로 인해 UI 이상(고정 위치 요소 차단 등)이 발생하는 경우가 많습니다.

그리고 “wxParse”의 코드 저장소를 잠깐 살펴보면 2년 동안 반복되지 않았음을 알 수 있습니다. 그래서 "WePY" 컴포넌트 모델을 기반으로 리치 텍스트 컴포넌트를 다시 작성하자는 아이디어가 나왔고, 그 결과물이 "WePY HTML" 프로젝트였습니다.

구현 과정

HTML 구문 분석

첫 번째 단계는 HTML 문자열을 트리 구조의 데이터로 구문 분석하는 것입니다. 저는 "특수 문자 분리 방법"을 사용합니다. HTML의 특수 문자는 ""이며, 전자는 시작 문자이고 후자는 끝 문자입니다.

•파싱할 콘텐츠가 시작 문자로 시작하는 경우 시작 문자와 끝 문자 사이의 콘텐츠를 가로채서 노드로 구문 분석합니다.
•파싱할 콘텐츠가 시작 문자로 시작하지 않는 경우 시작 문자 이전(또는 시작 문자가 없는 경우 끝)까지의 콘텐츠를 가로채서 일반 텍스트로 구문 분석합니다.
•남은 콘텐츠가 없을 때까지 남은 콘텐츠는 다음 분석에 들어갑니다.
아래 그림과 같이:

WeChat 애플릿에서 HTML 콘텐츠를 렌더링하는 방법(코드 예)

트리 구조를 형성하기 위해서는 구문 분석 과정에서 컨텍스트 노드(기본값은 루트 노드)가 유지되어야 합니다.

•차단된 콘텐츠가 다음과 같은 경우 시작 태그는 일치 항목을 기반으로 출력됩니다. 태그 이름과 속성은 현재 컨텍스트 노드 아래에 하위 노드를 생성합니다. 태그가 자체 닫는 태그(br, img 등)가 아닌 경우 컨텍스트 노드는 새 노드로 설정됩니다.
•가로챈 콘텐츠가 종료 태그인 경우 태그 이름에 따라 현재 컨텍스트 노드를 닫습니다(컨텍스트 노드를 상위 노드로 설정).
•일반 텍스트인 경우 현재 컨텍스트 노드 아래에 텍스트 노드가 생성되고 컨텍스트 노드는 변경되지 않습니다.
과정은 아래 표와 같습니다.

WeChat 애플릿에서 HTML 콘텐츠를 렌더링하는 방법(코드 예)

위 과정 후에 HTML 문자열이 노드 트리로 파싱됩니다.

비교
위 알고리즘을 다른 유사한 구문 분석 알고리즘과 비교합니다(성능은 "10,000 길이의 HTML 코드 구문 분석"으로 측정):

WeChat 애플릿에서 HTML 콘텐츠를 렌더링하는 방법(코드 예)

可见,在不考虑容错性(产生错误的结果,而非抛出异常)的情况下,本组件的算法与其余两者相比有压倒性的优势,符合小程序「 小而快 」的需要。而一般情况下,富文本编辑器所生成的代码也不会出现语法错误。因此,即使容错性较差,问题也不大(但这是需要改进的)。

模板渲染

树结构的渲染,必然会涉及到子节点的 递归 处理。然而,小程序的模板并不支持递归,这下仿佛掉入了一个大坑。

看了一下「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();
        }
    }
}


위 내용은 WeChat 애플릿에서 HTML 콘텐츠를 렌더링하는 방법(코드 예)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제