组件化
本节介绍Jiess组件的定义与使用
说明
Jiess组件基于原框架组件而封装;最后映射为原框架组件,并在原框架环境中渲染
初识Jiess组件
Jiess组件是一个对象,支持定义如下属性与方法
isJiess
- 类型
Boolean
- 必要 是
- 说明 必须设为true,该属性作为辨识Jiess组件的标识
setup
- 类型
Function
- 必要 是
- 说明 须定义为function函数,与setup渲染函数本质相同
name
- 类型
String
- 必要 否
- 说明 用于定义组件名,在开发模式下,能更精准的报错
props
- 类型
Function
- 必要 否
- 说明 用于校验传入组件的参数,并对参数进行过滤
data
- 类型
Function
- 必要 否
- 说明 用于定义组件的数据,且同时赋值于JsRender,用于暴露数据
methods
- 类型
{ [x:string]: Function }
- 必要 否
- 说明 用于定义组件的方法,且同时赋值于JsRender,用于暴露数据
为什么是一个对象
@jiess/plus
是基于原框架的封装,由于vue和react的组件多样,有options组件, 函数式组件,类组件,所以为了同时兼容这些组件,所以最终选择使用对象定义组件
setup方法
setup方法为必要属性,用于组件的渲染,它与setup配置函数语法一致
setup方法与配置函数的区别
Jiess组件接收入参是通过setup方法的首参提供,仅此一处区别
上手使用组件
接下来,咱们上手自定义Jiess组件,并使用
// 定义Jiess组件
const MyComponent = {
isJiess: true,
name: 'MyComponent',
data() {
// 此处的上下文与setup中一致
// 通过this.$props获取组件入参
return {
attr1: null
}
},
// 定义了props,则仅限满足props的属性能传入组件
props() {
return {
prop1: { type: 'string', default: '默认1' },
// 引用数据类型,无需使用函数进行包裹,所见即所得
prop2: { type: 'object|array', default: {} }
}
},
// 如果未定义props,则所有属性不经过过滤,直接传入组件
setup({ prop1, prop2 }) {
this.add(
// 将配置对象转化为渲染单元
this.render({
// div标签中的文本内容
children: '没有is属性,默认为div'
})
)
},
methods: {
methods1() {
// 此处的上下文与setup中一致
// 通过this.$props获取组件入参
console.log(this.$props.prop1)
}
}
};
// ================以下为组件的使用=====================
function setup() {
// 使用Jiess组件,与使用标签的用法一致
const renderNode = this.render({
is: MyComponent,
prop1: '传入参数'
});
// 通过JsRender节点获取组件暴露的属性
renderNode.attr1();
// 通过JsRender节点获取组件暴露的方法
renderNode.methods1();
// 添加元素,用于渲染
this.add(renderNode);
};
export default setup;
注:Jiess组件仅适用于Jiess环境,不能在原框架环境直接使用
全局注册组件
- 调用
$component
(Jiess实例对象)方法注册
import { $component } from '@jiess/plus';
// 一般在jiess全局安装过程中,全局注册Jiess组件
$component('MyComponent', MyComponent);
- 使用全局注册组件。
is
属性指定全局组件名称
function setup() {
this.render({
is : 'MyComponent'
})
}
父子传参
Jiess采用扁平化的配置方案,组件需要用到什么参数,就指定相应的属性
下面,我们接着使用前面定义好的MyComponent
组件做讲解:
// 传入参数 param1 和 param2
this.render({
is : MyComponent,
param1: 'xxx1',
param2: ['xxx2']
})
在Jiess组件setup方法的props对象中,可以获取传入的数据
const MyComponent = {
isJiess: true,
name: 'MyComponent',
// 未定义props,所有属性不过滤,直接传入组件
setup(props){
...
console.log(props.param1) // xxx1
console.log(props.param2) // ['xxx2']
...
}
}
props属性
在 父->子 传参时,子组件通常需要对传入的参数进行把控,所以,我们可以定义props方法, 对组件所需的属性,默认值,数据类型等进行过滤与校验
入参过滤
组件中当定义了props,则组件入参手动管控,只有满足props的条件属性能传入组件
props的定义
props方法返回对象,对象键为允许接收的字段,属性值为过滤条件
props为什么是函数
熟悉vue的小伙伴们都很清楚,在使用vue的props,若需要指定默认值为对象或数组,则default须赋值为函数,并返回该默认值
而在Jiess组件中使用props时,就简单明了;因为props本身定义为函数,所以在定义props函数内部属性的默认值时,无须转化
const MyComponent = {
isJiess: true,
props() {
return {
data: {
default: { name: '张三', age: 18 }
}
}
},
setup({ data }) {
// 组件内部仅接收data属性,传入的其他属性均会被过路
// 如果没有传入data属性,则会使用默认值,且默认值为对象
}
}
校验数据类型
定义 type 可属性用于校验数据类型,值为数据类型字符串
数据类型字符串
Jiess中的数据类型校验,基于Object.prototype.toString.call
方法;所以该方法可鉴别的类型, 均可将对应的类型字符串,作为校验参数,且首字母不区分大小写
常用类型字符串:string number boolean null undefined object promise function array
当属性需要支持多种类型时,使用|
连接类型字符串即可;如string|number
—— 传入该组件的当前参数,可以定义为字符串类型,也可以定义为数字类型
const MyComponent = {
isJiess: true,
props() {
return {
data: {
type: 'number|string|boolean'
}
}
},
setup({ data }) {
// 组件内部仅接收data属性,传入的其他属性均会被过路
// 如果没有传入data属性,不会报错
// data参数可以是 数字,字符串或布尔值
}
}
必填校验
这里需要定义 required 属性,值为布尔值
const MyComponent = {
isJiess: true,
props() {
return {
data: {
type: 'number|string|boolean',
required: true,
}
}
},
setup({ data }) {
// 组件内部仅接收data属性,传入的其他属性均会被过路
// 如果没有传入data属性,则会触发必填报错
// data参数可以是 数字,字符串或布尔值
}
}
与vue中props对比
Jiess的props参考了Vue,但总体有较大的区别:
- type类型为字符串类型,同时多类型使用
|
分割 - props可以定义为一个方法,返回一个数据校验对象
- 若指定参数默认值为对象或数组时,无须定义为返回该值的函数
插槽
顾名思义,就是在特定位置预留的槽位,用于插入一个或多个组件或元素
定义插槽
调用上下文的slot方法,用于定义插槽
slot方法
- 首参:定义插槽名,为必要参数
- 后参:未使用插槽时的默认渲染,与this.render()一致
const MyComponent = {
isJiess: true,
name: 'MyComponent',
setup() {
this.add('在Jiess组件中演示插槽的定义');
// 这是一个默认插槽
this.add(this.slot());
// 这是一个名为slot1的具名插槽
this.add(this.slot('slot1'));
// 这是一个名为slot2的具名插槽
// 如果没有使用该插槽,启用默认渲染
this.add(this.slot('slot2', {
is: 'span'
}, '默认渲染和render渲染语法一致'));
// 这是一个名为slot3的作用域插槽
this.add(this.slot('slot3', {
$slotData: { value: '提供给外部作用域的数据' }
}));
}
}
使用插槽
在使用默认插槽和其他插槽时,存在一定区别:
使用默认插槽
默认插槽,即组件中定义的default插槽,分为三种用法,酌情使用
// 定义带有默认插槽的Jiess组件
const Child1 = {
isJiess: true,
setup() {
this.add(
// 未指定插槽名,则定义为默认插槽
this.slot()
// 也可明确指定为default即默认插槽
// this.slot('default')
)
}
};
// ============插槽的使用过程==========
export default function() {
// 使用后参提供子元素使用默认插槽(优先级最低)
this.add(
this.render({ is: Child1 },
this.render({ children: '使用默认插槽' }),
this.render({ children: '使用默认插槽' })
)
);
// 定义children属性使用默认插槽(优先级次之)
this.add(
this.render({
is: Child1,
children: this.render({ children: '使用默认插槽' })
}),
this.render({
is: Child1,
children: [
'也可定义为数组',
'也可定义为数组'
]
})
);
// 以具名插槽的方式使用默认插槽(优先级最高)
this.add(
this.render({
is: Child1,
$slots: {
'default'() {
return this.render({ children: '使用默认插槽' })
}
}
}),
this.render({
is: Child1,
$slots: {
'default'() {
return [
'也可定义为数组',
'也可定义为数组'
]
}
}
})
)
}
说明:具名使用default插槽,具有最高的优先级,children使用默认插槽次之
使用具名插槽
顾名思义,就是使用带有具体命名的插槽
// 定义含有插槽的Jiess组件
const Child1 = {
isJiess: true,
setup() {
this.add(
// 定义一个普通的具名插槽
this.slot('test1'),
// 定义一个带有默认内容的插槽
this.slot('test2', {
is: 'span',
style: { color: '#f40' }
}, '未使用该插槽时渲染该内容'),
// 定义一个作用域插槽
this.slot('test3', {
// 通过$slotData定义需要反向传给父级组件的数据
$slotData: {
innerData: '这里的数据通过作用域插槽传出'
}
})
)
}
}
// ============插槽的使用过程==========
export default function() {
this.add(
this.render({
is: Child1,
// 用$slots使用插槽
$slots: {
test1() {
return '使用test1插槽'
},
// 作用域插槽,用于传出子元素的数据
test2({ innerData }) {
return this.render({
children: innerData
})
}
}
})
)
}
使用内置属性
$slotData
,可以从组件内部对外部作用域提供作用域参数
数据透传
数据透传可跨多层组件共享数据,在祖先组件中共享数据,后代组件中可获取该数据
警告
数据透传会让代码可读性变差,应谨慎使用
用法
const Child = {
isJiess: true,
setup() {
// Parent与Grandpa均注入了grandpa2属性,则使用更近注入的属性
// 祖先级数据1
const grandpa1 = this.inject('grandpa1');
// 父级重新注入
const grandpa2 = this.inject('grandpa2');
// 父级数据
const parent = this.inject('parent');
}
};
const Parent = {
isJiess: true,
setup() {
// Parent组件中可获取Grandpa注入的数据
// 祖先级数据1
const grandpa1 = this.inject('grandpa1');
// 祖先级数据2
const grandpa2 = this.inject('grandpa2');
// 调用provide方法共享数据
this.provide({
parent: '父级数据',
// 打断Grandpa传入的grandpa2
grandpa2: '父级重新注入'
});
this.add(this.render({
is: Child
}));
}
};
const Grandpa = {
isJiess: true,
setup() {
// 调用provide方法共享数据
this.provide({
grandpa1: '祖先级数据1',
grandpa2: '祖先级数据2'
});
this.add(this.render({
is: Parent
}));
}
};
// ======setup中使用Grandpa组件=======
export default function() {
this.add(
this.render({ is: Grandpa })
)
}
在示例中 Grandpa 和 Child 为直系关系,所以可以数据透传
注:目前仅支持Jiess组件直接嵌套,而无法跨Jiess基础组件进行数据透传