程序员的资源宝库

网站首页 > gitee 正文

vue3项目-小兔鲜儿笔记-一级分类页面01

sanyeah 2024-04-13 16:21:03 gitee 4 ℃ 0 评论

1. 顶级类目-面包屑组件的封装

了解render函数和h函数:

render函数的返回值是html结构,渲染到#app容器,相对于template里传入html结构,render函数的优先级更高

h函数是一个创建vnode的函数,它接受三个参数:

  1. 一个html标签名

  2. 与这个html标签相关的属性或事件,是一个对象

  3. 与这个html标签相关的内容或子节点

 

render函数内部会调用h函数,返回多个vnode组成的虚拟DOM,可以类似看成html结构

 

需求:

  1. 创建xtx-bread父容器

  2. 获取默认插槽内容

  3. 去除xtx-bread-item组件的i标签,由render函数来生成

  4. 遍历插槽中的item,得到一个动态创建的节点,最后一个item不加i标签

  5. 把动态创建的节点渲染到xtx-bread标签中

// xtx-bread组件
<script>
import { h } from 'vue'

export default {
  name: 'XtxBread',
  // 使用render函数来创建html结构
  // render函数内部调用h函数,h函数会生成虚拟Dom节点,组合起来形成虚拟DOM树,也就是vue中的html结构
  // h函数接收3个参数:参数1是标签名,参数2是标签的属性(class等),参数3是标签的内容或子节点

  // 需求:
  // 1.创建一个xtx-bread容器
  // 2.获取默认插槽的内容
  // 3.去除xtx-bread-item组件的i标签,这个箭头应该由render函数来生成并控制
  // 4.遍历插槽中的xtx-bread-item,得到一个动态创建的节点,最后一个item后不加i标签
  // 5.把动态创建的节点渲染到xtx-bread容器上
  render() {
    // 从默认插槽中获取的是一个数组,数组的每一项代表每一个xtx-bread-item组件
    const items = this.$slots.default()
    const dynamicItems = []
    items.forEach((item, i) => {
      dynamicItems.push(item)
      if (i < items.length - 1) {
        dynamicItems.push(h('i', { class: 'iconfont icon-angle-right' }, ''))
      }
    })
    return h('div', { class: 'xtx-bread' }, dynamicItems)
  }
}
</script>

<style lang="less">
// 去除scoped属性,使得xtx-bread-item属性可作用于全局
// xtx-bread和xtx-bread-item之间不是父子关系,而是插槽关系,所以使用:deep也是不可以的
// xtx-bread-item 组件
<template>
  <!-- 面包屑的单个路由组件item -->
  <div class="xtx-bread-item">
    <!-- 名字由插槽写入 -->
    <router-link v-if="to" :to="to"><slot /></router-link>
    <span v-else><slot /></span>
  </div>
</template>

<script setup>
defineProps({
  to: {
    type: [String, Object],
    default: ''
  }
})
</script>

 

2. 自动批量注册组件

大致步骤:

  • 使用require提供的函数context加载某一个目录下的所有.vue后缀的文件

  • context函数会返回一个导入函数importFn

    • importFn.keys()可以获取所有的文件路径

  • 拿到文件路径数组,遍历然后使用importFn()根据路径导入组件对象,遍历的同时进行全局注册

const importFn = require.context('./', false, /\.vue$/)

export default {
  install(app) {
    // 自定义指令
    defineDirective(app)
    // 自动批量导入组件
    importFn.keys().forEach((filePath) => {
      // 拿到组件对象
      const component = importFn(filePath).default
      app.component(component.name, component)
    })
  }
}

 

3. 封装二级类目的面包屑

因为二级类目需要处理的逻辑比较复杂,这里额外封装了二级类目的面包屑

二级类目的面包屑html结构

<template>
  <!-- 二级类目的面包屑组件封装 -->
  <xtx-bread>
    <xtx-bread-item to="/">首页</xtx-bread-item>
    <transition name="fade-right">
      <xtx-bread-item
        v-if="category.topCate"
        :key="category.topCate.id"
        :to="`/category/${category.topCate.id}`"
      >
        {{ category.topCate.name }}
      </xtx-bread-item>
    </transition>
    <transition name="fade-right">
      <xtx-bread-item v-if="category.subCate" :key="category.subCate.id">
        {{ category.subCate.name }}
      </xtx-bread-item>
    </transition>
  </xtx-bread>
</template>

逻辑实现:

1. 获取路由的二级类目ID:route.params.id

2.从状态管理中获取存储的一级类目数据,在这里找寻符合id的二级类目,需要配合计算属性查找,因为路由的二级类目ID可以改变,让计算属性依赖于路由的二级类目ID,就可以找到变化的二级类目数据

3.将找到的二级类目和对应的一级类目组合起来,渲染到面包屑组件中

<script setup>
import useCategoryStore from '@/store/category'
import { storeToRefs } from 'pinia'
import { useRoute } from 'vue-router'
import { computed } from 'vue'

const route = useRoute()

const categoryStore = useCategoryStore()
const { list } = storeToRefs(categoryStore)

// 需要的数据对象:category: {topCate: {id: xxx, name: xxx}, subCate: {id: xxx, name: xxx}}
const category = computed(() => {
  const cate = {}

  list.value.forEach((topCategory) => {
    if (topCategory.children) {
      // 从每个一级类目的children下找二级类目
      const subCategory = topCategory.children.find(
        (subCategory) => subCategory.id === route.params.id
      )
      if (subCategory) {
        // 找到二级类目
        cate.topCate = { id: topCategory.id, name: topCategory.name }
        cate.subCate = { id: subCategory.id, name: subCategory.name }
      }
    }
  })

  return cate
})
</script>

 

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表