Skip to content

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 · JSXJiess · 纯 JS 配置对象
vue
<template>
  <div>
    <span>{{ count }}</span>
    <button @click="increment">+1</button>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => count.value++
</script>
<style scoped>
span { font-size: 24px; margin-right: 10px; }
</style>
jsx
import { useState } from 'react'

function Counter() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <span>{count}</span>
      <button onClick={() => setCount(count + 1)}>
        +1
      </button>
    </div>
  )
}
// 样式需另写 CSS/CSS-in-JS
javascript
// 本段代码在 Vue 3、React 和 Web Components 中完全一致
import { $ref } from '@jiess/core'

const count = $ref(0)

function setup() {
  this.add({ children: [
    { is: 'span',
      style: { fontSize: '24px', marginRight: '10px' },
      children: { $proxy: count } },
    { is: 'button', children: '+1',
      onClick: () => count.value++ }
  ]})
}

Jiess 的核心差异:绕开模板与 JSX ​

传统前端开发绕不开两样东西:模板语法和编译步骤。Vue 需要 <template> 和 v-if/v-for 指令,React 需要 JSX 和 Babel 编译。两者都要求开发者学习一套"框架特有的表达式语言"。

Jiess 的选择是:不引入任何新语法,全部使用 JavaScript。看下面这个真实对比:

需求Vue 模板React JSXJiess(纯 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 文件就是组件的全部。

javascript
// 这就是一个完整的 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 含环境维度实现隔离

配置式开发 ​

javascript
// 这段代码在 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 安装 ​

bash
# 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 环境配置:

javascript
// @/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 环境配置:

javascript
// @/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 环境配置:

javascript
// @/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 配置(推荐) ​

json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "react-jsx",
    "strict": true
  }
}

配置提示 ​

  1. 统一配置文件:创建专门的 jiessConfig.js,方便全局管理
  2. 导出策略:同时导出 JiessComponent 和 jiess 实例,满足不同使用场景
  3. 版本锁定:在 package.json 中锁定 @jiess/core 版本,确保稳定性

快速开始 ​

在 Vue 3 中使用 Jiess ​

创建一个 Vue 组件,引入配置好的 JiessComponent:

vue
<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:

jsx
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 中使用响应式数据:

vue
<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 版本) ​

jsx
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

响应式示例解析 ​

  1. 响应式数据创建:$ref(0) 创建了一个响应式的计数变量
  2. 数据绑定:通过 $proxy: count 将响应式数据绑定到 UI 上
  3. 数据格式化:done 函数在数据流入视图前做一次格式化
  4. 事件驱动:点击事件修改 count.value++,触发 UI 自动更新
  5. 跨框架一致:Vue 3、React 和 Web Components 版本的 setup 函数完全相同

使用 CDN 引入 ​

无需构建工具,直接在 HTML 中引入即可。

基于 React 的 CDN 示例 ​

html
<!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 示例 ​

html
<!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:

html
<!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 版本 ​

vue
<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 版本 ​

jsx
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

计数器示例解析 ​

  1. 响应式数据:$ref(0) 创建响应式计数变量,通过 .value 读写
  2. UI 结构:通过嵌套配置对象构建显示区域和按钮组
  3. 数据绑定:$proxy: count 将响应式数据连接到 UI,done 做格式化
  4. 事件处理:按钮点击修改响应式数据,视图自动更新
  5. 跨框架一致:Vue 3、React 和 Web Components 的 setup 函数完全相同,仅外层容器代码不同

技术要点 ​

  1. 响应式系统:使用 $ref 创建数据,通过 .value 访问和修改
  2. 数据绑定:$proxy 将响应式数据连接到 UI,实现自动更新
  3. 节点配置:通过 JavaScript 对象配置属性、样式和事件
  4. 嵌套结构:通过嵌套配置对象构建复杂 UI 树

跨框架组件复用 ​

Jiess 的最大优势之一:用 isJiess: true 标记一个纯 JS 对象,它就成为可在 Vue 3、React 和 Web Components 三个 Web 环境中直接使用的组件。此外,Three.js(3D 场景)和 Konva(2D Canvas)适配器支持配置式声明场景图。

定义跨框架组件 ​

javascript
// @/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 中使用 ​

vue
<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 中使用 ​

jsx
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

跨框架组件开发要点 ​

  1. 组件结构:isJiess: true 必须设置,props() 定义入参,setup(props) 写逻辑,style 写样式
  2. 避免框架专属 API:不在组件内使用 useState、ref()(Vue)等框架特定方法
  3. 通信方式:属性传递 > 事件回调 > provide/inject,优先用标准 JavaScript 方式
  4. 样式管理:通过 style 属性定义组件样式,确保样式隔离
  5. 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 的值:

javascript
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 属性传递插槽内容:

javascript
this.add({
  is: SomeComponent,
  $slots: {
    default: '默认插槽内容',
    header: { is: 'div', children: '头部插槽' }
  }
})

性能优化 ​

Q: 如何优化 Jiess 应用的性能?

A: 主要方向:① 合理使用浅响应式减少代理创建;② 用 $computed 缓存计算结果;③ 组件拆分降低渲染粒度;④ 及时调用 $clearReactiveCache 释放内存;⑤ 避免不必要的 $watch。

表单处理 ​

Q: 如何在 Jiess 中处理表单?

A: 使用 $reactive + $model 实现双向绑定:

javascript
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:

javascript
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,确保数据变化时列表自动更新:

javascript
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,不存在"编译器帮你解包"这一层。

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 语义完全一致,只是表达方式不同:

javascript
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 更新。

javascript
// 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 3React (Hooks)Jiess触发时机
onBeforeMountuseEffect(fn, [])this.onBeforeMount(fn)挂载前
onMounteduseEffect(fn, [])this.onMounted(fn)挂载后
onBeforeUpdate—this.onBeforeUpdate(fn)更新前
onUpdated—this.onUpdated(fn)更新后
onBeforeUnmountuseEffect(() => cleanup, [])this.onBeforeUnmount(fn)卸载前
javascript
function setup() {
  this.onMounted(() => {
    console.log('组件已挂载——可以发起请求了')
  })

  // ✅ 在 setup 任何位置注册,不限于"顶层"
  if (someCondition) {
    this.onBeforeUpdate(() => console.log('即将更新'))
  }
}

为什么叫 onBeforeUnmount 而不是 onUnmounted? 卸载前是做清理工作的最后时机——卸载后组件已销毁,再操作 DOM 会报错。命名故意强调了这一点。


Q5: Jiess 怎么接入 antd / element-plus 等第三方 UI 库? ​

直接 import,直接当 is 用。不需要任何适配器、包装器或注册步骤:

javascript
// ✅ 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 的强大功能,构建出高效、跨框架的组件和应用。