模拟 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)
        }
    })

}
相关推荐
一只小灿灿8 分钟前
前端计算机视觉:使用 OpenCV.js 在浏览器中实现图像处理
前端·opencv·计算机视觉
前端小趴菜0520 分钟前
react状态管理库 - zustand
前端·react.js·前端框架
Jerry Lau1 小时前
go go go 出发咯 - go web开发入门系列(二) Gin 框架实战指南
前端·golang·gin
我命由我123451 小时前
前端开发问题:SyntaxError: “undefined“ is not valid JSON
开发语言·前端·javascript·vue.js·json·ecmascript·js
0wioiw01 小时前
Flutter基础(前端教程③-跳转)
前端·flutter
落笔画忧愁e1 小时前
扣子Coze纯前端部署多Agents
前端
海天胜景1 小时前
vue3 当前页面方法暴露
前端·javascript·vue.js
GISer_Jing2 小时前
前端面试常考题目详解
前端·javascript
Boilermaker19922 小时前
【Java EE】SpringIoC
前端·数据库·spring
中微子3 小时前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript