模拟 VueRouter 3

initComponents

initComponents 是用于创建 router-linkrouter-view 组件的方法,她接收 Vue 参数,虽然 Vue 已经被我们记录到了全局变量中的 _Vue 变量上,但是这里当作参数传入,就是为了减少这个方法和外部的依赖。

js 复制代码
initComponent(Vue) {
    Vue.component("router-link", { ... })
    Vue.component("router-view", { ... })
}    

再回顾一下 router-linkrouter-view 组件的使用:

js 复制代码
<div id="nav"> 
   <router-link to="/">Index</router-link> | 
   <router-link to="/blog">Blog</router-link> | 
   ... 
</div> 
<router-view/>
  1. 最终要渲染成一个超链接 -- <a>
  2. 接收一个字符串类型的参数 to 作为超链接的地址 -- props
  3. 将标签之间的内容作为超链接的内容渲染出来 -- <slot>
js 复制代码
Vue.component("router-link", {
   props: {
     to: String
   },
   template:"<a :href='to'><slot></slot></a>"
})

试一试

导出一下我们写到现在的 VueRouter(export default class VueRouter),然后在我们的 VueCli 项目中修改导入 router 的地址,将 import VueRouter from 'vue-router' 中的 vue-router 改为我们 VueRouter 文件的地址,试着看看我们的 router-link 组件是否能正确显示。

当我们启动项目后,可以在控制台看到以下报错:

这里提示我们使用的是 runtime-only 版本的 vue,其中是不包含编译 template 的模块的,那我们想要避免这个错误有两种方法:

  1. 使用完整版本的 vue

VueCli 默认使用的是运行时版本的 Vue ,因为这样效率更高,如果我们需要使用完整版本的 Vue,就需要在项目根目录下添加 vue.config.js 文件,然后添加配置 runtimeCompiler

js 复制代码
// vue.config.js
module.exports = {
  runtimeCompiler: true
}

再次运行项目就会发现报错消失,页面正常显示了 router-link 组件。

  1. template 改为 render 函数的写法 render 函数就是渲染函数,它接收一个参数 createElement 方法(通常简写成 h),通过调用 createElement 可以创建虚拟 DOM
js 复制代码
    Vue.component("router-link", {
      props: {
        to: String
      },
      render(h) {
        // `createElement` 方法可以接收3个参数,分别是要创建元素的`选择器`、`属性`、`子元素`。
        return h("a", {
          attrs: {
            href: this.to
          }
        }, [this.$slots.default])   // 默认插槽
      },
   })

创建 router-view

router-view 是根据当前路由地址(this.data.current)在 routeMap 中找到对应显示组件的。需要注意的有2点:

  1. 写在 render 函数中的 this 不是 VueRouter 的实例而是 routerView 组件,所以需要在 initComponent 方法内先获取 this,此时的 thisVueRouter 的实例。
  2. h 可以接收一个组件,然后将组件转换为虚拟 DOM
js 复制代码
const self = this
Vue.component("router-view", {
  render(h) {
    const cm = self.routeMap[self.data.current]
    return h(cm)
  }
})

我们已经完成了 router-linkrouter-view 两个组件,可以在浏览器上看下效果,没有报错,但是当我们点击 router-link 切换路由时,我们可以看到页面的 url 改变了,并且向服务端发起请求重新刷新了页面。

但在单页面应用中,我们其实不希望这样,那我们就需要做出以下修改:

1. 注册 click 事件覆盖超链接默认向服务器发起请求的行为

这里如果不加 e.preventDefault(),则点击事件会让整个页面向服务器发起请求并刷新页面,也不要删除 href 属性,删掉这个属性会导致超链接的样式变得和普通文本一样。

2. 使用 history.pushState 方法改变地址栏的路径,但是不会向服务器发起请求

pushState 接收三个参数,分别是 data, title(网页的标题), url(要转跳的路径),暂时只设置 url 即可。

3. 修改 current 路由地址,触发 router-view 的更新

VueRouter 的属性 data 是响应式的,在 router-view 中我们根据当前地址 data.current 来决定显示的组件。那么我们只需要改变 data.current 的值,就可以同步更新 router-view 的组件了。

那么怎么在 click 方法里,修改 data.current 的值呢?首先明确 click 方法中的 this 就是 router-view 组件,它是一个 vue 实例,对于所有的 vue 实例都具有 $router 属性,其中的包含 current 表示当前路由地址。

js 复制代码
render(h){
    return h("a",{
        attrs:{ href:'' },
        on:{
            // 不要加小括号,否则会变成立即调用
            click:this.clickhander
        } 
    },[this.$slots.default])
},
methods:{
    clickhander(e){
        history.pushState({},"",this.to)
        this.$router.data.current=this.to
        e.preventDefault()
    }
}

完整代码

js 复制代码
initComponent(Vue){
    Vue.component("router-link",{
        props:{
            to:String
        },
        render(h){
            return h("a",{
                attrs:{
                    href:''
                },
                on:{
                    click:this.clickhander
                }
            },[this.$slots.default])
        },
        methods:{
            clickhander(e){
                history.pushState({},"",this.to)
                this.$router.data.current=this.to
                e.preventDefault()
            }
        }
    })
    const self = this
    Vue.component("router-view",{
        render(h){
            const cm=self.routeMap[self.data.current]
            return h(cm)
        }
    })

}
相关推荐
sxmas10 分钟前
用状态机与DAG建模四阶段业务流程:以MIND模型为例
前端
倔强的石头1061 小时前
【Linux指南】动静态库系列(七):符号表与重定位:链接器如何把函数调用接起来
linux·前端·javascript
a1117762 小时前
3D 脑图谱 / Bilingual 3D Brain Atlas 项目
前端
T1mzhou2 小时前
ARM64 Linux 6.10内核启动流程2-rootfs 怎么挂上
linux·服务器·前端
小小尚@3 小时前
WebGIS/ECharts 地图开发|MapLand 在线行政区划边界一键下载 GeoJSON 工具
前端·javascript·echarts
默_笙4 小时前
🚍 一条 Todo 的奇幻漂流:TypeScript 全栈类型安全的"护照检查"
前端·javascript
计算机魔术师4 小时前
NVIDIA 以 129.3 亿美元收购 Hugging Face
前端
平头哥技术团队4 小时前
Day 01 | 用 HTML 写第一个网页:双击就能在浏览器打开
前端
计算机魔术师5 小时前
两年翻45倍!英伟达靠买买买建成千亿投资帝国
前端
paopaokaka_luck5 小时前
基于springboot3+vue3的精准扶贫管理系统(AI 问答、ECharts 图形化分析)
前端·人工智能·echarts