框架相关
原生JS虽能实现绝大部分功能,但要么就是过于繁琐,要么就是存在缺陷,故绝大多数开发者都会首选框架开发方案。现阶段较热门是React、Vue两大框架,两者工作原理上存在共通点,也存在一些不同点,对于校招来说,不需要两个框架都学得特别熟,一般面试官会针对你简历中写的框架进行提问。
在框架方面,生命周期、钩子函数、虚拟DOM这些基本知识是必须要掌握的,在学习的过程可以结合框架的官方文档
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
Vue框架
知识要点:
1. vue-cli工程
2. vue核心知识点
3. vue-router
4. vuex
5. http请求
6. UI样式
7. 常用功能
8. MVVM设计模式
React框架
知识要点:
1. 基本知识
2. React 组件
3. React Redux
4. React 路由
你可以在路由组件内直接定义路由导航守卫(传递给路由配置的)
可用的配置 API
你可以为路由组件添加以下配置:
-
beforeRouteEnter
-
beforeRouteUpdate
-
beforeRouteLeave
const UserDetails = {
template: ...
,
beforeRouteEnter(to, from) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 this
!
// 因为当守卫执行时,组件实例还没被创建!
},
beforeRouteUpdate(to, from) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /users/:id
,在 /users/1
和 /users/2
之间跳转的时候,
// 由于会渲染同样的 UserDetails
组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 this
},
beforeRouteLeave(to, from) {
// 在导航离开渲染该组件的对应路由时调用
// 与 beforeRouteUpdate
一样,它可以访问组件实例 this
},
}
beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
不过,你可以通过传一个回调给 next 来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数:
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 vm
访问组件实例
})
}
这个 离开守卫 通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回 false 来取消。
beforeRouteLeave (to, from) {
const answer = window.confirm(‘Do you really want to leave? you have unsaved changes!’)
if (!answer) return false
}
完整的导航解析流程
-
导航被触发。
-
在失活的组件里调用 beforeRouteLeave 守卫。
-
调用全局的 beforeEach 守卫。
-
在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
-
在路由配置里调用 beforeEnter。
-
解析异步路由组件。
-
在被激活的组件里调用 beforeRouteEnter。
-
调用全局的 beforeResolve 守卫(2.5+)。
-
导航被确认。
-
调用全局的 afterEach 钩子。
-
触发 DOM 更新。
-
调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
路由元信息
有时,你可能希望将任意信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过接收属性对象的meta属性来实现,并且它可以在路由地址和导航守卫上都被访问到。
const routes = [
{
path: ‘/posts’,
component: PostsLayout,
children: [
{
path: ‘new’,
component: PostsNew,
// 只有经过身份验证的用户才能创建帖子
meta: { requiresAuth: true }
},
{
path: ‘:id’,
component: PostsDetail
// 任何人都可以阅读文章
meta: { requiresAuth: false }
}
]
}
]
我们称呼 routes 配置中的每个路由对象为 路由记录。路由记录可以是嵌套的,因此,当一个路由匹配成功后,它可能匹配多个路由记录。
一个路由匹配到的所有路由记录会暴露为 r o u t e 对象 ( 还有在导航守卫中的路由对象 ) 的 route 对象(还有在导航守卫中的路由对象)的 route 对象(还有在导航守卫中的路由对象)的route.matched 数组。
我们需要遍历这个数组来检查路由记录中的 meta 字段,但是 Vue Router 还为你提供了一个 $route.meta 方法,它是一个非递归合并所有 meta 字段的(从父字段到子字段)的方法。这意味着你可以简单地写
router.beforeEach((to, from) => {
// 而不是去检查每条路由记录
// to.matched.some(record => record.meta.requiresAuth)
if (to.meta.requiresAuth && !auth.isLoggedIn()) {
// 此路由需要授权,请检查是否已登录
// 如果没有,则重定向到登录页面
return {
path: ‘/login’,
// 保存我们所在的位置,以便以后再来
query: { redirect: to.fullPath },
}
}
})
TypeScript
可以通过扩展 RouteMeta 接口来输入 meta 字段:
// typings.d.ts or router.ts
import ‘vue-router’
declare module ‘vue-router’ {
interface RouteMeta {
// 是可选的
isAdmin?: boolean
// 每个路由都必须声明
requiresAuth: boolean
}
}
数据获取 数据获取 | Vue Router (vuejs.org)")
有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:
-
导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
-
导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。
从技术角度讲,两种方式都不错 —— 就看你想要的用户体验是哪种。
导航完成后获取数据
当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。
在导航完成前获取数据
通过这种方式,我们在导航转入新的路由前获取数据。我们可以在接下来的组件的 beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法:
在为后面的视图获取数据时,用户会停留在当前的界面,因此建议在数据获取期间,显示一些进度条或者别的指示。如果数据获取失败,同样有必要展示一些全局的错误提醒。
Vue Router 和 组合式 API
在 setup 中访问路由和当前路由
route 对象是一个响应式对象,所以它的任何属性都可以被监听,但你应该避免监听整个 route 对象:
因为我们在 setup 里面没有访问 this,所以我们不能再直接访问 this. r o u t e r 或 t h i s . router 或 this. router 或 this.route。作为替代,我们使用 useRouter 函数:
请注意,在模板中我们仍然可以访问 r o u t e r 和 router 和 router 和 route,所以不需要在 setup 中返回 router 或 route。
导航守卫
虽然你仍然可以通过 setup 函数来使用组件内的导航守卫,但 Vue Router 将更新和离开守卫作为 组合式 API 函数公开:
import { onBeforeRouteLeave, onBeforeRouteUpdate } from ‘vue-router’
export default {
setup() {
// 与 beforeRouteLeave 相同,无法访问 this
onBeforeRouteLeave((to, from) => {
const answer = window.confirm(
‘Do you really want to leave? you have unsaved changes!’
)
// 取消导航并停留在同一页面上
if (!answer) return false
})
const userData = ref()
// 与 beforeRouteLeave 相同,无法访问 this
onBeforeRouteUpdate(async (to, from) => {
//仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
if (to.params.id !== from.params.id) {
userData.value = await fetchUser(to.params.id)
}
})
},
}
组合式 API 守卫也可以用在任何由 渲染的组件中,它们不必像组件内守卫那样直接用在路由组件上。
useLink
Vue Router 将 RouterLink 的内部行为作为一个组合式 API 函数公开。它提供了与 v-slot API 相同的访问属性:
过渡动效
想要在你的路径组件上使用转场,并对导航进行动画处理,你需要使用 v-slot API:
对所有的路由使用相同的过渡
//把原来这个路由视图换成这个加动画的试图即可
单个路由的过渡
想让每个路由的组件有不同的过渡,你可以将元信息和动态的 name 结合在一起,放在 上:
const routes = [
{
path: ‘/custom-transition’,
component: PanelLeft,
meta: { transition: ‘slide-left’ },
},
{
path: ‘/other-transition’,
component: PanelRight,
meta: { transition: ‘slide-right’ },
},
]
基于路由的动态过渡
也可以根据目标路由和当前路由之间的关系,动态地确定使用的过渡。使用和刚才非常相似的片段:
我们可以添加一个 after navigation hook (后置钩子),根据路径的深度动态添加信息到 meta 字段。
router.afterEach((to, from) => {
const toDepth = to.path.split(‘/’).length
const fromDepth = from.path.split(‘/’).length
to.meta.transitionName = toDepth < fromDepth ? ‘slide-right’ : ‘slide-left’
})
滚动行为
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。
注意: 这个功能只在支持 history.pushState 的浏览器中可用。
当创建一个 Router 实例,你可以提供一个 scrollBehavior 方法:
const router = createRouter({
history: createWebHashHistory(),
routes: […],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
}
})
scrollBehavior 函数接收 to和 from 路由对象,如 Navigation Guards。第三个参数 savedPosition,只有当这是一个 popstate 导航时才可用(由浏览器的后退/前进按钮触发)。
该函数可以返回一个 ScrollToOptions 位置对象:
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
// 始终滚动到顶部
return { top: 0 }
},
})
你也可以通过 el 传递一个 CSS 选择器或一个 DOM 元素。在这种情况下,top 和 left 将被视为该元素的相对偏移量。
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
// 始终在元素 #main 上方滚动 10px
return {
// 也可以这么写
// el: document.getElementById(‘main’),
el: ‘#main’,
top: -10,
}
},
})
如果返回一个 falsy 的值,或者是一个空对象,那么不会发生滚动。
返回 savedPosition,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样:
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
},
})
如果你要模拟 “滚动到锚点” 的行为:
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
return {
el: to.hash,
}
}
},
})
如果你的浏览器支持滚动行为,你可以让它变得更流畅:
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
return {
el: to.hash,
behavior: ‘smooth’,
}
}
}
})
延迟滚动
当处理过渡时,我们希望等待过渡结束后再滚动。要做到这一点,你可以返回一个 Promise,它可以返回所需的位置描述符。
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ left: 0, top: 0 })
}, 500)
})
},
})
我们可以将其与页面级过渡组件的事件挂钩,以使滚动行为与你的页面过渡很好地结合起来
路由懒加载
/ 将
// import UserDetails from ‘./views/UserDetails’
// 替换成
const UserDetails = () => import(‘./views/UserDetails’)
const router = createRouter({
// …
routes: [{ path: ‘/users/:id’, component: UserDetails }],
})
component (和 components) 配置接收一个返回 Promise 组件的函数,Vue Router 只会在第一次进入页面时才会获取这个函数,然后使用缓存数据。这意味着你也可以使用更复杂的函数,只要它们返回一个 Promise :
const UserDetails = () =>
Promise.resolve({
/* 组件定义 */
})
const router = createRouter({
routes: [{ path: ‘/users/:id’, component: UserDetails }],
})
一般来说,对所有的路由都使用动态导入是个好主意。
把组件按组分块
路由懒加载 | Vue Router (vuejs.org)")
扩展 RouterLink
扩展 RouterLink | Vue Router (vuejs.org)")
检测导航故障
如果导航被阻止,导致用户停留在同一个页面上,由 router.push 返回的 Promise 的解析值将是 Navigation Failure。否则,它将是一个 falsy 值(通常是 undefined)。这样我们就可以区分我们导航是否离开了当前位置:
const navigationResult = await router.push(‘/my-profile’)
if (navigationResult) {
// 导航被阻止
} else {
// 导航成功 (包括重新导航的情况)
this.isMenuOpen = false
}
Navigation Failure 是带有一些额外属性的 Error 实例,这些属性为我们提供了足够的信息,让我们知道哪些导航被阻止了以及为什么被阻止了。要检查导航结果的性质,请使用 isNavigationFailure 函数:
import { NavigationFailureType, isNavigationFailure } from ‘vue-router’
// 试图离开未保存的编辑文本界面
const failure = await router.push(‘/articles/2’)
/如果你忽略第二个参数: isNavigationFailure(failure),那么就只会检查这个 failure 是不是一个 Navigation Failure。/
if (isNavigationFailure(failure, NavigationFailureType.aborted)) {
// 给用户显示一个小通知
showToast(‘You have unsaved changes, discard and leave anyway?’)
}
鉴别导航故障
正如我们在一开始所说的,有不同的情况会导致导航的中止,所有这些情况都会导致不同的 Navigation Failure。它们可以用 isNavigationFailure 和 NavigationFailureType 来区分。总共有三种不同的类型:
-
aborted:在导航守卫中返回 false 中断了本次导航。
-
cancelled: 在当前导航还没有完成之前又有了一个新的导航。比如,在等待导航守卫的过程中又调用了 router.push。
-
duplicated:导航被阻止,因为我们已经在目标位置了。
导航故障的属性
所有的导航失败都会暴露 to 和 from 属性,以反映失败导航的当前位置和目标位置:
// 正在尝试访问 admin 页面
router.push(‘/admin’).then(failure => {
if (isNavigationFailure(failure, NavigationFailureType.redirected)) {
failure.to.path // ‘/admin’
failure.from.path // ‘/’
}
})//在所有情况下,to 和 from 都是规范化的路由地址。
检测重定向
当在导航守卫中返回一个新的位置时,我们会触发一个新的导航,覆盖正在进行的导航。与其他返回值不同的是,重定向不会阻止导航,而是创建一个新的导航。因此,通过读取路由地址中的 redirectedFrom 属性,对其进行不同的检查:
await router.push(‘/my-profile’) if (router.currentRoute.value.redirectedFrom) { // redirectedFrom 是解析出的路由地址,就像导航守卫中的 to和 from }
await router.push(‘/my-profile’)
if (router.currentRoute.value.redirectedFrom) {
// redirectedFrom 是解析出的路由地址,就像导航守卫中的 to和 from
}
动态路由
添加路由到你的路由上通常是通过 routes 配置来完成的,但是在某些情况下,你可能想在应用程序已经运行的时候添加或删除路由。
具有可扩展接口(如 Vue CLI UI )这样的应用程序可以使用它来扩展应用程序。
添加路由
动态路由主要通过两个函数实现。router.addRoute() 和 router.removeRoute()。它们只注册一个新的路由,也就是说,如果新增加的路由与当前位置相匹配,就需要你用 router.push() 或 router.replace() 来手动导航,才能显示该新路由。我们来看一个例子:
const router = createRouter({
history: createWebHistory(),
routes: [{ path: ‘/:articleName’, component: Article }],
})
进入任何页面,/about,/store,或者 /3-tricks-to-improve-your-routing-code 最终都会呈现 Article 组件。如果我们在 /about 上添加一个新的路由:
router.addRoute({ path: ‘/about’, component: About })
页面仍然会显示 Article 组件,我们需要手动调用 router.replace() 来改变当前的位置,并覆盖我们原来的位置(而不是添加一个新的路由,最后在我们的历史中两次出现在同一个位置):
router.addRoute({ path: ‘/about’, component: About })
// 我们也可以使用 this.$route 或 route = useRoute() (在 setup 中)
router.replace(router.currentRoute.value.fullPath)
如果你需要等待新的路由显示,可以使用 await router.replace()。
在导航守卫中添加路由如果你决定在导航守卫内部添加或删除路由,你不应该调用 router.replace(),而是通过返回新的位
router.beforeEach(to => {
if (!hasNecessaryRoute(to)) {
router.addRoute(generateRoute(to))
// 触发重定向
return to.fullPath
}
})
上面的例子有两个假设:第一,新添加的路由记录将与 to 位置相匹配,实际上导致与我们试图访问的位置不同。第二,hasNecessaryRoute() 在添加新的路由后返回 false,以避免无限重定向。
因为是在重定向中,所以我们是在替换将要跳转的导航,实际上行为就像之前的例子一样。而在实际场景中,添加路由的行为更有可能发生在导航守卫之外,例如,当一个视图组件挂载时,它会注册新的路由。
删除路由
有几个不同的方法来删除现有的路由:
- 通过添加一个名称冲突的路由。如果添加与现有途径名称相同的途径,会先删除路由,再添加路由:
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’)
框架相关
原生JS虽能实现绝大部分功能,但要么就是过于繁琐,要么就是存在缺陷,故绝大多数开发者都会首选框架开发方案。现阶段较热门是React、Vue两大框架,两者工作原理上存在共通点,也存在一些不同点,对于校招来说,不需要两个框架都学得特别熟,一般面试官会针对你简历中写的框架进行提问。
在框架方面,生命周期、钩子函数、虚拟DOM这些基本知识是必须要掌握的,在学习的过程可以结合框架的官方文档
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
Vue框架
知识要点:
1. vue-cli工程
2. vue核心知识点
3. vue-router
4. vuex
5. http请求
6. UI样式
7. 常用功能
8. MVVM设计模式
React框架
知识要点:
1. 基本知识
2. React 组件
3. React Redux
4. React 路由
er.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’)
框架相关
原生JS虽能实现绝大部分功能,但要么就是过于繁琐,要么就是存在缺陷,故绝大多数开发者都会首选框架开发方案。现阶段较热门是React、Vue两大框架,两者工作原理上存在共通点,也存在一些不同点,对于校招来说,不需要两个框架都学得特别熟,一般面试官会针对你简历中写的框架进行提问。
在框架方面,生命周期、钩子函数、虚拟DOM这些基本知识是必须要掌握的,在学习的过程可以结合框架的官方文档
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
Vue框架
知识要点:
1. vue-cli工程
2. vue核心知识点
3. vue-router
4. vuex
5. http请求
6. UI样式
7. 常用功能
8. MVVM设计模式
[外链图片转存中…(img-pGGbGvOq-1715433359678)]
React框架
知识要点:
1. 基本知识
2. React 组件
3. React Redux
4. React 路由
[外链图片转存中…(img-f9iF8wpK-1715433359679)]