Context 上下文
Jiess中的上下文,特指setup渲染函数中的this
,其指向Context的实例对象, 正是因为在配置函数中需要用到this
,所以setup一定要定义为function函数
视频中介绍上下文中的add和render这两个基础方法
上下文中提供了当前环境的生命周期钩子,各种工具方法等……
Jiess上下文与原框架上下文
- vue2中,也常用到this,其指向当前vue组件的上下文环境
- 本节的this,指向Jiess中的Context实例对象,所以两者不同
Jiess上下文中获取原框架上下文
function setup() {
const nativeContext = this.frame;
}
扁平化配置
在Jiess中,通过配置对象为标签或组件提供属性,事件……;这些事件和属性不分层级, 以摊平的形式到配置对象中,所以成为扁平化配置对象
Jiess内置属性
除了属性,事件,类名,样式外;Jiess内置属性也是配置重要的组成部分
is 属性
用于指定该配置对象最终会渲染成什么标签或组件
不指定is属性
未指定is属性,会渲染成div标签
this.add(
//未指定 is 属性
this.render({
children:'未指定is属性,会渲染成div标签'
})
)
指定is属性
is支持如下多种方案渲染:
- 字符串:一般为普通的标签元素,也可以是全局的Jiess组件
- Jiess组件:定义的Jiess组件可以不用全局注册,单纯局部使用
- 原框架组件:支持原框架组件(如:您可以直接提供UI框架组件)
render子元素
render中,首参为配置,后面的参数为子元素
- 数字或字符串
- jsRender对象
- 原框架虚拟节点
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节点
// 调用jiess环境中的render方法,返回jsRender节点
const jsRender = this.render({
is: 'h3',
children: 'Hello Jiess!'
})
上面示例的jsRender
会被渲染为:
<h3>Hello Jiess!</h3>
渲染结构树
利用render
方法的嵌套特性,将小小的渲染单元组合成庞大的渲染结构树
// 调用jiess环境中的render方法,返回jsRender对象
const jsRender = this.render({
is: 'ul',
className: 'box'
},
this.render({
is: 'li',
children: '张三'
}),
this.render({
is: 'li',
children: '李四'
})
)
以上示例的jsRender
会被渲染为:
<ul class="box">
<li>张三</li>
<li>李四</li>
</ul>
渲染对象 与 虚拟节点
在Jiess
对jsRender
的解析过程中,渲染结构树最终会被解析为虚拟节点树, 就是说渲染对象会被解析为虚拟节点,最后使用原框架的渲染能力生成页面
相互转化
上面提到了渲染对象
与虚拟节点
间的关联,这里介绍两者间的转化关系
渲染对象 转 虚拟节点
通过调用渲染对象中的getNative
方法,即可转化
// 调用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对象,原框架组件,或基础数据)加入到渲染集合中
//添加到渲染集合中
this.add(
//事件和属性,扁平化配置
this.render({
is: 'button',
onClick() {
// ...coding
}
}, '按钮')
)
注:只有加入到渲染集合中的渲染元素才会被渲染
add分段添加
如果有很多元素需要添加,可以分成多断多次添加元素
function setup() {
this.add(123);
this.add('第二断');
this.add(this.render({}, '第三断'));
}
add链式添加
add方法返回上下文对象,所以支持链式调用
function setup() {
this.add(123)
.add('第二断')
.add(this.render({}, '第三断'));
}
render
- 类型
Function
- 参数 (config , child1, child2, ...)
- 返回 jsRender
- 详细
用于将用配置对象转化为jsRender节点,配置对象遵循扁平化的配置原则; 首参传入配置对象,后面的参数为子渲染元素,调用返回jsRender实例
function setup() {
this.add(
this.render({ is: 'span' }, '构造渲染对象并加入渲染集合')
);
}
render子元素
- render子元素为首参之后的元素,可以放零个到多个
function setup() {
this.add(
this.render({ is: 'span' }, '元素一', '元素二', '元素三')
);
}
- render子元素可以是基础数据或 jsRender 渲染对象
function setup() {
this.add(
this.render({ is: 'ul' },
this.render({ is: 'li' }, '元素一'),
this.render({ is: 'li' }, '元素二')
)
);
}
- render子元素可以放多层嵌套的数组,在底层有自动展平操作
function setup() {
this.add(
this.render({ is: 'ul' },
// 底层会自动将所有子元素集合展平为一维数组
[[[[[this.render({ is: 'li' }, '元素一')]]]]],
[[[[[this.render({ is: 'li' }, '元素二')]]]]]
)
);
}
- jsRender嵌套形成的渲染树,最后会映射为Dom节点树
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组件
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
,可以从组件内部对外部作用域提供作用域参数
forceUpdate
- 类型
Function
- 参数 ——
- 返回 ——
用于强制更新当前组件;通常应用于异步添加元素后的页面更新
async function setup() {
this.add('这里被立即渲染');
setTimeout(() => {
this.add('异步添加元素,需要强制更新');
this.forceUpdate();
}, 1000);
}
注:除非父级再次发生更新,否则即使更新当前组件,setup也不会调用
provide/inject
- 类型
Function
- 参数 (param = {}) / (field: string)
- 返回 —— / 根据field检索到的值
provide
和inject
均为上下文提供的方法,父级调用provide注入共享数据, 后代Jiess组件中,使用inject方法,可获取注入的数据
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 跨组件传递数据,造成代码的可读行降低,请谨慎使用