《构建 Vue 应用的简易导航之路》

171 阅读5分钟

《构建 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 }
  ]
})

image.png

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 则创建应用实例、集成路由并挂载应用,最终实现页面根据路由动态切换和展示不同组件内容的效果。