Jiess 工具与导出 API
目录
简介
@jiess/core 提供响应式数据管理、组件注册、框架集成的工具函数,API 风格统一,可按需导入:
import { $ref, $reactive, $computed } from '@jiess/core'工具函数分为四大类:
| 分类 | 用途 | 核心函数 |
|---|---|---|
| 响应式数据工具 | 创建和管理响应式数据 | $reactive $ref $computed $watch $watchEffect $set |
| 响应式辅助工具 | 检查和操作响应式数据 | $isReactive $isRef $clearReactiveCache |
| 库集成工具 | 初始化 Jiess 环境 | $entry |
1. 响应式数据工具
$reactive
创建对象或数组的响应式代理,直接返回响应式对象,无需 .value。支持深响应式和浅响应式。
function $reactive<T extends object | any[]>(object: T, options?: { shallow?: boolean }): T| 参数 | 说明 |
|---|---|
object | 要创建响应式的对象或数组 |
options.shallow | 是否浅响应式(默认 false) |
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 返回同一实例)。
function $ref<T>(value?: T, mark?: string, options?: { shallow?: boolean }): { value: T }| 参数 | 说明 |
|---|---|
value | 初始值,任意类型 |
mark | 缓存标记,相同标记返回同一实例 |
options.shallow | 是否浅响应式(默认 false) |
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
创建计算属性,依赖不变时不重新计算。支持只读和可写两种模式。
function $computed<T>(
val: Function | { get: Function, set?: Function },
flush?: 'pre' | 'post' | 'sync'
): { value: T }| 参数 | 说明 |
|---|---|
val | 计算函数或 { get, set } 对象 |
flush | 'pre'(默认微任务)、'post'(宏任务)、'sync'(同步) |
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
精确监听指定字段变化,支持嵌套路径。返回停止监听函数。
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) |
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
自动收集依赖并监听,依赖变化时自动触发。支持返回清理函数。
function $watchEffect(
fn: () => (void | (() => void)),
flush?: 'pre' | 'post' | 'sync'
): () => voidimport { $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 替代的核心原因。
function $set(proxy: object, field: string | number, value: any, param?: any): any| 参数 | 说明 |
|---|---|
proxy | 响应式对象($reactive 创建) |
field | 属性名或数组索引 |
value | 要设置的值;对象/数组会自动转为响应式代理 |
| 返回值 | 当 value 为复杂类型时返回新创建的代理;基本类型时返回 undefined |
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) |
|---|---|---|
| 基本类型值 | ✅ 相同 | ✅ 相同 |
| 对象/数组(新值) | ❌ 不创建代理 | ✅ 创建响应式代理 |
| 嵌套属性自动响应 | ❌ 不响应 | ✅ 响应 |
| 父级观察者收到子代理变更 | ❌ 不冒泡 | ✅ 冒泡 |
// ❌ 反例:直接赋值添加嵌套对象(不可响应)
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 创建的响应式对象。
function $isReactive(value: any): booleanimport { $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 创建的响应式引用。
function $isRef(value: any): booleanimport { $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
清除响应式系统内部缓存,主要用于测试场景保证测试隔离。
function $clearReactiveCache(obj?: object): voidimport { $reactive, $clearReactiveCache } from '@jiess/core'
const obj = $reactive({ name: '张三' })
// 全局清理
$clearReactiveCache()
// 清理指定对象缓存
$clearReactiveCache(obj)警告:生产环境谨慎使用,可能影响全局状态。
3. 库集成工具
$entry
初始化 Jiess 环境,返回 Jiess 实例。是项目中第一个需要调用的 API。
function $entry(
envAdapter: object,
staticInject?: object,
dynamicInject?: object
): {
JiessComponent: Component // Jiess 基础组件,用于挂载到框架
}| 参数 | 说明 |
|---|---|
envAdapter | 框架环境适配器,如 JiessEnv |
staticInject | 静态注入到 Jiess 实例中的对象(框架 API 等) |
dynamicInject | 动态注入到组件上下文中的对象(工具函数等) |
Vue 3 示例:
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.JiessComponentReact 示例:
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.JiessComponentWeb Components 示例:
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 场景):
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.JiessComponentKonva 示例(2D Canvas 场景):
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.JiessComponent4. 工具函数速查表
以下为 @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()(必须,直接赋值不会建立代理链)
监听策略
// ✅ 推荐:$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 配置规范
// ✅ 推荐:staticInject 放框架 API
const jiess = $entry(JiessEnv, {
createElement: React.createElement,
useState: React.useState
}, {
// dynamicInject 放业务工具 — setup 中通过 this 访问
$msg: message.success,
$router: router
})性能优化
// ✅ 批量更新时使用 $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())