导航组件

NavItem.vue

目标 Objective

内容 Content

前置 Prerequisite

温故而知新

学以致用

  1. 列表渲染 v-for
  2. . 基于一个数组或对象来渲染一个列表

    . 以数组为例;titles 是数据源;title 是迭代变量,表示数据项,可以是任何有效标识符;index 是数据项索引/位置序号

    . 更多信息,请访问 列表渲染 v-for

    import { ref } from 'vue';
    const  titles = ref([
      { id:0, title: 'Foo' },
      { id:1, title: 'Bar' },
      { id:2, title: 'Baz' },
    ])
    <div v-for="(title, index) in titles" :key = "title.id">
      // 其它内容
      <div>{{ title.title }}</div>
    </div>
  3. 自定义属性 defineProps()
  4. . 父组件 → 子组件传递参数/通信

    . 更多信息,请访问 父传子 defineProps()

    • 子组件 Child.vue
    • . 定义属性参数,暴露接口;这里暴露了一个字符串类型的属性 msg,默认为空;使用变量 props 接受

      const props = defineProps({
        msg: {
          type: String,
          default: ''
        }
      })

      . 在结构中使用属性参数

      <div>{{ msg }}</div>
    • 父组件 Super.vue
    • . 引入子组件、定义属性参数

      import { ref } from 'vue';
      import Child from './components/Child.vue';
      const  msg = ref('hi, there.')

      . 在结构中使用子组件并传递属性参数

      <Child :msg = 'msg'></Child>
  5. 插槽 <slot>
  6. . 从结构上扩展组件功能;以默认插槽为例

    . 更多信息,请访问 插槽 Slot

    • 子组件 Child.vue
    • . 定义插槽

      <div>
        // 组件其它内容
        <slot></slot>
      </div>
    • 父组件 Super.vue
    • . 引入、使用子组件

      import Child from './components/Child.vue';

      . 传递插槽,可以是任意内容;只能使用双标签形式

      <Child>
        <div>
          // 插槽内容
        </div>
      </Child>
  7. 阿里字体图标 iconfont
  8. . 基于矢量图形的字体文件;轻量、兼容性好、易于使用、可定制

    . 更多信息,请访问 阿里字体图标 Iconfont

    • 创建项目
    • 查找图标;添加到购物车
    • 选择使用方式:类 class
    • 生成链接
    • 复制链接到项目 main.css 中并引入
    • @import url(//at.alicdn.com/t/c/font_3859342_qe1ea0cgv5.css);
    • 使用图标:基类和图标类
    • <span class="iconfont icon-home"></span>
  9. 定位 position
  10. . 最好显式的指定较高的层级 z-index,确保位于其它元素上层

    . 更多信息,请访问 固定定位 Fixed粘性定位 Sticky

    • 主导航固定定位在视图底部
    • .main-nav {
        position: fixed;
        bottom: 0;
        z-index: 99;
      }
    • 二级导航粘性定位在视图顶部
    • .menu-nav {
        position: sticky;
        top: 0;
        z-index: 99;
      }
  11. 路由样式
    • 实现
    • . 默认类:.router-link-active、.router-link-exact-active

      . 标签属性类:activeClass、exactActiveClass

      . 路由属性类:linkActiveClass、linkExactActiveClass

    • 作用域
    • . 全局样式、组件样式

    . 更多信息,请访问 路由样式 router style

回顾 Review

. 略

引言 Introduction

. 略

原则 Principle

过程 Procedure

组件化过程
  1. 需求分析 Requirement Analysis
  2. . 明确组件的功能、输入输出,确定组件的使用场景

    . 确定需要暴露的 props 和 emits

  3. 组件设计 Design
  4. . 拆分组件结构,包括模板 template、逻辑 script 和样式style

    . 使用 <slot> 实现内容扩展,提高灵活性

  5. 组件开发 Implementation
  6. . 在 Vue 中,使用 defineProps() 接收父组件传入的数据

    . 使用 defineEmits() 定义子组件向父组件通信的事件

    . 编写组件内部逻辑和交互

    . 完善组件的基本样式

  7. 组件注册 Registration
  8. . 全局注册

    . 在父组件中引入并注册子组件

  9. 组件使用 Usage
  10. . 通过属性绑定传递数据

    . 通过插槽传递内容

  11. 测试 Testing
  12. . 测试组件在不同场景下的表现

    . 开发者工具或第三方工具

  13. 优化 Optimization
  14. . 对组件进行性能优化,如避免不必要的渲染、合理拆分逻辑等

  15. 文档编写 Documentation
  16. . 提供组件的使用说明、API 文档以及示例代码

实操 Drill

熟能生巧

没有规矩不成方圆

  1. 需求分析
  2. . 功能:导航

    . 必选:使用文字提示

    . 可选:其它提示,如图片、字体图标等

  3. 组件设计
  4. . 核心功能:使用 <RouterLink> 配合属性传参 defineProps() 实现

    . 拓展功能:使用插槽 <slot> 实现;采用主流字体图标 Iconfont

  5. 素材准备
  6. . 字体图标:查找并引入字体图标在线链接到主样式文件 app.css

    @import url(//at.alicdn.com/t/c/font_4235521_imop9zdge2.css);
  7. 组件开发
  8. . 遵循 先静态后动态 的开发原则

    . 在项目适当目录创建导航组件 NavItem.vue

    . 静态设计

    <RouterLink class="nav-item" to="/">
      <span>Home</span>
    </RouterLink>

    . 动态设计:使用自定义的属性参数替换静态数据部分

    . 这里直接使用 <RouterLink>;如果使用额外的容器作为根节点,后续的样式选择器要相应调整;必要时,需要使用深度选择器 :deep()

    <RouterLink class="nav-item" :to="item.to">
      <slot></slot>
      <span>{{ item.label }}</span>
    </RouterLink>
    const props = defineProps({
      item: {
        type: Object,
        default: {}
      }
    })
  9. 首页视图 HomeView.vue 中注册并使用导航组件 NavItem.vue 构建主导航菜单
  10. . 引入导航组件、准备数据

    . 这里采用静态数据源 items

    import NavItem from '@/components/NavItem.vue'
    const items = [
      { label: 'Home', icon: 'icon-home', to: '/' },
      { label: 'Menu', icon: 'icon-thlist', to: '/menu' },
      { label: 'Mall', icon: 'icon-shoppingcart', to: '/mall' },
      { label: 'Memeber', icon: 'icon-statis', to: '/memeber' },
      { label: 'Mine', icon: 'icon-account', to: '/mine' }
    ]

    . 额外使用一个 <div> 便于布局

    . 列表渲染导航数据

    . 使用字体图标填充插槽;因为使用插槽,自定义组件必须使用双标签形式

    <div class="nav">
      <NavItem class="nav-item" v-for="item in items" :key="item.label" :item>
        <span class="iconfont" :class="item.icon"></span>
      </NavItem>
    </div>

    . 固定定位在视图底部

    . 提升层级

    . 弹性盒子水平分布导航项

    .nav {
      position: fixed;
      left: 0;
      bottom: 0;
      width: 100%;
      height: var(-main-nav-h);
      z-index: 9;
      display: flex;
      justify-content: space-between;
      align-items: center;
      background-color: #f7f7f7;
      padding: 0 var(--p-m-g);
    }

    . 路由样式:当前导航颜色为蓝色

    组件样式的特点是什么?

    . 默认情况下,父组件只能操作子组件根节点的样式,以便布局

    .router-link-active,
    .router-link-exact-active{
      color: var(--main-color);
    }
    主导航菜单
  11. 菜单视图 MennuView.vue 中引入/注册并使用导航组件 NavItem.vue,构建二级导航菜单
  12. . 引入导航组件、准备数据

    . 这里采用静态数据源 items;注意:嵌套路由 to 应为全路径

    import NavItem from '@/components/NavItem.vue'
    const items = [
      { label: 'Goods', to: '/menu/goods' },
      { label: 'Vip', to: '/menu/vip' },
      { label: 'Rank', to: '/menu/rank' },
      { label: 'Favorite', to: '/menu/favorite' },
    ]

    . 额外使用一个 <div> 便于布局

    . 列表渲染导航数据

    . 没有使用插槽,仅仅展示文字,自定义组件可以使用单标签形式

    <div class="nav">
      <NavItem class="nav-item" v-for="item in items" :key="item.label" :item></NavItem>
    </div>
    

    . 粘性定位在视图顶部:视图滚动时,保持二级菜单始终显式在顶部

    . 提升层级

    . 弹性盒子水平分布导航项

    .nav {
      position: sticky;
      top: 0;
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 0 var(--p-m-g);
    }

    . 路由样式:当前导航底部为蓝色实线

    .nav-item {
      position: relative;
    }
    
    .router-link-exact-active::after {
      content: '';
      position: absolute;
      left: 50%;
      bottom: 0;
      transform: translateX(-50%);
      width: 60%;
      height: 4px;
      background-color: var(--main-color);
    }
    二级导航菜单
  13. 测试 - 略
  14. 优化 - 略
  15. 文档 - 略

小结 Summary

  1. 组件模块化开发的基本原则
  2. 组件模块化开发的基本方法和流程

作业 Homework

  1. 实操:完成导航组件 NavItem.vue 的设计与开发;
  2. 思考:是否可以全部采用插槽的方式封装导航组件?
  3. 拓展:使用模块化开发的思想,优化项目中其它部分;

参考 Reference

  1. 组件 Component
  2. 列表渲染 v-for
  3. 父传子 defineProps()
  4. 子传父 defineEmits()
  5. 双向绑定 defineModel()
  6. 插槽 Slot
  7. 声明式导航 <RouterLink>
  8. 路由样式 style
  9. 阿里字体图标 Iconfont
  10. 弹性盒子 Flex
  11. 定位 Position
  12. 固定定位 Fixed
  13. 粘性定位 Sticky