Vue.js 组件提供了一个 functional 开关,设置为 true 后,就可以让组件变为无状态、无实例的函数化组件。因为只是函数,所以渲染的开销相对来说较小。
在对使用Ant Design Vue项目的左侧菜单国际化改造时,由于翻译函数$t
是挂载在Vue实例上的,而子菜单是通过函数化组件(functional template)递归生成,因此需要将子菜单改成非函数化组件实现。
函数化组件实现
官方给出的demo是这样的:single-file-recursive-menu-单文件递归菜单
Github上讨论的相关issue:使用SubMenu报错Cannot read property ‘isRootMenu’ of undefined"
<template functional>
<a-sub-menu :key="props.menuInfo.key">
<span slot="title">
<a-icon type="mail" /><span>{{ props.menuInfo.title }}</span>
</span>
<template v-for="item in props.menuInfo.children">
<a-menu-item v-if="!item.children" :key="item.key">
<a-icon type="pie-chart" />
<span>{{ item.title }}</span>
</a-menu-item>
<sub-menu v-else :key="item.key" :menu-info="item" />
</template>
</a-sub-menu>
</template>
<script>
export default {
props: ['menuInfo'],
};
</script>
这种方式需要menuInfo在传入前已经被处理为需要的数据,如果子菜单无需处理数据,则可以满足需求,也有更好的渲染速度。
不使用函数化组件实现
当需要调用实例上的方法,则需要改造为非函数化实现,如果只是单纯的去掉functional
和props
则会报错Cannot read property ‘isRootMenu’ of undefined"。
查阅相关资料,发现是需要自行传递组件参数,因此导入Menu组件,将组件参数填充至自定义的subMenu组件,并通过v-bind="$props"
传递,最终改造如下:
<template>
<a-sub-menu :key="menuInfo.key" v-bind="$props" v-on="$listeners">
<span slot="title">
<a-icon v-show="menuInfo.icon" :type="menuInfo.icon" />
<span>{{ menuInfo.title }}</span>
</span>
<template v-for="item in menuInfo.child">
<a-menu-item v-if="!item.child || (item.child && item.child.length === 0)" :key="item.menuId" :fullPath="item.urlAddr" :menuId="item.menuId">
<a-icon v-show="item.icon" :type="item.icon" />
<span>{{ item.menuName }}</span>
</a-menu-item>
<sub-menu v-else :key="item.menuId" :menu-info="item" v-on="$listeners" />
</template>
</a-sub-menu>
</template>
<script>
import { Menu } from 'ant-design-vue'
export default {
name: "subMenu",
isSubMenu: true,
props: {
...Menu.SubMenu.props,
menuInfo: {
type: Object
}
}
}
</script>
此时非函数式子菜单改造完成,可以使用实例上的相关方法或自行定义相关内容了。