是否有A' Simple'在Vue中动态渲染视图的方法?
让我们退后一步,看看用例:
您正在定义模块化接口,并且任何实现其实现的模块都必须能够“渲染自身”到给定的插槽和状态的应用程序中。
您如何在Vue中进行?
示例解决方案
让我们看看我可以组装的最基本的实现:(
完整示例: https://stackblitz.com/edit/vitejs-vite- 8zclnp?file = src/app.vue )
我们有一个布局:
# Layout.vue
<template>
<div>
<hr />
<slot name="moduleView" />
<hr />
</div>
</template>
...和一个带有模块的应用程序:
# App.vue
<script lang="ts" setup>
import type { MyModuleState } from "./MyModule";
import Layout from "./Layout.vue";
import { ref } from "vue";
import { MyModule } from "./MyModule";
import ModView from "./ModView.vue";
const state = ref<MyModuleState>({ value: 0 });
const module = new MyModule();
const onClick = () => {
state.value = { value: state.value.value + 1 };
};
const renderModule = () => {
return module.view(state.value);
};
</script>
<template>
<div>currentValue: {{ state.value }}</div>
<div>update: <button @click="onClick">++</button></div>
<div>
<Layout>
<template v-slot:moduleView>
<mod-view :render="renderModule" :state="state" /> // <--- But this!
</template>
</Layout>
</div>
</template>
...但是呈现到插槽中需要大量通过晦涩难懂的箍:
# ModView.vue
<script lang="ts" setup>
import ModRender from "./ModRender";
import { ref, watch } from "vue";
import type { VNode } from "vue";
const props = defineProps<{
render: (state?: any) => VNode | Array<VNode>;
state?: any;
}>();
const nodes = ref(props.render(props.state));
watch( // <-- Obscure! The view won't update unless you explicitly watch props?
() => props.state,
(nextState) => {
nodes.value = props.render(nextState);
}
);
</script>
<template>
<mod-render :nodes="nodes" />
</template>
# ModRender.ts
import type { VNode } from "vue";
const ModRender = (props: { nodes: VNode | Array<VNode> }) => {
return props.nodes;
};
ModRender.props = {
nodes: {
required: true,
},
};
export default ModRender; // <--- Super obscure, why do you need a functional component for this?
在我们可以定义之前实际模块:
# MyModule.ts
import type { VNode } from "vue";
import { h } from "vue";
import ModuleView from "./MyModuleDisplay.vue";
interface AbstractModule<T> {
view: (state: T) => VNode;
}
export interface MyModuleState {
value: number;
}
export class MyModule implements AbstractModule<MyModuleState> {
view(state: MyModuleState): VNode {
return h(ModuleView, { state });
}
}
...和一个组件:
# MyModuleView.vue
<script setup lang="ts">
import type { MyModuleState } from "./MyModule";
const props = defineProps<{ state: MyModuleState }>();
</script>
<template>
<div>{{ state.value }}</div>
</template>
什么。
这似乎非常钝和冗长。
我想念什么吗?
在其他组件系统中,实现可能看起来像:
export class MyModule implements AbstractModule<MyModuleState> {
view(state: MyModuleState): VNode {
return (<div>{state.value}</div>);
}
}
...
<div>
<Layout>{renderModule(state)}</Layout>
</div>
令人惊讶的是,必须在Vue中完成如此多的包装和篮球,这使我感到自己缺少某些东西。
有一个更简单的方法吗?
Let's take a step back and look at the use case:
You're defining a modular interface, and any module that implements it must be able to 'render itself' into the application given a slot and a state.
How do you do it in vue?
Example solution
Let's have a look at the most basic implementation I can assemble:
(full example:
https://stackblitz.com/edit/vitejs-vite-8zclnp?file=src/App.vue)
We have a layout:
# Layout.vue
<template>
<div>
<hr />
<slot name="moduleView" />
<hr />
</div>
</template>
...and an app with a module:
# App.vue
<script lang="ts" setup>
import type { MyModuleState } from "./MyModule";
import Layout from "./Layout.vue";
import { ref } from "vue";
import { MyModule } from "./MyModule";
import ModView from "./ModView.vue";
const state = ref<MyModuleState>({ value: 0 });
const module = new MyModule();
const onClick = () => {
state.value = { value: state.value.value + 1 };
};
const renderModule = () => {
return module.view(state.value);
};
</script>
<template>
<div>currentValue: {{ state.value }}</div>
<div>update: <button @click="onClick">++</button></div>
<div>
<Layout>
<template v-slot:moduleView>
<mod-view :render="renderModule" :state="state" /> // <--- But this!
</template>
</Layout>
</div>
</template>
...but rendering into the slot requires a lot of jumping through obscure hoops:
# ModView.vue
<script lang="ts" setup>
import ModRender from "./ModRender";
import { ref, watch } from "vue";
import type { VNode } from "vue";
const props = defineProps<{
render: (state?: any) => VNode | Array<VNode>;
state?: any;
}>();
const nodes = ref(props.render(props.state));
watch( // <-- Obscure! The view won't update unless you explicitly watch props?
() => props.state,
(nextState) => {
nodes.value = props.render(nextState);
}
);
</script>
<template>
<mod-render :nodes="nodes" />
</template>
# ModRender.ts
import type { VNode } from "vue";
const ModRender = (props: { nodes: VNode | Array<VNode> }) => {
return props.nodes;
};
ModRender.props = {
nodes: {
required: true,
},
};
export default ModRender; // <--- Super obscure, why do you need a functional component for this?
Before we can define the actual module:
# MyModule.ts
import type { VNode } from "vue";
import { h } from "vue";
import ModuleView from "./MyModuleDisplay.vue";
interface AbstractModule<T> {
view: (state: T) => VNode;
}
export interface MyModuleState {
value: number;
}
export class MyModule implements AbstractModule<MyModuleState> {
view(state: MyModuleState): VNode {
return h(ModuleView, { state });
}
}
...and a component for it:
# MyModuleView.vue
<script setup lang="ts">
import type { MyModuleState } from "./MyModule";
const props = defineProps<{ state: MyModuleState }>();
</script>
<template>
<div>{{ state.value }}</div>
</template>
What.
This seems extremely obtuse and verbose.
Am I missing something?
In other component systems an implementation might look like:
export class MyModule implements AbstractModule<MyModuleState> {
view(state: MyModuleState): VNode {
return (<div>{state.value}</div>);
}
}
...
<div>
<Layout>{renderModule(state)}</Layout>
</div>
It seems surprising that so many wrappers and hoops have to be done in vue to do this, which makes me feel like I'm missing something.
Is there an easier way of doing this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Vnode对象不能像
modrender
这样的组件中呈现在组件模板中。如果它们被用作通用方式来交换应用程序中的模板数据,那是一个问题。 Vnodes仍然可以直接用于组件渲染函数和功能组件,具有JSX或H
喜欢&lt; layout&gt; {rendermodule(state)}&lt;/layout;/layout&gt;
,这限制他们的用法。AbstractModule
如果会导致不必要的代码,则可能需要重新考虑约定。从某个时候,需要与动态&gt;
一起使用“视图”的事实,并且它将尽可能简单地使用。可能没有必要进行“模块”抽象,但是即使存在,
module.view
也可以返回组件(功能或状态)而不是vnodes。或者它可以构建一个组件并使其作为属性可用,例如:Vnode objects cannot be rendered in component templates as is and need to be wrapped in a component like
ModRender
. If they are used as universal way to exchange template data in the app, that's a problem. Vnodes still can be directly used in component render functions and functional components with JSX orh
like<Layout>{renderModule(state)}</Layout>
, this limits their usage.AbstractModule
convention may need to be reconsidered if it results in unnecessary code. Proceed from the fact that a "view" needs to be used with dynamic<component>
at some point, and it will be as straightforward as possible.There may be no necessity for "module" abstraction, but even if there is,
module.view
can return a component (functional or stateful) instead of vnodes. Or it can construct a component and make it available as a property, e.g.: