在ArkUI的ETS中實現【插槽】的功能
距離ETS的發布也有一段時間,也有不少小伙伴通過ETS制作出很多精美的頁面,但在我查閱ETS的組件和API中發現,現有版本的ETS并沒有插槽的功能。經過一段時間的探索終于找到曲線救國方式實現插槽功能,得以讓組件之間進行解耦。
什么是插槽
了解插槽的小伙伴可以跳過
vue官方定義是:插槽是一套內容分發的API,當組件渲染的時候,
通俗一點就是插槽就像一個占位符,將組件外的內容通過API分發至組件內。
實現步驟
定義一個slot類
旨在提供一個具名的插槽,故定義一個slot類做后續委托。這不是實現的關鍵點,也可不定義。
- class Slot{
- name:string="default"
- builder:any
- constructor (name:string,builder:any){
- this.name=name;
- this.builder=builder
- }
- }
創建一個組件CompA
創建一個自定義組件CompA,并提供兩個具名插槽的處理,一個defualt,一個slot2。
- @Component
- struct CompA{
- @State text:string=""
- @State data:string[]=[]
- @State slot:Slot=new Slot(null)
- build(){
- Column(){
- Column(){
- Text("CompA組件內的內容")
- .fontColor("#00F")
- .fontSize(16)
- .margin(10)
- }
- Column(){
- Row(){
- if(this.slot.name=="default"){
- ForEach(["這是默認插槽【default】"],
- this.slot.builder)
- }
- if(this.slot.name=="slot2"){
- ForEach(this.data,
- this.slot.builder)
- }
- }
- }
- }
- }
- }
構建頁面的組件
構建一個Index的頁面,在頁面內創建兩個Buider bulder1 ,builder2,并實例化兩個Slot類slot1、slot2,將builder1,builder2分別給到slot1,slot2。
builder1內通過Text組件顯示一段文字。
builder2內通構建稍微復雜一點的模型,設置一個文字和二維碼。
- @Entry
- @Component
- struct Index {
- @Builder builder1(str:string){
- Text(str).fontSize(18).fontColor("#f00")
- }
- @Builder builder2(obj:any){
- Column(){
- Row(){
- Text(obj.title).fontSize(16)
- }
- Row(){
- QRCode(obj.title).width(100).height(100)
- }.margin(10)
- }.margin(10)
- }
- slot1:Slot=new Slot(this.builder1)
- slot2:Slot=new Slot(this.builder2,"slot2")
- build() {
- Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
- Column(){
- CompA(){
- Text("這樣是不會顯示的").fontSize(24)
- }
- CompA({slot:this.slot1})
- CompA({slot:this.slot2,data:[{title:"這是第二個插槽"},{title:"http://www.baidu.com"}]})
- }
- }
- .width('100%')
- .height('100%')
- }
- }
顯示效果:

通過圖片可以看到,builder1,builder2真實位置是在了CompA的slot處。
重點
上面就提到Slot類可以不用創建,因為實現原理是通過ForEach+Builder實現,也可以將Builder通過函數綁定到組件內。
再看一下官方文檔中ForEach:

全部代碼供參考
- @Entry
- @Component
- struct Index {
- @Builder builder1(str:string){
- Text(str).fontSize(18).fontColor("#f00")
- }
- @Builder builder2(obj:any){
- Column(){
- Row(){
- Text(obj.title).fontSize(16)
- }
- Row(){
- QRCode(obj.title).width(100).height(100)
- }.margin(10)
- }.margin(10)
- }
- slot1:Slot=new Slot(this.builder1)
- slot2:Slot=new Slot(this.builder2,"slot2")
- build() {
- Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
- Column(){
- CompA(){
- Text("這樣是不會顯示的").fontSize(24)
- }
- CompA({slot:this.slot1})
- CompA({slot:this.slot2,data:[{title:"這是第二個插槽"},{title:"http://www.baidu.com"}]})
- }
- }
- .width('100%')
- .height('100%')
- }
- }
- @Component
- struct CompA{
- @State text:string=""
- @State data:string[]=[]
- @State slot:Slot=new Slot(null)
- build(){
- Column(){
- Column(){
- Text("CompA組件內的內容")
- .fontColor("#00F")
- .fontSize(16)
- .margin(10)
- }
- Column(){
- Row(){
- if(this.slot.name=="default"){
- ForEach(["這是默認插槽【default】"],
- this.slot.builder)
- }
- if(this.slot.name=="slot2"){
- ForEach(this.data,
- this.slot.builder)
- }
- }
- }
- }
- }
- }
- class Slot{
- name:string="default"
- builder:any
- constructor (builder:any,name?:string){
- name && (this.name=name);
- this.builder=builder
- }
- }