《构建 Vue 应用的简易导航之路》
路由懒加载
路由懒加载,也叫延迟加载或按需加载,是一种在单页面应用(SPA)中优化性能的技术。 在没有路由懒加载之前,加载页面会把所有路由都加载出来,这将会使得页面非常庞大,往往加载一个页面需要很长时间,不利于用户体验。
而路由懒加载的主要作用是将路由对应的组件打包成一个个独立的 js 代码块,只有在访问对应的路由时,才会加载相应的组件,否则不会加载,按需加载。
使用 ES6 的动态导入(import())示例代码如下:
const home = () => import('@/views/home.vue');
const about = () => import('@/views/about.vue');
const router = new VueRouter({
routes: [
{ path: '/home', name: 'home', component: home },
{ path: '/about', name: 'about', component: about }
]
})
src/router/grouter
index.js
import RouterLink from "./RouterLink.vue";
// 从相对路径"./RouterLink.vue"导入名为 RouterLink 的模块
import RouterView from "./RouterView.vue";
// 从相对路径"./RouterView.vue"导入名为 RouterView 的模块
import { ref, inject } from "vue";
// 从 Vue 库中导入 ref 和 inject 函数
// 单例
export const createRouter = (options) => {
// 定义一个名为 createRouter 的导出函数,接受 options 参数
return new Router(options);
// 创建并返回一个 Router 类的实例,使用传入的 options 参数
};
export const createWebHashHistory = () => {
// 定义一个名为 createWebHashHistory 的导出函数
function bindEvents(fn) {
// 定义一个内部函数 bindEvents,接受一个回调函数 fn
window.addEventListener("hashchange", fn);
// 为窗口的 hashchange 事件添加监听,当 hash 改变时执行 fn 函数
}
// history 对象
return {
url: window.location.hash.slice(1) || "/",
// 返回一个对象,包含当前的 URL(如果 hash 存在则截取,否则默认为'/')
bindEvents,
// 包含绑定事件的函数
};
};
// hooks 函数式编程
export const useRouter = () => {
// 定义一个名为 useRouter 的导出函数
return inject(ROUTER_KEY);
// 使用 inject 函数获取具有 ROUTER_KEY 标识的值
};
// 标记一下 router 要向全局暴露
const ROUTER_KEY = "__router__";
// 定义一个常量 ROUTER_KEY 并赋值为 "__router__"
class Router {
// 定义 Router 类
constructor(options) {
// 类的构造函数,接受 options 参数
this.history = options.history;
// 将 options 中的 history 赋值给当前实例的 history 属性
this.routes = options.routes;
// 将 options 中的 routes 赋值给当前实例的 routes 属性
this.current = ref(this.history.url);
// 使用 ref 创建一个响应式数据 current,初始值为 history 的 url
this.history.bindEvents(() => {
// 为 history 的绑定事件设置回调函数
// console.log('//////');
this.current.value = window.location.hash.slice(1);
// 当 hash 改变时,更新 current 的值为新的 hash(截取掉'#')
});
}
// use方法会调用install方法
install(app) {
// 定义 install 方法,接受 app 参数
// 全局声明一个router 全局使用的对象
app.provide(ROUTER_KEY, this);
// 使用 provide 方法向 app 提供具有 ROUTER_KEY 标识的当前实例
console.log("准备与vue 对接", app);
app.component("router-link", RouterLink);
// 将 RouterLink 注册为名为 "router-link" 的组件
app.component("router-view", RouterView);
// 将 RouterView 注册为名为 "router-view" 的组件
}
}
这部分代码定义了与路由相关的各种功能和类:
createRouter函数用于根据给定的选项创建Router类的实例。createWebHashHistory函数用于创建处理哈希历史的对象。useRouter函数用于通过inject获取全局暴露的路由实例。ROUTER_KEY常量作为标识用于全局暴露。Router类处理路由的具体逻辑,包括属性初始化和对哈希变化事件的监听,install方法用于将路由与Vue应用进行集成,提供路由实例并注册相关组件。
RouterLink.vue
<template>
<a :href="'#' + props.to">
<slot />
</a>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
to: {
type: String,
required: true
}
})
</script>
<style lang="css" scoped></style>
这个组件定义了一个路由链接:
- 在模板中,使用
<a>标签创建链接,href属性根据传入的props.to动态生成。 - 在脚本部分,使用
defineProps定义了必需的字符串类型的to属性。
RouterView.vue
<template>
<component :is="component" />
</template>
<script setup>
import { useRouter } from './index'
import { computed } from 'vue';
const router = useRouter()
// router-view 动态组件展示 依赖于url的变化
// 响应式 router.curent 设置为ref
const component = computed(() => {
const route = router.routes.find(
(route) => route.path == router.current.value
)
return route ? route.component : null
})
</script>
<style lang="css" scoped></style>
这个组件用于根据当前路由动态显示相应的组件:
- 导入了
useRouter函数来获取路由信息。 - 通过计算属性
component根据当前路由匹配找到要显示的组件。
src/router
index.js
import { createRouter,createWebHashHistory } from './grouter/index'
import Home from '../pages/Home.vue'
import About from '../pages/About.vue'
const router = createRouter({
history: createWebHashHistory(),
routes:[{
path:'/',
name:'home',
component:Home
},
{
path:'/about',
name:'About',
component:About
}
]
})
export default router
在此文件中:
- 从
grouter/index导入创建路由和哈希历史的函数。 - 导入具体的页面组件
Home和About。 - 使用
createRouter函数创建路由实例,并配置哈希历史和路由规则。 - 最后将创建的路由实例导出。
src
App.vue
<template>
<header>
<nav>
<RouterLink to="/">首页</RouterLink>
<RouterLink to="/about">About</RouterLink>
<RouterView>aaaaaaaaaaaaa</RouterView>
</nav>
</header>
</template>
<script setup>
import RouterLink from "./router/grouter/RouterLink.vue";
import RouterView from "./router/grouter/RouterView.vue";
</script>
<style lang="scss" scoped></style>
这是应用的主组件:
- 在模板中使用了
RouterLink组件创建导航链接,使用RouterView组件展示根据路由匹配的内容。 - 在脚本部分导入了
RouterLink和RouterView组件。
src
main.js
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
import router from "./router/index.js";
// import router from "./router/grouter/index.js";
// createApp(App).use(router).mount("#app");
const app = createApp(App);
//use 方法做了什么事?
// vue 只负责 组件思想,mvvm? 响应式 等核心
// 其他的交给生态系统,一起开源 vue-router 是vue 生态中的路由模块
// vue 和他的生态对接呢? 就是这个use方法
app
// .vue(router)
.use(router)
.mount("#app");
在这个文件中:
- 导入
createApp函数创建Vue应用实例。 - 导入应用的主组件
App和路由配置。 - 创建应用实例
app,通过use方法使用路由配置,最后将应用挂载到指定的元素上。
这些文件之间的关联:
grouter/index.js定义了路由相关的核心功能和类。RouterLink.vue和RouterView.vue分别是路由链接和路由视图的组件定义。router/index.js基于grouter/index.js的功能创建并配置路由实例,并将其导出。App.vue使用路由相关的组件,并在页面中进行布局和展示。main.js整合了应用组件App和路由配置,创建并启动整个Vue应用。
总结
这是一个基于 Vue 框架构建的路由项目,它通过多个模块和组件协同工作来实现路由功能。grouter/index.js 定义了路由的核心逻辑及相关功能,RouterLink.vue 和 RouterView.vue 分别是路由链接和动态视图展示组件,router/index.js 配置并创建路由实例,App.vue 作为主组件使用路由组件进行页面布局,main.js 则创建应用实例、集成路由并挂载应用,最终实现页面根据路由动态切换和展示不同组件内容的效果。