十、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.html
,vue
根据不同的地址,渲染不同的组件。由于真实页面是唯一的,用户看到的页面切换,实际上是组件的切换,这种应用称之为单页应用
开发单页应用涉及到两个核心问题:
- 在哪个位置切换组件
- 访问路径如何对应组件
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中引入
这里的引入直接使用的是./routers
直接指定的是文件夹,实际上其实是指定的./routers/index.js
,在vue中约定,如果不指定具体的文件,那么就会直接指向这个文件夹下面的index.js文件
有了路由的配置之后,就需要确定,哪些地方需要使用到路由
在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()函数获取值的部分,与网页模板中对于频道值的循环
Comments