热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

vue暴露的全局方法_Vue全家桶之vuerouter手写实现

经过自己平时的学习,也因为自己现在也是从事的Vue的开发,所以平时对Vue相对来说还是较为熟悉,但是平时自己一直有一个很大的痛点ÿ

经过自己平时的学习,也因为自己现在也是从事的Vue的开发,所以平时对Vue相对来说还是较为熟悉,但是平时自己一直有一个很大的痛点,那就是对Vue的底层实现原理等等这些不够了解,所以很多时候定位错误都可能需要花费很多时间,所以深度学习并且掌握一门框架的核心实现是非常有必要的。

主题:学习Vue的全家桶vue-router的实现原理和步骤

目标:清晰思想,了解步骤,动手实现简化版

预备知识:

  • Vue的插件实现写法

  • render函数的使用

  • 数据响应式方法:Vue.util.defineReactive / new Vue ( { data: {} } )

一、预备知识回顾

1.Vue的插件实现写法:Vue的插件一般是用来为Vue来添加全局功能的,可以用来添加全局方法或者属性、添加全局资源(指令/过滤器等等)、通过全局混入来添加一些组件的选项以及可以添加Vue的实例方法。

  • 插件的声明:Vue的插件需要实现暴露一个install方法,方法的第一个参数是Vue构造器,第二个参数是一个可选的选项对象

MyPlugin.install = function (Vue, options) { // 1.添加全局方法或者属性 Vue.globalMethod = function () {} // 2.添加全局资源 Vue.directive('my-directive', {}) // 3.混入注入组件选项 Vue.mixin({ created: function (){} }) // 4.添加实例方法 Vue.prototype.$myMethod = function(methodOptions){}}

  • 插件的使用

插件的使用可以有2种形式1.第一种那就是在实现的插件代码里面加上代码:if (typeof window !== 'undefined' && window.Vue) { // 使用插件 window.Vue.use(MyPlugin)}2.第二种就是可以直接在main.js中直接使用Vue.use(MyPlugin)

2.render函数的使用:Vue推荐在日常使用中应该使用模板来创建HTML,但是有的时候需要JS的能力,那么这时候就可以使用渲染函数来完成

  • 基础

render: function (createElement) { // createElement函数返回结果是VNode return createElement( tag, // 标签名称 data, // 传递数据 children // 子节点数组 )}

  • 拓展:为什么常见的使用的是render(h)?

Why:从例子中我们可以看到我们常见的都是使用的render(h),通过学习发现,其实在Vue的底层在生成虚拟DOM方面其实是借鉴的Snabbdom.js,而在这个里面他的生成虚拟DOM的函数就叫h,所以其实h就是我们上面看到的createElement

// 例子heading组件// {{title}}Vue.component('heading', { props: { title: { type: String, default: '' } }, render(h) { return h( 'h2', // 参数1:tagname {attrs: {title: this.title} } // 参数2:与模板中属性对应的数据对象 this.$slots.default // 参数3:子节点VNode数组 ) }})

3.数据响应式方法:Vue.util.defineReactive / new Vue ({data: {}})

我们知道Vue最大的亮点之一就是响应式,凡是写入data里面的都会被变成响应式数据,而Vue2.x当中实现响应式就是使用的defineReactive,来看下具体的用法:

// defineReactive:定义一个对象的响应属性 Vue.util.defineReactive(obj, key, value, fn) obj:目标对象 key:目标对象属性 value:属性值 fn:只在node调试环境下set时调用

其实Vue.util中还有一些函数,如:

d03dbd7302f3e233d30bd930f14daa0f.png

二、Vue全家桶之Vue-router的手动实现

    1.在我们手动实现vue-router之前,我们先看一下他在Vue中的使用

  • 安装

vue add router

  • 核心步骤:

    • 使用vue-router插件(router.js)

import Router from 'vue-router'Vue.use(Router)

  • 创建Router实例(router.js)

export default new Router({...})

  • 在根组件上添加该实例(main.js)

import router from './router'new Vue({ router}).$mount('#app')

  • 添加路由视图(App.vue)

  • 导航

"/">home"/about">aboutthis.$router.push('/')this.$router.push('/about')

2.我们发现作为Vue全家桶之一的vue-router使用起来非常方便,帮助我们很好的处理了路由的跳转的问题,那么现在我们就来看看怎么实现一个简化版的vue-router,从而更加了解他的内部原理

f76553a63a7b3bc2489aa13dcdc33cfd.png

4.代码编写:

  • 实现一个插件:创建VueRouter类和install方法

    • 一个问题:我们在install的方法中为什么要使用mixin混入的形式?

我们在使用vue-router的时候我们可以发现,我们是先使用use注册的,然后再去创建的Router实例,但是我们在写install方法的时候又需要用到,所以我们只能只用mixin来做延迟处理,等到有了组件实 例之后我们再进行使用

// 新建一个kvue-router.js// 引用构造函数,VueRouter中要使用let Vue// 保存用户的选项class VueRouter() { constructor(options){ this.$options = options }}// 实现VueRouter的install方法,注册$router在Vue的原型之上VueRouter.install = function(_Vue) { // 引用构造函数,VueRouter要使用 Vue = _Vue Vue.mixin({ beforeCreate(){ if(this.$options.router) { // 将$router挂载到Vue的原型之上,方便所有的组件都可以使用this.$router Vue.prototype.$router = this.$options.router } } }) // 实现俩个全局的组件router-link和router-view Vue.component('router-link', RouterLink) Vue.component('router-view', RouterView)}export default VueRouter

  • 创建router-view和router-link

// router-linkVue.component('router-link', { props: { to:{ type: String, required: true } }, render(h) { return h( 'a', attrs: { href: '#'+ this.to }, this.$slots.default ) }})// router-viewVue.component('router-view', { render(h) { return h(null) }})

  • 监控URL变化:定义响应式的current属性,监听hashChange事件

class VueRouter(){ constructor(options) { // 定义一个响应式的数据current表示路由的变化 const initial = window.location.hash.slice(1) || '/' // Vue中的响应式方法 Vue.defineReactive(this, 'current', initial) // 监听hashChange事件 window.addEventListener('hashChange', this.onHashChange.bind(this)) } onHashChange(){ this.current = window.location.hash.slice(1) }}

  • 动态获取对应组件

// router-viewVue.component('router-view', { render(h) { // 动态的获取组件,进行内容的渲染 let component = null const route = this.$router.$options.routes.find(route => route.path === this.$router.current) if(route) component = route.component return h(component) }})

  • 提前处理路由表:写到这里我们可以发现其实我们已经实现了整个vue-router的功能,我们去用我们写好的kvue-router去替换掉vue-router,发现是可以实现我们想要的功能的,但是从上一步我们动态获取对应组件的逻辑里面我们不难发现,如果直接就这样写是会有性能问题的,我们会发现只要路由一变化,render就会执行一遍,这时候里面的遍历循环也会相应执行一遍,所以这个地方我们需要做一下优化,我们可以提前去处理好path和route的映射关系,这样就可以避免每次都要循环了

class VueRouter { constructor(options) { // 缓存path和route映射关系 this.routeMap = {} this.$options.routes.forEach(route => { this.routeMap[route.path] = route }); }}// router-viewexport default { render(h) { const {routeMap, current} = this.$router const component = routeMap[current] ? routeMap[current].component : null; return h(component); }}

四、总结:

    通过学习,Vue全家桶之一的Vue-router就算是手撸完成了,虽然只是简单版本的,但是感觉对于自己来说也是一个小的进展,基本原理基本清晰了,而且写完之后再去看源代码也清晰容易了不少,然后现在写完的这个简单的版本其实还差了一块,那就是没有实现子路由的嵌套,子路由的嵌套的逻辑上是路由嵌套,但是物理上其实就是router-view的嵌套,通过后续翻看源码发现,源码的思路是:先给所有的router-view组件的data都设置一个routerView属性,然后设置一个变量depth路由深度,然后去遍历,如果parent的$vnode.data里的routerView属性是true的话,depth就++,然后因为每次URL匹配的时候,结果都是一个数组,从根开始依次按层级放入,然后我们通过route.matched[depth]就可以知道该渲染哪一层的component的了。




推荐阅读
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 从零基础到精通的前台学习路线
    随着互联网的发展,前台开发工程师成为市场上非常抢手的人才。本文介绍了从零基础到精通前台开发的学习路线,包括学习HTML、CSS、JavaScript等基础知识和常用工具的使用。通过循序渐进的学习,可以掌握前台开发的基本技能,并有能力找到一份月薪8000以上的工作。 ... [详细]
author-avatar
大女人爱上淘包_502
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有