单页面原理

   Vue是单页面开发,即页面不刷新。

   页面不刷新,而又要根据用户选择完成内容的更新该怎么做?Vue中采用锚点来完成。

   如访问http://127.0.0.1#/index就是主页,而访问http://127.0.0.1#/home就是家目录。

   手动分析url组成与处理视图的切换非常麻烦,所以Vue提供插件Vue-router,它能够根据用户在地址栏中输入的不同链接展示不同的内容,十分的方便。

Vue-router

   打开Vue.js官网,在生态系统中找到vue-router

  

   然后根据它的官方文档进行安装即可:

  

   文档链接

示例演示

   使用<router-view>当作组件的显示容器。

   使用<router-link>与属性toVue切换显示的组件。

   注意查看下图中url的变化,它确实没有刷新。

  

<body>

<div id="app">
<router-view></router-view>
</div> <script src="./vue.js"></script>
<script src="./vue-router.js"></script>
<script>
// 第一步:书写组件
const index = {
template: `
<div>
<h1>欢迎访问主页</h1>
<router-link to="/settings">设置</router-link>
<router-link to="/backend">后台</router-link>
</div>
`
} const backend = {
template: `
<div>
<h1>欢迎访问后台</h1>
<router-link to="/">访问主页</router-link>
</div>
`
} const settings = {
template: `
<div>
<h1>欢迎访问个人设置页面</h1>
<router-link to="/">访问主页</router-link>
</div>
`
} // 第二步:配置路由
// 根据资源请求来展示或隐藏对应的组件
const routes = [
// 主页,/
{path: "/", component: index},
{path: "/backend", component: backend},
{path: "/settings", component: settings},
] // 第三步:实例化路由对象
// {routes:routes}
const router = new VueRouter({routes,}) // 第四步:根组件中添加路由信息
// {router:router}
const app = new Vue({
el: "#app",
router,
})
</script>
</body>

参数相关

参数传递

   在很多情况下,我们都需要用户的请求地址携带一些参数,然后通过这个参数再进行查询。

   比如查询某一本书时,地址栏通常是这样的:

#/book/2

   而且很有可能在今后的<router-link>以及methods中都要使用这个参数,如何操作?

{path: "/book/:id(\\d+)", component: book},  // 转义 \d+

我该如何从Template里拿到参数:{{ $route.params }}

我该如何从Methods里拿到参数:this.$route.params

   示例如下:

  

<body>

<div id="app">
<router-view></router-view>
</div> <script src="./vue.js"></script>
<script src="./vue-router.js"></script>
<script>
// 第一步:书写组件
const index = {
template: `
<div>
<h1>主页</h1>
<input type="text" v-model="book_id" placeholder="输入要查询的图书编号">
<router-link :to="'/book/'+book_id">查询图书</router-link>
</div>
`,
data() {
return {
book_id: 0,
};
}
} const book = {
// 在模板以及方法中,你都可以拿到查询的参数
template: `
<div>
<p>我该如何从Template里拿到参数:{{ $route.params }}</p>
<p>我该如何从Methods里拿到参数:{{ show() }}</p>
<p v-for="item in books" v-if="item.id==$route.params.id">你查询的图书是:{{ item }}</p>
</div>
`,
data() {
return {
books: [
{id: 1, name: "红楼梦", author: "曹雪芹", price: 199,},
{id: 2, name: "西游记", author: "吴承恩", price: 179,},
{id: 3, name: "三国演义", author: "罗贯中", price: 158,},
{id: 4, name: "水浒传", author: "施耐庵", price: 128,},
]
}
},
methods:{
show(){
return this.$route.params
}
}
} // 第二步:配置路由
const routes = [
// 主页,/
{path: "/", component: index},
{path: "/book/:id(\\d+)", component: book}, // 转义 \d+
] // 第三步:实例化路由对象
// {routes:routes}
const router = new VueRouter({routes,}) // 第四步:根组件中添加路由信息
// {router:router}
const app = new Vue({
el: "#app",
router,
})
</script>
</body>

默认参数

   根据RestAPI规范,如果没有携带参数则代表查询所有。

   如果按照上面的示例,直接不带参数进行请求路由将匹配不上,所以我们还需要做一个查询所有的功能。

   其实对上面路由进行改进即可,设置为默认参数:

{path: "/book/:id?", component: book},  // ?代表可以有也可以没有

   示例如下:

  

<body>

<div id="app">
<router-view></router-view>
</div> <!--查询模板-->
<template id="result">
<div>
<table border="1" :style="{borderCollapse:'collapse'}">
<caption :style="{border:'1px solid #000',fontSize:'1.2rem'}">查询结果</caption>
<thead>
<tr>
<th>编号</th>
<th>名称</th>
<th>作者</th>
<th>价格</th>
</tr>
</thead> <!-- 查询一本 -->
<tbody v-if="$route.params.id">
<tr v-for="item in books" v-if="item.id == $route.params.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.author }}</td>
<td>{{ item.price }}</td>
</tr>
</tbody> <!-- 查询所有 -->
<tbody v-else>
<tr v-for="item in books">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.author }}</td>
<td>{{ item.price }}</td>
</tr>
</tbody>
</table>
</div>
</template> <script src="./vue.js"></script>
<script src="./vue-router.js"></script>
<script>
// 第一步:书写组件
const index = {
template: `
<div>
<h1>主页</h1>
<input type="text" v-model="book_id" placeholder="输入要查询的图书编号">
<router-link :to="'/book/'+book_id">查询单个图书</router-link>
<router-link to="/book/">查询所有图书</router-link>
</div>
`,
data() {
return {
book_id: 0,
};
}
} const book = {
// 在模板以及方法中,你都可以拿到查询的参数
template: `#result`,
data() {
return {
books: [
{id: 1, name: "红楼梦", author: "曹雪芹", price: 199,},
{id: 2, name: "西游记", author: "吴承恩", price: 179,},
{id: 3, name: "三国演义", author: "罗贯中", price: 158,},
{id: 4, name: "水浒传", author: "施耐庵", price: 128,},
]
}
},
} // 第二步:配置路由
const routes = [
// 主页,/
{path: "/", component: index},
{path: "/book/:id?", component: book}, // 转义 \d+,?可以有也可以没有
] // 第三步:实例化路由对象
// {routes:routes}
const router = new VueRouter({routes,}) // 第四步:根组件中添加路由信息
// {router:router}
const app = new Vue({
el: "#app",
router,
}) </script>
</body>

路由别名

路由name

   为每一个路由匹配规则添加name属性,使其更方便的在模板以及Js代码中进行跳转:

   如下所示:

    const routes = [
{path: "/", component: index, name: "index"},
{path: "/book/:id?", component: book, name: "query_book"},
]

router-link

   模板中使用router-link与路由的别名name进行跳转时,格式如下:

<!-- 有参数就传递,没有就不传递。注意在to前添加: -->
<router-link :to="{name:'query_book',params:{id:书籍编号}}">查询</router-link>

$route.push

   Js代码中使用跳转时要用到this.$router.push()这个方法,如下所示:

// 模板中的按钮
<button @click="func(书籍编号)">查看详情</button> // js
func(bookId){
// 使用url拼接跳转
let url = {path:"/book/"+bookId};
// 使用别名进行跳转跳转:
// {name:'book_query',params:{id:id}}
this.$router.push(url); // 使用$router.push(url)进行跳转
}

视图布局

视图嵌套

   一个大的组件中可以包含很多小的组件。

   如下所示,有一个school组件,在school组件中你可以查看到当前的teacherclasses

   当然teacherclasses也都是两个组件。

   换而言之,我在school组件中点击查看教师或者班级,我并不希望他跳转到新的页面而是在当前页面的其他位置进行显示其他组件就可以使用路由嵌套。

<div id="app">
<!-- 第一层,展示学校 -->
<router-view></router-view>
</div> # 子组件中的关键代码
<router-view></router-view> # 路由中的代码,第一层的路由匹配第一层的router-view,第二层的路由就在第二层的router-view中显示
path: "/school", component: school, name: "school", children: [
{path: "/school/teacher", component: teacher, name: "teacher"},
{path: "/school/classes", component: classes, name: "classes"},
]

  

<body>

<div id="app">
<!-- 第一层,展示学校 -->
<router-view></router-view>
</div> <script src="./vue.js"></script>
<script src="./vue-router.js"></script>
<script>
// 第一步:书写组件
const school = {
template: `
<div>
<h1>欢迎来到大肥羊学校</h1>
<router-link :to="{name:'classes'}">查看班级</router-link>
<router-link :to="{name:'teacher'}">查看老师</router-link>
<!-- 第二层,展示班级或者教师 -->
<router-view></router-view>
</div>
`
} const teacher = {
template: `
<div>
<ul>
<li v-for="item in teacherMessage">{{item.id}}-{{item.name}}</li>
</ul>
</div>
`,
data() {
return {
teacherMessage: [
{id: 1, name: "王老师",},
{id: 2, name: "张老师",},
{id: 3, name: "李老师",},
]
}
}
} const classes = {
template: `
<div>
<ul>
<li v-for="item in classMessage">{{item.id}}-{{item.name}}</li>
</ul>
</div>
`,
data() {
return {
classMessage: [
{id: 1, name: "一年级一班",},
{id: 2, name: "一年级二班",},
{id: 3, name: "一年级三班",},
]
}
}
} // 第二步:配置路由
const routes = [
{
path: "/school", component: school, name: "school", children: [
{path: "/school/teacher", component: teacher, name: "teacher"},
{path: "/school/classes", component: classes, name: "classes"},
]
},
] // 第三步:实例化路由对象
// {routes:routes}
const router = new VueRouter({routes,}) // 第四步:根组件中添加路由信息
// {router:router}
const app = new Vue({
el: "#app",
router,
}) </script>
</body>

嵌套的问题

   当页面发生变化,如#school/classes跳转到#school/teacherschool组件将会产生复用。

   这代表school的组件声明周期钩子函数不会被重复调用,就可能造成数据更新不及时的问题。

   举一个例子,上述示例中的school欢迎语是欢迎来到大肥羊学校,如果它是钩子函数created()从后端获取的数据,在用户查看#school/classes后跳转到#school/teacher这个时间点中间后端数据发生了改变,变成了欢迎来到小肥羊学校,由于组件复用问题不会再次执行created(),则代表用户依旧看到的是欢迎来到大肥羊学校。如下所示,我们只有手动更新标语才能执行更新,这显然是不符合常理的:

  

<body>

<div id="app">
<router-view></router-view>
</div> <script src="./vue.js"></script>
<script src="./vue-router.js"></script>
<script> // 假设是从后端抓取数据
let schoolTitle = "欢迎来到大肥羊学校"; // 5s后发生改变
setTimeout(() => {
schoolTitle = "欢迎来到小肥羊学校";
}, 5000); // 第一步:书写组件
const school = {
template: `
<div>
<h1>{{ title }}</h1>
<router-link :to="{name:'classes'}">查看班级</router-link>
<router-link :to="{name:'teacher'}">查看老师</router-link>
<p><button @click="updateTitle">更新新的标语</button></p>
<router-view></router-view>
</div>
`,
data(){
return {
title : "",
}
},
created(){
// 假设发送异步请求
console.log("school钩子函数触发了...")
this.title = schoolTitle;
},
methods:{
updateTitle(){
this.title = schoolTitle;
}
}
} const teacher = {
template: `
<div>
<h3>老师太多了,显示不过来...</h3>
</div>
`,
} const classes = {
template: `
<div>
<h3>班级太多了,显示不过来...</h3>
</div>
`,
} // 第二步:配置路由
const routes = [
{
path: "/school", component: school, name: "school", children: [
{path: "/school/teacher", component: teacher, name: "teacher"},
{path: "/school/classes", component: classes, name: "classes"},
]
},
] // 第三步:实例化路由对象
// {routes:routes}
const router = new VueRouter({routes,}) // 第四步:根组件中添加路由信息
// {router:router}
const app = new Vue({
el: "#app",
router,
}) </script>
</body>

解决问题

   如果想解决组件复用钩子函数不执行的问题,我们可以使用watch来监听$route对象,也就是使用watch来监听地址栏变化,当发生变化时就重新获取数据。

   或者使用 2.2 中引入的 beforeRouteUpdate 导航守卫,解决思路如下图所示:

  

  

   代码如下,使用watch进行解决:

    const school = {
template:"...",
data(){
return {
title : "",
}
},
created(){
// 假设发送异步请求
this.getTitle();
},
methods:{
getTitle(){
// 从后端获取数据
this.title = schoolTitle;
}
},
watch:{
$route(to,from){
// to 要跳转的页面
// from 从那个页面进行跳转
this.getTitle();
}
}
}

   使用导航守卫进行解决的代码如下:

    // 第一步:书写组件
const school = {
template:"...",
data(){
return {
title : "",
}
},
created(){
// 假设发送异步请求
this.getTitle();
},
methods:{
getTitle(){
// 从后端获取数据
this.title = schoolTitle;
}
},
beforeRouteUpdate (to, from, next) {
this.getTitle();
}
}

命名视图

   命名视图就是说可以在一个页面上,使用多个<router-view>,相较于路由嵌套的层级关系,它是一种扁平化的设计。

   如,头部导航栏,左侧菜单栏,右边内容块三个组件,都显示在一个页面上,就可以使用命名视图。

   核心代码如下:

# 根组件模板
<div id="app">
<!-- 这里只放个人主页 -->
<router-view></router-view>
<router-view name="menu"></router-view>
<router-view name="show"></router-view>
</div> # Js配置路由,/代表根目录。有三个视图,router-view
path: "/", components: {
default: header, // 如果 view-router没有name属性,则用default
menu: menu,
show: show,
}

  

 <style>
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
header{
height: 45px;
display: flex;
justify-content: space-evenly;
background-color: #ddd;
align-items: center;
}
body>div>div:nth-child(2){
display: inline-flex;
width: 15%;
border: 1px solid #ddd;
height: 1000px;
}
menu>ul{
list-style-type: none;
display: inline-flex;
flex-flow: column; }
menu>ul>li{
margin: 10px 0 0 10px;
}
body>div>div:last-child{
display: inline-flex;
justify-content: center;
border: 1px solid #ddd;
width: 70%;
height: 1000px;
} </style> <body>
<div id="app">
<!-- 这里只放个人主页 -->
<router-view></router-view>
<router-view name="menu"></router-view>
<router-view name="show"></router-view>
</div> <!-- 头部组件 -->
<template id="header">
<div>
<header><span>首页</span><span>新闻</span><span>关注</span><span>链接</span></header>
</div>
</template> <!-- 左侧菜单 -->
<template id="menu">
<div>
<menu>
<ul>
<li>最新</li>
<li>最热</li>
<li>最多评论</li>
</ul>
</menu>
</div>
</template> <!-- 内容区域 -->
<template id="show">
<div>
<section>
<h1>内容区域</h1>
</section>
</div>
</template> <script src="vue.js"></script>
<script src="vue-router.js"></script>
<script> // 第一步:书写组件
const header = {
template: "#header",
} const menu = {
template: "#menu",
} const show = {
template: "#show",
} // 第二步:配置路由
const routes = [
{
// 当你访问主页时,有三个组件扁平显示
path: "/", components: {
default: header, // 如果 view-router没有name属性,则用default
menu: menu,
show: show,
}
} ] // 第三步:实例化路由对象
const router = new VueRouter({
routes, // es6新语法
}) // 第四步:根组件中添加路由信息
const app = new Vue({
el: "#app",
router,
}); </script>
</body>

重定向

redirect

   当你访问一个页面时,可以重定向至另一个页面,如下示例,使用redirect进行重定向。

   访问/doc,重定向到/help中。但是地址栏中显示的还是/help

const routes = [
// 当用户访问doc时,将会跳转到help中,地址栏中显示是help
{ path: "/help", component: help, name: "help"},
{ path: "/doc", redirect: { name: "help" } }
]

alias

   如果你使用alias参数进行匹配,就方便许多了,并且地址栏中显示的是用户输入的值,但是当输入的路径不存在,则不会显示:

  

const routes = [
// 用户输入在alias中的所有路径,都会交给help组件进行处理
{ path: "/help", component: help, name: "help", alias: ["/doc", "/doc.html", "/help.html"] },
]

history模式

   如果你的url中不想有#号的锚点,可开启history模式。

   同时你还需要在后端做相应的配置,参见官方文档:

   点我

切换动画

   相信现在你已经对单页面开发有所了解,单页面开发说白了就是根据url请求的#后的参数不停的更换要显示的组件。

   所以我们可以为这一切换过程加上过渡动画,你可以在其他子组件模板中添加<transition>标签,并自己书写css类或者引用第三方库。

   如下所示:

  

   我这里是单独给每个子组件加的动画:

        // 书写组件
const index = {
template:
`
<transition enter-active-class="animate__animated animate__bounce">
<h1>wecome to index</h1>
</transition>
`, } const backend = {
template: `
<transition enter-active-class="animate__animated animate__bounce">
<h1>wecome to backend</h1>
</transition>
`,
}

   如想了解更多,请参考官方文档。

Vue-router插件使用的更多相关文章

  1. 「vue基础」一篇浅显易懂的 Vue 路由使用指南( Vue Router 上)

    大家好,今天的内容,我将和大家一起聊聊 Vue 路由相关的知识,如果你以前做过服务端相关的开发,那你一定会对程序的URL结构有所了解,我没记错的话也是路由映射的概念,需要进行配置. 其实前端这些框架的 ...

  2. vue.js插件使用(02) vue-router

    概述 vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用.vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来.传统的 ...

  3. vue.js及项目实战[笔记]— 03 vue.js插件

    一. vue补充 1. 获取DOM元素 救命稻草,document.querySelector 在template中标示元素`ref = "xxx" 在要获取的时候,this.$r ...

  4. 深入浅出的webpack构建工具--webpack4+vue+router项目架构(十四)

    阅读目录 一:vue-router是什么? 二:vue-router的实现原理 三:vue-router使用及代码配置 四:理解vue设置路由导航的两种方法. 五:理解动态路由和命名视图 六:理解嵌套 ...

  5. Vue Router的入门以及简单使用

    Vue Router 是Vue官方的路由管理器,是Vue用来实现SPA的插件.它和 Vue.js 的核心深度集成,让构建单页面应用(SPA)变得易如反掌. 基本概念: 路由:是一种映射关系,是 “pa ...

  6. Vue躬行记(8)——Vue Router

    虽然Vue.js未提供路由功能,但是官方推出了Vue Router(即vue-router库),以插件的形式支持.它与Vue.js深度集成,可快速的创建单页应用(Single Page Applica ...

  7. 8. Vue - Router

    一.Vue Router 的使用 JavaScript: 1.创建组件:创建单页面应用需要渲染的组件 2.创建路由:创建VueRouter实例 3.映射路由:调用VueRouter实例的map方法 4 ...

  8. vue Router——基础篇

    vue--Router简介 vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用. vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路 ...

  9. vue使用插件的流程

    1.引入vue 2.引入插件 3.通过vue.use()调用 例子:使用router插件 import Vue from "vue"; import VueRouter from ...

  10. Vue Router详细教程

    1.什么是路由 1.1路由简介 说起路由你想起了什么?路由是一个网络工程里面的术语. 路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动. --- 维基百科 额,啥玩意? 没听 ...

随机推荐

  1. git 克隆项目 与 分支简单操作

    git clone http://abcde.com/myproject/abc.git 克隆远程项目到本地githome文件夹git branch -a 查看所有分支 包括远程和本地 *号开头表示当 ...

  2. SharePoint 2013 开发——SharePoint Designer 2013工作流

    博客地址:http://blog.csdn.net/FoxDave SharePoint Designer 2013为开发者和高级用户提供了两种创建定制工作流的模式: 基于文本的设计器--即我们一直 ...

  3. iOS开发——高级UI&amp;带你玩转UITableView

    带你玩装UITableView 在实际iOS开发中UITableView是使用最多,也是最重要的一个控件,如果你不会用它,那别说什么大神了,菜鸟都不如. 其实关于UItableView事非常简单的,实 ...

  4. EJB理解

    1. 我们不禁要问,什么是"服务集群"?什么是"企业级开发"? 既然说了EJB 是为了"服务集群"和"企业级开发",那么 ...

  5. Java基础学习(二)—数组

    一.数组的概念 定义: 数组是存储同一种数据类型的多个元素的集合. 数组既可以存储基本数据类型,也可以存储引用数据类型. 格式: 格式1: 数据类型[] 数组名; 格式2: 数据类型 数组名[]; 这 ...

  6. C. Maximal Intersection(STL)

    这道题,关键在于怎么求多个区间的交集,使用multiset就可以 分别将 r , l 存在不同的mutiset中. 然后,我们来看一下 是不是 交集的 l 是最大的, 交集的 r 是最小的 #incl ...

  7. Linux常用命令——软件包管理

    Linux常用命令--软件包管理 Linux 模块依赖查询网址http://www.rpmfind.net/ ISO挂载 将所需ISO文件添加到虚拟机 建立挂载文件夹mkdir /mnt/cdrom ...

  8. 为什么访问json接口出现文件下载

    在IE9,10,11下,当服务器端返回数据格式为json,且明确设置Content-Type为”application/json;charset=utf-8“时,会提示文件下载.如图所示: 解决办法是 ...

  9. app性能测试指标

    性能测试在软件的质量保证中起着重要的作用,它包括的测试内容丰富多样.中国软件评测中心将性能测试概括为三个方面:应用在客户端性能的测试.应用在网络上性能的测试和应用在服务器端性能的测试.通常情况下,三方 ...

  10. Python开发——数据类型【集合】

    集合的定义 由一个或多个确定的元素所构成的整体 可变集合 s=set('hello') print(s) # {'e', 'l', 'o', 'h'} s=set(['alex','alex','Lu ...