第5章 Vue 路由
5.1 初始路由 & vue-router
初识路由
后端路由,每次跳转都会重新访问服务器,会有延迟
前端路由,访问新页面的时候仅仅是变换了一下 hash 值而已,无延迟,提升用户体验
后端路由 | 前端路由 |
---|---|
http://localhost/home | /index.html#/home |
http://localhost/about | /index.html#/about |
vue-router
官方推出的路由管理器,用来管 URL
有两个模式,hash 模式有 #,不美观,而 history 模式没有
使用 URL 的 hash 来模拟一个完整的 URL,当 URL 改变时,页面不会重新加载
# 就是 hash 符号,中文名为哈希符或锚点,哈希符后的值 叫哈希值
三个基本概念 | |
---|---|
route | 表示一条路由 |
routes | 表示一组路由 |
router | 是一个机制,充当路由管理者的角色,找到组里对应的路由 |
vue-router 的基本使用
v u e − r o u t e r 的 基 本 使 用 vue-router\ 的基本使用 vue−router 的基本使用
<script src="vue-router.js"></script>
<div id="app">
<router-link to='/login' tag='span'>to login</router-link> //不改 tag 默认 a
<router-view></router-view> // 占位符
</div>
<script>
var login = { template: '<h3>login component<h3>' }
var routerObj = new VueRouter({
// mode: 'history', // 不改默认 hash 模式,改后组件会闪过。。需要开启服务器对 history 的支持
routes: [
{path: '/login', component: login} // 配置路由匹配规则,对应 path 去往对应组件
]
})
var vm = new Vue({
el: '#app',
router: routerObj // 路由规则对象注册到 vm 上
})
</script>
// 总结:router-link 的 to 与 routes 里 path 匹配时,才会在 router-view 中显示 component
路由对象的属性
this.$router,全局路由对象
this.$route,当前路由对象
5.2 用户注册案例
用户注册案例
1. 准 备 工 作 1.\ 准备工作 1. 准备工作
// login_test 目录下
npm init -y // 生成 package.json
npm install vue@2.6.x vue-router@3.1.x // 生成 package-lock.json
npm install webpack@4.39.x webpack-cli@3.3.x webpack-dev-server@3.8.x html-webpack-plugin@3.2.x -D
// -D 表示安装到本地开发依赖 等于 --save-dev
"dev": "webpack-dev-server --inline --hot --port 8088"
npm install vue-loader@15.7.x vue-template-compiler@2.6.x -D
npm install css-loader@3.2.x style-loader@1.0.x -D
npm install less less-loader -D // (可不安装) 使用时 <style lang="less/scss/stylus"></style>
npm install sass-loader@7.2.x node-sass@4.12.x -D // 项目中用到的 安装时出了问题
npm install stylus stylus-loader -D // (可不安装) 无需配置
npm install url-loader@2.1.x file-loader@4.2.x -D
安装 node-sass 时出现问题
npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
然后成功安装了一个 5.0.0 版本的,根据报错找到这个地方改一下就完事了(问题成功解决.)
接下来安装 MUI,下载后解压出来,然后新建 lib/mui 目录,将 dist 下的 css 和 fonts 目录复制进去
然后在 webpack.config.js 中添加配置,修改了两处:rules 和 plugins
// 新建 webpack.config.js 文件
const htmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin') // vue-loader
module.exports = {
entry: './main.js', // 配置入口文件
output: { // 配置输出文件,设为当前路径,指定文件名
path: __dirname,
filename: 'bundle.js'
},
resolve: {
alias: {
'vue': 'vue/dist/vue.js' // vue.js 文件路径配置
}
},
devServer: {
historyApiFallback: true // 开启服务器对 history 模式支持
},
module: {
rules: [ // 模块规则
{
test: /\.vue$/,
use: 'vue-loader'
},
// more rules
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.(jpg|png|gif|bmp|jpeg)$/,
use: 'url-loader'
},
{
test: /\.(ttf|eot|svg|woff|woff2)$/,
use: 'url-loader'
},
]
},
plugins: [ // 插件
new htmlWebpackPlugin({
template: 'index.html' // 为 index.html 自动引入打包好的 bundle.js
}),
new VueLoaderPlugin()
]
}
2. 代 码 实 现 2.\ 代码实现 2. 代码实现
// 创建 index.html 首页
<div id="app"></div>
// 创建 main.js 逻辑入口
import Vue from 'vue'
import app from './App.vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import router from './router.js'
import './lib/mui/css/mui.css'
new Vue({
el: '#app',
render: c => c(app),
router
})
// 创建 route.js 一个单独的路由文件
import VueRouter from 'vue-router'
import Login from './components/Login.vue'
import Register from './components/Register.vue'
var router = new VueRouter({
mode: 'history', // 后来开 history 模式
linkActiveClass: 'my-active',
linkExactActiveClass: 'my-exact-active', // 这两行是自定义类名 对应 App.vue 导航菜单样式
routes: [
{path: '/', redirect: '/login'},
{path: '/login', component: Login},
{path: '/register', component: Register}
]
})
export default router
// 创建 APP.vue 渲染路由文件
<template>
<div id="app">
<div class="login-container">
<router-link to="/login" tag="span">login</router-link>
<router-link to="/register" tag="span">register</router-link>
</div>
<router-view></router-view>
</div>
</template>
<style lang='scss' scoped>
.login-container {
display: flex;
justify-content: center;
padding-top: 10px;
span {
padding: 5px 20px;
border-radius: 5px;
font-size: 16px;
}
}
.my-active, .my-exact-active { // 导航菜单样式 对应 router.js 文件里自定义的类名
background: #007aff;
font-weight: 800;
color: #fff;
}
</style>
// 创建 Login.vue 编写登录界面
<template>
<div class="login">
<div class="content">
<form class="mui-input-group login-form">
<div class="mui-input-row">
<label>账号</label>
<input type="text" class="mui-input-clear mui-input" placeholder="请输入账号">
</div>
<div class="mui-input-row">
<label>密码</label>
<input type="text" class="mui-input-clear mui-input" placeholder="请输入密码">
</div>
</form>
<div class="mui-content-padded">
<button type="button" class="mui-btn mui-btn-block mui-btn-primary">登录</button>
</div>
</div>
</div>
</template>
<script>
export default {
data () { return {} }
}
</script>
<style scoped>
.login-form {margin: 30px 0; background-color: transparent;}
.mui-input-group .mui-input-row {margin-bottom: 10px; background: #fff}
.mui-btn-block {padding: 10px 0;}
</style>
// 创建 Register.vue 编写注册界面
<template>
<div class="register">
<div class="content">
<form class="mui-input-group login-form">
<div class="mui-input-row">
<label>账号</label>
<input type="text" class="mui-input-clear mui-input" placeholder="请输入账号">
</div>
<div class="mui-input-row">
<label>密码</label>
<input type="text" class="mui-input-clear mui-input" placeholder="请输入密码">
</div>
<div class="mui-input-row">
<label>密码确认</label>
<input type="text" class="mui-input-clear mui-input" placeholder="请确认密码">
</div>
<div class="mui-input-row">
<label>邮箱</label>
<input type="text" class="mui-input-clear mui-input" placeholder="请输入邮箱">
</div>
</form>
<div class="mui-content-padded">
<button type="button" class="mui-btn mui-btn-block mui-btn-primary">注册</button>
</div>
</div>
</div>
</template>
<script>
export default {
data() { return {} }
}
</script>
<style scoped>
.register-form {
margin: 30px 0;
background-color: transparent;
}
.mui-input-group .mui-input-row {
margin-bottom: 10px;
background: #fff;
}
.mui-btn-block { padding: 10px 0; }
</style>
// 启动项目
npm run dev
运行成功:
5.3 动态路由
动态路由
如果切换的组件变动不大,只改几个参数,那就没必要销毁再重建组件,用动态路由改参数
使用 query 方式传参,数据以查询字符串的形式显示在地址栏中,不保密(参数在 ? 后)
用 params 方式传参,搭配 history,将参数放在路径中或隐藏
<div id="app">
<router-link to='/login' tag='span'>to login</router-link>
<router-view></router-view>
<router-link to='/user?id=10&name=admin'>Login</router-link>
<router-view></router-view>
<router-link to='/user/10/admin'>login</router-link>
<router-view></router-view> // 注意,router-view 没设 name 都是 default 视图
</div>
<template id="usertemp">
<div>
<h3>id: {{this.$route.query.id}} name: {{$route.query.name}} </h3>
<h3>id: {{$route.params.id}} name: {{$route.params.name}} </h3>
</div>
</template>
<script>
var login = { template: '<h3>login component</h3>' }
var user = { template: '#usertemp', }
var router = new VueRouter({
// mode: 'history',
routes: [
{path: '/login', component: login}, // 配置路由匹配规则,对应 path 去往对应组件
{path: '/user', component: user},
{path: '/user/:id/:name', component: user}
]
})
var vm = new Vue({
el: '#app',
router: router // 路由规则对象注册到 vm 上,此时可简写为 router
})
</script>
5.4 嵌套路由
嵌套路由
组件模板里嵌套路由,需要在 children 里列出相应的路由规则
<router-link to='/about' tag='li'>关于公司</router-link>
<template id="about-tmp">
<div class="about-detail">
<h1>xxx能力无限公司</h1>
<router-link to='/about/detail'>公司简介</router-link>
<router-link to='/about/governance'>公司治理</router-link>
<router-view></router-view>
</div>
</template>
// 组件对象
var about = { template: '#about-tmp' }
var detail = { template: '<p>xxx是全球领先的公司</p>'}
var governance = { template: '<p>治理嘛</p>'}
// routes 路由规则
{path: '/', redirect: '/about'}, // 路由重定向,即默认显示
{path: '/about', component: about, children: [
{path: 'detail', component: detail},
{path: 'governance', component: governance}
]} // 就是多个 children 列表
5.5 命名路由 & 命名视图
命名路由
不管路径多长,只要 name 匹配了就行
<router-link :to='{name: "user", params: {id:123}}'>LOGIN</router-link>
{path: '/user/:id', name: 'user', component: user}
// 注意:name 重复会有警告
命名视图
router-view 加个 name,可以分别用来显示组件了,不加默认 name = default
<router-link :to='{name: "user", params: {id:123}}'>LOGIN</router-link>
<router-view></router-view>
<div class="container">
<router-view name='left'></router-view>
<router-view name='main'></router-view>
</div>
var header = {template: '<h1 class="header">header</h1>'}
var sidebar = {template: '<h1 class="sidebar">sidebar</h1>'}
var mainBox = {template: '<h1 class="main">mainBox</h1>'}
{path: '/user/:id', name: 'user', component: user},
{path: '/', components: {'default': header, 'left': sidebar, 'main': mainBox}}
5.6编程式导航
编程式导航
就是不用 router-link 进行页面切换(也叫声明式导航)
编程式导航就是直接调用 $router.push()
方法(点击 router-link 就是调用该方法)
<template id="usertemp">
<div>
<h3>id: {{this.$route.query.id}} name: {{$route.query.name}} </h3>
<h3>id: {{$route.params.id}} name: {{$route.params.name}} </h3>
<p>用户名:{{this.$route.query.name}} </p>
<p>用户名:{{this.$route.params.name}} </p>
</div>
</template>
var user = { template: '#usertemp' }
var router = new VueRouter({
// mode: 'history',
routes: [
{path: '/login', component: login},
{path: '/user', component: user},
{path: '/user', name: 'user', component: user}
]
})
methods: {
goStart() {
this.$router.push( { path: '/user', query: {name: 'admin'} } )
this.$router.push( { name: 'user', params: {name: 'admin'} } ) // 注意 params 与 path 一起用时会被屏蔽
}
}
router.replace & router.go
$router.replace 同 $router.push,不过不向 history 栈中添加记录,就是没有历史,直接替换当前页面
$router.go 就是网页中的前进后退,参数 +x 表示前进 x 页,-x 表示后退 x 页
E n d . End. End.