插件的使用(1)
# 编写vue插件
安装第三方插件时, 都需要在main.js中调用下.
比如: Vue.use(ElementUi);
Vue.use(Vuex);
Vue.use(Router);
这样就算是完成了对三个插件的安装.
我们就可以在组件中调用 this.$router
、this.$route
、this.$store
、this.$alert()
(ElementUI的弹窗组件)参数(方法)
Q: 同样是插件, 为什么有些插件要有install方法才能正常运行(如VueRouter),有一些却可以没有install方法也可以使用(如axios)
A: 简单来说,因为axios是基于Promise封装的库,是完全独立于Vue的,根本不需要挂载在Vue上也能实现发送请求.
而因为VueRouter需要为我们提供$router、$routers之类的属性,要依赖与Vue或者操作Vue实例才能实现.
★★★ Vue.use实际上就是Vue实例与插件的一座桥梁
使用插件就是用于增强Vue!!
参考文档(了解即可!): https://juejin.cn/post/6844903946343940104#heading-7
2
3
4
5
6
7
那我们自己如何编写一个插件呢?
Step1: 在vue项目的src目录下新建 plugins文件夹, 在plugins文件夹下创建 index.js文件
Step2: 在plugins/index.js中编写代码, 定义插件
Step3: 在main.js中写两行代码使用插件!
# plugins/index.js
1> 可通过Vue.prototype在vue原型上放 变量、函数/方法等. 这些变量函数都可以在所有的vm、vc上使用!!
特别注意, 示例中只涉及到对Vue.prototype中变量的使用!! 更新啥的就别探究了,用到了再说. Ps: 若需要对Vue.prototype中里放的变量进行修改的话, 需考虑下该变量的类型..
我实验了下, 发现更新后, 页面上的插值不能响应式的更新.我找了半天也没解决. 需要用的时候再说吧!!
import Vue from "vue";
import axios from "axios";
export default {
// install: function (a) {}
// 其实这个a参数就是vue实例!!
install(a) {
// ★ 在vue原型上放东西 所有vc和vm都可以用hello
Vue.prototype.func1 = () => {
console.log("This is func1");
};
Vue.prototype.s_value = "bingo"
Vue.prototype.dict_obj = {'number': 30}
Vue.prototype.arr_obj = [1]
// ★ 定义混入,一旦该插件在main.js中使用后,所有vc和vm都可调用混入里的东西
Vue.mixin({
data() {
return {
city: '成都',
age: 20,
};
},
methods: {
// 注意一点哦, 若组件的data里定义了age,会优先使用组件里的.
myMixin() {
console.log(this.city, this.name, this.age)
}
},
});
// ★ 可以将axios对象放到vue实例上 - 开发中是会这样使用!!
Vue.prototype.$http = axios
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
可细细揣摩下 Vue中Vue.prototype
: 参考文档 - https://juejin.cn/post/6978378505402712071
# main.js
通过
Vue.use()
使用插件!!
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
import plugins from "@/plugins"
// 需要写在 new Vue({}) 之前.
Vue.use(plugins)
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# App.vue
在实例被创建之前就可以使用 插件里的东西!!
<template>
<div id="father">
{{ s_value }}
<button @click="myMixin">点击触发mixin</button>
</div>
</template>
<style>
#father {
width: 500px;
margin: auto;
padding: 10px;
border: 1px solid bisque;
}
</style>
<script>
export default {
name: 'App',
data() {
return {
name: "app",
age: 21,
}
},
methods: {},
// ★★★ 在实例被创建之前就可以使用!!
/* 小插曲(不是很重要,暂且不必深究):
你可发现,在生命周期beforeCreate函数里修改s_value的值,插值中可以成功渲染.
但当开始创建vue实例/也就是生命周期beforeCreate函数往后, 修改其值,插值里的数据不会同步更新.
* */
beforeCreate() {
this.func1()
console.log(this.s_value, this.dict_obj, this.arr_obj)
this.s_value = "1111"
},
created() {
this.$http.get('http://127.0.0.1:8000/user/').then(res => {
console.log(res)
this.s_value = "222"
})
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 样式库
web端/pc端 常用第三方库: Element、iview
小程序、安卓app 常用第三方库: uni-app、vant 通常会两个结合一起使用.uni-app的项目案例:
https://uniapp.dcloud.net.cn/case.html#
iview https://www.iviewui.com/view-ui-plus/guide/introduce
element-ui https://element.eleme.cn/#/zh-CN
vant3 https://vant-contrib.gitee.io/vant/v3/#/zh-CN/home (app移动端的ui组件库)
vue2和vue3引入jQuery和bootstrap3 参考文档: https://www.cnblogs.com/Neeo/articles/17311333.html
2
3
4
5
# bootstrap
▲ 安装
# -- 执行两条命令 通过cnpm安装jquery和bootstrap@3
# -S 代表在myfirstvue目录下生效,会加到项目的package.json中!!
One_Piece@DC的MacBook myfirstvue % cnpm install jquery -S
One_Piece@DC的MacBook myfirstvue % cnpm install bootstrap@3 -S
▲ 使用
# -- step1: 在项目的入口文件main.js中写代码
# 已经安装的模块,不需要写路径,直接导入即可!自定义的模块的导入才需要写路径!
import 'bootstrap'
import 'bootstrap/dist/css/bootstrap-theme.min.css'
# -- 因为bootstrap依赖于jquery,还要对jquery进行配置
# step2: 在项目根目录的 vue.config.js文件中写入代码,没有该文件就新建
const webpack = require("webpack");
module.exports = {
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
"window.$": "jquery",
Popper: ["popper.js", "default"]
})
]
}
};
# ★ 注意:
# 虽然在vue中引入了jquery,但往往不会通过jquery的ajax发送请求!!若非要用,会在created(){$.ajax()}周期中使用!!
# vue是不推荐使用jquery的,但像bootstrap等第三方库依赖于jquery.. So,要使用bootstrap就得在vue中配置jquery.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Element-Ui
每啥好说的, 看官方文档就行.
https://element.eleme.cn/#/zh-CN/component/installation
Ps: vue3.0版本 用的升级版 element plus
▲ 安装
# -- 开发项目建议直接安装,不建议使用CDN
One_Piece@DC的MacBook myfirstvue % cnpm install element-ui -S
▲ 引入
# -- 在项目的入口文件中写以下代码
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
直接copy官网的示例代码就可以使用啦!!
2
3
4
5
6
7
8
9
10
11
12
# Element Plus
每啥好说的, 看官方文档就行.
https://element-plus.org/zh-CN/guide/quickstart.html#完整引入
Ps: vue3.0版本 用的升级版 element plus
▲ 安装
安装element-plus
npm install element-plus --save (建议,通过该命令安装的版本新一些)
▲ 引入 main.js
import {createApp} from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
createApp(App).use(store).use(router).use(ElementPlus).mount('#app')
2
3
4
5
6
7
8
9
10
11
12
13
# vue-route
此篇幅会阐述vue-route的必备操作 (使用的是vue3的语法)
vue-route的应用场景 (我问的chatgpt4,简单概括下)
1> SPA单页面应用
构建单页应用,其中所有页面都在同一个HTML页面中加载,通过Vue Router进行路由切换,实现无刷新的页面切换和导航效果.
2> 可以配置url参数,配置这玩意儿有何用?
- 搜索功能、分页功能、筛选功能、排序功能等
Ps:就分页而言,前后端不分离分页器是后端实现的,前后端分离的话,分页器是前端实现的.
"内心os: 哇,别比较了,在学前后端分离,那就先别想着前后端分离.. 以前后端分离为主!!学完后,再对比加深记忆!! :-D"
3> 多级嵌套的路由
4> 导航守卫
2
3
4
5
6
7
8
9
vue是单应用/单页面开发, 可以简单理解就是App.vue组件!! 项目不管访问什么地址一直都在展示App.vue!!
既然展示的都是App.vue,那么为啥不同地址呈现的App.vue不一样呢?是因为路由的配置.
根据路由index.js的配置,配置中一级路由对应的组件通通都会渲染到 App.vue的<router-view>
处!!
若一级路由有children属性,那么该属性下配置的二级路由都会渲染到当前一级路由对应组件的<router-view>
处!!
# 安装
安装好后, 让我们来看看默认展示的页面样子是什么样的, 其内部的逻辑又是如何的.
方式一: npm install vue-route --save
并需手动创建文件和进行配置
方式二: vue add router
(会自动创建文件并进行配置) 选它!!
执行vue add router命令后,会让你选择是否使用历史模式.
使用历史模式(history mode)的路由器允许您在Web应用程序中使用普通、清晰的URL,而不是默认的基于哈希(hash-based)的URL.
这意味着您可以使用类似`www.example.com/home`而不是`www.example.com/#/home`这样的URL.
2
3
来, 理一理逻辑!! about路由的逻辑跟home路由的逻辑一样, 就未在截图中标注.
★ 简单来说, App.vue会根据<router-link>
跳转的地址去router/index.js配置文件中找到相应路由,
并将该路由对应的模版渲染到<router-view />
的位置!!
补充一下: <router-link>
会被渲染成a标签.. <router-view />
是为路由渲染的组件占位.
# 快速上手
粘贴下关键代码截图, 截图下面是完整代码
src/router/index.js
import {createRouter, createWebHistory} from 'vue-router'
import HomeView from '../views/HomeView.vue'
const routes = [
// 想要路由后缀为空,或者/home的时候,都跳转到HomeView组件,注意一点,name的设置不能一样!
{
path: '/',
name: 'Index',
component: HomeView
},
{
path: '/home',
// 习惯于将首字母大写区分name和path,当然你写成小写也没问题
name: 'Home',
component: HomeView
},
{
path: '/course',
name: 'Course',
component: () => import('../views/CourseView.vue')
},
{
path: '/news',
name: 'News',
component: () => import('../views/NewsView.vue')
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
src/App.vue
<template>
<div>
<div class="menu">
<div class="container">
<router-link to="/">源代码教育</router-link>
<router-link to="/home">首页</router-link>
<router-link to="/course">课程</router-link>
<router-link to="/news">咨讯</router-link>
</div>
</div>
<div class="container">
<router-view/>
</div>
</div>
</template>
<style>
body {
margin: 0;
}
.menu {
height: 48px;
line-height: 48px;
background-color: #499ef3;
}
.container {
width: 980px;
margin: 0 auto;
}
.menu a {
display: inline-block;
color: white;
text-decoration: none;
padding: 0 10px;
}
</style>
<script setup>
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
src/views/HomeView.vue
src/views/CourseView.vue
src/views/NewsView.vue
<!-- src/views/HomeView.vue -->
<template>
<div><h1>首页</h1></div>
</template>
<script>
export default {
name: 'HomeView',
}
</script>
<!-- src/views/CourseView.vue -->
<script>
export default {
name: "CourseView"
}
</script>
<template>
<div><h1>课程页面</h1></div>
</template>
<!-- src/views/NewsView.vue -->
<script>
export default {
name: "NewsView"
}
</script>
<template>
<div><h1>新闻页面</h1></div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# URL传值 - query
若我在url中通过?的形式get传递参数,要解决两个问题:
1> 前端/vue 如何携带? -- 点击后,url发生变化.
2> 对应 界面/vue组件 如何接收? -- url对应的组件应该接受url中携带的数据.
敲黑板
在前后端不分离的业务场景中,也可以在url中通过?的形式get传递参数,当然也可以使用动态路由,将参数写在路由中,.
最后进行路由匹配,执行对应的是视图函数,在函数中处理业务逻辑,将数据渲染到对应模版中,再将渲染好的模版返回给前端!!
★ 再次提醒: 在Django中的dtl模版语法,渲染是在后端完成的!! 而此处vue中{{name}}的渲染是由客户端浏览器来完成的!!
也就是说,你得清楚的明白一点,前后端分离的开发场景中.在浏览器地址栏中输入一个网址,展示出的页面是浏览器渲染出来的.
2
3
4
稍微了解下vue2中的路由 以及 vue3路由的变化
在vue2中,有 $router 和 $route
参考链接: https://juejin.cn/post/7006289875674595365
- this.$router 表示一个全局的路由对象,vue-router 的实例,提供addRoutes、back等方法,相当于一个路由的管理者角色
this.$router.push('/home') : 进行路由跳转到路径为 /home 的路由
- this.$route表示当前路由对象,包含具体的路由名称、path、query 、params 等属性.
其实就是routes(new Router时声明的routes)里面的一条具体的路由
this.$route.path : 只返回路径部分,不包括查询参数或哈希片段.
this.$route.fullPath : 获取完整的URL(包括查询参数和哈希片段)
this.$route.query : 对象,包含路由中query参数的键值对. 如“..?name=qq&age=18”会得到{name:"qq","age":18}
以 CompositionAPI 的方式来使用 VueRouter 首要注意的就是无法在 setup 中通过 this 来访问组件实例!!
由于 setup 中无法通过 this 来访问组件实例, 因此无法直接访问 this.$router 与 this.$route !!
作为替代,我们可使用 useRouter 函数来分别获取路由记录对象(route)和路由器对象(router) !!
const {route, router} = useRouter();
2
3
4
5
6
7
8
9
10
11
12
13
14
url传值过程中, 必遇到的问题: vue 同一路由跳转同一路由,页面不刷新,why? *(结合下面 url动态参数 那里红色文字的阐述来理解!!).
vue官网详细解释说明使用同一路由携带不同参数,本质上是<重用相同的组件实例>.
默认在跳转路由时会采用缓存策略,并不会刷新当前路由组件因此不会调用组件的生命周期挂钩.
此处解释下何为复用: 同一个组件在不同路由间切换时,不会销毁和重新创建组件实例,而是复用之前的实例
解决方案: 使用watch (暂略) ; 使用onBeforeRouteUpdate
onBeforeRouteUpdate:
简单来说,当路由仅是路径参数、query、hash 发生改变, 而不是 path 发生改变时, 便会触发 onBeforeRouteUpdate
<在组件复用时>,onBeforeRouteUpdate 钩子函数会被触发,可以用于在<路由更新之前>执行一些操作,比如重新获取数据、更新组件状态等.
onBeforeRouteUpdate 钩子函数可接收三个参数:
- to: 即将进入的目标路由对象
- from: 当前导航正要离开的路由对象
- next: 必须调用该函数来 resolve 这个钩子函数,可以传递一个参数来指示路由的行为
参考链接:
https://juejin.cn/post/6844904197326897159
https://juejin.cn/post/7002832430658764830
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Ps: 获取url中的参数,通常紧接着携带获取到的数据发送ajax请求,但此处我们从简,直接将获取的url中的数据展示在页面上!!!
具体代码如下:
App.vue
<template>
<div>
<div class="menu">
<div class="container">
<router-link to="/">源代码教育</router-link>
<router-link to="/home">首页</router-link>
<!-- 这三种写法是等同的!!
<router-link to="/course">课程</router-link>
<router-link :to="{path:'/course'}">课程_bindToPath</router-link>
<router-link :to="{name:'Course'}">课程_bindToName</router-link>
-->
<router-link to="/course">课程</router-link>
<router-link :to="{path:'/course',query:{size:10,page:11}}">课程_pathQuery</router-link>
<router-link :to="{name:'Course',query:{size:20,page:21}}">课程_nameQuery</router-link>
<router-link to="/news">咨讯</router-link>
</div>
</div>
<div class="container">
<router-view/>
</div>
</div>
</template>
<style>
body {
margin: 0;
}
.menu {
height: 48px;
line-height: 48px;
background-color: #499ef3;
}
.container {
width: 980px;
margin: 0 auto;
}
.menu a {
display: inline-block;
color: white;
text-decoration: none;
padding: 0 10px;
}
</style>
<script setup>
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
CourseView.vue
<template>
<div>
<h1>课程页面</h1>
数量:{{ size }} | 当前页:{{ page }}
</div>
</template>
<!--
<script setup>这样就无需在传统的export default{}中写setup函数
而且还不必像以往一样return模版中的数据.
<script>
import {ref} from 'vue'
export default {
name: "CourseView"
setup(){
const nums = ref(0)
return {nums,}
}
}
</script>
-->
<script setup>
/* eslint-disable */ // eslint语法提醒
import {ref} from 'vue'
import {useRoute, onBeforeRouteUpdate} from "vue-router"
const route = useRoute()
console.log(route.query) // {size: '10', page: '11'}
console.log(route.path) // course
console.log(route.fullPath) // /course?size=10&page=11
// ★ 获取url中的参数,通常紧接着携带获取到的数据发送ajax请求,但此处我们从简,直接将获取的url中的数据展示在页面上
// or语法 有真为真
const page = ref(route.query.page || 0);
const size = ref(route.query.size || 0);
/* 从其他页面跳转到course页面,没有任何问题.但从course页面切换到course页面时,就会出现问题!!
* 比如,通过点击,url可以从 /course?size=10&page=11 切换到 /course?size=20&page=21
* 对应的course组件,获取到的url中的参数不会从 {size: '10', page: '11'} 变成 {size: '20', page: '21'}
* 如何解决?需要借助onBeforeRouteUpdate
* */
onBeforeRouteUpdate((to, from) => {
console.log(to)
console.log(from)
page.value = to.query.page;
size.value = to.query.size;
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
HomeView.vue
<template>
<div>
<p>{{ message }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script setup>
import {ref} from 'vue'
let message = ref('Hello, world!')
function increment() {
message.value += '!';
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NewsView.vue
<script>
export default {
name: "NewsView"
}
</script>
<template>
<div><h1>新闻页面</h1></div>
</template>
2
3
4
5
6
7
8
9
# URL动态参数 - params
若我想通过id值跳转到指定的 详细页 , 如何实现呢?
仔细观察运行过程, 控制台的打印, 同一组件间路由切换, 是复用!! (在url传值小节我忽略的问题, 在这里突然注意到了.)
简单来说, 如果导航目的地和当前路由相同, 只有参数发生了改变 (比如从一个用户资料到另一个 /users/1
-> /users/2
).
那么就需要 组件复用, 组件复用意味着组件的生命周期钩子函数不会被调用 !
代码里console.log(route.query)和console.log(route.params)都只输出了一次, 就能很好的证明!!
那么我是否也可以认为, let v1 = reactive(route.query) 在页面切换的时候, 也没有重新的执行!
所以就导致了 同一组件间路由切换, 页面数据没有刷新!!
而 onBeforeRouteUpdate 使得我们可以 在页面切换/刷新之前, 可以额外做一些操作.. 比如改变变量的值!!
Ps: 手动改变url, 然后按回车刷新页面是强行发送请求重新加载..(基本不会这么做) 是不会出现上述问题的, 别被误导了..
关键代码如下:
http://192.168.2.106:8080/detail/99?size=40&page=41
route.query 对应 ?size=40&page=41
route.params 对应 99
2
3
# 嵌套
嵌套路由指的是在渲染的路由组件页面中包含了下级路由渲染出口(
<router-view>
),
浏览器的URL 中各段动态路径也按某种结构对应嵌套的各层组件.路由之间出现嵌套 相当于设置了主菜单和子菜单(类似于后台管理界面,当然不局限于2级,还可以更多级)
一定要先明白一点!! 在项目的app.vue 文件中<router-view>
是最顶层的出口, 渲染最高级路由匹配到的组件!!
<div id="app">
<router-view></router-view>
</div>
2
3
可以先看看网上已有的案例, 掘金网站.. 红框的需求在前面我们已经学会如何做, 黄框的需求要实现需要学会 路由嵌套.
我们实现一个简单的版本,效果如下: (主要关注点击一级菜单中"沸点"按钮后, 展示出来的二级菜单!! 务必注意下url的变化!!)
如何实现, 代码逻辑如下: (别忘了看截图上面的注释!!)
关于截图中蓝色标注的部分需要单独说一下:
进入http://192.168.2.106:8080/pins后,PinsView.vue中的<router-view/>部分自动显示的是NewView.vue这个最新组件的内容.
如何实现的? 关键代码如下:
1> 有两种方式实现,任选其一即可
进一步阐述下重定向. 路由配置{ path: '/a', redirect: '/b' }, 指当用户访问 /a时, 路由跳转到b路由, URL 将会被替换成 /b
2> 因为访问/pins页面时,代码会根据方式一或者方式二自动加载到/pins/new页面
所以在浏览器的控制台会提示 name:'Pins' 简单来说,完全可以不设置它,没有啥用武之地. 我们索性将其注释掉!
{
path: '/pins',
// name: 'Pins',
component: () => import('../views/PinsView.vue'),
children:[
{
path:'',
// 方式一: 匹配到 /pins后, 后面啥也没加, 默认展示NewView.vue这个子页面
// component: () => import('../views/NewView.vue'),
// 方式二: 路由重定向,自动跳转
redirect:{name:'New'},
},
{
path:'new',
name:'New',
component: () => import('../views/NewView.vue'),
},
{
path:'hot',
name:'Hot',
component: () => import('../views/HotView.vue'),
},
{
path:'following',
name:'Following',
component: () => import('../views/FollowingView.vue'),
},
]
},
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 路由导航
路由导航分为 声明式导航和编程式导航
前面示例中的 App.vue都是通过router-link
使用的是 声明式导航!!
编程式导航 基于router对象
来实现!
router.push()
会向history栈添加一个新的记录,所以当用户点击浏览器后退按钮时,则回到之前的 URL !!
router.replace()
跟router.push很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样,替换掉当前的 history 记录 !!
这篇博客写的挺好的,可阅读下: https://blog.csdn.net/zhouzuoluo/article/details/84874389
2
3
4
5
6
ψ(`∇´)ψ 以一个小案例来 回顾下上面vue-router相关的知识点, 并加上编程式导航的使用.
页面 + 对应代码, 截图对比理解.
# 声明式导航
简单点,App.vue/根组件 就是 一开始页面/根页面/"http://localhost:8080/" 展示的内容!!
这里注意几个点:
1> 点击编程式导航和首页,都展现的是 Home.vue 组件的内容! "Hello World!"
观察这两次点击,发现其url都是 http://localhost:8080/,其原因,要看index.js中路由的配置!关键在于 redirect 重定向!
2> App.vue组件中用 声明式导航写了 5个<router-link>
这5个点击 会将对应组件的模版 渲染到 App.vue的 <router-view/> 的位置!!
(★ 若是访问了 '/course-info/:courseId' CourseInfo组件也会渲染到 App.vue的 <router-view/> 的位置 !!)
2
3
4
5
6
7
# 编程式导航
你可以看到 App.vue 跳转到 CourseView.vue, 然后又跳转到 CourseInfoView.vue
那么跳转过去的组件 加载在哪呢? 加载的位置 在router的index.js文件中就已经写好了!!!
router的index.js文件 可以看出一开始就使用了路由嵌套
const routes = [{},{},..] routes数组中的配置的一个个路由都是 根路由"/" 的子路由.
通过浏览器上url的变化也能够证明!!
(当然配置的一个个路由里还可以用children属性写关联的子路由!都是一样的道理.)
2
3
4
5
6
# 回顾嵌套
截图中的逻辑,其实在 嵌套 的小节已经说的很明白了!
再次提出来,是为了说明一点:
其实无论是通过 router-link 还是 router对象 跳转过去,对应的组件渲染的位置 在index.js配置文件中写的很明白啦!!
(★ PinkView.vue中不写<router-view/>的话,New、Hot、Following这三个子路由对应的组件是不会显示的!!)
2
3
4
# 示例: 登陆跳转
关于登陆跳转的页面展示, 一般分为两种: 含顶部、不含顶部!!
这两种最主要的区别在于 - 路由嵌套不一样!!
需求: 登陆成功后, 跳转首页.
# 含顶部
没啥好说的, 知识点都是刚学过的, 代码截图如下:
# 不含顶部
简单来说, 就是不能让 登陆路由 跟 资讯、直播 这些路由处于同一级.
代码截图如下:
★ 对比分析下: (这个分析过程对单页面的进一步思考理解还蛮重要的!! 有点顿悟的感觉)
vue是单应用/单页面开发, 可以简单理解就是App.vue组件!! 项目不管访问什么地址一直都在展示App.vue!!
既然展示的都是App.vue,那么为啥不同地址呈现的App.vue不一样呢?是因为路由的配置.
根据路由index.js的配置,配置中一级路由对应的组件通通都会渲染到 App.vue的<router-view>处!!
若一级路由有children属性,那么该属性下配置的二级路由都会渲染到当前一级路由对应组件的<router-view>处!!
不含顶部和含顶部相比,其实就是多了个MenuView.vue组件,将原本写在App.vue中的写在了该组件里.
App.vue组件里啥也没有,就剩下一个<router-view>!!
意味着,每次往App.vue的<router-view>加载的组件就是该组件原本的内容.
所以,该登陆跳转的页面就不含顶部啦!!!
2
3
4
5
6
7
8
9
# 导航守卫(全局)
有些路由需要登陆后才能访问, 此时就需要导航守卫! - 可以类比于Django中的中间件,其他框架里的拦截器.
需求: 访问的路由需登录才能访问, 若未登录,访问该路由时, 重定向到登陆界面. (这里的登陆跳转不含顶部)
解决: 使用全局的导航守卫, 所有的请求在访问时都会提前执行它!
Ps: 当然还有单页面的导航守卫, 一般开发不会涉及到, 暂不讨论.
router/index.js 关键代码如下:
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
router.beforeEach((to, from, next) => {
/*
* to 即将访问的的路由对象
* from 当前要离开的路由对象
* next() 继续向后执行,去to的页面
* next(false) 不跳转,还在当前页面
* next("/xxx") next({name:"xxx"}) next({path:"/xxx"}) 跳转指定的页面
* */
// 白名单
// to.name name是路由配置中给路由设置的name
if (to.name === "Login" || to.name === "Index" || to.name === "Home") {
next()
return // 此处有return,白名单的话不会往后执行的
}
let token = sessionStorage.getItem("isLogin");
if (token) {
// 登陆成功,往后执行
next()
} else {
// 未登陆,重定向到登陆界面
next({name: "Login"})
}
})
export default router
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
记得登陆成功时, 在浏览器的sessionStorage中加入登陆成功的凭证
function doLogin() {
if (username.value.length > 0 && password.value.length > 0) {
alert("登陆成功.")
sessionStorage.setItem('isLogin', "true") // ★
router.replace({name: 'Home'})
} else {
alert("用户名或密码错误.")
}
}
2
3
4
5
6
7
8
9