この記事は、最初にVivo Internet Technology WeChatパブリックアカウント
リンクで公開されました 。https://mp.weixin.qq.com/s/Ka1pjJKuFwuVL8B-t7CwuA
著者:台湾での悟空R&Dチーム
1.背景
「生体内で1000万レベルのDAUイベントセンターを設定する方法をわかりやすく説明する」という技術的な謎解きを通じて、RSCについての理解が深まったと思います。RSC(リモートサービスコンポーネント)はリモートサービスコンポーネントであり、ホットプラグメカニズム、視覚的な構成、プラグアンドプレイ、アクティブページの迅速な構築を通じて、アクティブページのコアコンポーネントです。
RSCは、アクティブなページ構成単位の効率的な抽象デザインスキームであり、開発効率を最大化し、開発者の精神的負担を軽減します。開発者は、開発において[凝集度が高く、カップリングが弱い]の設計概念に従い、RSCコンポーネント内の表示とロジック処理のみに注意する必要があることを願っています。
(図1)
しかし、実際の事業開発では、上記の図1に示すように、毎日同じようなシナリオが数多く発生します。ユーザーが[Monopoly game]に参加してゲームのポイントを獲得し、[Monopolyコンポーネント]がゲームの結果を要求しますポイントは「カード組立部品」に通知され、「カード組立部品」から該当するカードを入手し、「カード反転」をクリックして「モノポリー部品」に通知して残りゲーム数を更新します。このアクティビティページのシナリオには、多数のコンポーネント間のコラボレーションとデータ共有が含まれます。したがって、アクティビティを小さなフロントエンドシステムとして見る場合、RSCはシステムの基本的な要素にすぎず、無視できないもう1つの非常に重要な要素はRSCコンポーネント間の接続です。もちろん、この関係はシーンのコンテキストにも関連しています。したがって、RSCコンポーネントの管理プロセスでは、最初に解決する必要があるのは、アクティブページのコンポーネント間のデータ状態の管理です。
第二に、結果
問題について継続的に綿密に検討し、現象の背後にある本質的な原理を探求することにより、さまざまなコンテキストでのコンポーネントの接続(状態管理)は、建築設計レベルから十分に解決されます。たとえば、次のとおりです。
-
アクティビティページでは、RSCコンポーネントとコンポーネント間の接続を解決しました。
-
プラットフォーム内で、RSCコンポーネントとプラットフォーム間の接続を解決しました。ビジネスRSCコンポーネントは、アクティビティの保存、エディターでのコンポーネントの削除など、プラットフォームの主要なアクションを認識する必要があります。
- エディターのセキュリティサンドボックスで、サンドボックス全体のコンポーネントと設定パネル間の接続を解決しました。
3.アーキテクチャの進化
今日は、活動ページでのRSCコンポーネントとコンポーネント間の接続のチャットに焦点を当てます。次の記事では、サンドボックス環境におけるプラットフォームとRSCコンポーネント間の接続について説明します。
フロントエンドのUI基本フレームワークとしてVueを使用しているため、以下の技術的ソリューションはすべてVueに基づいています。
4、EventBusイベントバス
(図2)
図2に示すように、1つの絵は1000語に相当します。もちろん、私たちが考えている最も簡単なソリューションは、集中型のイベント処理センターを実装してコンポーネントのサブスクライバーを記録し、コラボレーションが必要なときにカスタムイベントを通じて関連コンポーネントのサブスクライバーに通知することです。もちろん、通知にはペイロードパラメータ情報を含めて、データ共有の目的を達成できます。実際、Vue自体にもカスタムイベントシステムが付属しています。Vueコンポーネント間のカスタムイベントは、これに基づいて実装されます。詳細なAPIについては、Vueのドキュメントに参加してください。新しい依存関係を導入したり、バンドルの量を減らしたりすることなく、Vue自体に基づいてEventBusメカニズムを実装できます。APIは次のコードを使用します。
const vm = new Vue()
// 注册订阅者
vm.$on('event-name', (payload) => {/*执行业务逻辑*/})
// 注册订阅者,执行一次后自动取消订阅者
vm.$once('some-event-name', (payload) => {/*执行业务逻辑*/})
// 取消某事件的某个订阅者
vm.$off('event-name',[callback])
// 通知各个订阅者执行相对应的业务逻辑
vm.$emit('event-name',payload)
1.構造上の利点
実際には、EventBusに基づくデータ状態管理モデルの利点は次のとおりです。
- コードの実装は比較的単純で、設計スキームは理解しやすい
- コンポーネント間のデカップリングは非常に軽量な方法で実行でき、コンポーネント間の強い結合をEventBusへの弱い結合に変えます。
2.実際の問題点
もちろん、EventBusソリューションにはいくつかの欠点があります。
- ビジネスロジックは複数のコンポーネントサブスクライバーに分散しているため、ビジネスロジックの処理は断片化し、一貫したコンテキストが欠如しています。
- コードを読んで維持する場合、コード内のサブスクライバーを常に見つける必要があるため、ビジネスプロセスの理解が妨げられ、注意が散漫になります。
3.反省と改善
EventBusのアーキテクチャ設計の欠点を認識し、独自のドッグフードを食べ、一連の視覚メカニズムを実装します。コードの抽象構文ツリーの分析を通じて、サブスクライバーと送信者の情報を抽出し、視覚的に表示できます。それらの関係は、問題をすばやく理解するのに役立ちます。
さらに、複雑なビジネスロジックのために、[prescript]改善スキームが設計されています。たとえば、アクティブなページは複数のRSCコンポーネントで構成されていますが、要求されたサーバーインターフェイスは1つであり、ページの初期状態のすべてのデータが含まれています。このとき、プレスクリプトでデータを取得するロジックを均一に処理できます。各RSCコンポーネントに再同期されました。[フロントスクリプト]は、共有状態やビジネスロジックを含むグローバルオブジェクトを抽出します。複数のコンポーネントがこのグローバルオブジェクトに依存していますアーキテクチャの設計を図3に示します。これは、EventBusソリューションを補足するものです。
(図3)
4.まとめ
プレスクリプトにより、複雑なビジネスの維持と理解が困難であるという問題を解決できますが、グローバルオブジェクトを公開する必要があるなどのリスクポイントが生じ、上書きまたは変更されるリスクがあります。プレスクリプトの改善後、必要な状態管理モード、つまりVuexがどんどん明確になりました。次にVuexについて話しましょう。
V. Vuexの状態管理
1.背景
Vuexとは何ですか?
Vuexは、Vue.jsアプリケーション用に特別に開発された状態管理モデルです。集中型ストレージを使用して、アプリケーションのすべてのコンポーネントの状態を管理し、対応するルールを使用して、状態が予測可能な方法で変化するようにします。Vuexは、Vueの公式デバッグツールであるdevtools拡張機能にも統合されています。このツールは、ゼロ構成のタイムトラベルデバッグ、状態スナップショットのインポートとエクスポートなどの高度なデバッグ機能を提供します。
Vuexの特徴は何ですか?
- 一元化されたコンポーネントの状態管理、動的な登録ストアのサポート
- Vueとの高度な一致があり、最下層はVueの応答データ特性に基づいて実装され、Vueと同じデータ処理特性を維持します。
- Vueに慣れてからVuexをすぐに使い始めることができ、学習コストが比較的低い
- 完全な開発経験、公式の開発ツール、タイムトラベルデバッグなどにより、開発者は予測可能なデータの変更を整理できます
2.プラットフォームでのVuexのサポートの紹介
Vuexは一般的な状態管理フレームワークですが、RSCコンポーネントシステムにシームレスに統合するにはどうすればよいですか?プロジェクトにVuexの依存関係とサポートを導入し、ストアへの依存関係をトップレベルのVueに追加する必要があります。
プロジェクトの基本構造:
.
└── src
├── App.vue
├── app.less
├── assets
├── component
├── directive
├── main.js
├── stat
├── store
└── utils
├── babel.config.js
├── package.json
├── public
2.1依存関係を追加する
仕様に従って、最初にプロジェクトディレクトリのpackage.jsonにVuexへの依存関係を追加します
{
"dependencies": {
"vuex": "^3.0.1"
}
}
2.2ストアオブジェクトの作成
//store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
// 状态数据
state() {
return {}
},
getters: {},
mutations: {},
actions: {},
})
2.3ストアへのトップレベルのVueオブジェクトインジェクション
上記で作成したストアオブジェクトをトップレベルのVueオブジェクトに挿入し、すべてのVueコンポーネントがこれを通じてトップレベルのストアオブジェクトを取得するようにします。さらに、Vuexは、データの共有とコラボレーションのための使いやすいツールメソッド(mapState、mapActions、mapGetters、mapMutations)も提供します。
// App.vue
import { store } from './store'
new Vue({
// 注入 store
// 在所有的改 Vue 管理的 vue 对象中都可以通过 this.$store 来获取
store,
})
3. Vuexを使用してRSCコンポーネントを開発する
3.1 RSC自社ストア
コンポーネントを開発する場合、開発者はほとんどの場合、プレゼンテーションとビジネスロジックにのみ焦点を当て、コンポーネントがアクティブページにレンダリングされるときは、トップレベルストアでのみ独自の状態を共有することを期待します。したがって、コンポーネントには独自の独立したストア状態管理があり、モジュールの状態は名前空間によって分離され、コンポーネントのbeforeCreateライフサイクルメソッドでは、VuexのregisterModuleを通じて動的ストア登録が実行されます。
3.2 StoreMixin注入
パブリックStoreMixinを抽出することでこのプロセスを簡略化できます。また、namespaced:trueを自動的にオンにして、現在の名前空間のショートカットメソッドとプロパティを展開することもできます。コードは次のとおりです。
// store-mixn.js
export default function StoreMixin(ns, store) {
return beforeCreate() {
// 保证 namespace 唯一性
// 开发者可以通过函数生成唯一的namespace
// 框架可以生成唯一的namespace
const namespace = isFn(ns) ? ns(this) : gen(ns)
this.$ns = namespace
store.namespaced = true
this.$store.registerModule(namespace, store)
// 扩展快捷方法和属性
this.$state = this.$store.state[namespace]
this.$dispatch = (action, payload) =>
this.$store.dispatch(`${namespace}/${action}`, payload)
this.$getter = //...
this.$commit = //...
}
}
//store.js
// 当前组件自有store
export default {
// 组件自身的状态
state() {
return {}
},
mutations: {},
getters: {},
//...other things
}
// code.vue
// 组件对外的入口模块
import store from './store'
export default {
mixins: [StoreMixin(/*namespace*/ 'hello', /* 组件的 store */ store)],
}
3.3名前空間の競合を解決するにはどうすればよいですか?
RSCコンポーネントはアクティビティで繰り返しロードされるため、すべて同じ名前空間のストアモジュールが繰り返しロードされ、モジュールカバレッジが発生します。名前空間の一意性を確保するにはどうすればよいですか?StoreMixinに名前空間を登録するときに、同じ名前空間が存在するかどうかを判断できます。存在する場合は、名前空間の名前を一度変更します。例えば、コマンドスペースのストアとしてhelloを登録した場合、namspace helloを再度登録すると自動的にhello1になり、自動的に区別されます。単純なアルゴリズムは次のように実装されます。
// gen.js
// 生成唯一的 namespace
const g = window || global
g.__namespaceCache__ = g.__namespaceCache__ || {}
/**
* 生成唯一的 moduleName, 同名 name 默认自动增长
* @param {*} name
*/
export default function genUniqueNamespace(name) {
let cache = g.__namespaceCache__
if (cache[name]) {
cache[name].count += 1
} else {
cache[name] = {
count: 0,
}
}
return name + (cache[name].count === 0 ? '' : cache[name].count)
}
さらに、開発者はstore-mixinでカスタム関数を渡して、一意の名前空間IDを生成できます。たとえば、次のコードは、vue-routerの動的ルーティングパラメーターに従って名前空間を設定します
export default {
mixins: [StoreMixin((vm) => vm.$router.params.spuId), store],
}
3.4動的名前空間の課題
動的な名前空間は不確実性の問題をもたらすため、次のコード例では、helloがhello1に名前変更され、Vuexのmapue(mapState、mapMutationsなど)メソッドで、名前空間を正確に渡して、コンポーネント内のストアのコンテキストを取得する必要があります。 。
// code.vue
export default {
mixins: [StoreMixin('hello', store)],
computed: {
...mapGetters('hello', [
/* hello namespace store getter */
]),
...mapState('hello', [
/* hello namespace state property */
]),
},
methods: {
...mapActions('hello', [
/* hello namespace actions method */
]),
...mapMutations('hello', [
/* hello namespace mutations method */
]),
},
}
3.5 Vuexを拡張して動的名前空間をサポートする
Vuex mapXXXメソッドの動的名前空間の問題を解決するにはどうすればよいですか?まず、StoreMixinでVueのthis。$ Nsオブジェクトに名前空間を設定して、StoreMixinで混合されたコンポーネントが動的に名前空間を取得できるようにすることを考えました。
// store-mixn.js
export default function StoreMixin(ns, store) {
return beforeCreate() {
// 保证 namespace 唯一性
const namespace = gen(ns)
// 将重命名后的 namespace 挂载到当前 vue 对象的$ns 属性上
this.$ns = namespace
//...
}
}
これを使用できますが、コンポーネント内のストアの名前空間を取得するためにコンポーネント内で$ Nを使用できますが、次のことができると想像してください。
// code.vue
export default {
computed: {
...mapGetter(this.$ns, [
/* hello namespace store getter */
]),
...mapState(this.$ns, [
/* hello namespace state property */
]),
},
methods: {
...mapActions(this.$ns, [
/* hello namespace actions method */
]),
...mapMutations(this.$ns, [
/* hello namespace mutations method */
]),
},
}
残念ながら、現時点では、これは単にVueの現在のインスタンスではありません、これは$ Nsは豪華で未定義です。どうすればいいですか?JSには関数型プログラミングの多くの機能があります。関数も値であり、パラメーターとして渡すことができます。実際、関数の値の特性に加えて、非常に重要な機能が遅延計算されます。この考え方に基づいて、mapXXメソッドは動的な名前空間をサポートするように拡張されています。次に、mapXXXメソッドで、vmが現在のVueのコンポーネントインスタンスになるまで待ってから、現在のコンポーネントの名前空間を取得します。
// code.vue
import { mapGetters, mapState, mapActions, mapMutations } from 'vuex-helper-ext'
export default {
computed: {
...mapGetters((vm) => vm.$ns, [
/* hello namespace store getter */
]),
...mapState((vm) => vm.$ns, [
/* hello namespace state property */
]),
},
methods: {
...mapActions((vm) => vm.$ns, [
/* hello namespace actions method */
]),
...mapMutations((vm) => vm.$ns, [
/* hello namespace mutations method */
]),
},
}
3.6親子コンポーネントが動的名前空間を渡す方法
これらの問題の1つを発見したに違いないと思います。$ NはStoreMixinコンポーネントからのみ取得できます。このコンポーネントのサブコンポーネントについてはどうですか?子コンポーネントを解決して親コンポーネントの名前空間を取得するにはどうすればよいですか?現時点では、Vueの強力なミックスインシステムを使用してグローバルミックスインを設計し、コンポーネントの作成時に親コンポーネントに$ nsオブジェクトがあるかどうかを確認し、存在する場合は、現在のコンポーネントの$ nsを親コンポーネントと同じに設定します。そうでない場合はスキップします。
function injectNamespace(Vue) {
Vue.mixin({
beforeCreate: function _injectNamespace() {
const popts = this.$options.parent;
if (popts && popts.$ns) {
this.$ns = popts.$ns;
const namespace = this.$ns;
// 为组件扩展快捷方法和属性
this.$state = this.$store.state[namespace]
this.$dispatch = (action, payload) =>
this.$store.dispatch(`${namespace}/${action}`, payload)
this.$getter = //...
this.$commit = //...
}
}
});
}
// main.js
Vue.use(injectNamespace);
このようにして、子コンポーネントはデフォルトで親コンポーネントによって設定された名前空間を取得します。このミックスインの魔法を使うと、$ XX属性をmapXXメソッドのデフォルト名前空間として使用できるため、mapXXXメソッドのデザインをよりエレガントに拡張できます。Vuexをより適切にシステムに統合できるように、よりリフレッシュし、スタイルを公式と一致させます。
// code.vue
export default {
computed: {
...mapGetter([
/* hello namespace store getter */
]),
...mapState([
/* hello namespace state property */
]),
},
methods: {
...mapActions([
/* hello namespace actions method */
]),
...mapMutations([
/* hello namespace mutations method */
]),
},
}
3.7最後の完全な小さな栗
以下の小さな栗を通して、開発者にとって、標準のVuex開発方法に従って開発している限り、何も起こらなかったように見えます^ _ ^。実際、私たちは内部で多くの努力をしてきましたが、建築設計の目的は、[シンプルなものをシンプルにし、複雑なものを可能にすること]です。
store.js RSCコンポーネント独自のストア
export default {
state() {
return { mott: 'hello vue' }
},
mutations: {
changeMott(state) {
state.mott = 'hello vuex'
},
},
}
text.vueテキストサブコンポーネント、mapStateは自動的に名前空間を動的に取得します
<template>
<div @click="changeMott">{{ mott }}</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex-helper-ext'
export default {
computed: {
...mapState(['mott']),
},
methods: {
...mapMutations(['changeMott']),
},
}
</script>
code.vue
<tempalte>
<text></text>
</template>
<script>
import store from './store';
import text from './text';
export default {
mixins: [StoreMixin('hello', store)],
components: {
text
},
methods: {
// ....
}
}
</script>
第六に、思考と展望
この記事はここに書かれていて、終わりに近づいています。RSCコンポーネント化計画とWukong活動の中期の実際のビジネスシナリオを解決するために取った道を一緒に見直し、チームは技術的にRSCコンポーネントとコンポーネント間の状態管理を解決しようとしました。次の記事では、RSCコンポーネントとプラットフォーム間の接続の状態管理、およびクロスサンドボックス環境について説明します。