目录
后端路由的映射方案
SPA:single page web application
url的hash
vue-router
基础使用
路由默认url
异步打包
动态路由匹配
notfound
嵌套路由
动态路由
添加路由
添加嵌套路由
删除路由
路由导航守卫
全局前置守卫beforeEach
Vuex状态管理
状态管理概念
vuex基础使用
安装
创建store对象
main.js调用
组件使用store
state状态映射到组件
options api computed 属性方案
setup方案
直接解构(推荐)
getters
基础使用
getters函数同时调用getters
getters函数返回函数
getter函数映射到组件
Mutation
基本使用
映射使用
重要原则
后端路由的映射方案
浏览器向服务器通过url申请页面,然后服务器通过url映射到具体的html页面,再将html页面传递给服务器,这就是后端路由的映射方式,url 对应了一个具体的 html页面
SPA:single page web application
之前的后端方案是一个 url 对应了一个具体的 html页面
而现在的许多网站的方式不是这样,拿网易云网站举例
在点击导航栏时,例如点击 我的音乐
页面的url会发生改变,但是导航栏是没有变化的,而只变化了下面的界面
这说明这种方式,url发生变化时不会渲染整个页面,而只更新一部分
即url与组件一一对应,这就是SPA模式
其实也就是前端路由
url的hash
vue-router
基础使用
1、创建路由
先下载插件 npm i vue-router
一般会建立一个js文件,用来创建路由并保持映射关系(其实就是保存url和组件的对应关系)
//引入路由函数
import { createRouter,createWebHashHistory} from 'vue-router'
//引入组件
import page from '../components/page.vue'
import nav from '../components/navitem.vue'
//创建路由
const router = createRouter({
//确定映射模式:hash模式
history:createWebHashHistory(),
//确定映射关系
routes:[
{path:'/page',component:page},
{path:'/nav',component:nav}
]
})
//导出路由
export default router
每个对应关系是可以有独一无二的name属性的,以便后续使用
{
name: "nav",
path: '/nav',
component: nav
},
2、让路由生效
在main.js中引入路由并运用
import { createApp } from 'vue'
import App from './App.vue'
//引用路由
import router from './router/index'
const app = createApp(App)
//运用路由
app.use(router)
app.mount('#app')
3、router-view占位
在APP.vue或者其他组件中使用 router-view占位,以告诉页面当我切换url时,组件渲染在什么地方
4、router-link进行路由的切换
其实就是类似于a元素,点击时就可以切换url,使用 to属性
<template>
<router-view></router-view>
<router-link to="/page" class="link">page</router-link>
<router-link to="/nav" class="link">nav</router-link>
</template>
router-link自带class:router-link-active
选中哪个router-link,哪个router-link就会加入这个class,且具有排他作用
当然也可以自己定义按钮函数:点击按钮触发jump函数,跳转到page
const route = useRouter()
let jump = ()=>{
route.push('/page')
}
路由默认url
在初始时可以设定默认的跳转url
const router = createRouter({
history:createWebHashHistory(),
routes:[
//默认
{path:'/',redirect:'/page'},
{path:'/page',component:page},
{path:'/nav',component:nav}
]
})
异步打包
【VUE3】保姆级基础讲解(三)非父子组件通讯,$refs,动态组件,keep-alive,Composition API_独憩的博客-CSDN博客
const page = ()=>import(/* webpackChunkName: 'page' */'../components/page.vue')
const nav = ()=>import(/* webpackChunkName: 'nav' */'../components/navitem.vue')
这样在打包的时候就会单独创造js文件
注释是固定写法,既魔法注释,会告知打包时的js文件名称
动态路由匹配
对于用户或者商品数据,往往的url是 商品\商品编号 组成,例如 good\111
那么我们希望,即使url变成这个样子,也要渲染good组件,只是穿进去的id是111
用到了动态路由匹配
routes:[
{path:'/',redirect:'/page'},
{path:'/page',component:page},
{path:'/nav',component:nav},
{path:'/good/:id',component:good}
]
那么就定义了 id 用来传导编号
在使用时直接
<router-link to="/good/111" class="link">good1</router-link>
<router-link to="/good/222" class="link">good2</router-link>
在url转换成 good\111或者good\222时也能匹配到 good 组件
在good.vue中,如果想使用这个id:
<template>
<h1>{{$route.params.id}}</h1>
</template>
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params);
</script>
<style scoped>
</style>
在组件中 使用 $route.params.id
在js代码中,使用useRoute()获取路由
js代码中还可以使用 route.forward()或 route.back()跳转后一个和前一个界面
如果想在切换 id时能持续获取params,需要用到onBeforeRouteUpdate
<template>
<h1>{{$route.params.id}}</h1>
</template>
<script setup>
import { useRoute , onBeforeRouteUpdate} from 'vue-router'
const route = useRoute()
onBeforeRouteUpdate((to,from)=>{
console.log(to.params);
console.log(from.params);
})
console.log(route.params);
</script>
<style scoped>
</style>
notfound
当传入的url是没经过匹配时,这时可以设定显示一个 notfound 组件
{path:'/:pathMatch(.*)',component:notfound}
嵌套路由
<template>
<h1>page</h1>
<router-view></router-view>
</template>
一些应用程序的 UI 由多层嵌套的组件组成。在这种情况下,URL 的片段通常对应于特定的嵌套组件结构,例如:
/page/profile /page/posts
+------------------+ +-----------------+
| page | | page |
| +--------------+ | | +-------------+ |
| | Profile | | +------------> | | Posts | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
也就是说在 组件中再次使用 router-view占位
page.vue:
<template>
<h1>page</h1>
<router-view></router-view>
</template>
那么在配置routes时,应该加入children属性
{path:'/page',
component:page,
children:[
{
path:'profile',
component:()=>import('../components/profile.vue')
}
]},
动态路由
添加路由
使用addRoute函数直接添加根路由
router.addRoute({
name: "nav",
path: '/nav',
component: nav
})
添加嵌套路由
要将嵌套路由添加到现有的路由中,可以将路由的 name 作为第一个参数传递给 router.addRoute()
,这将有效地添加路由,就像通过 children
添加的一样:
router.addRoute("page", {
path: 'profile',
component: () => import('../components/profile.vue')
}
)
通过这种方式给page路由添加了children
删除路由
有几个不同的方法来删除现有的路由:
- 通过添加一个名称冲突的路由。如果添加与现有途径名称相同的途径,会先删除路由,再添加路由:
router.addRoute({ path: '/about', name: 'about', component: About }) // 这将会删除之前已经添加的路由,因为他们具有相同的名字且名字必须是唯一的 router.addRoute({ path: '/other', name: 'about', component: Other })
- 通过调用
router.addRoute()
返回的回调:
当路由没有名称时,这很有用。const removeRoute = router.addRoute(routeRecord) removeRoute() // 删除路由如果存在的话
- 通过使用
router.removeRoute()
按名称删除路由:
需要注意的是,如果你想使用这个功能,但又想避免名字的冲突,可以在路由中使用router.addRoute({ path: '/about', name: 'about', component: About }) // 删除路由 router.removeRoute('about')
Symbol
作为名字。
当路由被删除时,所有的别名和子路由也会被同时删除
路由导航守卫
在进行路由跳转时,可以进行回调函数,既 守卫
有一种使用场景是:在点击跳转时,先判断是否登陆,如果登陆了就跳转到对应的路由,如果没有登陆就跳转到登陆路由
全局前置守卫beforeEach
router.beforeEach((to, from) => {
if(to.path !== "/login"){
return "/login"
}
})
其他守卫:导航守卫 | Vue Router
Vuex状态管理
状态管理概念
在一个项目中存在着很多状态,其实这个状态就是我们一直使用的变量,例如网易云音乐这个项目,播放状态(暂停or播放),现在播放的歌曲名称,播放到哪一句....都是其状态,对于这些数据的管理我们就称之为是状态管理。
在前面我们是如何管理自己的状态呢?
在Vue开发中,我们使用组件化的开发方式;
- 而在组件中我们定义data或者在setup中返回使用的数据,这些数据我们称之为state;
- 在模块template中我们可以使用这些数据,模块最终会被渲染成DOM,我们称之为View;
- 在模块中我们会产生一些行为事件,处理这些行为事件时,有可能会修改state,这些行为事件我们称之为actions;
在之前的例子中,都是在组件中用变量直接控制的,但是这样存在弊端:
- 当项目组件十分庞大,状态很多,会使得组件十分臃肿,不好维护
- 虽然我们有props和emits等数据传递方法,但是当组件很多,且组件之间不是父子关系时,数据的传递会变得十分复杂
那么就需要创建一个状态库 store来管理这些状态:
- 第一幅图表示所有的组件都可以和store内部的状态进行交互
- 第二幅图表示,组件可以直接拿到数据state,但是如果想修改,必须走 actions---mutations---state流程,具体在下文阐述
vuex的基本元素为:state ,getters,mutation,actions,modules
vuex基础使用
安装
npm i vuex
创建store对象
与路由类似,一般会在src文件夹下创建store文件夹,下面创建一个index.js文件:
import {createStore} from 'vuex'
const store = createStore({
state:()=>{
return{
counter:100
}
},
})
export default store
这里创建了一个store实例,然后定义了内部的state,采用的是函数式编程
使用createStore创建store对象,默认内部的状态是响应式的
main.js调用
在main.js中:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
import store from './store/index'
const app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')
组件使用store
然后在任何一个组件中就可以使用state的数据进行展示:
<h1>{{$store.state.counter}}</h1>
在组件的js中获取数据方法:
import {useStore} from 'vuex'
const store = useStore()
console.log(store.state);
state状态映射到组件
options api computed 属性方案
当state里面的状态很多时,如果每个状态都采用 $store.state.xxx 的形式调用,会十分繁琐
那么我们希望将state状态整体映射到组件中
一般我们会使用 computed 属性,其属于options api
对于state:
import {createStore} from 'vuex'
const store = createStore({
state:()=>{
return{
counter:100,
name:'kobe',
age:18
}
},
})
export default store
在组件中可以引入mapState函数放置在computed属性中,这个函数前加 ... 表示其会返回多个函数,通过数组的形式将state中的状态映射进来,在模板中就可以直接使用了
<template>
<h1>{{name}}</h1>
</template>
<script>
import {mapState} from 'vuex'
export default{
computed:{
...mapState(['name','age','counte'])
}
}
</script>
setup方案
但是这个操作在setup里面写就很麻烦,一般不会这么用
const store = useStore()
const {name ,age,counter} =mapState(['name','age','counter'])
const Cname = computed(name.bind({$store:store}))
从mapState中返回的是函数形式,要想使用必须放在computed属性中
但是在上述的方案中,其实底层调用store的逻辑还是 this.$store.state.xxx
但是setup 中是不存在this的,所以要使用bind属性给其设置一个this指向
直接解构(推荐)
我们一般会使用setup编写逻辑,不希望还同时使用options api,所以上述方案都不太好
可以直接解构:
import {toRefs} from 'vue'
import {useStore} from 'vuex'
const store = useStore()
const{name,age,counter} = toRefs(store.state)
这样既映射成功,还保证了其响应式
getters
基础使用
类似于options api中的computed属性,当你想对state中的状态做一些操作时,可以使用getters
import {createStore} from 'vuex'
const store = createStore({
state:()=>{
return{
counter:100,
}
},
getters:{
doubleCounter(state){
return state.counter*2
}
}
})
export default store
使用方法和state类似
<h1>{{$store.getters.doubleCounter}}</h1>
getters函数同时调用getters
getters中的函数可以相互调用
import {createStore} from 'vuex'
const store = createStore({
state:()=>{
return{
counter:100,
name:'lee',
}
},
getters:{
doubleCounter(state){
return state.counter*2
},
//相互调用
message(state,getters){
return `${state.name} need ${getters.doubleCounter} yuan`
},
}
})
export default store
getters函数返回函数
getters中的函数可以返回函数,在调用时可以传参:
import {createStore} from 'vuex'
const store = createStore({
state:()=>{
return{
friends:[
{name:"111",age:12},
{name:"112",age:13},
{name:"113",age:14},
]
}
},
getters:{
findfriend(state){
return function(name){
return state.friends.find(item=>item.name == name)
}
}
}
})
export default store
findfriend函数返回了一个函数,形参是name,这个函数又通过传入的name找到对应的friend信息返回
调用时:
<h1>{{$store.getters.findfriend(112)}}</h1>
getter函数映射到组件
跟state方法的方案类似,也会有三个方案,也会有mapGetters函数,这里不再赘述,只写一下最推荐的直接解构方案
const{findfriend} = toRefs(store.getters)
Mutation
更改VUEX中状态的唯一方法是提交mutation(其实也能直接修改,但是vuex认为不规范)
基本使用
首先在store中定义mutation方法:
mutations:{
increment(state){
state.counter++
},
changename(state){
state.name = 'kobe'
}
}
在组件中使用,必须要使用.commit方法:
<template>
<button @click="changeName">跳转到page</button>
<h1>{{$store.state.name}}</h1>
</template>
<script>
export default {
methods: {
changeName() {
this.$store.commit('changename')
}
}
}
</script>
当然也可以传入参数:
mutations:{
changename(state,newname){
state.name = newname
}
}
<script>
export default {
methods: {
changeName() {
this.$store.commit('changename','lalla')
}
}
}
</script>
映射使用
mutations也可以映射使用
同样使用 mapMutations 函数
<template>
<button @click="changename('kobe')">跳转到page</button>
<h1>{{$store.state.name}}</h1>
</template>
<script>
import { useStore, mapMutations } from 'vuex'
export default {
methods: {
...mapMutations(['changename'])
}
}
</script>
重要原则
也就是说在mutations中不要进行异步操作,例如网络请求,如果要网络请求数据,需要用到actions