Vue果果记账-导航栏

388 阅读3分钟

小技巧1:webstorm中reformat可以自动将分号加入,如果没有的话请在seetings-editor-code style中设置

2.png

小技巧2:webstorm中可以设置组建模板,进入settings-file and code templates

image.png

初步导航栏

//index.ts
import Vue from 'vue';
import VueRouter from 'vue-router';
import Money from '@/views/Money.vue';
import Labels from '@/views/Labels.vue';
import Statistics from '@/views/Statistics.vue';

Vue.use(VueRouter);

const routes = [
  {
    path: '/',//默认路径
    redirect: '/money'
  },
  {
    path: '/money',
    component: Money
  },
  {
    path: '/labels',
    component: Labels
  },
  {
    path: '/statistics',
    component: Statistics
  }
];

const router = new VueRouter({
  routes
});

export default router;

在App.vue中使用 <router-view/> 进行页面渲染,使用 <router-link> 进行页面切换

<template>
  <div>
    <router-view/>
    <hr/>
    <div>
      <router-link to="/money">记账</router-link>
      |
      <router-link to="/labels">标签</router-link>
      |
      <router-link to="/statistics">统计</router-link>
    </div>
  </div>
</template>

<router-view>

<router-view> 组件是一个 functional 组件,渲染路径匹配到的视图组件。<router-view> 渲染的组件还可以内嵌自己的 <router-view>,根据嵌套路径,渲染嵌套组件。

其他属性 (非 router-view 使用的属性) 都直接传给渲染的组件, 很多时候,每个路由的数据都是包含在路由参数中。

<router-link>

<router-link> 组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to 属性指定目标地址,默认渲染成带有正确链接的 <a> 标签,可以通过配置 tag 属性生成别的标签.。另外,当目标路由成功激活时,链接元素自动设置一个表示激活的 CSS 类名。

<router-link> 比起写死的 <a href="..."> 会好一些,理由如下:

  • 无论是 HTML5 history 模式还是 hash 模式,它的表现行为一致,所以,当你要切换路由模式,或者在 IE9 降级使用 hash 模式,无须作任何变动。
  • 在 HTML5 history 模式下,router-link 会守卫点击事件,让浏览器不再重新加载页面。
  • 当你在 HTML5 history 模式下使用 base 选项之后,所有的 to 属性都不需要写 (基路径) 了。

添加404页面

//index.ts
{
    path: '*',//*表示匹配其他所有路径
    component: NotFound
}
//NotFound.vue
<template>
  <div>
    <div>当前页面不存在,请检查网址是否存在</div>
    <div>
      <router-link to="/">返回首页</router-link>
<!--      第二种写法<a href="#/">返回首页</a>-->
    </div>
  </div>
</template>

添加导航栏样式

忠告:千万不要在手机上使用fixed定位,本项目使用flex

Layout组件

使用插槽传递内容:

//Layout.vue
<template>
  <div class="nav-wrapper">
    <div class="content">
      <slot/>
<!--      插槽:放置用户传进来的内容-->
    </div>
    <Nav/>
  </div>
</template>

<script lang="ts">
export default {
  name: 'Layout'
};
</script>

<style lang="scss" scoped>
.nav-wrapper {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.content {
  overflow: auto;//超出后自动滚动
  flex-grow: 1; //尽量把所有高度都给content
}
</style>

在Money.vue(以及Labels.vue和Statistics.vue)中使用Layout

<template>
  <div>
    <Layout>
      Money.vue
<!--      向插槽传的内容-->
    </Layout>
  </div>
</template>

<script lang="ts">
export default {
  name: 'Money'
};
</script>

插槽

Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot> 元素作为承载分发内容的出口。

它允许你像这样合成组件:

<navigation-link url="/profile">
  Your Profile
</navigation-link>

然后你在 <navigation-link> 的模板中可能会写为:

<a
  v-bind:href="url"
  class="nav-link"
>
  <slot></slot>
</a>

当组件渲染的时候,<slot></slot> 将会被替换为“Your Profile”。插槽内可以包含任何模板代码,包括 HTML:

<navigation-link url="/profile">
  <!-- 添加一个 Font Awesome 图标 -->
  <span class="fa fa-user"></span>
  Your Profile
</navigation-link>

甚至其它的组件:

<navigation-link url="/profile">
  <!-- 添加一个图标的组件 -->
  <font-awesome-icon name="user"></font-awesome-icon>
  Your Profile
</navigation-link>

如果 <navigation-link> 的 template 中没有包含一个 <slot> 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。

引入SVG

先去阿里巴巴矢量图标库下载需要的icons

svg-sprite-loader -D

安装loader

yarn add svg-sprite-loader -D

注意vue组建里面没有webpack.config.js,所以需要把webpack的配置改到vue.config.js

const path = require('path')

module.exports = {
  lintOnSave: false,
  chainWebpack: config =>{
    const dir = path.resolve(__dirname, 'src/assets/icons')

    config.module
      .rule('svg-sprite')
      .test(/.svg$/)
      .include.add(dir).end() // 包含 icons 目录
      .use('svg-sprite-loader-mod').loader('svg-sprite-loader-mod').options({extract:false}).end()
      .use('svgo-loader').loader('svgo-loader')
      .tap(options => ({...options, plugins: [{removeAttrs: {attrs: 'fill'}}]})).end()
    config.plugin('svg-sprite').use(require('svg-sprite-loader/plugin'), [{plainSprite: true}])
    config.module.rule('svg').exclude.add(dir) // 其他 svg loader 排除 icons 目录

    // config.module
    //   .rule('svg-sprite')
    //   .test(/.(svg)(?.*)?$/)
    //   .include.add(dir).end()
    //   .use('svg-sprite-loader-mod').loader('svg-sprite-loader-mod').options({extract: false}).end()
    //   .use('svgo-loader').loader('svgo-loader')
    //   .tap(options => ({...options, plugins: [{removeAttrs: {attrs: 'fill'}}]}))
    //   .end()
    // config.plugin('svg-sprite').use(require('svg-sprite-loader-mod/plugin'), [{plainSprite: true}])
    // config.module.rule('svg').exclude.add(dir)

  }
}

注意:

.use('svgo-loader').loader('svgo-loader')
      .tap(options => ({...options, plugins: [{removeAttrs: {attrs: 'fill'}}]})).end()

这段代码会把icons本来的颜色覆盖掉从而可以在后续点击时对整个icon进行高亮。如果icon本身带颜色,可以选择去掉这两行代码。

使用active-class进行高亮选择

<template>
  <nav>
    <router-link to="/labels" class="item" active-class="selected">
      <Icon name="label"/>
      标签
    </router-link>
    <router-link to="/money" class="item" active-class="selected">
      <Icon name="money"/>
      记账
    </router-link>
    <router-link to="/statistics" class="item" active-class="selected">
      <Icon name="statistics"/>
      统计
    </router-link>
  </nav>
</template>

...

<style lang="scss" scoped>
  @import "~@/assets/style/helper.scss";
  nav {
    ...
    > .item.selected{
      color: $color-highlight;
    }
  }
</style>

active-class会在对象被点击后加入selected标签,从而后续通过 > .item.selected 调整颜色。