05月26, 2021

Vue实战(十)-- Vue-Router

十、Vue-Router

再次想来丰富页面,在整个界面的上方加入导航的Header组件

创建Header组件Component

Header组件

<template>
  <div class="header">
      <div class="header-container">
          <div class="container">
            <div class="logo">
              <a href="">
                <img :src="logUrl" alt="">
              </a>
            </div>
            <ul class="nav">
                <li><a href="">首页</a></li>
                <li><a href="">Voluptates.</a></li>
                <li><a href="">Molestiae?</a></li>
                <li><a href="">Ut.</a></li>
                <li><a href="">Nihil.</a></li>
            </ul>
            <div class="user">
                <a href="">登录</a>
                <a href="">注册</a>
            </div>
          </div>
      </div>
  </div>
</template>

<script>
import logo from '@/assets/logo.png'
export default {
  components:{
    logo
  },
  data() {
    return {
      // logUrl:require('@/assets/logo.png')
      logUrl:logo
    }
  },
}
</script>

<style scoped>
.header {
  height: 60px;
}
.header-container {
  height: 60px;
  background: #000;
  color: #fff;
  line-height: 60px;
  position: fixed;
  z-index: 100;
  left: 0;
  top: 0;
  width: 100%;
  display: flex;
}
.container {
  display: flex;
}
.logo a {
  display: flex;
  align-items: center;
  height: 100%;
}
.logo img {
  width: 55px;
  height: 55px;
}
.nav {
  margin: 0 60px;
  display: flex;
  flex-grow: 1;
}
.nav a {
  display: block;
  padding: 0 30px;
}
.nav .router-link-exact-active {
  color: #fcb85f;
}
.user {
  font-size: 14px;
}
.user * {
  margin-left: 10px;
}
.header a {
  color: #fff;
}
</style>

注意这里有一个知识点:

静态图标希望传递给页面参数进行显示,可以直接将图片作为一个组件引入。

当然,还是可以按照原来的方式,通过require('地址')引入

将Header组件加入到App.vue中

<template>
  <div id="app">
    <el-container>
      <el-header>
        <Header />
      </el-header>
      <el-main>
        <Home></Home>
      </el-main>
    </el-container>
  </div>
</template>

<script>
import Home from "./views/Home";
import Header from "./components/Header";
export default {
  name: "App", 
  components:{Header,Home}
};
</script>
<style>
</style>

插槽

这里创建几个单独的页面,比如注册,登录和没有找到页面,这里接入一个新的知识点:插槽

我们可能有这种需要,就是一个组件中,可能需要直接嵌套其他的代码,这个时候,就需要使用插槽Slot

比如我们可以创建一个Center.vue的组件

<template>
  <div class="center">
      <!-- 屏幕中央 -->
      <!-- 插槽,相当于一个预留位置,可以将任意的内容放入到插槽中 -->
      <slot></slot>
  </div>
</template>
<style scoped>
.center{
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%);
}
</style>

可以看到这个组件唯一的作用就是居中,但是具体要将什么内容居中并没有添加,而是预留了一个Slot插槽,到时候,需要将什么内容居中,直接调用Center组件,将需要添加的内容加入进来即可。

创建Login.vue

<template>
  <div>
    <Center>
      <el-form
        :model="ruleForm"
        status-icon
        :rules="rules"
        ref="ruleForm"
        label-width="100px"
        class="demo-ruleForm"
      >
        <el-form-item label="用户名称" prop="name">
          <el-input v-model="ruleForm.name"></el-input>
        </el-form-item>
        <el-form-item label="用户密码" prop="pass">
          <el-input
            type="password"
            v-model="ruleForm.pass"
            autocomplete="off"
          ></el-input>
        </el-form-item>
        <el-form-item label="确认密码" prop="checkPass">
          <el-input
            type="password"
            v-model="ruleForm.checkPass"
            autocomplete="off"
          ></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="submitForm('ruleForm')"
            >提交</el-button
          >
          <el-button @click="resetForm('ruleForm')">重置</el-button>
        </el-form-item>
      </el-form>
    </Center>
  </div>
</template>

<script>
import Center from "@/components/Center";
export default {
  components: {
    Center,
  },
  data() {
    var validatePass = (rule, value, callback) => {
      if (value === "") {
        callback(new Error("请输入密码"));
      } else {
        if (this.ruleForm.checkPass !== "") {
          this.$refs.ruleForm.validateField("checkPass");
        }
        callback();
      }
    };
    var validatePass2 = (rule, value, callback) => {
      if (value === "") {
        callback(new Error("请再次输入密码"));
      } else if (value !== this.ruleForm.pass) {
        callback(new Error("两次输入密码不一致!"));
      } else {
        callback();
      }
    };
    return {
      ruleForm: {
        name: "",
        pass: "",
        checkPass: "",
      },
      rules: {
        name: [
          { required: true, message: "请输入用户名称", trigger: "blur" },
          { min: 3, max: 5, message: "长度在 3 到 5 个字符", trigger: "blur" },
        ],
        pass: [{ validator: validatePass, trigger: "blur" }],
        checkPass: [{ validator: validatePass2, trigger: "blur" }],
      },
    };
  },
  methods: {
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          alert("submit!");
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
  },
};
</script>

<style></style>

可以看到,这个登录页面的template中使用了我们定义Center组件,然后再Center组件中插入了ElementUI相关的页面标签,那么,整个在Center组件中的内容就全部居中对齐了,这就是Slot插槽的作用。

可以把这个Login.vue页面,先放如到App.vue里面进行显示,为了可以看到后面的效果,我们还可以添加几个Views页面,试验马上要讲到的路由Router的效果 比如Reg.vue:

<template>
  <Center>
      注册
  </Center>
</template>
<script>
import Center from '@/components/Center'
export default {
  components:{
      Center
  }
}
</script>
<style>
</style>

NotFound.vue:

<template>
  <Center>
    <img :src="notFound" alt="">
  </Center>
</template>

<script>
import Center from '@/components/Center'
import notFound from '../assets/404.gif'
export default {
  components:{
      Center,notFound
  },
  data() {
    return {
      notFound:notFound
    }
  },
}
</script>
<style scoped>
img{
  width:300px;
}
</style>

页面设计的问题

  • 首页 Home.vue
  • 登录页 Login.vue
  • 注册页 Reg.vue
  • 频道新闻页 ChannelNews.vue
  • 404 NotFound.vue

浏览器无论访问什么地址,访问的真实页面始终是index.htmlvue根据不同的地址,渲染不同的组件。由于真实页面是唯一的,用户看到的页面切换,实际上是组件的切换,这种应用称之为单页应用

开发单页应用涉及到两个核心问题:

  1. 在哪个位置切换组件
  2. 访问路径如何对应组件

vue-router

使用vue-router可以非常轻松的构建单页应用程序

官网地址:https://router.vuejs.org/zh/

安装Vue-router

工程目录下运行下面的代码,安装vue-router

npm i vue-router

步骤

由于路由配置较多,因此常见的做法是将路由单独设置,因此一般会单独创建文件夹进行配置

创建routers文件夹,这个文件夹主要是路由设置

1、创建路由配置文件config.js

这个文件其实就是一个路径对应的配置文件

url路径 ----> view地址

config.js

export default{
    mode:"history",
    routes: [
        {
            path:"/",
            alias:"/index*",
            name:"Home",
            component:()=>import("@/views/Home")
        },
        {
            path:"/login",
            name:"Login",
            component:()=>import("@/views/Login")
        },
        {
            path:"/reg",
            name:"Reg",
            component:()=>import('@/views/Reg')
        },
        {
            path:"*",
            name:"404",
            component:()=>import('@/views/NotFound')
        },
    ]   
}

路由模式:

  • hash:路径来自于地址栏中#后面的值,这种模式兼容性比较好
  • history:路径来自于真实的地址路径,旧浏览器不兼容
  • abstract:路径来自于内存

2、创建index.js文件

这个文件主要就是new VueRouter对象,当然需要把配置的内容导入进来

import Vue from 'vue'
import VueRouter from 'vue-router'
import config from './config'; //路由一般配置较多,所以一般单独配置

//1.安装
Vue.use(VueRouter);

//2.创建路由对象
var router = new VueRouter(config);

export default router;

3、在main.js中引入

-w1044

这里的引入直接使用的是./routers直接指定的是文件夹,实际上其实是指定的./routers/index.js,在vue中约定,如果不指定具体的文件,那么就会直接指向这个文件夹下面的index.js文件

有了路由的配置之后,就需要确定,哪些地方需要使用到路由

-w1304

在Header上点击不同的链接,相当于就需要跳转切换不同的页面,这也就是路由最大的作用,帮助我们在单页应用中实现页面的跳转

从上图中可以看出,下面的部分都是需要通过路由去进行切换的,因此,确定要需要切换的部分,在页面上使用 路由标签:<router-view></router-view>,确定这部分的内容是需要进行路由切换的。 实际上,就是通过js由原来生成的虚拟DOM,切换成另外一个虚拟DOM

所以接下来,我们需要两步修改 1、将页面下面的部分,替换为路由标签 App.vue

<template>
  <div id="app">
    <el-container>
      <el-header>
        <Header />
      </el-header>
      <el-main>
        <!-- <Home></Home> -->
        <!-- 使用路由标签 -->
        <router-view></router-view>
      </el-main>
    </el-container>
  </div>
</template>
...下面的部分省略

2、修改Header组件a标签链接为路由配置的地址

......其他部分省略
<ul class="nav">
    <li><a href="/index">首页</a></li>
    <li><a href="">Voluptates.</a></li>
    <li><a href="">Molestiae?</a></li>
    <li><a href="">Ut.</a></li>
    <li><a href="">Nihil.</a></li>
</ul>
<div class="user">
    <a href="/login">登录</a>
    <a href="/reg">注册</a>
</div>
......

上面使用的还是a标签,a标签最大的问题是页面会发生刷新,导致整个页面都会重新渲染,所以,我们使用router的专属标签<router-link>进行替换

<ul class="nav">
    <li>
      <!-- 声明式导航,直接跟路径 -->
      <router-link to="/index">首页</router-link>
    </li>
    <li><a href="">Voluptates.</a></li>
    <li><a href="">Molestiae?</a></li>
    <li><a href="">Ut.</a></li>
    <li><a href="">Nihil.</a></li>
</ul>
<div class="user">
    <!-- 命名式导航,:to后面是config中配置的路由的名字 -->
    <router-link :to="{name:'Login'}">登录</router-link>
    <router-link :to="{name:'Reg'}">注册</router-link>
</div>

首页后面几个可以跟上频道信息,点击频道,就切换到这个频道的新闻列表,那么就会出现问题:切换频道需要传递频道ID,意味着这里的这里需要路由传值

路由传值

首先加入新闻列表的页面 ChannelNews.vue:

<template>
  <Center>
      新闻页面,频道id是===>{{$route.params.id}}
  </Center>
</template>
<script>
import Center from '@/components/Center'
export default {
  components:{
    Center
  }
}
</script>
<style>
</style>

当然这个页面最重要的是获取频道id,可以通过路由的代码{{$route.params.id}}得到,但是要得到这个传递过来的值,还需要做一些工作 1、配置相关路由,表示需要路由传值 2、父组件传递相关的值

所以,首先在路由的config.js中加入下面映射关系的代码

...
{
    path:"/channels/:id",
    name:"Channels",
    component:()=>import('@/views/ChannelNews')
},
...

这段代码的意思就是url地址是/channels/频道id,而这个id是需要父页面传递过来的

因此,现在Header组件中,需要加载频道的内容

<template>
  <div class="header">
      <div class="header-container">
          <div class="container">
            <div class="logo">
              <a href="">
                <img :src="logUrl" alt="">
              </a>
            </div>
            <ul class="nav">
                <li>
                  <!-- 声明式导航,直接跟路径 -->
                  <router-link to="/index">首页</router-link>
                </li>
                <li v-for="(channel) in channels.slice(0,5)" :key="channel.channelId">
                  <router-link
                  :to="{
                    name:'Channels',
                    params:{
                      id:channel.channelId
                    }
                  }">
                    {{channel.name}}
                  </router-link>
                </li>

            </ul>
            <div class="user">
                <!-- 命名式导航,:to后面是config中配置的路由的名字 -->
                <router-link :to="{name:'Login'}">登录</router-link>
                <router-link :to="{name:'Reg'}">注册</router-link>
            </div>
          </div>
      </div>
  </div>
</template>

<script>
import logo from '@/assets/logo.png'
import {getNewsChannels} from '@/services/NewsService';
export default {
  components:{
    logo
  },
  data() {
    return {
      // logUrl:require('@/assets/logo.png')
      logUrl:logo,
      channels:[]
    }
  },
  async created() {
    let resp = await getNewsChannels();
    this.channels = resp;
  },
}
</script>

<style scoped>
...省略
</style>

当然关键代码就是created()函数获取值的部分,与网页模板中对于频道值的循环

本文链接:http://www.yanhongzhi.com/post/VueInAction-10.html

-- EOF --

Comments