背景#
チーム内のマーケティング活動およびランディングページの要件は比較的頻繁に反復されるため、調査の結果、グループの自社開発の可視化プラットフォームを基に、チームのビジネス特性により近いマーケティング可視化構築プラットフォームを自ら構築しました。レンダリング層の実現においては、多くの歴史的プロジェクトページが依然としてコアビジネスに属し、長期的な反復メンテナンスが必要であることを考慮し、通常のページフレームのレンダリングに加えて、モジュールレベルのレンダリングをサポートする必要があります。これにより、歴史的プロジェクトを通常のコンポーネント形式で低コストで直接導入して使用できるようにするため、可視化モジュールエンジンを設計・実装しました。これはチーム内の 2 つのビジネスラインプロジェクトで展開されており、ここにまとめて記録します。
具体実装#
まず、モジュールエンジンの基本形態と機能を考慮します:
形態として、チーム内の C 端の H5 プロジェクトの技術スタックはすべて Vue であるため、このレンダリングエンジンの基本形態は Vue コンポーネントです(プロジェクト接続体験を考慮し、実際に外部にエクスポートされるのは Vue プラグインであり、内部でこのコンポーネントをグローバルに登録します);
機能として、開箱即用の体験を保証するため、少なくとも構成素材の取得とレンダリングのクローズドループロジックを備えている必要があります。したがって、レンダリングエンジンは構成素材に対応する json キー、接続プロジェクトサービスの環境(構成素材のリクエストアドレスを区別する)およびコンポーネントレンダリングソース(つまり、パッケージ化されたコンポーネントライブラリコード)を知っている必要があります。また、ビジネスプロジェクトと内部コンポーネントが柔軟に通信および相互作用できるように、モジュールエンジンは任意の属性とリスニングイベントの透過をサポートする必要があります;
最終的に外部のコンポーネント API 設計は以下の通りです:
Props:
属性 | 説明 | 型 |
---|---|---|
s | json キーを直接渡すことをサポートし、モジュールエンジンはこれに基づいて構成素材を取得できます | string |
env | 構成素材に対応するリクエスト環境を取得します(テスト、プレプロダクション、本番) | string |
widgets | コンポーネントレンダリングソース、モジュールエンジンが構成素材を解析する際に対応するコンポーネントを処理します | function |
props | ビジネスコンポーネントのカスタム属性、モジュールエンジンはすべての構成コンポーネントに渡されます | object |
Events:
@getXpubData(data)
構成素材を取得した後のコールバック関数で、構成データに基づいてカスタム処理ロジックを定義できます
@xxx(...)
ビジネスコンポーネントのカスタムイベントで、モジュールエンジンはすべての構成コンポーネントで発生する対応するイベントをリッスンします
Vue コンポーネントで一般的に使用される Slot スロットについては、その役割がコンポーネント内部の特定の位置のレンダリング内容をカスタマイズすることであり、この部分は可視化エディタが完全に引き受けるため、ここでは考慮しません。
プロジェクト接続の例:
import Roo from '@vision/roo'
// ビジネスコンポーネントライブラリは自分でインストールし、対応するjs/cssソースファイルをインポートする必要があります
import renderWidgets from '@vision-xxx/dist/h5/index.js'
import '@vision-xxx/dist/h5/index.css'
// プラグインのインストール
Vue.use(Roo, {
renderWidgets: [
renderWidgets
]
})
// グローバルコンポーネントの使用
<template>
<roo-component
// jsonキーと対応する環境はモジュールエンジン内部でリクエストして取得します
s="op-json-xxx"
env="production"
// 渡されたpropsは内部で使用されるすべてのコンポーネントに注入されます
props="{
token: 'xxx'
}"
// コンポーネント内で公開されたイベントをリッスンして適切に処理します。同名のイベントは繰り返し発生します
@customEvent="eventHandler"
// モジュールエンジン内で公開された素材構成受信イベント
@getXpubData="handlerXpubData"
/>
</template>
次に、コアのレンダリングロジックにおいて、可視化エディタが公開した標準構成データを解析変換してレンダリング関数を生成します。詳細なプロセスは以下の通りです:
- まず、json キーに基づいて cdn から素材構成データをリクエストして取得します;
// 構成データの例
{
// モジュールID
"id": "000",
// ページタイトル
"title": "アンケート",
// 一般的なグローバル関連拡張フィールド
"global": {
"dir": "ltr",
"code": {
"js": "",
"css": "",
"jsForHeader": ""
},
"share": {},
"mixins": {
"globalmixin": {
"pageStatus": 1
}
},
"pubArea": "dpub",
"canShare": true,
"onlineUrl_json": "http://xxx.com/op-json-xxx.json"
},
// ページコンポーネント、スタイル表示関連フィールド
"scenes": {
"music": {
"url": "",
"name": ""
},
// 上部ノードスタイル
"style": {
"overflow": "auto",
"minHeight": 603,
"backgroundSize": "100% 100%",
"backgroundColor": "rgba(0,0,0,0)",
"backgroundImage": "",
"backgroundRepeat": "no-repeat",
"backgroundPosition": "center center"
},
"dsList": [],
// コンポーネントリスト構成
"layers": [
{
// コンポーネントID、キー
"id": "widget-xxx",
"key": "ManhattanQuestionnaireNps",
// コンポーネント名
"name": "小アンケート - NPS",
// コンポーネント要素タイプ
"type": "ManhattanQuestionnaireNps",
"label": "",
// コンポーネントに渡される属性
"props": {
"img": {
"url": "https://xxx.com/xxx.png",
"fileName": "nps背景.png"
},
"maxText": "10点は絶対に推薦します",
"minText": "0点は絶対に推薦しません"
},
// コンポーネントスタイル
"style": {
"top": 0,
"left": 0,
"color": "#333",
"right": "unset",
"border": "0px solid #000000",
"boxShadow": "#000000 0px 0px 0px 0px",
"marginRight": 0,
"marginBottom": 0,
"backgroundColor": ""
},
"id_name": "小アンケート - NPS3",
"effectList": [],
"editorStatus": {
"show": true,
"active": false,
"isLock": false,
"status": 0
}
}
]
}
}
- 構成データのページ状態フィールド
global?.mixins?.globalmixin?.pageStatus
に基づいて表示するかどうかを判断し、表示しない場合は空のノードを直接返します(モジュール構成がオフラインのシナリオ)、表示する場合はgetXpubData
イベントを上に発生させます(上記の定義を参照); - widgets コンポーネントレンダリングソースと組み合わせてレンダリング関数を生成して返します:
- 構成データからコンポーネントリスト
layers
を取得し、渡されたカスタム属性とリスニングイベントオブジェクト(現在のコンポーネントコンテキスト内のlisteners
)をそれぞれ各コンポーネントオブジェクトのprops
とon
フィールドにマウントし、初期の上部ノードオブジェクトを生成します;// 上部ノードオブジェクトの例 { type: 'div', style: { "overflow": "auto", "minHeight": 603, "backgroundSize": "100% 100%", "backgroundColor": "rgba(0,0,0,0)", "backgroundImage": "", "backgroundRepeat": "no-repeat", "backgroundPosition": "center center" }, children: [ { "id": "widget-xxx", "key": "ManhattanQuestionnaireNps", "name": "小アンケート - NPS", "type": "ManhattanQuestionnaireNps", "label": "", "props": { "img": { "url": "https://xxx.com/xxx.png", "fileName": "nps背景.png" }, "maxText": "10点は絶対に推薦します", "minText": "0点は絶対に推薦しません", // マウントされた渡された属性 "token": "xxx" }, // マウントされたイベントオブジェクト "on": { "customEvent": eventHandler }, "style": { "top": 0, "left": 0, "color": "#333", "right": "unset", "border": "0px solid #000000", "boxShadow": "#000000 0px 0px 0px 0px", "marginRight": 0, "marginBottom": 0, "backgroundColor": "" }, "id_name": "小アンケート - NPS3", "effectList": [], "editorStatus": { "show": true, "active": false, "isLock": false, "status": 0 } } ], }
- 上部ノードオブジェクトに基づいて createElement レンダリング函数字符列を生成します:
- 最初のタグパラメータは
HTML
保留要素とコンポーネント要素の 2 種類に区別されます; - 2 番目のパラメータデータオブジェクトは
props
、style
、attrs
、on
を対応する文字列形式に変換し、style
数値は統一してvw
基準に変換し、on
イベントオブジェクトはグローバルなカスタムイベントプールを使用してイベント登録およびトリガー処理を行います; - 最後の子要素パラメータは再帰的に実行して生成します。
- 最初のタグパラメータは
- レンダリング函数字符列を
new Function
を通じて関数宣言を生成し、widgets を関数のスコープにバインドした後、レンダリング関数を返します。
- 構成データからコンポーネントリスト
最終的に返されるレンダリング関数はプロジェクト内の Vue レンダラーによってレンダリング表示されます。
プロジェクト内の完全な構成接続および解析プロセスは以下の通りです:
接続プロセスの性能最適化#
モジュールエンジンはページ内の任意の位置に接続でき、デフォルトではモジュールエンジン内部で構成データとレンダリングを取得するプロセスは以下の通りです:
性能要件が非常に高いページでは、上記のプロセスによりコンポーネント全体の読み込みレンダリング時間が長くなり、最終的にページ全体のインタラクティブ時間に影響を与えるため、このようなシナリオではより合理的な方法で接続を考慮する必要があります。
高性能要件のビジネスプロジェクトでは、新しい読み込みプロセスのソリューションを提供し、素材構成のリクエストと各コンポーネント内のビジネスデータリクエストを統一されたページ初期化リクエストインターフェース内で呼び出すようにしました。モジュールエンジンは静的レンダリングのみを行うため、モジュールエンジンには構成データシナリオを直接受け取るための data
属性が新たに拡張されました。
また、異なるビジネスコンポーネントのリクエストインターフェースと処理ロジックは異なる可能性があるため、一般的なビジネスインターフェースとデータ処理関数の構成基準を定め、解析処理の SDK を実装しました。初期化インターフェース呼び出し時にこの SDK を直接呼び出すことができます。
データ取得前置き後のレンダリングプロセスは以下の通りです:
ほとんどのビジネスプロジェクトにも前置き初期データ取得ノードが存在し、接続後の全体的なインタラクティブ時間には基本的に影響がありません。