木灵鱼儿
阅读:892
vue-router4 一些常见配置的改动
创建路由实例方式改变
3版本的时候是通过new的方式创建路由实例,4版本是改用了createRouter
的方式:
import { createRouter } from 'vue-router'
const router = createRouter({
// ...
})
路由模式配置改变
3版本路由模式是由mode属性控制,值为字符串,现在通过import引入不同函数来创建不同的路由模式:
"history"
改为createWebHistory()
"hash"
改为createWebHashHistory()
"abstract"
:createMemoryHistory()
这里着重解释一下第三种模式!
abstract是一种在内存中模拟路由path地址的方式,适用于没有hash和history的情况下,router会自动使用该模式,听说在weex上有用到这个。
需要注意的是,如果使用了这个模式,页面刷新后会回到首页。
在4版本中,mode属性改为了history
:
import { createRouter, createWebHistory } from 'vue-router'
// 还有 createWebHashHistory 和 createMemoryHistory
createRouter({
history: createWebHistory(),
routes: [],
})
base配置
base的定义是基路径,默认是/
,如果我们应用最终会部署在某个域名的/app
下,那么直接通过域名去访问单页应用,会导致路由路径与真实的路径不一致,我们就需要改动base。
比如我们现在的域名是:www.mule.com
打包的应用存放的是该域名下的/app
文件夹下
这就导致我们需要通过:www.mule.com/app 才能访问到网页
那么我就需要将base设为"/app"
具体我测了下好像没啥效果,设不设都不影响使用,文件夹这种问题,一般处理资源路径就行了,设置这个个人没感觉出效果,如果以后了解到了再补充。
但是找到一个不错的用例,就是多语言处理。
通过动态的base来实现一个语言前缀:
function getBasePath() {
const path = location.pathname
const subs = path.substr(0,3)
if (subs == '/en') {
return '/en'
}
if (subs == '/fr') {
return '/fr'
}
}
const router = new VueRouter({
routes: routes,
base: getBasePath(),
mode: 'history'
})
这个方式我只在createWebHistory模式下有效果,他会自动在路由路径的前面加上对应的语言。
在3版本中base还是单独选项,在4版本中,base是作为模式函数的第一个参数使用的。
createWebHistory("/app")
删除了fallback属性
fallback在3版本的时候,是作为一个回退属性配置的,接受一个布尔值,默认为true,表示当浏览器不支持 history.pushState
控制路由是否应该回退到 hash
模式。
原因是因为vue所能支持的浏览器中全部都对history api支持了,所以没有必要回退到hash,而且vue3已经放弃了对ie的支持。
删除了*星号通配符路由
弃用了之前*
通配符匹配方式,改用了正则参数的方式。
比如我们希望一个404页面,一般会在路由数组的最后加上:
{
path: '/:pathMatch(.*)*',
name: '404',
component: () => import('./views/404'),
}
当我们访问链接:http://localhost/:8081/about
会被重定向到404页面,我们还可以通过this.$route.params.pathMatch
来获取正则匹配到的路径,此时输出的是:
["about"]
如果有多个路径:http://localhost/:8081/about/aaa/detail
["about", "aaa", "detail"]
如果我希望这个路由是以特定路径前缀来进行匹配,比如test,那么可以这样:
{
path: '/test:id(.*)',
component: () => import('./views/xxx'),
name: 'xxx',
}
id是动态参数,假设链接为:http://localhost/:8081/test0912
this.$route.params.id //得到0912
如果我们希望手动跳转到这个404路由页,且携带参数的话,注意参数必须如下写法:
router.push({
name: "404",
params: {
pathMatch: ["xx", "xxx"]
}
});
router.reslove()
在nuxt中我们可能会用到这个属性来进行页面的新建打开,比如我有一个路由页b,我希望用户点击按钮是新建一个页面打开这个路由b,那么我们就必须知道完整的href,而reslove方法,可以接受一个本地路由参数,和平时push传的参一样,它最终会返回一个RouteLocation
对象。
RouteLocation 可以包含重定向记录的解析的 RouteLocationRaw。除此之外,它还具有与 RouteLocationNormalized 相同的属性。
其中还包括一个包含任何现有 base
的 href
属性,我们通过获取href属性可以拿到完整的链接地址进行跳转。
const href = router.reslove({
name: "about",
params: {
id: 12
}
}).href;
onReady改为isReady
改方法用于判断当前路由是否完成初始化导航,也就是第一个路由是否已经加载成功。
// 将3版本的
router.onReady(onSuccess, onError)
// 替换成
router.isReady().then(onSuccess).catch(onError)
// 或者使用 await:
try {
await router.isReady()
// 成功
} catch (err) {
// 报错
}
scrollBehavior
原来我们3版本可能会这么写:
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
4版本中,将x改为left,y改为top。
scrollBehavior (to, from, savedPosition) {
return { left: 0, top: 0 }
}
<router-view>、<keep-alive> 和 <transition>
transition
和 keep-alive
现在必须通过 v-slot
API 在 RouterView
内部使用:
<router-view v-slot="{ Component }">
<transition>
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
这么改可能会感觉写的很啰嗦,但是是符合我们的逻辑思维的,因为我们使用transition或者keep-alive最终命中的目标并不是router-view,而是router-view对应渲染的那个vue组件,所以新版本将其作为插槽参数拆分出来,使得代码逻辑上更加准确。
移除router-link的append属性
append用于表示当前router-link的跳转是一个相对路径,相对于当前页面路径的跳转路径。
由于使用频率不高被移除,官方也有提供对应的办法,查看文档:append
通过自己实现一个拼接方法实现该功能。
删除 <router-link>中的event 和 tag属性
<router-link>
中的 event
和 tag
属性都已被删除。你可以使用 v-slot
API 来完全定制 <router-link>
:
将
<router-link to="/about" tag="span" event="dblclick">About Us</router-link>
替换成
<router-link to="/about" custom v-slot="{ navigate }">
<span @click="navigate" @keypress.enter="navigate" role="link">About Us</span>
</router-link>
官方为了减少包体大小,毕竟这两个功能完全可以自己实现,通过插槽的方式更加方便自由。
删除 <router-link>中的exact属性
exact之前在3版本常常用于路由的高亮必备属性,现在4版本移除了,因为4版本不会有3的那种嵌套路径也会高亮的问题,所以放心移除。
mixins中路由守卫将被忽略
mixins在vue3中弃用,所以对应的路由守卫也不会被支持
所有的导航现在都是异步的
所有的导航,包括第一个导航,现在都是异步的,这意味着,如果你使用一个 transition
,你可能需要等待路由 ready 好后再挂载程序:
app.use(router)
// 注意:在服务器端,你需要手动跳转到初始地址。
router.isReady().then(() => app.mount('#app'))
否则会有一个初始过渡,就像你提供了 appear
属性到 transition
一样,因为路由会显示它的初始地址(什么都没有),然后显示第一个地址。
请注意,如果在初始导航时有导航守卫,你可能不想阻止程序渲染,直到它们被解析,除非你正在进行服务器端渲染。否则,在这种情况下,不等待路由准备好挂载应用会产生与 Vue2 中相同的结果。
跳转不存在的路由
在3版本的时候,如果我们跳转到一个不存在的路由,我们的浏览器的地址会变成根路径,如:http://localhost:8080/
,然后页面变成空白显示,然后并不会报错。
这显然并不符合我们逻辑思维,按道理,如果跳转到一个不存在的路由他应该报错才对,所以在4版的时候,我们跳转到一个不存在的路由,实际上会报错的,我们可以try...catch
捕获,或者catch
接一下promise的错误返回。
然后就可以做自己的判断了。
空path的子路由,不再追加斜线/
以前我们访问一个默认的子路由的时候,也就是空path的子路由时,我们的页面地址会在后面加上一个斜线。
const routes = [
{
path: '/parent',
component: Parent,
children: [
{ path: '', component: Child },
],
},
]
当我们访问parent时,他会默认展示子路由Child
,地址显示如下:
http://localhost:8080/parent/
而我们4版本不再添加斜线,展示如下:
http://localhost:8080/parent
原因:这是为了使尾部的斜线行为保持一致:默认情况下,所有路由都允许使用尾部的斜线。可以通过使用 strict
配置和手动添加(或不添加)斜线来禁用它。
不添加斜线回来一些副作用:
当我们通过默认子路由重定向到另一个子路由的时候,注意,如果使用path作为redirect
重定向参数,那么就需要注意了,你的path最好是完整路径,否则就会导致跳转过去的地址作为一个非子路由来进行跳转了。
import { h } from "vue";
import { createRouter, createWebHistory, RouterView } from "vue-router";
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/parent",
component: {
render() {
return h(RouterView);
},
},
children: [
{
path: "",
name: "Parent",
redirect: "children",
},
{
path: "children",
name: "Children",
component: () => import("./views/Chint2.vue"),
},
],
},
],
});
当我们访问/parent
路由的时候,会被重定向到:
http://localhost:8080/children
这显然不是我们需要的,如果你非要使用path,那么就需要这么写:
redirect: "/parent/children"
我建议是使用name:
redirect: { name: "Children" }
这点还是需要大家注意。
$route属性编码行为
path、fullPath
现在路由的path
、fullPath
不再默认做解码操作了
举个例子:
我们拿上面那个子路由复用一下,我们在Children2.vue里面输出一个fullPath属性。
<script lang="ts">
import { defineComponent } from "vue";
import { useRoute } from "vue-router";
export default defineComponent({
setup() {
const route = useRoute();
console.log(route.fullPath);
return {};
},
});
然后我们的网址是:http://localhost:5173/parent/children?name=木灵鱼儿
按照以往的获取,我们拿到的fullPath
也一定是/parent/children?name=木灵鱼儿
。
这里我们就需要了解一个知识,就是我们的浏览器其实会将特殊字符进行转码,虽然看上去和我们自己输入内容一致,但是实际使用的是被转码的内容,不信的话,我们在浏览器地址重新复制一下网址,再去聊天界面粘贴,你会发现你粘贴的内容是这样的:
http://localhost:5173/parent/children?name=%E6%9C%A8%E7%81%B5%E9%B1%BC%E5%84%BF
这个转码我们使用js也能实现,就是encodeURI
这套函数,具体可以自行百度。
我们打印下route.fullPath
,得到一下内容:
/parent/children?name=%E6%9C%A8%E7%81%B5%E9%B1%BC%E5%84%BF
hash
hash
现在被解码了,这样就可以复制过来。router.push({ hash: $route.hash })
可以直接用于 scrollBehavior 的 el
配置中。
push、replace、resolve
当使用 push
、resolve
和 replace
并在对象中提供 string
地址或 path
属性时,必须进行编码(像以前的版本一样)。另一方面,params
、query
和 hash
必须以未编码的版本提供。
带斜线字符参数
斜线字符(/
)现在已在 params
内正确解码,同时仍在 URL 上产生一个编码版本:%2F
原因:这样,在调用 router.push()
和 router.resolve()
时,可以很容易地复制一个地址的现有属性,并使产生的路由地址在各浏览器之间保持一致。router.push()
现在是幂等的,这意味着调用 router.push(route.fullPath)
、router.push({ hash: route.hash })
、router.push({ query: route.query })
和router.push({ params: route.params })
不会产生额外的编码。
版权申明
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。
相关推荐
使用vue2.7的一些踩坑事项
eslint校验的一些问题(暂时无解)在初始化项目时勾选了eslint校验之后,升级vue 2.7版本后,eslint-plugin-vue这个插件需要升级到9+版本,我目前使用的版本是:"eslint-plugin-vue": "^9.4.0"具体的一些可以参考官方提供的2.7升级指南:2.7日志官方居然把这个写在了变更日志里面,按道理最好官方的文档上也有一份说明的,但是目前没有。虽然eslint的依赖更新到新版后确实解决了一些项目启动报错的问题,但是有时候我们的SFC单文件组件开发的时候,template中的一些变量绑定还是会出现波浪线警告,这...
vue-router4 新增功能
动态路由这个其实在3版也有,不过3版原来使用的api是addRoutes,这个api顾名思义,就是可以传入一个路由数组来进行注册,后来被废弃,现在3版和4版统一采用addRouteapi。addRoute一次只能添加一个路由,但是有两种用法:添加一条新路由router.addRoute({ path: "/about", name: "About", component: () => import("../views/About.vue"), })给已存在的路由注册一个子路由,父路由必须有name属性route...
vue3 filter过滤器
vue3直接移除了filter过滤器,官方推荐,如果要使用过滤器可以使用computed计算属性和method函数来代替。如果使用了全局过滤器,官方也提供了一个属性来进行迁移修复,但是也只推荐用于迁移。const app = createApp(App) //给当前app的全局属性上挂载一个过滤器对象 app.config.globalProperties.$filters = { currencyUSD(value) { return '$' + value } }使用的时候:<template> <h1>Bank Account Bala...
vue3 keyCode修饰符
vue2我们在监听按键事件的时候,是可以通过在事件后面加上按键码来实现监听某一个按键的。<input @keyup.13="submit" />甚至我们还可以使用按钮别名alias的方式调用<input @keyup.enter="submit" />还可以全局自定义别名,通过Vue.config.keyCodes属性。Vue.config.keyCodes = { f1: 112 }随着ECMAScript的标准推荐,官方已经不推荐使用keyCode键码了,这个功能在js的官方定义上是一个已弃用的状态,最新的标准是使用...
vue3 取消$on、$off、$once
在vue2的时候,我们可以通过$on、$off、$once、$emit来实现一个事件总线EventBus,但是vue3的时候,又换了一套逻辑思想。官方不鼓励使用EventBus在组件之间进行通信,虽然短期内确实是非常简单的解决方案,但是从长远来看,它总是很难维护的,所以官方更加推荐使用其他方式:父子组件之间可以通过Props和自定义事件来进行沟通,如果是兄弟组件,可以通过父级组件来沟通使用Provide / inject方式来跨层级沟通,比如与插槽的内容组件。Provide / inject也可以用于跨层级沟通,以避免props,emit事件层级过深的问题通过使用插槽的形式来避免层级沟通...
vue3 transition、watch、挂载api的变化
transition动画的变化就是vue3调整了一下动画类名,原来vue2是:.v-enter{ opacity: 0; } .v-leave{ opacity: 1; }这两个在vue3中改为:.v-enter-from{ opacity: 0; } .v-leave-to{ opacity: 1; }其相关属性也对应发生了变化:leave-class被重命名为 leave-from-class(可以写成 leaveFromClass在渲染函数或 JSX 中)enter-class被重命名为 enter-from-class(可以写成 enterFromClass在...
vue3 自定义指令
vue3将自定义指令的生命钩子函数全部改为与组件的生命钩子函数相同,原来vue2时,自定义指令的生命钩子是一套完全不同的,但是也正因为如此,它的钩子函数有点晦涩难懂,这次统一了我觉得是一件非常好的事情,让我们的记忆力压力减轻了很多,也方便了我们的理解。vue3写法:const myDirective = { // 在绑定元素的 attribute 前 // 或事件监听器应用前调用 created(el, binding, vnode, prevVnode) { // 下面会介绍各个参数的细节 }, // 在元素被插入到 DOM 前调用 beforeMoun...
vue3 异步组件
书接上文,我们的函数式组件在vue3里只能声明一个函数再通过h函数组合处理,然后导出使用,你会发现这个组件是一个函数类型。然后我再去看一下vue2时,我们的异步组件引入方式:const asyncModal = () => import('./Modal.vue')你会发现除了异步组件也是一个函数类型,这就尴尬了,怎么区分呢?vue3提供了一个defineAsyncComponent函数用于区分异步组件。import { defineAsyncComponent } from 'vue' const asyncModal = defineAsyncComponent(() =>...
vue3 函数式组件 functional
由于vue3对dom的渲染做了优化,优化了diff算法,在vue2时不管元素是否参与更新,都会被重新创建,vue3则针对不参与更新的元素,打上一个静态标记,此时这个元素只会创建一次,后续的渲染更新则不会重新创建。于是函数式组件在vue3中性能的提升可以忽略不计,于是functional组件的声明被移除,不在支持:template中不能再写上functional来表示函数式组件vue组件中的{ functional: true }选项也被删除但是,这也并不意味着无法创建函数式组件了,vue3也有提供创建的方式,但是需要引入h函数。创建一个js组件:_test.js_import { h }...