距离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
- }
- }