Vue2 爆改 Vue3 + TS
Vue2 爆改 Vue3 + TS
挂载 Vue 组件
Vue2
import {HtmlNode, HtmlNodeModel, h} from '@logicflow/core'
import baseNode from './baseNode.vue'
class BaseNodeView extends HtmlNode {
constructor(props) {
super(props)
this.root = document.createElement('div')
this.vueComponent = baseNode
}
setHtml(rootEl) {
rootEl.appendChild(this.root)
if (this.vm) {
this.vm.$mount(this.root)
} else {
this.vm = new Vue({
render: (h) =>
h(this.vueComponent, {
props: {
model: this.props.model,
graphModel: this.props.graphModel,
disabled: this.props.graphModel.editConfigModel.isSilentMode,
isSelected: this.props.model.isSelected,
isHovered: this.props.model.isHovered,
properties: this.props.model.getProperties()
}
})
})
// 挂载
this.vm.$mount(this.root)
}
}
}
Vue3
Vue3 不再使用 new Vue()
来创建一个新的 Vue 应用实例,而是使用 createApp
函数。 此外,h
函数现在是 vue
包的一部分,不再是 vue 实例的一部分。
Vue3 中 h
函数,第一个参数是……,第二个参数:传递给组件的所有属性,组件中用 props 接收。
// 注意,如果 h 已被其他组件用了,则 Vue 中需要用别名 h as vueH
import {App, createApp, h as vueH} from "vue";
import {HtmlNode, HtmlNodeModel, h} from '@logicflow/core'
import baseNode from '@/components/processRouteDesign/kwChart/node/BaseNode.vue'
class BaseNodeView extends HtmlNode {
// Vue app instance
declare vueApp: App<Element>;
declare rootElement: any;
declare vueComponent: any;
constructor(props) {
super(props)
this.rootElement = document.createElement('div')
this.vueComponent = baseNode
}
setHtml(rootEl: HTMLElement) {
rootEl.appendChild(this.rootElement);
if (this.vueApp) {
this.vueApp.mount(this.rootElement);
} else {
const component = {
render: () =>
vueH(this.vueComponent, {
model: this.props.model,
graphModel: this.props.graphModel,
disabled: this.props.graphModel.editConfigModel.isSilentMode,
isSelected: this.props.model.isSelected,
isHovered: this.props.model.isHovered,
properties: this.props.model.getProperties()
})
};
this.vueApp = createApp(component);
this.vueApp.mount(this.rootElement);
}
}
}
props
Vue2
<!-- vue2 -->
<script>
export default {
props: {
model: Object,
graphModel: Object,
isSelected: Boolean,
isHovered: Boolean,
disabled: Boolean,
properties: Object
},
}
</script>
Vue3
<!-- vue3 -->
<script setup lang="ts">
const props = defineProps<{
model: any,
graphModel: any
isSelected: any,
isHovered: any,
disabled: any,
properties: any
}>();
</script>
watch 监听
Vue2 代码
<!-- vue2 -->
<script>
export default {
props: {
model: Object,
graphModel: Object,
isSelected: Boolean,
isHovered: Boolean,
disabled: Boolean,
properties: Object
},
watch: {
isHovered(nv) {
// console.log('nv --->>>', nv);
if (nv) {
this.enterNode()
} else {
this.leaveNode()
}
}
}
}
</script>
Vue3
在 Vue 3 中,当你在 watch
函数中直接使用 props.isHovered
时,可能无法正确地监听到其变化。 这是因为 props 对象本身并没有被 Vue 3 设计为响应式的
,而是 props 对象中的各个属性是响应式的
。 因此,你需要使用一个函数来返回 props.isHovered
,这样 watch 函数就能正确地监听其变化了。
<!-- vue3 -->
<script setup lang="ts">
import {watch} from "vue";
const props = defineProps<{
model: any,
graphModel: any
isSelected: any,
isHovered: any,
disabled: any,
properties: any
}>();
watch(
() => props.isHovered,
(val) => {
console.log('val --->>>', val);
if (val) {
enterNode();
} else {
leaveNode();
}
}
)
// 注意,如果直接使用 watch(props.isHovered, (val) => {}) 不会生效,浏览器控制台会有警告。
</script>
emit
vue2
<!-- Vue2 -->
<script>
export default {
methods: {
handleChange(e) {
this.val = e
const option = this.options.find((item) => item.value === e) || {}
const data = {
type: 'option',
value: e,
valueDesc: option.label
}
this.$emit('change', data)
},
}
}
</script>
vue3
<!-- Vue3 -->
<script setup lang="ts">
const emit = defineEmits(['change']);
const someFunction = (data: any) => {
emit('change', data);
}
</script>
常见报错
实际运行时浏览器控制台会有一个 [Vue warn]
。
ERROR
[Vue warn]: App has already been mounted.
If you want to remount the same app, move your app creation logic into a factory function and create fresh app instances for each mount - e.g. const createMyApp = () => createApp(App)
这个警告是因为你尝试多次挂载同一个 Vue 应用实例。 在 Vue.js 中,一个应用实例只能被挂载一次。 如果你需要重新挂载应用,你需要创建一个新的应用实例。
问题描述:上述代码试图检查 vueApp
是否已经存在,如果存在就尝试重新挂载它。但是,Vue 不允许同一个应用实例被挂载多次,所以当 setHtml
被第二次调用时,你的代码就会抛出错误。 所以,我们需要修改这部分的逻辑。当 setHtml
被调用时,我们应该始终创建一个新的 Vue
应用实例并挂载它,而不是试图重新挂载现有的应用实例。
class BaseNodeView extends HtmlNode {
// Vue app instance
declare vueApp: App<Element>;
declare rootElement: any;
declare vueComponent: any;
constructor(props) {
super(props)
this.rootElement = document.createElement('div')
this.vueComponent = baseNode
}
setHtml(rootEl: HTMLElement) {
rootEl.appendChild(this.rootElement);
const component = {
render: () =>
vueH(this.vueComponent, {
model: this.props.model,
graphModel: this.props.graphModel,
disabled: this.props.graphModel.editConfigModel.isSilentMode,
isSelected: this.props.model.isSelected,
isHovered: this.props.model.isHovered,
properties: this.props.model.getProperties()
})
};
this.vueApp = createApp(component);
this.vueApp.mount(this.rootElement);
}
}
// 在这个版本中,保留了 vueApp 属性,并在 setHtml 方法中更新了它。
// 这样,每次 setHtml 被调用时,都会创建一个新的 Vue 应用实例并挂载它,避免了重复挂载的问题。
ERROR
[Vue warn]: Failed to resolve component: el-popconfirm
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
Vue 无法解析 el-popconfirm 组件。