vue-menu组件

1,395 阅读1分钟

menu组件可以借助于多插槽和递归进行渲染

多slot插槽

组件中可能遇到多个插槽的情况,可以通过插槽的name属性定义使用额外的插槽

  父传子:插槽中使用的数据默认为当前实例数据,即将父组件的数据传递给了子组件
  子传父:作用域插槽:将插槽对应子组件的数据传递给父组件,默认使用的为父组件数据,当子组件向上传递时,使用的为子组件的数据
<template>
  <div>
    <myDialog ref='dialog'>
        <!--父传子-->
      <template v-slot:header>header{{msg}}</template>
        <!--子传父-->
      <template v-slot:footer='{isShow, a}'>footer-{{isShow}}-{{a}}</template>
      helloWorld
    </myDialog>
    <button @click='change'>点我</button>
  </div>
</template>
------------------------------------------------------
<script>
  import myDialog from "./my-dialog"
  export default {
    components: {
      myDialog
    },
    data() {
      return {msg: "hihi"}
    },
    methods: {
      change() {
        this.$refs.dialog.change();
      }
    }
  }
</script>

// my-dialog.js
<template>
  <div v-if='isShow'>
    <slot name='header'></slot>
    这是一个弹框
   <slot name='footer' :isShow='isShow' a='a'></slot>
  </div>
</template>
<script>
export default {
  data() {
    return {
      isShow: true,
    }
  },
  methods: {
    change() {
      this.isShow = !this.isShow
    }
  },
  beforeDestroy() {
    this.$bus.$off('监听事件')
  }
}
</script>

menu组件

  • el-menu:ul元素包裹所有的菜单元素
  • el-menu-item:li元素包裹菜单子元素的标题,用于渲染没有children的菜单子元素
  • el-submenu:li元素包裹菜单子元素的标题+新的ul元素,用于渲染有children的菜单子元素;根据slot的name进行区分渲染;其中新的ul元素用于包裹子元素下的children
  • my-menu:函数组件,通过render方法根据菜单数据进行递归渲染
// el-menu.vue
<template>
  <ul>
    <slot></slot> 
  </ul>
</template>
----------------------------------------------
// el-menu-item.vue
<template>
  <li>
    <slot></slot> 
  </li>
</template>
----------------------------------------------
// el-submenu.vue
<template>
  <li>
    <div>
      <slot name='title'></slot>
    </div>
    <ul>
      <slot name='default'></slot>
    </ul>
  </li>
</template>

// my-menu.js
import elMenu from './components/el-menu'
import elMenuItem from './components/el-menu-item'
import elSubmenu from './components/el-submenu'
export default {
  props: {
    data: {
      type: Array,
      default: () => ([])
    }
  },
  render() {
    let renderChidren = (data) => {
      return data.map(child => {
        return child.children ? 
        <elSubmenu>
          <div slot='title'>{child.title}</div>
          {renderChidren(child.children)}
        </elSubmenu> : 
      <elMenuItem nativeOnClick={() => {
        console.log('原生点击事件')
      }}>{child.title}</elMenuItem>
      })
    }
    return <elMenu>
      {renderChidren(this.data)}
    </elMenu>
  }
}

// app.vue
<my-menu :data='data'></my-menu>
data() {
  return {
    data:[{
      title: '根1', id: 1
    }, {
      title: '根2',
      id: 2,
      children: [{
        title: '根2-1', id: 21,
        children: [{
          title: '根2-1-1', id: 211,
          children: [{
            title: '根2-1-1-1', id: 2111,
          }, {
            title: '根2-1-1-2', id: 2112
          }]
        }, {
          title: '根2-1-2', id: 221
        }]
      }, {
        title: '根2-2', id: 22
      }]
    }, {
      title: '根3', id: 3
    }, {
      title: '根4', id: 4
    }]
  }
}

子组件如何监听父组件的$mounted事件---通过eventBus

  • vue的原型上定义$bus,保证eventBus来自同一个vue实例
  • 在子组件的mounted上订阅事件xx
  • 在父组件的mounted上发布事件xx
// main.js
Vue.prototype.$bus = new Vue()


// 子组件
mounted(){
  this.$bus.$on('监听父组件mounted', (arg) => {
    console.log('父组件ok了', arg)
  })
},

// 父组件
mounted(){
  this.$bus.$emit('监听父组件mounted', 1)
},