Skip to content

组件化

本节介绍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组件,并使用

js
// 定义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实例对象)方法注册
js
import { $component } from '@jiess/plus';
// 一般在jiess全局安装过程中,全局注册Jiess组件
$component('MyComponent', MyComponent);
  • 使用全局注册组件。is属性指定全局组件名称
js
function setup() {
	this.render({
		is : 'MyComponent'
	})
}

父子传参

Jiess采用扁平化的配置方案,组件需要用到什么参数,就指定相应的属性

下面,我们接着使用前面定义好的MyComponent组件做讲解:

js
// 传入参数 param1 和 param2
this.render({
	is : MyComponent,
	param1: 'xxx1',
	param2: ['xxx2']
})

在Jiess组件setup方法的props对象中,可以获取传入的数据

js
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函数内部属性的默认值时,无须转化

js
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—— 传入该组件的当前参数,可以定义为字符串类型,也可以定义为数字类型

js
const MyComponent = {
	isJiess: true,
	props() {
		return {
			data: {
				type: 'number|string|boolean'
			}
		}
	},
	setup({ data }) {
		// 组件内部仅接收data属性,传入的其他属性均会被过路
		// 如果没有传入data属性,不会报错
		// data参数可以是 数字,字符串或布尔值
	}
}

必填校验

这里需要定义 required 属性,值为布尔值

js
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()一致
js
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插槽,分为三种用法,酌情使用

js
// 定义带有默认插槽的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使用默认插槽次之


使用具名插槽

顾名思义,就是使用带有具体命名的插槽

js
// 定义含有插槽的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,可以从组件内部对外部作用域提供作用域参数

数据透传

数据透传可跨多层组件共享数据,在祖先组件中共享数据,后代组件中可获取该数据

警告

数据透传会让代码可读性变差,应谨慎使用

用法

js
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基础组件进行数据透传