Skip to content

Jiess 工具与导出 API ​

目录 ​

简介 ​

@jiess/core 提供响应式数据管理、组件注册、框架集成的工具函数,API 风格统一,可按需导入:

javascript
import { $ref, $reactive, $computed } from '@jiess/core'

工具函数分为四大类:

分类用途核心函数
响应式数据工具创建和管理响应式数据$reactive $ref $computed $watch $watchEffect $set
响应式辅助工具检查和操作响应式数据$isReactive $isRef $clearReactiveCache
库集成工具初始化 Jiess 环境$entry

1. 响应式数据工具 ​

$reactive ​

创建对象或数组的响应式代理,直接返回响应式对象,无需 .value。支持深响应式和浅响应式。

typescript
function $reactive<T extends object | any[]>(object: T, options?: { shallow?: boolean }): T
参数说明
object要创建响应式的对象或数组
options.shallow是否浅响应式(默认 false)
javascript
import { $reactive } from '@jiess/core'

// 深响应式(默认)
const user = $reactive({
  name: '张三',
  age: 18,
  address: { city: '北京', district: '海淀区' }
})
user.name = '李四'           // 触发更新
user.address.city = '上海'   // 嵌套也触发

// 浅响应式 — 仅顶层属性响应
const shallow = $reactive({ nested: { value: 1 } }, { shallow: true })
shallow.nested.value = 2       // 不触发
shallow.nested = { value: 3 }  // 触发

// 循环引用自动处理
const obj = { name: 'test' }
obj.self = obj
const proxy = $reactive(obj)  // 正常返回

$ref ​

创建任意类型的响应式引用,返回带 .value 的包装对象。支持 ref 缓存(同 mark 返回同一实例)。

typescript
function $ref<T>(value?: T, mark?: string, options?: { shallow?: boolean }): { value: T }
参数说明
value初始值,任意类型
mark缓存标记,相同标记返回同一实例
options.shallow是否浅响应式(默认 false)
javascript
import { $ref } from '@jiess/core'

// 基本类型 — 通过 .value 读写
const count = $ref(0)
count.value++

// 对象 / 数组
const user = $ref({ name: '张三' })
const list = $ref([1, 2, 3])

// ref 缓存 — 同标记返回同一实例
const ref1 = $ref({ name: 'test' }, 'myCache')
const ref2 = $ref({ name: 'test' }, 'myCache')
console.log(ref1 === ref2)  // true

// 浅响应式
const shallow = $ref({ nested: { value: 1 } }, undefined, { shallow: true })
shallow.value.nested.value = 2      // 不触发
shallow.value = { nested: { value: 3 } }  // 触发

$computed ​

创建计算属性,依赖不变时不重新计算。支持只读和可写两种模式。

typescript
function $computed<T>(
  val: Function | { get: Function, set?: Function },
  flush?: 'pre' | 'post' | 'sync'
): { value: T }
参数说明
val计算函数或 { get, set } 对象
flush'pre'(默认微任务)、'post'(宏任务)、'sync'(同步)
javascript
import { $ref, $computed } from '@jiess/core'

const firstName = $ref('张')
const lastName = $ref('三')

// 只读计算属性
const fullName = $computed(() => `${firstName.value}${lastName.value}`)
console.log(fullName.value)  // '张三'
lastName.value = '四'
console.log(fullName.value)  // '张四'

// 可写计算属性
const fullNameRW = $computed({
  get: () => `${firstName.value}${lastName.value}`,
  set: (val) => {
    firstName.value = val[0]
    lastName.value = val[1]
  }
})
fullNameRW.value = '王五'
console.log(firstName.value, lastName.value)  // '王' '五'

// 缓存验证 — 依赖不变时跳过计算
const count = $ref(0)
let calcTimes = 0
const doubled = $computed(() => { calcTimes++; return count.value * 2 })
doubled.value  // calcTimes: 1
doubled.value  // calcTimes: 1(缓存命中)
count.value++
doubled.value  // calcTimes: 2

$watch ​

精确监听指定字段变化,支持嵌套路径。返回停止监听函数。

typescript
function $watch(
  proxy: object,
  param: string | object,
  callback: (newValue: any, oldValue: any) => void
): () => void
参数说明
proxy响应式数据($ref 或 $reactive 创建)
param字段名(如 'name'、'address.city')或配置对象 { immediate }
callback变化回调 (newVal, oldVal)
javascript
import { $ref, $reactive, $watch } from '@jiess/core'

// 监听 $ref — 字段名为 'value'
const count = $ref(0)
const stop = $watch(count, 'value', (newVal, oldVal) => {
  console.log(`count: ${oldVal} → ${newVal}`)
})
count.value++  // 触发:0 → 1
stop()         // 停止监听
count.value++  // 不再触发

// 监听 $reactive 嵌套字段 — 点号分隔路径
const user = $reactive({ name: '张三', address: { city: '北京' } })
$watch(user, 'address.city', (newVal, oldVal) => {
  console.log(`城市: ${oldVal} → ${newVal}`)
})
user.address.city = '上海'  // 触发

// immediate 立即执行
$watch(user, 'name', { immediate: true }, (newVal, oldVal) => {
  console.log('姓名变化', newVal, oldVal)
})
// 立即输出: 姓名变化 张三 undefined

$watchEffect ​

自动收集依赖并监听,依赖变化时自动触发。支持返回清理函数。

typescript
function $watchEffect(
  fn: () => (void | (() => void)),
  flush?: 'pre' | 'post' | 'sync'
): () => void
javascript
import { $ref, $reactive, $watchEffect } from '@jiess/core'

const count = $ref(0)
const message = $ref('Hello')

// 自动收集依赖
const stop = $watchEffect(() => {
  console.log(`count: ${count.value}, message: ${message.value}`)
})
count.value++        // 自动触发
message.value = 'World'  // 自动触发
stop()               // 停止监听

// 多依赖
const state = $reactive({ a: 0, b: 0 })
$watchEffect(() => console.log(`sum: ${state.a + state.b}`))
state.a = 1  // sum: 1
state.b = 2  // sum: 3

// 清理副作用 — 返回清理函数,下次执行前自动调用
const timerRef = $ref(0)
$watchEffect(() => {
  const timer = setInterval(() => console.log('tick'), 1000)
  return () => { clearInterval(timer) }
})
timerRef.value = 1  // 先清理 → 再重新执行

$set ​

为响应式对象动态添加属性,为新值创建响应式代理并建立父子代理链。这是 $set 不可被 proxy.field = val 替代的核心原因。

typescript
function $set(proxy: object, field: string | number, value: any, param?: any): any
参数说明
proxy响应式对象($reactive 创建)
field属性名或数组索引
value要设置的值;对象/数组会自动转为响应式代理
返回值当 value 为复杂类型时返回新创建的代理;基本类型时返回 undefined
javascript
import { $reactive, $set } from '@jiess/core'

// 动态添加属性 — 值自动转为响应式
const user = $reactive({ name: '张三' })
$set(user, 'age', 18)
$set(user, 'address', { city: '北京' })
user.address.city = '上海'   // 嵌套属性也是响应式的

// 按索引赋值数组
const arr = $reactive([1, 2, 3])
$set(arr, 1, 10)  // [1, 10, 3]

// 嵌套对象添加属性
const state = $reactive({ user: { name: '张三' } })
$set(state.user, 'age', 18)

重要:$set 内部会建立 JIESS_PARENT 父子代理链,使得父级观察者能收到子代理变更通知。直接 proxy.field = obj 不会建立该链路,子代理变更无法冒泡到祖先。

$set vs 直接赋值的核心差异 ​

场景proxy.field = value$set(proxy, field, value)
基本类型值✅ 相同✅ 相同
对象/数组(新值)❌ 不创建代理✅ 创建响应式代理
嵌套属性自动响应❌ 不响应✅ 响应
父级观察者收到子代理变更❌ 不冒泡✅ 冒泡
javascript
// ❌ 反例:直接赋值添加嵌套对象(不可响应)
const state = $reactive({})
state.user = { name: '张三' }
state.user.name = '李四'  // 不会触发响应式更新

// ✅ 正例:使用 $set 添加嵌套对象
const state2 = $reactive({})
$set(state2, 'user', { name: '张三' })
state2.user.name = '李四'  // 触发响应式更新

注意:$set 仅对 $reactive 对象有效。已存在属性的赋值与直接赋值等效。

2. 响应式辅助工具 ​

$isReactive ​

检查值是否为 $reactive 创建的响应式对象。

typescript
function $isReactive(value: any): boolean
javascript
import { $reactive, $isReactive } from '@jiess/core'

const user = $reactive({ name: '张三' })
console.log($isReactive(user))        // true
console.log($isReactive(user.name))   // false(基本类型)
console.log($isReactive({}))          // false(普通对象)

// 嵌套对象也是响应式的
const nested = $reactive({ user: { name: '张三' } })
console.log($isReactive(nested.user)) // true

$isRef ​

检查值是否为 $ref 创建的响应式引用。

typescript
function $isRef(value: any): boolean
javascript
import { $ref, $isRef } from '@jiess/core'

const count = $ref(0)
console.log($isRef(count))          // true
console.log($isRef(count.value))    // false
console.log($isRef(10))             // false

// 统一处理 ref 和普通值
function unwrap(data) {
  return $isRef(data) ? data.value : data
}

$clearReactiveCache ​

清除响应式系统内部缓存,主要用于测试场景保证测试隔离。

typescript
function $clearReactiveCache(obj?: object): void
javascript
import { $reactive, $clearReactiveCache } from '@jiess/core'

const obj = $reactive({ name: '张三' })

// 全局清理
$clearReactiveCache()

// 清理指定对象缓存
$clearReactiveCache(obj)

警告:生产环境谨慎使用,可能影响全局状态。

3. 库集成工具 ​

$entry ​

初始化 Jiess 环境,返回 Jiess 实例。是项目中第一个需要调用的 API。

typescript
function $entry(
  envAdapter: object,
  staticInject?: object,
  dynamicInject?: object
): {
  JiessComponent: Component   // Jiess 基础组件,用于挂载到框架
}
参数说明
envAdapter框架环境适配器,如 JiessEnv
staticInject静态注入到 Jiess 实例中的对象(框架 API 等)
dynamicInject动态注入到组件上下文中的对象(工具函数等)

Vue 3 示例:

javascript
import { $entry } from '@jiess/core'
import JiessEnv from '@jiess/core/vue3'
import * as Vue from 'vue'
import { ElMessage } from 'element-plus'

const jiess = $entry(JiessEnv, Vue, {
  $msg: (msg, type = 'success') => ElMessage[type](msg),
  $midder: new JiessMidder('GlobalMidder')
})

// Jiess 基础组件已自动全局注册为 <jiess/>
export const JiessComponent = jiess.JiessComponent

React 示例:

javascript
import { $entry } from '@jiess/core'
import JiessEnv from '@jiess/core/react'
import React from 'react'
import { message } from 'antd'

const jiess = $entry(JiessEnv, {
  useRef: React.useRef,
  useEffect: React.useEffect,
  useLayoutEffect: React.useLayoutEffect,  // 必须传入:生命周期 beforeUpdate/beforeUnmount 依赖它
  useState: React.useState,
  createElement: React.createElement
}, {
  $msg: (msg, type = 'success') => message[type](msg)
})

// React 需手动导出 JiessComponent 使用
export const JiessComponent = jiess.JiessComponent

Web Components 示例:

javascript
import { $entry } from '@jiess/core'
import JiessEnv from '@jiess/core/web-component'

// Web Components 适配器无需外部框架,仅需传入 $tag 作为默认根标签
const jiess = $entry(JiessEnv, { $tag: 'div' }, {
  $msg: (msg) => console.log(`[Jiess] ${msg}`)
})

export const JiessComponent = jiess.JiessComponent

参数说明:$entry 的第二个参数(源码名 frameParam,本文常称 staticInject)用于传入框架/环境 API——如 Vue 命名空间、THREE/Konva 命名空间、或 React 的 hooks;第三个参数(源码名 contextParam,本文常称 dynamicInject)用于传入业务上下文,可在组件 setup 中通过 this 访问(如 $msg、$router)。Web Components 适配器不依赖任何前端框架,第二个参数仅需提供 $tag 指定根元素标签名。

Three.js 示例(3D 场景):

javascript
import { $entry } from '@jiess/core'
import JiessEnv from '@jiess/core/three'
import * as THREE from 'three'

// Three.js 适配器需传入 THREE 命名空间
const jiess = $entry(JiessEnv, THREE, {})

export const JiessComponent = jiess.JiessComponent

Konva 示例(2D Canvas 场景):

javascript
import { $entry } from '@jiess/core'
import JiessEnv from '@jiess/core/konva'
import Konva from 'konva'

// Konva 适配器需传入 Konva 命名空间
const jiess = $entry(JiessEnv, Konva, {})

export const JiessComponent = jiess.JiessComponent

4. 工具函数速查表 ​

以下为 @jiess/core 全部导出:

响应式数据工具 ​

函数用途关键参数
$reactive(obj, opts?)创建对象/数组响应式代理object, { shallow }
$ref(val?, mark?, opts?)创建任意类型响应式引用value, mark, { shallow }
$computed(val, flush?)创建计算属性fn | { get, set }, 'pre'|'post'|'sync'
$watch(proxy, param, cb)精确监听字段变化proxy, 'field' | { immediate }, (newVal, oldVal)
$watchEffect(fn, flush?)自动收集依赖监听fn, 'pre'|'post'|'sync'
$set(proxy, field, val)动态添加响应式属性proxy, field, value

响应式辅助工具 ​

函数用途返回值
$isReactive(val)检查是否为 reactive 对象boolean
$isRef(val)检查是否为 ref 引用boolean
$clearReactiveCache(obj?)清除响应式对象缓存void
$clearRefCache(mark)清除指定标记的 ref 缓存void

渲染缓存管理(JsRender 静态实例) ​

函数用途说明
staticJsRenders静态组件缓存表(LRU,上限 1000)使用 _static_ 前缀 key 的节点会被缓存复用
clearStaticCache()清空全部静态缓存测试/热更新场景使用
getCacheSize()获取当前缓存条目数调试用

库集成工具 ​

函数用途关键参数
$entry(adapter, frameParam?, contextParam?)初始化 Jiess 环境envAdapter, frameParam(框架 API,本文常称 staticInject), contextParam(业务上下文,本文常称 dynamicInject)

返回值约定 ​

  • $reactive → 直接返回响应式代理对象,直接读写属性
  • $ref → 返回 { value: T },通过 .value 读写
  • $computed → 返回 { value: T },通过 .value 读取
  • $watch / $watchEffect → 返回停止监听函数
  • $entry → 返回 { JiessComponent } Jiess 实例

5. 最佳实践 ​

选择合适的响应式 API ​

  • 对象/数组且需要深层响应 → $reactive()
  • 基本类型或需要整体替换 → $ref()
  • 派生数据需要缓存 → $computed()
  • 动态添加属性(特别是嵌套对象) → $set()(必须,直接赋值不会建立代理链)

监听策略 ​

javascript
// ✅ 推荐:$watchEffect 自动收集依赖
$watchEffect(() => {
  console.log(state.a + state.b)  // 自动追踪 a 和 b
})

// ✅ 推荐:$watch 精确监听特定字段
$watch(user, 'address.city', (newVal, oldVal) => {
  validateCity(newVal)
})

// ⚠️ 注意:及时停止不需要的监听
const stop = $watch(count, 'value', handler)
onUnmount(() => stop())  // 组件卸载时清理

$entry 配置规范 ​

javascript
// ✅ 推荐:staticInject 放框架 API
const jiess = $entry(JiessEnv, {
  createElement: React.createElement,
  useState: React.useState
}, {
  // dynamicInject 放业务工具 — setup 中通过 this 访问
  $msg: message.success,
  $router: router
})

性能优化 ​

javascript
// ✅ 批量更新时使用 $set 减少触发次数
const user = $reactive({ name: '张三', age: 25, address: { city: '新城市' } })
$set(user, 'name', '新名称')
$set(user, 'age', 25)
$set(user.address, 'city', '新城市')

// ✅ 计算属性自动缓存
const expensive = $computed(() => heavyComputation(data.value))

// ✅ 清除无用缓存(测试环境)
afterEach(() => $clearReactiveCache())