ホームページ >WeChat アプレット >ミニプログラム開発 >WeChat ミニ プログラムをコンポーネント化するためのソリューションと方法
この記事では、主に WeChat ミニ プログラムのコンポーネント化のソリューションのアイデアと方法について説明します。ミニ プログラムの基本ライブラリ バージョン 1.6.3 以降、ミニ プログラムは単純なコンポーネント プログラミングをサポートします。使用しているミニ プログラムの基本ライブラリのバージョンを確認するには、開発者ツールの右側にある [詳細] をクリックして、最も基本的なコンポーネントを表示します。
アプレットのコンポーネントは実際にはディレクトリであり、次の 4 つのファイルを含める必要があります:
xxx.json
xxx.wxml
xxx.wxss
xxx.js
まずコンポーネントを宣言します
json ファイルでカスタム コンポーネント宣言を作成します (この一連のファイルをカスタム コンポーネントとして設定するには、コンポーネント フィールドを true に設定します)
{ "component": true }
次に、コンポーネントが導入されるページの json ファイルで、このような参照宣言
{ "usingComponents": { //自定义的组件名称 : 组件路径,注意是相对路径,不能是绝对路径 "component-tag-name": "path/to/the/custom/component" } }
は、メインページで利用できます。
vue コンポーネントの導入と比較して、ミニプログラム ソリューションはよりシンプルです。 vue コンポーネントを導入するには、インポートとコンポーネントへの登録を同時に行う必要があります。ミニ プログラムのコンポーネントは、wxml で使用する前に .json に登録するだけで済みます。
スロットを使用する
vueと同じように、ミニプログラムにもスロットの概念があります。
単一のスロット
は、コンポーネントが参照されるときに提供される子ノードを運ぶために使用される、コンポーネント テンプレート内の
// 主页面内,<addlike>是组件 <addlike item="item" my_properties="sssss"> <text>我是被slot插入的文本</text> </addlike> // addlike 组件 <view class="container"> <view>hello, 这里是组件</view> <view>hello, {{my_properties}}</view> <slot></slot> </view> // 渲染后 <view class="container"> <view>hello, 这里是组件</view> <view>hello, {{my_properties}}</view> <text>我是被slot插入的文本</text> </view>
複数のスロット
コンポーネントで複数のスロットを使用する必要がある場合は、コンポーネント js で有効化を宣言する必要があります:
Component({ options: { multipleSlots: true // 在组件定义时的选项中启用多slot支持 }, properties: { /* ... */ }, methods: { /* ... */ } })
Use:
// 主页面 <addlike item="item" my_properties="sssss"> // 在普通的元素上加入 slot 属性,指定slotname, 就可以变成子元素的slot了 <text slot="slot1">我是被slot1插入的文本</text> <text slot="slot2">我是被slot2插入的文本</text> </addlike> // 子页面 <view class="container"> <view>hello, 这里是组件</view> <view>hello, {{my_properties}}</view> <slot name="slot1"></slot> <slot name="slot2"></slot> </view>
コンポーネント コンストラクター
今、コンポーネントには以下を含める必要があると言いました。 js 、 wxml 、 wxss 、 json の 4 つのファイル。 wxmlはHTML、wxssはcssに相当しますが、jsでは何を書けばいいのでしょうか?
WeChat によって公式に提供されている場合:
Component({ behaviors: [], properties: { }, data: {}, // 私有数据,可用于模版渲染 // 生命周期函数,可以为函数,或一个在methods段中定义的方法名 attached: function(){}, moved: function(){}, detached: function(){}, methods: { onMyButtonTap: function(){ }, _myPrivateMethod: function(){ }, _propertyChange: function(newVal, oldVal) { } } })
はコンポーネント コンストラクターを呼び出します。 Component コンストラクターを使用してコンポーネントを定義する場合、コンポーネントのプロパティ、データ、メソッドなどを指定できます。特定のコンポーネントに配置できるものは次のとおりです。
コンポーネントとデータ通信
コンポーネント間のデータメンテナンスの問題を解決するには、vue、react、angular は別のコンポーネントである必要があります。解決。ミニ プログラムの解決策ははるかに簡単です。
メインページはコンポーネントにデータを渡します
プロパティはvueのpropsに相当し、外部データを渡すための入り口です。
// 主页面使用组件 <a add_like="{{add_like}}"> </a> // 组件a.js 内 Component({ properties:{ add_like:{ type:Array, value:[], observer:function(){ } } } })
注: 受信データは、単純データ型であろうと参照型であろうと、値のコピーと同じです (渡される js 関数のパラメーターが値のコピーであるという Red Book の説明とは異なります)。 Red Book での意味 はい: 単純なデータ型は値を直接コピーし、参照型は参照をコピーします。つまり、関数内でパラメーター オブジェクトのプロパティを変更すると、関数の外のオブジェクトのプロパティに影響します。
Vue の props の場合は、.sync を通じて同期できます。ミニ プログラムのサブコンポーネントでは、this.setData() を呼び出して親コンポーネントのデータを変更しても、親コンポーネントのデータには影響しません。つまり、子コンポーネントのプロパティの変更は親コンポーネントとは何の関係もないようです。では、子コンポーネントの親コンポーネントのデータを変更したい場合、または兄弟コンポーネントのデータを変更したい場合、簡単な方法はあるのでしょうか?
コンポーネントがメイン ページ
にデータを送信することについては以下で説明します。 vue と同様に、コンポーネント間の対話の主な形式はカスタム イベントです。
コンポーネントは this.triggerEvent() を通じてカスタム イベントをトリガーし、メイン ページはコンポーネントの binding:component_method="main_page_mehod" を通じてカスタム イベントを受け取ります。
そのうち、 this.triggerEvent() メソッドはカスタム イベント名を受け取り、eventDetail とeventOptions という 2 つのオブジェクトも受け取ります。
// 子组件触发自定义事件 ontap () { // 所有要带到主页面的数据,都装在eventDetail里面 var eventDetail = { name:'sssssssss', test:[1,2,3] } // 触发事件的选项 bubbles是否冒泡,composed是否可穿越组件边界,capturePhase 是否有捕获阶段 var eventOption = { composed: true } this.triggerEvent('click_btn', eventDetail, eventOption) } // 主页面里面 main_page_ontap (eventDetail) { console.log(eventDetail) // eventDetail // changedTouches // currentTarget // target // type // …… // detail 哈哈,所有的子组件的数据,都通过该参数的detail属性暴露出来 }
コンポーネント間のデータ通信
vueが提案するvuexソリューションとは異なり、ミニプログラムのコンポーネント間の通信はシンプルかつコンパクトです。メイン ページがコンポーネントと通信するのと同じように、カスタム イベントを使用して通信することもできます。もちろん、より簡単で便利な方法は、コンポーネント コンストラクターの属性である
関係を使用することです。 2 つのコンポーネント リレーション属性は関連付けられており、それら 2 つはお互いをキャプチャし、お互いにアクセスして、自分の属性を変更するのと同じように互いの属性を変更できます。
Component({ relations:{ './path_to_b': { // './path_to_b'是对方组件的相对路径 type: 'child', // type可选择两组:parent和child、ancestor和descendant linked:function(target){ } // 钩子函数,在组件linked时候被调用 target是组件的实例, linkChanged: function(target){} unlinked: function(target){} } }, })
たとえば、コードに示されているように 2 つのコンポーネントがあります:
// 组件a slot 包含了组件b<a><b></b></a>
それらの間の関係は、以下の図に示されているとおりです:
2 つのコンポーネントは、this.getRelationNodes を通じて他のコンポーネントのインスタンスをキャプチャします。 (' ./path_to_a') メソッド。相手のコンポーネントのインスタンスを取得したので、相手のコンポーネントのデータにアクセスしたり、相手のコンポーネントにデータを設定したりすることはできますが、相手のコンポーネントのメソッドを呼び出すことはできません。
// 在a 组件中 Component({ relations:{ './path_to_b': { type: 'child', linked:function(target){ } // target是组件b的实例, linkChanged: function(target){} unlinked: function(target){} } }, methods:{ test () { var nodes = this.getRelationNodes('./path_to_b') var component_b = nodes[0]; // 获取到b组件的数据 console.log(component_b.data.name) // 设置父组件的数据 // 这样的设置是无效的 this.setData({ component_b.data.name:'ss' }) // 需要调用对方组件的setData()方法来设置 component_b.setData({ name:'ss' }) } } }) // 在b 组件里面 Component({ relations:{ './path_to_a': { //注意!必须双方组件都声明relations属性 type:'parent' } }, data: { name: 'dudu' } }) 注意:1. 主页面使用组件的时候,不能有数字,比如说 <component_sub1> 或 <component_sub_1>,可以在主页面的json 里面设置一个新名字 { "usingComponents":{ "test_component_subb": "../../../components/test_component_sub2/test_component_sub2" } }
2. リレーション内のパス (例:
) は、コンポーネント間の論理パスではなく、他のコンポーネントの実際の相対パスです。
3. リレーションが関連付けられていない場合、this.getRelationNodes は他のコンポーネントを取得できません
4. 本组件无法获取本组件的实例,使用this.getRelatonsNodes('./ path_to_self ') 会返回一个null
4. type 可以选择的 parent 、 child 、 ancestor 、 descendant
现在我们已经可以做到了两个组件之间的数据传递,那么如何在多个组件间传递数据呢?
如上图所示,同级的组件b 和同级的组件c , b 和 c 之间不可以直接获取,b可以获取到a, c 也可以获取到a,而a可以直接获取到 b 和 c。所以,如果想获取到兄弟元素,需要先获取到祖先节点,然后再通过祖先节点获取兄弟节点
我在组件b 里面,我需要先找到祖先组件a的实例,然后用祖先组件a的实例的getRelationNodes方法获取到组件c的实例。
看见没?恐怕我们又要写一大堆重复性的代码了。
幸好,微信小程序还提供了behavior 属性, 这个属性相当于 mixin,很容易理解的,是提高代码复用性的一种方法。
思路:
假设目前有三个组件,组件a, 组件b, 组件c, 其中组件b和组件c是兄弟组件,组建a是b和c的兄弟组件。为了减少代码的重复性,我们把获取父组件的方法,和获取兄弟组件的方法封装一下,封装在 behavior 的 methods 中。只要是引入该behavior的组件,都可以便捷的调用方法。
实现:
新建一个behavior文件,命名无所谓,比如说relation_behavior.js
// 在 get_relation.js 文件里面 module.exports = Behavior({ methods:{ // 获取父组件实例的快捷方法 _parent () { // 如果根据该路径获取到acestor组件为null,则说明this为ancesor var parentNode = this.getRelationNodes('../record_item/record_item') if (parentNode&&parentNode.length !== 0) { return parentNode[0] } else { return this } }, // 获取兄弟组件实例的快捷方法 _sibling(name) { var node = this._parent().getRelationNodes(`../${name}/${name}`) if (node &&node.length > 0) { return node[0] } } } })
然后在组件b, 和 组件c 上引入该behavior,并且调用方法,获取父组件和兄弟组件的实例
// 组件b中 var relation_behavior = require('./path_to_relation_behavior') Component({ behaviors:[relation_behavior], methods:{ test () { // 获得父组件的实例 let parent = this._parent() // 访问父组件的数据d console.log(parent.data.name) // 修改父组件的数据 parent.setData({ name: 'test1' }) // 获得兄弟组件的实例 let sibling = this._sibling('c') // 访问兄弟组件的数据 console.log(sibling.data.name) // 修改兄弟组件的数据 sibling.setData({ name:"test" }) } } }) // 组件c中 var relation_behavior = require('./path_to_relation_behavior') Component({ behaviors:[relation_behavior], methods:{ test () { // 获得父组件的实例 let parent = this._parent() // 访问父组件的数据d console.log(parent.data.name) // 修改父组件的数据 parent.setData({ name: 'test1' }) // 获得兄弟组件的实例 let sibling = this._sibling('b') // 访问兄弟组件的数据 console.log(sibling.data.name) // 修改兄弟组件的数据 sibling.setData({ name:"test" }) } } })
同时需要注意,c和b两个组件,从relations属性的角度来说,是a的后代组件。
但是组件b和组件c 所处的作用域, 都是主页面的作用域,传入的property都是主页面的property,这样就保证了组件数据的灵活性。relations 像一个隐形的链子一样把一堆组件关联起来,关联起来的组件可以相互访问,修改对方的数据,但是每一个组件都可以从外界独立的获取数据。
看了这么多理论的东西,还是需要一个具体的场景来应用。
比如说,我们有个一个分享记录图片心情的页面,当用户点击【点赞】的按钮时候,该心情的记录 点赞按钮会变红,下面的一栏位置会多出点赞人的名字。
如果不通过组件化,很可能的做法是 修改一个点赞按钮,然后遍历数据更新数据,最后所有记录列表的状态都会被重新渲染一遍。
如果是通过组件化拆分:把点赞的按钮封装为 组件b, 下面点赞人的框封装为组件c, 每一个心情记录都是一个组件a
下面是代码实现
// 在主页面内 <view wx:for='{{feed_item}}'> <a item='{{item}}'> <b></b> <c></c> </a> <view> // 在组件a内 var behavior_relation = require('../../relation_behavior.js) //这里引入上文说的Behavior Component({ behaviors:[behavior_relation], relations:{ '../b/b':{ type: 'descendant' } } }) // 在组件b内 var behavior_relation = require('../../relation_behavior.js) //这里引入上文说的Behavior Component({ behaviors:[behavior_relation] relations:{ '../a/a':{ type: 'ancestor' } }, data: { is_like: false //控制点赞图标的状态 }, methods:{ // 当用户点赞的时候 onClick () { // 修改本组件的状态 this.setData({ is_like: true }) // 修改 c 组件的数据 this._sibling('c').setData({ likeStr: this._sibling('c').data.likeStr + '我' }) } } }) // 在组件c内 var behavior_relation = require('../../relation_behavior.js) //这里引入上文说的Behavior Component({ behaviors:[behavior_relation], relations:{ '../a/a':{ type: 'ancestor' } }, data:{ likeStr:'晓红,小明' } })
这样,组件b 可以修改组件c中的数据。同时,组件b 和 组件c 又可以通过 properties 和 事件系统,和主页面保持独立的数据通信。
相关推荐:
以上がWeChat ミニ プログラムをコンポーネント化するためのソリューションと方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。