@variojs/vue 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -848
- package/dist/bindings.js +1 -199
- package/dist/composable.js +1 -386
- package/dist/features/attrs-builder.js +1 -143
- package/dist/features/children-resolver.js +1 -171
- package/dist/features/component-resolver.js +1 -110
- package/dist/features/computed.js +1 -161
- package/dist/features/event-handler.js +1 -103
- package/dist/features/expression-evaluator.js +1 -28
- package/dist/features/lifecycle-wrapper.js +1 -102
- package/dist/features/loop-handler.js +1 -168
- package/dist/features/path-resolver.js +1 -171
- package/dist/features/provide-inject.js +1 -139
- package/dist/features/refs.js +1 -113
- package/dist/features/teleport.js +1 -32
- package/dist/features/validators.js +1 -297
- package/dist/features/watch.js +1 -154
- package/dist/index.js +1 -24
- package/dist/renderer.js +1 -244
- package/dist/types.js +0 -16
- package/package.json +18 -5
package/dist/features/watch.js
CHANGED
|
@@ -1,154 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Watch 支持
|
|
3
|
-
*
|
|
4
|
-
* 在 Schema 节点上支持监听器,监听表达式变化并调用 methods
|
|
5
|
-
*
|
|
6
|
-
* 特性:
|
|
7
|
-
* - 精细化依赖追踪:只追踪表达式中使用的状态路径
|
|
8
|
-
* - 支持 immediate 和 deep 选项
|
|
9
|
-
* - 自动清理:组件卸载时停止所有 watcher
|
|
10
|
-
*/
|
|
11
|
-
import { watch } from 'vue';
|
|
12
|
-
import { evaluate, parseExpression, extractDependencies } from '@vario/core';
|
|
13
|
-
/**
|
|
14
|
-
* 从表达式中提取依赖的状态路径
|
|
15
|
-
*/
|
|
16
|
-
function extractExprDependencies(expr) {
|
|
17
|
-
try {
|
|
18
|
-
const ast = parseExpression(expr);
|
|
19
|
-
return extractDependencies(ast);
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
return [];
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* 根据依赖路径从响应式状态中读取值(触发依赖追踪)
|
|
27
|
-
*/
|
|
28
|
-
function trackDependencies(state, deps) {
|
|
29
|
-
if (!state || deps.length === 0)
|
|
30
|
-
return;
|
|
31
|
-
for (const dep of deps) {
|
|
32
|
-
if (dep.endsWith('.*')) {
|
|
33
|
-
const basePath = dep.slice(0, -2);
|
|
34
|
-
const value = getNestedValue(state, basePath);
|
|
35
|
-
if (Array.isArray(value)) {
|
|
36
|
-
void value.length;
|
|
37
|
-
}
|
|
38
|
-
else if (value && typeof value === 'object') {
|
|
39
|
-
void Object.keys(value);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
else if (dep.includes('.')) {
|
|
43
|
-
void getNestedValue(state, dep);
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
void state[dep];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* 获取嵌套路径的值
|
|
52
|
-
*/
|
|
53
|
-
function getNestedValue(obj, path) {
|
|
54
|
-
const parts = path.split('.');
|
|
55
|
-
let current = obj;
|
|
56
|
-
for (const part of parts) {
|
|
57
|
-
if (current == null)
|
|
58
|
-
return undefined;
|
|
59
|
-
current = current[part];
|
|
60
|
-
}
|
|
61
|
-
return current;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* 规范化 watch 配置
|
|
65
|
-
*/
|
|
66
|
-
function normalizeWatchConfigs(watchDef) {
|
|
67
|
-
if (!watchDef)
|
|
68
|
-
return [];
|
|
69
|
-
const configs = [];
|
|
70
|
-
for (const [source, config] of Object.entries(watchDef)) {
|
|
71
|
-
const handlerName = typeof config === 'string' ? config : config.handler;
|
|
72
|
-
const options = typeof config === 'string'
|
|
73
|
-
? {}
|
|
74
|
-
: {
|
|
75
|
-
immediate: config.immediate,
|
|
76
|
-
deep: config.deep
|
|
77
|
-
};
|
|
78
|
-
configs.push({
|
|
79
|
-
source,
|
|
80
|
-
handler: handlerName,
|
|
81
|
-
options,
|
|
82
|
-
deps: extractExprDependencies(source)
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
return configs;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* 为 Schema 节点创建监听器
|
|
89
|
-
* 返回停止监听的函数数组
|
|
90
|
-
*
|
|
91
|
-
* @param schema Schema 节点
|
|
92
|
-
* @param ctx 运行时上下文
|
|
93
|
-
* @param getState 获取响应式状态的函数
|
|
94
|
-
*/
|
|
95
|
-
export function createWatchers(schema, ctx, getState) {
|
|
96
|
-
const configs = normalizeWatchConfigs(schema.watch);
|
|
97
|
-
if (configs.length === 0) {
|
|
98
|
-
return [];
|
|
99
|
-
}
|
|
100
|
-
const stopHandles = [];
|
|
101
|
-
for (const config of configs) {
|
|
102
|
-
const { source, handler: handlerName, options, deps } = config;
|
|
103
|
-
const handler = ctx.$methods?.[handlerName];
|
|
104
|
-
if (!handler) {
|
|
105
|
-
console.warn(`[Vario] Watch handler "${handlerName}" not found in methods`);
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
// 创建 getter 函数(精细化依赖追踪)
|
|
109
|
-
const getter = () => {
|
|
110
|
-
try {
|
|
111
|
-
if (getState) {
|
|
112
|
-
trackDependencies(getState(), deps);
|
|
113
|
-
}
|
|
114
|
-
return evaluate(source, ctx);
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
console.warn(`[Vario] Watch source "${source}" evaluation error:`, error);
|
|
118
|
-
return undefined;
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
// 注册监听器
|
|
122
|
-
const stopHandle = watch(getter, (newValue, oldValue) => {
|
|
123
|
-
try {
|
|
124
|
-
const result = handler(ctx, { newValue, oldValue, source });
|
|
125
|
-
// 处理异步 handler
|
|
126
|
-
if (result && typeof result === 'object' && 'catch' in result) {
|
|
127
|
-
result.catch((err) => {
|
|
128
|
-
console.warn(`[Vario] Watch handler "${handlerName}" error:`, err);
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
catch (err) {
|
|
133
|
-
console.warn(`[Vario] Watch handler "${handlerName}" error:`, err);
|
|
134
|
-
}
|
|
135
|
-
}, options);
|
|
136
|
-
stopHandles.push(stopHandle);
|
|
137
|
-
}
|
|
138
|
-
// 返回停止函数
|
|
139
|
-
return stopHandles.map(handle => () => handle());
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* 批量停止所有 watcher
|
|
143
|
-
*/
|
|
144
|
-
export function stopAllWatchers(stopHandles) {
|
|
145
|
-
stopHandles.forEach(stop => {
|
|
146
|
-
try {
|
|
147
|
-
stop();
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
// 忽略停止时的错误
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
//# sourceMappingURL=watch.js.map
|
|
1
|
+
import{watch as w}from"vue";import{evaluate as W,parseExpression as V,extractDependencies as g}from"@vario/core";function $(e){try{const o=V(e);return g(o)}catch{return[]}}function b(e,o){if(!(!e||o.length===0))for(const r of o)if(r.endsWith(".*")){const t=r.slice(0,-2),n=f(e,t);Array.isArray(n)?n.length:n&&typeof n=="object"&&Object.keys(n)}else r.includes(".")?f(e,r):e[r]}function f(e,o){const r=o.split(".");let t=e;for(const n of r){if(t==null)return;t=t[n]}return t}function j(e){if(!e)return[];const o=[];for(const[r,t]of Object.entries(e)){const n=typeof t=="string"?t:t.handler,s=typeof t=="string"?{}:{immediate:t.immediate,deep:t.deep};o.push({source:r,handler:n,options:s,deps:$(r)})}return o}function x(e,o,r){const t=j(e.watch);if(t.length===0)return[];const n=[];for(const s of t){const{source:i,handler:a,options:l,deps:p}=s,h=o.$methods?.[a];if(!h){console.warn(`[Vario] Watch handler "${a}" not found in methods`);continue}const d=w(()=>{try{return r&&b(r(),p),W(i,o)}catch(u){console.warn(`[Vario] Watch source "${i}" evaluation error:`,u);return}},(u,m)=>{try{const c=h(o,{newValue:u,oldValue:m,source:i});c&&typeof c=="object"&&"catch"in c&&c.catch(y=>{console.warn(`[Vario] Watch handler "${a}" error:`,y)})}catch(c){console.warn(`[Vario] Watch handler "${a}" error:`,c)}},l);n.push(d)}return n.map(s=>()=>s())}function v(e){e.forEach(o=>{try{o()}catch{}})}export{x as createWatchers,v as stopAllWatchers};
|
package/dist/index.js
CHANGED
|
@@ -1,24 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Vario Vue Renderer
|
|
3
|
-
*
|
|
4
|
-
* Vue 3 integration backend for rendering Vario Schemas.
|
|
5
|
-
*
|
|
6
|
-
* 深度集成 Vue 3 特性:
|
|
7
|
-
* - ref: 模板引用(声明映射到 Vue ref)
|
|
8
|
-
* - 生命周期钩子(声明映射到 Vue 钩子)
|
|
9
|
-
* - provide/inject: 依赖注入(声明映射到 Vue API)
|
|
10
|
-
* - teleport: 传送
|
|
11
|
-
* - transition: 过渡动画
|
|
12
|
-
* - keep-alive: 缓存
|
|
13
|
-
*
|
|
14
|
-
* 注意:computed 和 watch 应该在 Vue 组件中使用原生 API 定义,
|
|
15
|
-
* 然后通过 useVario 的 computed 选项传入
|
|
16
|
-
*/
|
|
17
|
-
export * from './renderer.js';
|
|
18
|
-
export * from './composable.js';
|
|
19
|
-
export * from './bindings.js';
|
|
20
|
-
export * from './types.js';
|
|
21
|
-
export * from './features/refs.js';
|
|
22
|
-
export * from './features/teleport.js';
|
|
23
|
-
export * from './features/provide-inject.js';
|
|
24
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
export*from"./renderer.js";export*from"./composable.js";export*from"./bindings.js";export*from"./types.js";export*from"./features/refs.js";export*from"./features/teleport.js";export*from"./features/provide-inject.js";
|
package/dist/renderer.js
CHANGED
|
@@ -1,244 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Vue renderer implementation
|
|
3
|
-
*
|
|
4
|
-
* 功能:
|
|
5
|
-
* - Schema → VNode 转换
|
|
6
|
-
* - 通用组件解析(支持全局注册的组件)
|
|
7
|
-
* - 双向绑定处理
|
|
8
|
-
* - 控制流转换(cond/show/loop)
|
|
9
|
-
* - 事件处理
|
|
10
|
-
* - 表达式求值
|
|
11
|
-
*/
|
|
12
|
-
import { h, Fragment, Transition, KeepAlive } from 'vue';
|
|
13
|
-
import { attachRef, RefsRegistry } from './features/refs.js';
|
|
14
|
-
import { createTeleport, shouldTeleport } from './features/teleport.js';
|
|
15
|
-
import { ModelPathResolver } from './features/path-resolver.js';
|
|
16
|
-
import { ComponentResolver } from './features/component-resolver.js';
|
|
17
|
-
import { ExpressionEvaluator } from './features/expression-evaluator.js';
|
|
18
|
-
import { EventHandler } from './features/event-handler.js';
|
|
19
|
-
import { AttrsBuilder } from './features/attrs-builder.js';
|
|
20
|
-
import { LoopHandler } from './features/loop-handler.js';
|
|
21
|
-
import { ChildrenResolver } from './features/children-resolver.js';
|
|
22
|
-
import { LifecycleWrapper } from './features/lifecycle-wrapper.js';
|
|
23
|
-
/**
|
|
24
|
-
* Vue 渲染器
|
|
25
|
-
* 将 Vario Schema 转换为 Vue VNode
|
|
26
|
-
*/
|
|
27
|
-
export class VueRenderer {
|
|
28
|
-
refsRegistry;
|
|
29
|
-
getState;
|
|
30
|
-
// 功能模块
|
|
31
|
-
pathResolver;
|
|
32
|
-
componentResolver;
|
|
33
|
-
expressionEvaluator;
|
|
34
|
-
eventHandler;
|
|
35
|
-
attrsBuilder;
|
|
36
|
-
loopHandler;
|
|
37
|
-
childrenResolver;
|
|
38
|
-
lifecycleWrapper;
|
|
39
|
-
constructor(options = {}) {
|
|
40
|
-
this.getState = options.getState;
|
|
41
|
-
this.refsRegistry = options.refsRegistry || new RefsRegistry();
|
|
42
|
-
// modelPath 选项保留于 API,供后续路径解析扩展使用
|
|
43
|
-
// 初始化功能模块
|
|
44
|
-
// 优先级:components > app._context.components > instance?.appContext?.components
|
|
45
|
-
const globalComponents = options.components ||
|
|
46
|
-
options.app?._context?.components ||
|
|
47
|
-
options.instance?.appContext?.components ||
|
|
48
|
-
{};
|
|
49
|
-
this.componentResolver = new ComponentResolver(globalComponents);
|
|
50
|
-
this.expressionEvaluator = new ExpressionEvaluator();
|
|
51
|
-
this.eventHandler = new EventHandler((expr, ctx) => this.expressionEvaluator.evaluateExpr(expr, ctx));
|
|
52
|
-
this.pathResolver = new ModelPathResolver((expr, ctx) => this.expressionEvaluator.evaluateExpr(expr, ctx));
|
|
53
|
-
this.attrsBuilder = new AttrsBuilder(this.getState, this.pathResolver, this.eventHandler);
|
|
54
|
-
this.lifecycleWrapper = new LifecycleWrapper();
|
|
55
|
-
// LoopHandler 和 ChildrenResolver 需要 createVNode 方法,使用箭头函数绑定 this
|
|
56
|
-
const createVNodeFn = (schema, ctx, modelPathStack) => this.createVNode(schema, ctx, modelPathStack || []);
|
|
57
|
-
this.loopHandler = new LoopHandler(this.pathResolver, createVNodeFn, (expr, ctx) => this.expressionEvaluator.evaluateExpr(expr, ctx));
|
|
58
|
-
this.childrenResolver = new ChildrenResolver(createVNodeFn, this.expressionEvaluator);
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* 渲染 Schema 为 VNode
|
|
62
|
-
*/
|
|
63
|
-
render(schema, ctx) {
|
|
64
|
-
const vnode = this.createVNode(schema, ctx);
|
|
65
|
-
// 如果返回 null,返回一个空的 Fragment 作为占位符
|
|
66
|
-
// Vue 需要有效的 VNode,不能是 null
|
|
67
|
-
if (vnode === null || vnode === undefined) {
|
|
68
|
-
return h(Fragment, null, []);
|
|
69
|
-
}
|
|
70
|
-
return vnode;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* 创建 VNode
|
|
74
|
-
* @param schema Schema 节点
|
|
75
|
-
* @param ctx 运行时上下文
|
|
76
|
-
* @param modelPathStack 当前 model 路径栈(用于自动路径拼接)
|
|
77
|
-
*/
|
|
78
|
-
createVNode(schema, ctx, modelPathStack = []) {
|
|
79
|
-
// 验证 schema 基本结构
|
|
80
|
-
if (!schema || typeof schema !== 'object') {
|
|
81
|
-
return h('div', { style: 'color: red; padding: 10px;' }, 'Invalid schema');
|
|
82
|
-
}
|
|
83
|
-
if (!schema.type) {
|
|
84
|
-
return h('div', { style: 'color: red; padding: 10px;' }, 'Schema missing type property');
|
|
85
|
-
}
|
|
86
|
-
// 处理条件渲染(优化:提前返回,避免不必要的处理)
|
|
87
|
-
if (schema.cond) {
|
|
88
|
-
try {
|
|
89
|
-
const condition = this.expressionEvaluator.evaluateExpr(schema.cond, ctx);
|
|
90
|
-
if (!condition) {
|
|
91
|
-
// 条件不满足,返回 null(Vue 会正确处理 null VNode)
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch (error) {
|
|
96
|
-
// 表达式求值错误,返回错误提示节点
|
|
97
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
98
|
-
return h('div', {
|
|
99
|
-
style: 'color: red; padding: 10px; border: 1px solid red;'
|
|
100
|
-
}, `Condition evaluation error: ${errorMessage}`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
// 处理列表渲染
|
|
104
|
-
if (schema.loop) {
|
|
105
|
-
const loopVNode = this.loopHandler.createLoopVNode(schema, ctx, modelPathStack);
|
|
106
|
-
return loopVNode || null;
|
|
107
|
-
}
|
|
108
|
-
// 解析组件(先解析,用于双向绑定的自动检测)
|
|
109
|
-
const component = this.componentResolver.resolveComponent(schema.type);
|
|
110
|
-
// 确保 component 有效
|
|
111
|
-
if (!component) {
|
|
112
|
-
return h('div', { style: 'color: red; padding: 10px;' }, `Component "${schema.type}" not found`);
|
|
113
|
-
}
|
|
114
|
-
// 处理 Vue 特有的扩展
|
|
115
|
-
const vueSchema = schema;
|
|
116
|
-
// 处理 model 路径(更新路径栈,供子级使用)
|
|
117
|
-
// model 为 string 或 object+scope 时压栈
|
|
118
|
-
let currentModelPathStack = [...modelPathStack];
|
|
119
|
-
const scopePath = this.pathResolver.getScopePath(schema.model);
|
|
120
|
-
if (scopePath) {
|
|
121
|
-
currentModelPathStack = this.pathResolver.updateModelPathStack(scopePath, modelPathStack, ctx, schema);
|
|
122
|
-
}
|
|
123
|
-
// 构建属性(传入 component 和路径栈用于自动检测)
|
|
124
|
-
// buildAttrs 中会解析 model 路径,这里传入当前路径栈
|
|
125
|
-
let attrs = this.attrsBuilder.buildAttrs(schema, ctx, component, modelPathStack, (props, ctx) => this.childrenResolver.evalProps(props, ctx));
|
|
126
|
-
// 解析子节点(支持作用域插槽,传递路径栈)
|
|
127
|
-
let children = this.childrenResolver.resolveChildren(schema, ctx, currentModelPathStack);
|
|
128
|
-
// 处理可见性控制(v-show,优化:错误处理)
|
|
129
|
-
if (schema.show) {
|
|
130
|
-
try {
|
|
131
|
-
const isVisible = this.expressionEvaluator.evaluateExpr(schema.show, ctx);
|
|
132
|
-
if (!isVisible) {
|
|
133
|
-
// 确保 style 是对象格式
|
|
134
|
-
const currentStyle = attrs.style;
|
|
135
|
-
if (typeof currentStyle === 'string') {
|
|
136
|
-
// 如果 style 是字符串,转换为对象
|
|
137
|
-
const styleObj = {};
|
|
138
|
-
currentStyle.split(';').forEach(rule => {
|
|
139
|
-
const [key, value] = rule.split(':').map(s => s.trim());
|
|
140
|
-
if (key && value) {
|
|
141
|
-
styleObj[key] = value;
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
attrs.style = { ...styleObj, display: 'none' };
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
// style 已经是对象或未定义
|
|
148
|
-
attrs.style = { ...(currentStyle || {}), display: 'none' };
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
// 表达式求值错误,隐藏元素并显示错误提示
|
|
154
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
155
|
-
console.warn(`Show expression evaluation error: ${errorMessage}`, schema);
|
|
156
|
-
// 默认隐藏元素
|
|
157
|
-
attrs.style = { ...(attrs.style || {}), display: 'none' };
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// 确保 attrs 是对象(避免 undefined 导致的问题)
|
|
161
|
-
const safeAttrs = attrs || {};
|
|
162
|
-
// 处理 children:Vue 3 组件推荐使用函数插槽以获得更好的性能
|
|
163
|
-
let finalAttrs = safeAttrs;
|
|
164
|
-
let finalChildren = null;
|
|
165
|
-
if (children && typeof children === 'object' && !Array.isArray(children)) {
|
|
166
|
-
// 作用域插槽对象(已经是函数),直接使用
|
|
167
|
-
finalChildren = children;
|
|
168
|
-
}
|
|
169
|
-
else if (children !== undefined && children !== null) {
|
|
170
|
-
// 普通 children:对于组件,包装成函数插槽以避免警告
|
|
171
|
-
// 对于原生 DOM 元素,可以直接使用数组
|
|
172
|
-
const isNativeElement = typeof component === 'string';
|
|
173
|
-
if (isNativeElement) {
|
|
174
|
-
finalChildren = children;
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
// Vue 3 组件推荐使用函数插槽
|
|
178
|
-
finalChildren = { default: () => children };
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
// 检查是否需要组件包装(生命周期、provide/inject 都需要在 setup 中处理)
|
|
182
|
-
const hasLifecycle = vueSchema.onMounted || vueSchema.onUnmounted || vueSchema.onUpdated ||
|
|
183
|
-
vueSchema.onBeforeMount || vueSchema.onBeforeUnmount || vueSchema.onBeforeUpdate;
|
|
184
|
-
const hasProvideInject = (vueSchema.provide && Object.keys(vueSchema.provide).length > 0) ||
|
|
185
|
-
(vueSchema.inject && (Array.isArray(vueSchema.inject) ? vueSchema.inject.length > 0 : Object.keys(vueSchema.inject).length > 0));
|
|
186
|
-
let vnode;
|
|
187
|
-
if (hasLifecycle || hasProvideInject) {
|
|
188
|
-
vnode = this.lifecycleWrapper.createComponentWithLifecycle(component, finalAttrs, finalChildren, vueSchema, ctx);
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
try {
|
|
192
|
-
vnode = h(component, finalAttrs, finalChildren);
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
return h('div', { style: 'color: red; padding: 10px;' }, `Failed to render "${schema.type}": ${error}`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
// 处理 ref
|
|
199
|
-
if (vueSchema.ref) {
|
|
200
|
-
vnode = attachRef(vnode, vueSchema, this.refsRegistry);
|
|
201
|
-
}
|
|
202
|
-
// 处理 keep-alive
|
|
203
|
-
if (vueSchema.keepAlive) {
|
|
204
|
-
const keepAliveProps = typeof vueSchema.keepAlive === 'object'
|
|
205
|
-
? vueSchema.keepAlive
|
|
206
|
-
: {};
|
|
207
|
-
vnode = h(KeepAlive, keepAliveProps, () => vnode);
|
|
208
|
-
}
|
|
209
|
-
// 处理 transition
|
|
210
|
-
if (vueSchema.transition) {
|
|
211
|
-
const transitionProps = typeof vueSchema.transition === 'string'
|
|
212
|
-
? { name: vueSchema.transition }
|
|
213
|
-
: {
|
|
214
|
-
...vueSchema.transition,
|
|
215
|
-
// 确保 duration 符合类型要求
|
|
216
|
-
duration: vueSchema.transition.duration && typeof vueSchema.transition.duration === 'object'
|
|
217
|
-
? (vueSchema.transition.duration.enter && vueSchema.transition.duration.leave
|
|
218
|
-
? { enter: vueSchema.transition.duration.enter, leave: vueSchema.transition.duration.leave }
|
|
219
|
-
: undefined)
|
|
220
|
-
: vueSchema.transition.duration
|
|
221
|
-
};
|
|
222
|
-
vnode = h(Transition, transitionProps, () => vnode);
|
|
223
|
-
}
|
|
224
|
-
// 处理 teleport(必须在最外层)
|
|
225
|
-
if (shouldTeleport(vueSchema.teleport)) {
|
|
226
|
-
vnode = createTeleport(vueSchema.teleport, vnode);
|
|
227
|
-
}
|
|
228
|
-
return vnode;
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* 清除组件解析缓存
|
|
232
|
-
* 用于组件注册变更后的缓存失效
|
|
233
|
-
*/
|
|
234
|
-
clearComponentCache() {
|
|
235
|
-
this.componentResolver.clearComponentCache();
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* 使特定组件的缓存失效
|
|
239
|
-
*/
|
|
240
|
-
invalidateComponentCache(type) {
|
|
241
|
-
this.componentResolver.invalidateComponentCache(type);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
//# sourceMappingURL=renderer.js.map
|
|
1
|
+
import{h as a,Fragment as C,Transition as A,KeepAlive as w}from"vue";import{attachRef as b,RefsRegistry as j}from"./features/refs.js";import{createTeleport as S,shouldTeleport as V}from"./features/teleport.js";import{ModelPathResolver as N}from"./features/path-resolver.js";import{ComponentResolver as H}from"./features/component-resolver.js";import{ExpressionEvaluator as B}from"./features/expression-evaluator.js";import{EventHandler as M}from"./features/event-handler.js";import{AttrsBuilder as k}from"./features/attrs-builder.js";import{LoopHandler as P}from"./features/loop-handler.js";import{ChildrenResolver as L}from"./features/children-resolver.js";import{LifecycleWrapper as W}from"./features/lifecycle-wrapper.js";class Q{refsRegistry;getState;pathResolver;componentResolver;expressionEvaluator;eventHandler;attrsBuilder;loopHandler;childrenResolver;lifecycleWrapper;constructor(t={}){this.getState=t.getState,this.refsRegistry=t.refsRegistry||new j;const n=t.components||t.app?._context?.components||t.instance?.appContext?.components||{};this.componentResolver=new H(n),this.expressionEvaluator=new B,this.eventHandler=new M((o,e)=>this.expressionEvaluator.evaluateExpr(o,e)),this.pathResolver=new N((o,e)=>this.expressionEvaluator.evaluateExpr(o,e)),this.attrsBuilder=new k(this.getState,this.pathResolver,this.eventHandler),this.lifecycleWrapper=new W;const i=(o,e,f)=>this.createVNode(o,e,f||[]);this.loopHandler=new P(this.pathResolver,i,(o,e)=>this.expressionEvaluator.evaluateExpr(o,e)),this.childrenResolver=new L(i,this.expressionEvaluator)}render(t,n){const i=this.createVNode(t,n);return i??a(C,null,[])}createVNode(t,n,i=[]){if(!t||typeof t!="object")return a("div",{style:"color: red; padding: 10px;"},"Invalid schema");if(!t.type)return a("div",{style:"color: red; padding: 10px;"},"Schema missing type property");if(t.cond)try{if(!this.expressionEvaluator.evaluateExpr(t.cond,n))return null}catch(r){const l=r instanceof Error?r.message:String(r);return a("div",{style:"color: red; padding: 10px; border: 1px solid red;"},`Condition evaluation error: ${l}`)}if(t.loop)return this.loopHandler.createLoopVNode(t,n,i)||null;const o=this.componentResolver.resolveComponent(t.type);if(!o)return a("div",{style:"color: red; padding: 10px;"},`Component "${t.type}" not found`);const e=t;let f=[...i];const v=this.pathResolver.getScopePath(t.model);v&&(f=this.pathResolver.updateModelPathStack(v,i,n,t));let d=this.attrsBuilder.buildAttrs(t,n,o,i,(r,l)=>this.childrenResolver.evalProps(r,l)),p=this.childrenResolver.resolveChildren(t,n,f);if(t.show)try{if(!this.expressionEvaluator.evaluateExpr(t.show,n)){const l=d.style;if(typeof l=="string"){const h={};l.split(";").forEach(E=>{const[y,m]=E.split(":").map(x=>x.trim());y&&m&&(h[y]=m)}),d.style={...h,display:"none"}}else d.style={...l||{},display:"none"}}}catch(r){const l=r instanceof Error?r.message:String(r);console.warn(`Show expression evaluation error: ${l}`,t),d.style={...d.style||{},display:"none"}}let u=d||{},c=null;p&&typeof p=="object"&&!Array.isArray(p)?c=p:p!=null&&(typeof o=="string"?c=p:c={default:()=>p});const g=e.onMounted||e.onUnmounted||e.onUpdated||e.onBeforeMount||e.onBeforeUnmount||e.onBeforeUpdate,R=e.provide&&Object.keys(e.provide).length>0||e.inject&&(Array.isArray(e.inject)?e.inject.length>0:Object.keys(e.inject).length>0);let s;if(g||R)s=this.lifecycleWrapper.createComponentWithLifecycle(o,u,c,e,n);else try{s=a(o,u,c)}catch(r){return a("div",{style:"color: red; padding: 10px;"},`Failed to render "${t.type}": ${r}`)}if(e.ref&&(s=b(s,e,this.refsRegistry)),e.keepAlive){const r=typeof e.keepAlive=="object"?e.keepAlive:{};s=a(w,r,()=>s)}if(e.transition){const r=typeof e.transition=="string"?{name:e.transition}:{...e.transition,duration:e.transition.duration&&typeof e.transition.duration=="object"?e.transition.duration.enter&&e.transition.duration.leave?{enter:e.transition.duration.enter,leave:e.transition.duration.leave}:void 0:e.transition.duration};s=a(A,r,()=>s)}return V(e.teleport)&&(s=S(e.teleport,s)),s}clearComponentCache(){this.componentResolver.clearComponentCache()}invalidateComponentCache(t){this.componentResolver.invalidateComponentCache(t)}}export{Q as VueRenderer};
|
package/dist/types.js
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vue 特有的类型定义
|
|
3
|
-
*
|
|
4
|
-
* 扩展 SchemaNode 以支持 Vue 的特性:
|
|
5
|
-
* - ref: 模板引用(声明映射到 Vue ref)
|
|
6
|
-
* - 生命周期钩子(声明映射到 Vue 钩子)
|
|
7
|
-
* - provide/inject: 依赖注入(声明映射到 Vue API)
|
|
8
|
-
* - teleport: 传送
|
|
9
|
-
* - transition: 过渡动画
|
|
10
|
-
* - keep-alive: 缓存
|
|
11
|
-
*
|
|
12
|
-
* 注意:computed 和 watch 不在 Schema 中定义,
|
|
13
|
-
* 应该在 Vue 组件中使用原生 API 定义,然后通过 useVario 的 computed 选项传入
|
|
14
|
-
*/
|
|
15
|
-
export {};
|
|
16
|
-
//# sourceMappingURL=types.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@variojs/vue",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Vario Vue Renderer - Vue 3 integration for Vario Schema rendering",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsc",
|
|
17
|
+
"postbuild": "node ../../scripts/minify.js",
|
|
17
18
|
"dev": "tsc --watch",
|
|
18
19
|
"test": "NODE_OPTIONS=--max-old-space-size=4096 vitest run",
|
|
19
20
|
"test:perf": "NODE_OPTIONS=--max-old-space-size=8192 vitest run __tests__/performance.test.ts",
|
|
@@ -21,10 +22,6 @@
|
|
|
21
22
|
"test:watch": "vitest",
|
|
22
23
|
"clean": "rm -rf dist"
|
|
23
24
|
},
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"@variojs/core": "^0.0.1",
|
|
26
|
-
"@variojs/schema": "^0.0.1"
|
|
27
|
-
},
|
|
28
25
|
"devDependencies": {
|
|
29
26
|
"typescript": "^5.3.3",
|
|
30
27
|
"vitest": "^1.2.0",
|
|
@@ -37,5 +34,21 @@
|
|
|
37
34
|
],
|
|
38
35
|
"publishConfig": {
|
|
39
36
|
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@variojs/core": "^0.0.2",
|
|
40
|
+
"@variojs/schema": "^0.0.2",
|
|
41
|
+
"vue": "^3.4.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependenciesMeta": {
|
|
44
|
+
"@variojs/core": {
|
|
45
|
+
"optional": false
|
|
46
|
+
},
|
|
47
|
+
"@variojs/schema": {
|
|
48
|
+
"optional": false
|
|
49
|
+
},
|
|
50
|
+
"vue": {
|
|
51
|
+
"optional": false
|
|
52
|
+
}
|
|
40
53
|
}
|
|
41
54
|
}
|