Jiess 核心库介绍
Jiess 是一个基于 JavaScript/TypeScript 的配置式渲染核心库,同时支持 Vue 3、React、Web Components、Three.js 和 Konva。它的核心思想是用 JavaScript 对象描述界面,一套代码跨框架运行——不需要写模板,不需要写 JSX。v6.0 新增 Three.js 适配器(3D 场景)和 Konva 适配器(2D Canvas 场景),支持配置式声明 3D/2D 场景。
什么是 Jiess?
Jiess 的核心价值在于用纯配置驱动界面:你描述"要什么元素、要什么属性、要什么行为",Jiess 自动转换为对应框架的渲染结果。这带来几个关键收益:
- 跨框架复用:同一份 Jiess 组件,在 Vue 3、React 和 Web Components 中表现完全一致;Three.js 和 Konva 适配器支持配置式 3D/2D 场景渲染
- 配置式开发:扁平化的对象写法,像 HTML 一样直观,但拥有 JS 的全部表达能力
- 内置响应式:
$ref/$reactive/$computed/$watch开箱即用,数据驱动视图 - 无模板语法:不学 Vue 模板指令、不学 React JSX 规则——你会 JavaScript 就会 Jiess
Jiess 的设计理念
- 跨框架兼容:一套配置,多框架运行,无需修改代码
- 配置式开发:通过 JavaScript 对象描述组件结构,像写 HTML 标签一样自然
- 响应式驱动:内置高效响应式系统,数据变化时视图自动更新
- 组件化思想:完整的组件系统,支持 props、插槽、生命周期、依赖注入
- 性能优先:智能代理缓存、延迟创建嵌套代理,减少不必要计算
- 类型安全:全面支持 TypeScript,提供完整类型定义
为什么选择 Jiess?
三框架对比:同一个计数器(Web 框架)
以下是同一个功能在三种 Web 技术栈下的实现对比。注意代码量的差异,以及 Jiess 一份代码同时运行在 Vue 3、React 和 Web Components 中的能力:
| Vue 3 · 模板语法 | React · JSX | Jiess · 纯 JS 配置对象 |
|---|---|---|
vue | jsx | javascript |
Jiess 的核心差异:绕开模板与 JSX
传统前端开发绕不开两样东西:模板语法和编译步骤。Vue 需要 <template> 和 v-if/v-for 指令,React 需要 JSX 和 Babel 编译。两者都要求开发者学习一套"框架特有的表达式语言"。
Jiess 的选择是:不引入任何新语法,全部使用 JavaScript。看下面这个真实对比:
| 需求 | Vue 模板 | React JSX | Jiess(纯 JS 对象) |
|---|---|---|---|
| 条件渲染 | v-if="show" | {show && <div/>} | { is: 'div', $if: show } |
| 列表渲染 | v-for="item in list" | list.map(i => <li/>) | list.map(i => ({ is: 'li', children: i })) |
| 动态样式 | :class="{ active: isActive }" | className={isActive ? 'active' : ''} | style: { color: isActive ? 'red' : 'blue' } |
| 事件绑定 | @click="handler" | onClick={handler} | onClick: handler |
| 插槽 | <slot name="header"/> | {props.children} | $slots: { header: [...] } |
| 数据绑定 | {{ message }} | {message} | { $proxy: message } |
关键区别:Vue 和 React 的左边两列都需要编译工具链(Vue SFC Compiler / Babel JSX Transform),而 Jiess 的配置对象是标准 JavaScript 运行时求值——不需要任何编译器,不需要任何特殊文件格式(.vue / .jsx),你的 .js 文件就是组件的全部。
// 这就是一个完整的 Jiess 组件——所见即所得,无需编译
const Counter = {
isJiess: true,
name: 'Counter',
setup() {
this.add({ /* ... 纯 JS 配置对象 ... */ })
},
style: `
.counter { padding: 16px; }
`
}
// 在 .js 文件中写完,Vue 3、React 和 Web Components 项目都能直接用这带来的实际好处
| 传统框架 | Jiess |
|---|---|
需要 .vue / .jsx 特殊文件 | 只需要 .js / .ts |
| 模板语法和逻辑切换心智负担高 | 全程 JS,无语法切换 |
| Vue 组件不能给 React 用 | 同一份组件,三个环境通用 |
动态渲染需 v-if 或三元表达式 | 直接 if/else 或三目运算,JS 怎么写你就怎么写 |
| 编译错误难以定位 | 运行时 JS 错误,堆栈清晰可读 |
| 需额外学习框架专属的模板语法 | 会 JavaScript 就会 Jiess |
目录
核心特性
跨框架兼容
- 一套配置渲染到五个环境:组件定义中不出现任何框架专属 API;Web 框架(Vue3/React/Web Components)行为完全一致,Three.js 和 Konva 适配器支持配置式 3D/2D 场景
- 统一开发体验:无论 Vue 3、React、Web Components、Three.js 还是 Konva,
this.add()/$watch/$computed行为完全一致 - 与现有项目无缝集成:Jiess 组件可以嵌入到已有的 Vue 或 React 页面中;Three.js/Konva 场景可在同一页面与其他框架共存
- 运行时环境检测:自动检测当前框架,应用相应适配策略;多环境共存时通过缓存 key 含环境维度实现隔离
配置式开发
// 这段代码在 Vue 3、React 和 Web Components 中完全一致
this.add({ is: 'button', children: '点我', onClick: () => alert('Hi') })配置对象的字段就是 HTML 属性名,平铺直叙:
is指定标签或组件,children写内容,onXxx绑定事件——没有模板语法,没有特殊指令- 支持完整的组件化模型:props、插槽、生命周期、依赖注入
- 全面 TypeScript 类型支持,增强开发体验
高效的响应式系统
内置轻量级响应式引擎:
$ref/$reactive创建响应式数据,$computed创建带缓存的计算属性$watch(proxy, 'field', callback)精确监听某个字段,$watchEffect(fn)自动追踪依赖- 支持深度嵌套对象和数组,支持
$model双向绑定表单 - 智能代理缓存:相同原始对象返回同一代理,延迟创建嵌套代理
灵活的节点创建
this.add() 是唯一创建 UI 的方法,覆盖全部场景:
- 文本节点、HTML 元素、自定义 Jiess 组件、第三方 UI 库组件
$if/$elseIf条件渲染,数组map实现列表渲染$slots插槽系统,支持内容灵活分发- 事件绑定、属性传递、样式管理,全部通过配置对象完成
易于扩展
- 模块化设计:核心功能模块化,易于扩展和定制
- 支持第三方库:可以与 Antd、Element Plus 等任意 UI 组件库配合使用
- 开放的架构:清晰的代码结构和 API 设计,便于二次开发
核心 API 概览
响应式系统 API
| API | 功能描述 |
|---|---|
$reactive | 创建响应式对象/数组,直接操作属性 |
$ref | 创建响应式引用(含 .value),适用所有类型 |
$computed | 创建计算属性,自动缓存,惰性求值 |
$watch | 监听指定字段/索引/整个对象的变化,返回停止函数 |
$watchEffect | 自动收集依赖的副作用,返回停止函数 |
$set | 向响应式对象动态添加属性 |
$isReactive | 判断是否为 $reactive 代理 |
$isRef | 判断是否为 $ref 引用 |
$clearReactiveCache | 清除指定对象的响应式缓存 |
组件系统 API
| API | 功能描述 |
|---|---|
$entry | 初始化 Jiess 环境,绑定框架,返回 Jiess 实例 |
上下文 API(setup 内 this)
| API | 功能描述 |
|---|---|
this.add() | 添加节点到组件渲染树 |
this.provide() | 提供数据给子组件,实现依赖注入 |
this.inject() | 从父组件注入数据,支持默认值 |
this.expose() | 暴露组件属性给父组件 |
this.forceUpdate() | 强制组件重新渲染 |
this.nextTick() | 下一次 DOM 更新后执行回调 |
this.onBeforeMount() | 注册组件挂载前钩子 |
this.onMounted() | 注册组件挂载后钩子 |
this.onBeforeUpdate() | 注册组件更新前钩子 |
this.onUpdated() | 注册组件更新后钩子 |
this.onBeforeUnmount() | 注册组件卸载前钩子 |
$ref vs $reactive 行为差异速查
| 场景 | $ref | $reactive |
|---|---|---|
| 创建 | $ref(0) / $ref({ a: 1 }) | $reactive({ a: 1 }) |
| 访问 | .value | 直接属性访问 |
| 整体替换 | ref.value = newObj ✅ | obj = xxx 丢失响应式 ❌ |
$watch(obj, 'field', cb) | $watch(ref, 'value', cb) | $watch(reactive, 'name', cb) |
$watch(obj, {}, cb) | 自动补 field='value',同步触发 | field=undefined,异步防抖触发 |
| 嵌套属性变更 | 需要 ref.value.xxx | 直接 state.xxx.yyy |
isSync 配置 | 默认同步,无需使用 | { isSync: true } 跳过防抖 |
| 推荐场景 | 基本类型、需整体替换、跨组件传递 | 表单数据、多字段对象 |
安装
使用 npm 安装
# Vue 3 环境
npm install @jiess/core vue
# React 环境
npm install @jiess/core react react-dom
# Web Components 环境(零框架依赖)
npm install @jiess/core
# TypeScript 项目(推荐)
npm install --save-dev typescript @types/react @types/react-dom配置 Jiess 环境
创建一个配置文件统一管理,然后在项目中引用。
Vue 3 环境配置:
// @/jiessConfig.js
import * as Vue from 'vue'
import { $entry } from '@jiess/core'
import JiessEnv from '@jiess/core/vue3'
/**
* 注册 Jiess 环境
* @param JiessEnv Jiess 环境类(Vue 3 版本)
* @param Vue Vue 框架实例(第二个参数,框架 API 层)
* @param contextParam 业务上下文(可选,组件 setup 中通过 this 访问)
* @returns Jiess 实例
*/
const jiess = $entry(JiessEnv, Vue)
// 提示:默认实例名为 'default',无需配置。
// 如需自定义实例名(多实例隔离),$name 属于第二个参数(框架 API 层):
// const jiess = $entry(JiessEnv, { ...Vue, $name: 'myApp' })
export default jiess.JiessComponent
export { jiess }React 环境配置:
// @/jiessConfig.js
import * as React from 'react'
import { $entry } from '@jiess/core'
import JiessEnv from '@jiess/core/react'
const jiess = $entry(JiessEnv, React)
// 自定义实例名:$entry(JiessEnv, { ...React, $name: 'myApp' })
export default jiess.JiessComponent
export { jiess }Web Components 环境配置:
// @/jiessConfig.js
import { $entry } from '@jiess/core'
import JiessEnv from '@jiess/core/web-component'
// Web Components 适配器无需外部框架,仅需传入 $tag 作为默认标签
const jiess = $entry(JiessEnv, { $tag: 'div' }, {})
export default jiess.JiessComponent
export { jiess }TypeScript 配置(推荐)
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"strict": true
}
}配置提示
- 统一配置文件:创建专门的
jiessConfig.js,方便全局管理 - 导出策略:同时导出
JiessComponent和jiess实例,满足不同使用场景 - 版本锁定:在
package.json中锁定 @jiess/core 版本,确保稳定性
快速开始
在 Vue 3 中使用 Jiess
创建一个 Vue 组件,引入配置好的 JiessComponent:
<template>
<div class="jiess-demo">
<h1>Vue 3 + Jiess 示例</h1>
<JiessComponent :setup="setup" />
</div>
</template>
<script setup>
import JiessComponent from '@/jiessConfig'
function setup() {
this.add('Hello, Jiess!')
this.add({
is: 'button',
children: '点击我',
className: 'demo-button',
style: {
padding: '10px 20px',
backgroundColor: '#409eff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
},
onClick: () => alert('你点击了按钮!')
})
}
</script>
<style>
.jiess-demo {
padding: 20px;
font-family: Arial, sans-serif;
}
.demo-button {
margin-top: 10px;
}
</style>在 React 中使用 Jiess
创建一个 React 组件,同样引入配置好的 JiessComponent:
import React from 'react'
import JiessComponent from '@/jiessConfig'
function setup() {
this.add('Hello, Jiess!')
this.add({
is: 'button',
children: '点击我',
className: 'demo-button',
style: {
padding: '10px 20px',
backgroundColor: '#409eff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
},
onClick: () => alert('你点击了按钮!')
})
}
function JiessDemo() {
return (
<div className="jiess-demo">
<h1>React + Jiess 示例</h1>
<JiessComponent setup={setup} />
</div>
)
}
const style = document.createElement('style')
style.textContent = `
.jiess-demo {
padding: 20px;
font-family: Arial, sans-serif;
}
.demo-button {
margin-top: 10px;
}
`
document.head.appendChild(style)
export default JiessDemo重要:
setup函数必须使用普通函数声明(不是箭头函数),Jiess 需要通过this注入组件上下文。
一个简单的响应式示例(Vue 3)
下面展示如何在 Jiess 中使用响应式数据:
<template>
<div class="reactive-demo">
<h2>Vue 3 + Jiess 响应式示例</h2>
<JiessComponent :setup="setup" />
</div>
</template>
<script setup>
import JiessComponent from '@/jiessConfig'
import { $ref } from '@jiess/core'
const count = $ref(0)
function setup() {
this.add({
is: 'div',
children: {
$proxy: count,
done: (val) => `当前计数:${val}`
}
})
this.add({
is: 'button',
children: '增加计数',
onClick: () => count.value++
})
}
</script>
<style>
.reactive-demo {
padding: 20px;
font-family: Arial, sans-serif;
}
</style>响应式示例(React 版本)
import React from 'react'
import JiessComponent from '@/jiessConfig'
import { $ref } from '@jiess/core'
const count = $ref(0)
function setup() {
this.add({
is: 'div',
children: {
$proxy: count,
done: (val) => `当前计数:${val}`
}
})
this.add({
is: 'button',
children: '增加计数',
onClick: () => count.value++
})
}
function ReactiveDemo() {
return (
<div className="reactive-demo">
<h2>React + Jiess 响应式示例</h2>
<JiessComponent setup={setup} />
</div>
)
}
const style = document.createElement('style')
style.textContent = `
.reactive-demo {
padding: 20px;
font-family: Arial, sans-serif;
}
`
document.head.appendChild(style)
export default ReactiveDemo响应式示例解析
- 响应式数据创建:
$ref(0)创建了一个响应式的计数变量 - 数据绑定:通过
$proxy: count将响应式数据绑定到 UI 上 - 数据格式化:
done函数在数据流入视图前做一次格式化 - 事件驱动:点击事件修改
count.value++,触发 UI 自动更新 - 跨框架一致:Vue 3、React 和 Web Components 版本的 setup 函数完全相同
使用 CDN 引入
无需构建工具,直接在 HTML 中引入即可。
基于 React 的 CDN 示例
<!DOCTYPE html>
<html lang="zh">
<head>
<title>React + Jiess 实例</title>
<meta name="referrer" content="no-referrer" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.staticfile.org/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/18.2.0/umd/react-dom.development.js"></script>
<script src="//gitee.com/wgzimg/jiess-dist/raw/master/jiess-core/umd/JiessCore.js"></script>
<script src="//gitee.com/wgzimg/jiess-dist/raw/master/jiess-core/umd/envReact.js"></script>
<style>
body {
background-color: rgb(243, 244, 246);
font-family: Arial, sans-serif;
padding: 20px;
}
.demo-button {
margin-top: 10px;
padding: 10px 20px;
background-color: #409eff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="root" />
<script type="module">
const { $entry } = JiessCore
const { JiessComponent } = $entry(JiessEnv, React)
function setup() {
this.add('Hello, Jiess from CDN!')
this.add({
is: 'button',
children: '点击我',
className: 'demo-button',
onClick: () => alert('CDN 引入的 Jiess 按钮被点击了!')
})
}
const container = document.getElementById('root')
const root = ReactDOM.createRoot(container)
root.render(React.createElement(JiessComponent, { setup }))
</script>
</body>
</html>基于 Vue 3 的 CDN 示例
<!DOCTYPE html>
<html lang="zh">
<head>
<title>Vue 3 + Jiess 实例</title>
<meta name="referrer" content="no-referrer" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.staticfile.net/vue/3.3.4/vue.global.js"></script>
<script src="//gitee.com/wgzimg/jiess-dist/raw/master/jiess-core/umd/JiessCore.js"></script>
<script src="//gitee.com/wgzimg/jiess-dist/raw/master/jiess-core/umd/envVue3.js"></script>
<style>
body {
background-color: rgb(243, 244, 246);
font-family: Arial, sans-serif;
padding: 20px;
}
.demo-button {
margin-top: 10px;
padding: 10px 20px;
background-color: #409eff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="root" />
<script type="module">
const { createApp, h } = Vue
const { $entry } = JiessCore
const { JiessComponent } = $entry(JiessEnv, Vue)
function setup() {
this.add('Hello, Jiess from CDN!')
this.add({
is: 'button',
children: '点击我',
className: 'demo-button',
onClick: () => alert('CDN 引入的 Jiess 按钮被点击了!')
})
}
createApp({
render: () => h(JiessComponent, { setup })
}).mount('#root')
</script>
</body>
</html>基于 Web Components 的 CDN 示例
Web Components 适配器零框架依赖,无需引入 Vue 或 React,直接使用浏览器原生 API:
<!DOCTYPE html>
<html lang="zh">
<head>
<title>Web Components + Jiess 实例</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
background-color: rgb(243, 244, 246);
font-family: Arial, sans-serif;
padding: 20px;
}
.demo-button {
margin-top: 10px;
padding: 10px 20px;
background-color: #409eff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="module">
import { $entry } from './LIB/jiess-core/src/index.js'
import JiessEnv from './LIB/jiess-core/src/env/web-component/index.js'
// Web Components 适配器无需外部框架
const jiess = $entry(JiessEnv, { $tag: 'div' }, {})
const { JiessComponent } = jiess
function setup() {
this.add('Hello, Jiess from Web Components!')
this.add({
is: 'button',
children: '点击我',
className: 'demo-button',
onClick: () => alert('Web Components Jiess 按钮被点击了!')
})
}
// JiessComponent 返回 DOM 节点,直接挂载到页面
const root = document.getElementById('root')
const result = JiessComponent({ setup })
root.appendChild(result)
</script>
</body>
</html>Web Components 特点:
JiessComponent返回的是真实 DOM 节点,通过appendChild挂载到页面,无需createApp或createRoot。
一个完整的示例:计数器
下面创建一个完整的计数器组件,展示 Jiess 响应式系统和组件开发能力。
Vue 3 版本
<template>
<div class="counter-demo">
<h2>Vue 3 + Jiess 计数器</h2>
<JiessComponent :setup="setup" />
</div>
</template>
<script setup>
import JiessComponent from '@/jiessConfig'
import { $ref } from '@jiess/core'
const count = $ref(0)
function setup() {
this.add({
is: 'div',
className: 'count-display',
children: {
$proxy: count,
done: (val) => `当前计数:${val}`
}
})
this.add({
is: 'div',
className: 'button-group',
children: [
{
is: 'button',
children: '-',
className: 'counter-button',
onClick: () => count.value--
},
{
is: 'button',
children: '+',
className: 'counter-button',
onClick: () => count.value++
},
{
is: 'button',
children: '重置',
className: 'counter-button reset-button',
onClick: () => count.value = 0
}
]
})
}
</script>
<style>
.counter-demo {
padding: 20px;
font-family: Arial, sans-serif;
max-width: 400px;
margin: 0 auto;
text-align: center;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.count-display {
font-size: 24px;
margin: 20px 0;
font-weight: bold;
color: #333;
}
.button-group {
display: flex;
justify-content: center;
gap: 10px;
}
.counter-button {
width: 40px;
height: 40px;
font-size: 18px;
font-weight: bold;
border: none;
border-radius: 4px;
cursor: pointer;
background-color: #409eff;
color: white;
}
.counter-button:hover {
background-color: #66b1ff;
}
.reset-button {
width: auto;
padding: 0 20px;
background-color: #67c23a;
}
.reset-button:hover {
background-color: #85ce61;
}
</style>React 版本
import React from 'react'
import JiessComponent from '@/jiessConfig'
import { $ref } from '@jiess/core'
const count = $ref(0)
function setup() {
this.add({
is: 'div',
className: 'count-display',
children: {
$proxy: count,
done: (val) => `当前计数:${val}`
}
})
this.add({
is: 'div',
className: 'button-group',
children: [
{
is: 'button',
children: '-',
className: 'counter-button',
onClick: () => count.value--
},
{
is: 'button',
children: '+',
className: 'counter-button',
onClick: () => count.value++
},
{
is: 'button',
children: '重置',
className: 'counter-button reset-button',
onClick: () => count.value = 0
}
]
})
}
function CounterDemo() {
return (
<div className="counter-demo">
<h2>React + Jiess 计数器</h2>
<JiessComponent setup={setup} />
</div>
)
}
const style = document.createElement('style')
style.textContent = `
.counter-demo {
padding: 20px;
font-family: Arial, sans-serif;
max-width: 400px;
margin: 0 auto;
text-align: center;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.count-display {
font-size: 24px;
margin: 20px 0;
font-weight: bold;
color: #333;
}
.button-group {
display: flex;
justify-content: center;
gap: 10px;
}
.counter-button {
width: 40px;
height: 40px;
font-size: 18px;
font-weight: bold;
border: none;
border-radius: 4px;
cursor: pointer;
background-color: #409eff;
color: white;
}
.counter-button:hover {
background-color: #66b1ff;
}
.reset-button {
width: auto;
padding: 0 20px;
background-color: #67c23a;
}
.reset-button:hover {
background-color: #85ce61;
}
`
document.head.appendChild(style)
export default CounterDemo计数器示例解析
- 响应式数据:
$ref(0)创建响应式计数变量,通过.value读写 - UI 结构:通过嵌套配置对象构建显示区域和按钮组
- 数据绑定:
$proxy: count将响应式数据连接到 UI,done做格式化 - 事件处理:按钮点击修改响应式数据,视图自动更新
- 跨框架一致:Vue 3、React 和 Web Components 的 setup 函数完全相同,仅外层容器代码不同
技术要点
- 响应式系统:使用
$ref创建数据,通过.value访问和修改 - 数据绑定:
$proxy将响应式数据连接到 UI,实现自动更新 - 节点配置:通过 JavaScript 对象配置属性、样式和事件
- 嵌套结构:通过嵌套配置对象构建复杂 UI 树
跨框架组件复用
Jiess 的最大优势之一:用 isJiess: true 标记一个纯 JS 对象,它就成为可在 Vue 3、React 和 Web Components 三个 Web 环境中直接使用的组件。此外,Three.js(3D 场景)和 Konva(2D Canvas)适配器支持配置式声明场景图。
定义跨框架组件
// @/components/GlobalCounter.js
import { $ref } from '@jiess/core'
const GlobalCounter = {
isJiess: true,
name: 'GlobalCounter',
props() {
return {
initialValue: { type: Number, default: 0 },
step: { type: Number, default: 1 }
}
},
setup(props) {
const { initialValue, step } = props
const count = $ref(initialValue)
this.add({
is: 'div',
className: 'global-count',
children: {
$proxy: count,
done: (val) => `计数:${val}`
}
})
this.add({
is: 'div',
className: 'global-buttons',
children: [
{
is: 'button',
children: `+${step}`,
onClick: () => count.value += step
},
{
is: 'button',
children: `-${step}`,
onClick: () => count.value -= step
},
{
is: 'button',
children: '重置',
onClick: () => count.value = initialValue
}
]
})
},
style: `
.global-count {
font-size: 20px;
margin: 10px 0;
font-weight: bold;
}
.global-buttons {
display: flex;
gap: 10px;
}
.global-buttons button {
padding: 5px 10px;
border: none;
border-radius: 4px;
background-color: #409eff;
color: white;
cursor: pointer;
}
.global-buttons button:hover {
background-color: #66b1ff;
}
`
}
export default GlobalCounter在 Vue 3 中使用
<template>
<div class="vue-counter">
<h3>Vue 3 使用跨框架组件</h3>
<JiessComponent :setup="setup" />
</div>
</template>
<script setup>
import JiessComponent from '@/jiessConfig'
import GlobalCounter from '@/components/GlobalCounter'
function setup() {
this.add({ is: GlobalCounter, initialValue: 10, step: 2 })
}
</script>
<style>
.vue-counter {
padding: 20px;
background-color: #f0f9ff;
border-radius: 8px;
}
</style>在 React 中使用
import React from 'react'
import JiessComponent from '@/jiessConfig'
import GlobalCounter from '@/components/GlobalCounter'
function setup() {
this.add({ is: GlobalCounter, initialValue: 5, step: 3 })
}
function ReactCounter() {
return (
<div className="react-counter">
<h3>React 使用跨框架组件</h3>
<JiessComponent setup={setup} />
</div>
)
}
const style = document.createElement('style')
style.textContent = `
.react-counter {
padding: 20px;
background-color: #fef3c7;
border-radius: 8px;
}
`
document.head.appendChild(style)
export default ReactCounter跨框架组件开发要点
- 组件结构:
isJiess: true必须设置,props()定义入参,setup(props)写逻辑,style写样式 - 避免框架专属 API:不在组件内使用
useState、ref()(Vue)等框架特定方法 - 通信方式:属性传递 > 事件回调 >
provide/inject,优先用标准 JavaScript 方式 - 样式管理:通过
style属性定义组件样式,确保样式隔离 - Web Components 注意:Web Components 适配器中
JiessComponent返回 DOM 节点,需手动appendChild挂载;响应式数据修改后 DOM 更新为已知限制(初始渲染正常)
最佳实践建议
组件化开发
- 拆分组件:将 UI 拆分为独立的 Jiess 组件,提高复用性和可维护性
- 单一职责:每个组件只负责一个功能,保持简洁
- 组件通信:优先使用属性传递,复杂场景使用
provide/inject依赖注入
响应式数据管理
- 选择合适 API:基本类型用
$ref,对象用$reactive - 合理使用计算属性:复杂计算逻辑用
$computed缓存结果 - 注意浅响应式:大型对象用
shallow: true减少性能开销
性能优化
- 避免在 setup 中执行复杂计算:初始化时保持轻量
- 合理使用监听器:只监听必要数据,用
$computed替代频繁的$watch - 及时清理:组件卸载时停止
$watchEffect等监听
样式管理
- 作用域样式:优先使用组件
style属性,避免样式污染 - CSS 变量:使用 CSS 变量提高样式可维护性
- 语义化命名:使用清晰的类名,遵循一致的命名规范
跨框架兼容
- 避免框架特定 API:不使用 Vue 或 React 特有方法
- 标准类型传参:用标准 JavaScript 类型传递数据
- 多框架测试:在 Vue 3、React 和 Web Components 中测试组件,确保行为一致
代码质量
- TypeScript:使用 TypeScript 获得更好的类型提示
- 错误处理:合理处理异常,提供友好提示
- 注释规范:为复杂逻辑添加清晰注释
常见问题解答
安装和配置
Q: 安装 Jiess 后出现 "Cannot find module '@jiess/core/vue3'" 错误怎么办?
A: 请检查:① 是否正确安装了 @jiess/core;② 是否正确配置了模块解析路径;③ 尝试 rm -rf node_modules && npm install 重新安装。
Q: 使用 CDN 引入时出现 "JiessCore is not defined" 错误怎么办?
A: 确保脚本加载顺序正确:先加载框架(React/Vue),再加载 JiessCore,最后加载环境适配脚本。检查网络连接确保 CDN 资源正常加载。
响应式系统
Q: 为什么修改了响应式数据,视图没有更新?
A: 检查以下几点:① 数据是否通过 $ref 或 $reactive 创建;② $ref 数据是否通过 .value 访问;③ 是否正确使用了 $proxy 绑定;④ 是否使用了浅响应式(嵌套属性变化不触发更新)。
Q: $ref 和 $reactive 如何选择?
A: 简单规则——基本类型(数字、字符串、布尔值)用 $ref;表单数据、需要直接操作属性的对象用 $reactive;需要整体替换对象用 $ref;大型对象需要性能则用 $reactive(obj, { shallow: true })。
Q: 如何处理大型对象的响应式转换?
A: 建议:① 使用浅响应式 $reactive(largeObj, { shallow: true });② 只对需要响应式的部分转换;③ 用 $computed 缓存派生状态。
组件开发
Q: setup 函数可以用箭头函数吗?
A: 不可以。必须使用普通函数声明,Jiess 通过 this 注入上下文(this.add、this.onMounted 等)。
Q: 如何在 Jiess 中使用 Antd / Element Plus 等第三方组件?
A: 直接传组件对象作为 is 的值:
import { Button } from 'antd'
import { ElInput } from 'element-plus'
this.add({ is: Button, type: 'primary', children: '提交' })
this.add({ is: ElInput, modelValue: '', placeholder: '请输入' })Q: 如何实现组件间通信?
A: Jiess 支持三种方式:① 属性传递——父组件通过 props 传数据给子组件;② 事件回调——子组件通过 onXxx 回调通知父组件;③ 依赖注入——provide('key', val) 和 inject('key') 实现跨层级通信。
Q: 如何使用插槽?
A: 通过 $slots 属性传递插槽内容:
this.add({
is: SomeComponent,
$slots: {
default: '默认插槽内容',
header: { is: 'div', children: '头部插槽' }
}
})性能优化
Q: 如何优化 Jiess 应用的性能?
A: 主要方向:① 合理使用浅响应式减少代理创建;② 用 $computed 缓存计算结果;③ 组件拆分降低渲染粒度;④ 及时调用 $clearReactiveCache 释放内存;⑤ 避免不必要的 $watch。
表单处理
Q: 如何在 Jiess 中处理表单?
A: 使用 $reactive + $model 实现双向绑定:
import { $reactive } from '@jiess/core'
const form = $reactive({ name: '', email: '' })
function setup() {
this.add({
is: 'form',
onSubmit: e => { e.preventDefault(); console.log(form) },
children: [
{ is: 'input', $model: form.name, placeholder: '姓名' },
{ is: 'input', $model: form.email, type: 'email', placeholder: '邮箱' },
{ is: 'button', type: 'submit', children: '提交' }
]
})
}条件与列表渲染
Q: 如何实现条件渲染?
A: 使用 $if / $elseIf:
import { $ref } from '@jiess/core'
const visible = $ref(true)
function setup() {
this.add({
is: 'div',
children: [
{
is: 'div',
$if: visible,
children: '条件成立时显示'
},
{
is: 'div',
$elseIf: visible,
children: '条件不成立时显示'
}
]
})
}Q: 如何实现列表渲染?
A: 使用 $computed 包裹 map,确保数据变化时列表自动更新:
import { $ref, $computed } from '@jiess/core'
const items = $ref([{ id: 1, name: '项目1' }, { id: 2, name: '项目2' }])
function setup() {
const itemList = $computed(() => items.value.map(item => ({
is: 'li', key: item.id, children: item.name
})))
this.add({
is: 'ul',
children: itemList
})
}⚠️ 如果直接写
items.value.map(...),map 只在 setup 执行一次,后续 push/splice 操作不会更新视图。
从 Vue / React 迁移 FAQ
如果你熟悉 Vue 3 或 React,以下 5 个概念最容易混淆,看完立刻上手。
Q1: $ref 为什么必须用 .value?Vue 3 的 ref 不是自动解包吗?
核心区别:Vue 3 在 <template> 中自动解包 ref,这是模板编译器的功劳。Jiess 没有模板——所有代码都是纯 JavaScript,不存在"编译器帮你解包"这一层。
// Vue 3:模板中自动解包(编译时行为)
// <span>{{ count }}</span> → 编译器自动转 count.value
// Jiess:纯 JS 运行时,必须手动 .value
const count = $ref(0)
count.value++ // ✅ 正确:显式读 .value
this.add({ $proxy: count }) // ✅ Jiess 内部自动读 count.value
// count++ // ❌ 错误:count 是 { value: 0 } 对象好消息:传给
$proxy时不需要写.value——Jiess 会自动处理,就像 Vue 模板一样。
Q2: $if 和 Vue 的 v-if 有什么区别?为什么是属性而不是指令?
Jiess 的配置对象就是普通 JS 对象,$if 是对象的一个属性,值可以是静态 boolean 或响应式数据。这与 Vue 模板的 v-if 语义完全一致,只是表达方式不同:
const show = $ref(true)
// ── Vue 模板写法 ──
// <div v-if="show">内容</div>
// ── Jiess 配置对象写法(等价) ──
this.add({ is: 'div', $if: show, children: '内容' })
// $if 的值可以是:静态 boolean | $ref | $reactive 字段 | $computed
this.add({ is: 'div', $if: true }) // 始终渲染
this.add({ is: 'div', $if: show }) // 响应式
this.add({ is: 'div', $if: $computed(() => count.value > 0) }) // 计算属性不需要学新指令——
$if就是 JS 对象的属性,值为真就渲染,为假就跳过。
Q3: React 的 useState 返回 [state, setState],Jiess 怎么修改数据?
Jiess 没有 setter 函数——直接赋值即可。响应式系统通过 Proxy 拦截赋值操作,自动触发 UI 更新。
// React:声明式,通过 setter 触发重渲染
const [count, setCount] = useState(0) // 读 count,写 setCount(count + 1)
// Jiess:直接赋值,Proxy 自动追踪
const count = $ref(0) // 读 count.value,写 count.value++
const user = $reactive({ name: '张三', age: 28 })
user.age = 29 // ✅ 直接赋值,自动触发更新
// ⚠️ 注意:$reactive 后新增的属性不是响应式的!
const state = $reactive({ name: '张三' })
state.age = 28 // 可以读写,但不是响应式(不会触发 UI 更新)
// ✅ 解决方案:用 $set()
import { $set } from '@jiess/core'
$set(state, 'age', 28) // 新增的 age 现在是响应式的核心原则:初次创建的属性自动响应式,后续新增的属性需用
$set。
Q4: Jiess 的生命周期和 Vue/React 怎么对应?为什么没有 onUnmounted?
Jiess 的生命周期命名更加直白,且直接用 JS 函数注册,不需要像 React Hooks 那样受调用顺序限制:
| Vue 3 | React (Hooks) | Jiess | 触发时机 |
|---|---|---|---|
onBeforeMount | useEffect(fn, []) | this.onBeforeMount(fn) | 挂载前 |
onMounted | useEffect(fn, []) | this.onMounted(fn) | 挂载后 |
onBeforeUpdate | — | this.onBeforeUpdate(fn) | 更新前 |
onUpdated | — | this.onUpdated(fn) | 更新后 |
onBeforeUnmount | useEffect(() => cleanup, []) | this.onBeforeUnmount(fn) | 卸载前 |
function setup() {
this.onMounted(() => {
console.log('组件已挂载——可以发起请求了')
})
// ✅ 在 setup 任何位置注册,不限于"顶层"
if (someCondition) {
this.onBeforeUpdate(() => console.log('即将更新'))
}
}为什么叫
onBeforeUnmount而不是onUnmounted? 卸载前是做清理工作的最后时机——卸载后组件已销毁,再操作 DOM 会报错。命名故意强调了这一点。
Q5: Jiess 怎么接入 antd / element-plus 等第三方 UI 库?
直接 import,直接当 is 用。不需要任何适配器、包装器或注册步骤:
// ✅ React + antd
import { Button, Input, Table } from 'antd'
function setup() {
this.add({ is: Button, type: 'primary', children: '提交' })
this.add({ is: Input, placeholder: '请输入', style: { width: 200 } })
}
// ✅ Vue 3 + element-plus
import { ElButton, ElInput, ElTable } from 'element-plus'
function setup() {
this.add({ is: ElButton, type: 'primary', children: '提交' })
this.add({ is: ElInput, placeholder: '请输入', style: { width: 200 } })
}
// ✅ 事件透传 — 直接 onXxx
this.add({
is: Button,
children: '点击',
onClick: () => console.log('clicked'), // 原生事件
onChange: (val) => console.log(val) // 组件事件
})唯一的注意点:React 的
children传给 Jiess 后最终会渲染到组件的 children prop 中,Vue 3 的 slot 同理——Jiess 自动做了这份适配工作。
接下来学习什么?
| 文档 | 内容 |
|---|---|
| Jiess 组件 | 组件定义、props 校验、生命周期、样式管理、插槽 |
| 创建节点 | this.add() 全部能力:事件、条件、列表、第三方组件 |
| 响应式绑定 | $proxy / field / done / update 绑定语法详解 |
| 响应式观察 | $ref / $reactive / $watch / $computed 完整用法 |
| 工具 & 导出 | 全部 API 速查表 |
学习路径建议
通过持续学习和实践,你将能够充分发挥 Jiess 的强大功能,构建出高效、跨框架的组件和应用。