Skip to content

Context 上下文

Jiess中的上下文,特指setup渲染函数中的this,其指向Context的实例对象, 正是因为在配置函数中需要用到this,所以setup一定要定义为function函数

视频中介绍上下文中的add和render这两个基础方法

上下文中提供了当前环境的生命周期钩子,各种工具方法等……

Jiess上下文与原框架上下文

  • vue2中,也常用到this,其指向当前vue组件的上下文环境
  • 本节的this,指向Jiess中的Context实例对象,所以两者不同

Jiess上下文中获取原框架上下文

js
function setup() {
	const nativeContext = this.frame;
}

扁平化配置

在Jiess中,通过配置对象为标签或组件提供属性,事件……;这些事件和属性不分层级, 以摊平的形式到配置对象中,所以成为扁平化配置对象

Jiess内置属性

除了属性,事件,类名,样式外;Jiess内置属性也是配置重要的组成部分

is 属性

用于指定该配置对象最终会渲染成什么标签或组件

不指定is属性

未指定is属性,会渲染成div标签

js
this.add(
	//未指定 is 属性
	this.render({
		children:'未指定is属性,会渲染成div标签'
	})
)

指定is属性

is支持如下多种方案渲染:

  • 字符串:一般为普通的标签元素,也可以是全局的Jiess组件
  • Jiess组件:定义的Jiess组件可以不用全局注册,单纯局部使用
  • 原框架组件:支持原框架组件(如:您可以直接提供UI框架组件)

render子元素

render中,首参为配置,后面的参数为子元素

  • 数字或字符串
  • jsRender对象
  • 原框架虚拟节点
js
import React from 'react';
// 构建原框架的虚拟节点
const native = React.createElement('span', null, '原框架虚拟节点');
// 在jiess环境中
function setup() {
	// 加入渲染集合
	this.add(
		this.render({ className: 'box' },
			88,
			'字符串',
			['数组'],
			[
				['会自动展开的多层数组']
			],
			this.render({
				is: 'span',
				children: 'jsRender对象'
			}),
			// 虚拟节点
			native,
			// 被忽略
			null,
			// 被忽略
			undefined,
		)
	)
}

子元素为null或undefined时,忽略渲染

JsRender

通过调用this.render方法可以得到jsRender实例对象

称呼说明

jsRender实例是最小的渲染单元,可渲染成html中的标签,也可以渲染指定的组件; 在本文档中也常称其为jsRender节点渲染对象

与 html 相似,在构建页面或组件的过程中,可以将渲染对象作为子元素嵌套于另一个 渲染对象中,从而形成渲染对象的嵌套树,这棵树最终会渲染成html节点树

jsRender节点

示例中演示使用配置构造<h3/>标签,并得到jsRender节点

js
// 调用jiess环境中的render方法,返回jsRender节点
const jsRender = this.render({
	is: 'h3',
	children: 'Hello Jiess!'
})

上面示例的jsRender会被渲染为:

html
<h3>Hello Jiess!</h3>

渲染结构树

利用render方法的嵌套特性,将小小的渲染单元组合成庞大的渲染结构树

js
// 调用jiess环境中的render方法,返回jsRender对象
const jsRender = this.render({
		is: 'ul',
		className: 'box'
	},
	this.render({
		is: 'li',
		children: '张三'
	}),
	this.render({
		is: 'li',
		children: '李四'
	})
)

以上示例的jsRender会被渲染为:

html
<ul class="box">
	<li>张三</li>
	<li>李四</li>
</ul>

渲染对象 与 虚拟节点

JiessjsRender的解析过程中,渲染结构树最终会被解析为虚拟节点树, 就是说渲染对象会被解析为虚拟节点,最后使用原框架的渲染能力生成页面

相互转化

上面提到了渲染对象虚拟节点间的关联,这里介绍两者间的转化关系

渲染对象 转 虚拟节点

通过调用渲染对象中的getNative方法,即可转化

js
// 调用jiess环境中的render方法,返回jsRender对象
const jsRender = this.render({
	is: 'h3',
	children: 'Hello Jiess!'
})
// 渲染对象 转 虚拟节点
jsRender.getNative()

虚拟节点 转 渲染对象

准确来说,虚拟节点无法转化渲染对象,但该过程也是不需要的; 因为render方法的子元素天然支持虚拟节点,无需这样的转化包装过程

Attrs 属性

jiess 实例对象

  • 类型 Object
  • 详细

该对象是Jiess构建过程中的全局单例,其提供了关键的全局属性和方法

frame 原框架上下文

  • 类型 Object
  • 详细

配置函数中,可通过该属性获取原框架上下文

Methods 方法

add

  • 类型 Function
  • 参数 (...arg: any[])
  • 返回 context
  • 详细

将一个或多个渲染元素(JsRender对象,原框架组件,或基础数据)加入到渲染集合中

js
//添加到渲染集合中
this.add(
	//事件和属性,扁平化配置
	this.render({
		is: 'button',
		onClick() {
			// ...coding
		}
	}, '按钮')
)

注:只有加入到渲染集合中的渲染元素才会被渲染

add分段添加

如果有很多元素需要添加,可以分成多断多次添加元素

js
function setup() {
	this.add(123);
	this.add('第二断');
	this.add(this.render({}, '第三断'));
}

add链式添加

add方法返回上下文对象,所以支持链式调用

js
function setup() {
	this.add(123)
		.add('第二断')
		.add(this.render({}, '第三断'));
}

render

  • 类型 Function
  • 参数 (config , child1, child2, ...)
  • 返回 jsRender
  • 详细

用于将用配置对象转化为jsRender节点,配置对象遵循扁平化的配置原则; 首参传入配置对象,后面的参数为子渲染元素,调用返回jsRender实例

js
function setup() {
	this.add(
		this.render({ is: 'span' }, '构造渲染对象并加入渲染集合')
	);
}

render子元素

  • render子元素为首参之后的元素,可以放零个到多个
js
function setup() {
	this.add(
		this.render({ is: 'span' }, '元素一', '元素二', '元素三')
	);
}
  • render子元素可以是基础数据或 jsRender 渲染对象
js
function setup() {
	this.add(
		this.render({ is: 'ul' },
			this.render({ is: 'li' }, '元素一'),
			this.render({ is: 'li' }, '元素二')
		)
	);
}
  • render子元素可以放多层嵌套的数组,在底层有自动展平操作
js
function setup() {
	this.add(
		this.render({ is: 'ul' },
			// 底层会自动将所有子元素集合展平为一维数组
			[[[[[this.render({ is: 'li' }, '元素一')]]]]],
			[[[[[this.render({ is: 'li' }, '元素二')]]]]]
		)
	);
}
  • jsRender嵌套形成的渲染树,最后会映射为Dom节点树
js
function setup() {
	this.add(
		// 多层嵌套
		this.render({ class: 'container' },
			this.render({ is: 'ul' },
				this.render({ is: 'li' }, '元素一'),
				this.render({ is: 'li' }, '元素二')
			)
		)
	);
}

slot

  • 类型 Function
  • 参数 (name = 'default' , param?:object, ...childs: any[])
  • 返回 jsRender
  • 详细 定义名为name的插槽,name默认值为default

定义带插槽的Jiess组件

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,可以从组件内部对外部作用域提供作用域参数

forceUpdate

  • 类型 Function
  • 参数 ——
  • 返回 ——

用于强制更新当前组件;通常应用于异步添加元素后的页面更新

js
async function setup() {
	this.add('这里被立即渲染');

	setTimeout(() => {
		this.add('异步添加元素,需要强制更新');
		this.forceUpdate();
	}, 1000);
}

注:除非父级再次发生更新,否则即使更新当前组件,setup也不会调用

provide/inject

  • 类型 Function
  • 参数 (param = {}) / (field: string)
  • 返回 —— / 根据field检索到的值

provideinject均为上下文提供的方法,父级调用provide注入共享数据, 后代Jiess组件中,使用inject方法,可获取注入的数据

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 })
	)
}

注:provide/inject 跨组件传递数据,造成代码的可读行降低,请谨慎使用