ホームページ  >  記事  >  WeChat アプレット  >  WeChat アプレットで HTML コンテンツをレンダリングする方法 (コード例)

WeChat アプレットで HTML コンテンツをレンダリングする方法 (コード例)

不言
不言転載
2018-10-25 16:29:075053ブラウズ

この記事の内容は、WeChat ミニ プログラムで HTML コンテンツをレンダリングする方法 (コード例) に関するもので、困っている友人が参考になれば幸いです。 。

ほとんどの Web アプリケーションのリッチ テキスト コンテンツは、HTML 文字列の形式で保存されます。HTML ドキュメントを通じて HTML コンテンツを表示することに問題はありません。しかし、コンテンツのこの部分は、WeChat ミニ プログラム (以下、「ミニ プログラム」と呼びます) でどのように表示されるべきでしょうか?

解決策

wxParse

アプレットが最初に起動されたとき、HTML コンテンツを直接レンダリングすることは不可能だったので、「wxParse」というライブラリが生まれました。その原理は、HTML コードをツリー構造のデータに解析し、ミニ プログラムのテンプレートを通じてデータをレンダリングすることです。

リッチテキスト

その後、ミニ プログラムには、リッチ テキスト コンテンツを表示するための「リッチ テキスト」コンポーネントが追加されました。ただし、このコンポーネントには大きな制限があります。すべてのノードのイベントはコンポーネント内でブロックされます。つまり、このコンポーネントでは「プレビュー画像」のような単純な機能すら実装することができません。

web-view

その後、ミニ プログラムにより、「web-view」コンポーネントを介して Web ページをネストできるようになり、Web ページを通じて HTML コンテンツを表示することが最も互換性のあるソリューションになりました。ただし、さらに 1 ページをロードする必要があるため、パフォーマンスは低下します。

「WePY」と「wxParse」の出会い

ユーザー エクスペリエンスと機能的なインタラクションの考慮事項に基づいて、2 つのネイティブ コンポーネント「リッチテキスト」と「Web ビュー」を放棄し、「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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。