导航栏总结
这次所做的导航栏是为了练习components(组件)layout(布局)responsive(响应式)下拉框(collapse)。
导航栏的特点是:根据导航数据结构渲染,三层目录不定量选项卡,web端与mobile端自适应变化,mobile端导航栏特殊设计。
重点学习的技术包含:组件获取数据的方法和不定项的渲染方法,响应式布局,动画设计,手风琴导航栏。
组件数据获取和渲染
- 组件数据获取
目前项目broading阶段,使用的数据是与后端沟通后的mock数据。组件获取数据的方法是创建时fetch获取,利用cms提供的获取方法,返回具体的Navigation数据。
async fetch(): Promise<void> {
this.navigationList = (await getNavigations()).data
},
- 内层组件数据获取
在需要不定项获取时,利用v-for
中传入列表数据来实现。通常列表中需要重新定义格式时,就在每一行li
中加入一个代入数据的新的组件。
<ul class="navbar-nav">
<li v-for="item in navigationList.navigation" :key="item.id" class="alert-second-nav pl-5 nav-item active">
<a :href="item.link.href" class="nav-link text-black-50">{{ item.link.label }}</a>
<DropDown class="second-nav border-top border-bottom border-dark-50" :data="item.sections" />
<div class="mask-bg w-100 h-100 m-0 p-0"></div>
</li>
</ul>
其中DropDown就是新组件用于接收数据。
具体的接收方法为:
props: {
data: {
type: Array as PropType<NaviSectionEntity[]>,
default(): NaviSectionEntity[] {
return [] as NaviSectionEntity[]
},
},
},
- 数据渲染
数据得到后的渲染中主要就是直接使用$props
原型数据。
特殊的需要注意,区分data
数据与$props.data
的区别。data
数据可以方便的随时更改再获取,但原型不能更改。
响应式布局
这里说到的响应式主要是web端和mobile端的区别。
响应式最重要的几个内容是1.不同端的具体布局2.屏幕大小的监听3.不同端内容的控制。
- 屏幕大小的监听
mounted() {
window.onresize = () => {
// 监听屏幕变化
this.page_width()
}
this.page_width()
}
监听后对屏幕大小做一个规定,判定mobile端与web端的屏幕大小分界,根据分解转化为一个bool值。
page_width() {
// 获取屏幕宽度
const screenWidth = document.body.clientWidth
this.fullWidth = screenWidth >= 1000
}
- 内容显示的控制
在不同端的布局上都加上v-show这个标签属性,该属性用于控制这个标签的内容是否显示。用屏幕的bool值来控制内容是否显示。
<div v-show="fullWidth" id="navbarSupportedContent" class="collapse navbar-collapse">
<ul class="navbar-nav">
</ul>
</div>
特殊的:项目中移动端还会出现点击出现或显示的功能。
<div v-show="!fullWidth && !disMenu" class="collapse navbar-collapse mobile-menu-open">
<ul class="navbar-nav">
</ul>
</div>
动画设计
项目中暂时用到的动画设计只有从右边划入,向右边划出这种。
设计方案就是针对需要使用动画的div外包裹一层transition
标签。
<transition name="mobile-menu-slide-fade">
<div>
</div>
</transition>
具体设计这个动画transition
时,即在style中对该name
的enter,leave
属性进行修改。
.mobile-menu-slide-fade-enter-active {
transition: all 0.5s ease;
}
.mobile-menu-slide-fade-leave-active {
transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
}
.mobile-menu-slide-fade-enter,
.mobile-menu-slide-fade-leave-to {
transform: translateX(300px);
opacity: 0;
}
手风琴导航栏
手风琴指的是列表中点击一行时,行内的下级列表内容全部显示,此时点击另一行时,会把之前全部内容隐藏,显示当前行下的下级列表内容。
项目中用到的手风琴导航栏主要是在移动端的第二层菜单时,希望不再继续往下跳转。
具体实现时的难点是:数据的传递和显示与隐藏的控制。
数据传递和控制上需要注意的是v-for
是可以获得index
值的,通过index
的值可以完成对具体某一行内容的操作与获取。
具体的控制方法有两种实现:
- 对每一项数据内容中加入一个是否显示的字段,该字段中保证在一组同级数据下仅有一个
true
,代表为显示,每次点击事件发生时都会判断全部内容下的该字段,保证字段准确性。 - 在全局data中添加一个
showIndex
字段,初始值为-1,用于记录当前可以显示的某行,如果为-1则为都不显示。每次点击事件发生时都对showIndex
字段进行更新。
本项目因为原数据已经确定,所以使用的第二种实现方法。
<template>
<div>
<ul class="list-unstyled">
<li v-for="(item, index) in $props.data" :key="item.id" :class="[{ active: isShowing(index) }]">
<div class="nav-link text-black-50">
<p class="mb-0" @click="changeShowingIndex(index)">{{ item.title }}</p>
</div>
<ul v-show="isShowing(index)" class="list-unstyled pl-3">
<li v-for="subItem in item.links" :key="subItem.id">
<div class="nav-link text-black-50">
<p class="mb-0">{{ subItem.label }}</p>
</div>
</li>
</ul>
</li>
</ul>
</div>
</template>
<script lang="ts">
import Vue, { PropType } from 'vue'
import { NaviSectionEntity } from '~/apis/cms'
type NaviData = {
showingIndex: number
}
export default Vue.extend({
name: 'MobileSecondNavi',
props: {
data: {
type: Array as PropType<NaviSectionEntity[]>,
default(): NaviSectionEntity[] {
return [] as NaviSectionEntity[]
},
},
},
data(): NaviData {
return {
showingIndex: -1,
}
},
methods: {
changeShowingIndex(index) {
if (this.showingIndex === index) {
this.showingIndex = -1
} else {
this.showingIndex = index
}
},
isShowing(index) {
return this.showingIndex === index
},
},
})
</script>