热门标签 | 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的了。




推荐阅读
  • 本文介绍了如何使用 Node.js 和 Express(4.x 及以上版本)构建高效的文件上传功能。通过引入 `multer` 中间件,可以轻松实现文件上传。首先,需要通过 `npm install multer` 安装该中间件。接着,在 Express 应用中配置 `multer`,以处理多部分表单数据。本文详细讲解了 `multer` 的基本用法和高级配置,帮助开发者快速搭建稳定可靠的文件上传服务。 ... [详细]
  • 为了确保iOS应用能够安全地访问网站数据,本文介绍了如何在Nginx服务器上轻松配置CertBot以实现SSL证书的自动化管理。通过这一过程,可以确保应用始终使用HTTPS协议,从而提升数据传输的安全性和可靠性。文章详细阐述了配置步骤和常见问题的解决方法,帮助读者快速上手并成功部署SSL证书。 ... [详细]
  • 如何使用ES6语法编写Webpack配置文件? ... [详细]
  • 为开发者提供了一系列实用的参考网站和资源链接,包括HTML速查手册( 和 ),帮助开发者快速查找和学习相关技术知识。此外,还涵盖了其他重要的开发工具和文档,为编程工作提供全面支持。 ... [详细]
  • JavaScript XML操作实用工具类:XmlUtilsJS技巧与应用 ... [详细]
  • HTML 页面中调用 JavaScript 函数生成随机数值并自动展示
    在HTML页面中,通过调用JavaScript函数生成随机数值,并将其自动展示在页面上。具体实现包括构建HTML页面结构,定义JavaScript函数以生成随机数,以及在页面加载时自动调用该函数并将结果呈现给用户。 ... [详细]
  • 在深入研究 React 项目的过程中,特别是在探索 react-router 源码时,我发现了其中蕴含的中间件概念。这激发了我对中间件的进一步思考与整理。本文将详细探讨 Redux 中间件的原理及其在实际项目中的应用,帮助读者更好地理解和使用这一强大工具。通过具体示例和代码解析,我们将揭示中间件如何提升应用的状态管理和异步操作处理能力。 ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • 本文介绍了一种使用 JavaScript 计算两个日期之间时间差的方法。该方法支持多种时间格式,并能返回秒、分钟、小时和天数等不同精度的时间差。 ... [详细]
  • 单元测试:使用mocha和should.js搭建nodejs的单元测试
    2019独角兽企业重金招聘Python工程师标准BDD测试利器:mochashould.js众所周知对于任何一个项目来说,做好单元测试都是必不可少 ... [详细]
  • 本文以 www.域名.com 为例,详细介绍如何为每个注册用户提供独立的二级域名,如 abc.域名.com。实现这一功能的核心步骤包括:首先,确保域名支持泛解析,即将 A 记录设置为 *.域名.com,以便将所有二级域名请求指向同一服务器。接着,在服务器端使用 ASP.NET 2.0 进行配置,通过解析 HTTP 请求中的主机头信息,动态识别并处理不同的二级域名,从而实现个性化内容展示。此外,还需在数据库中维护用户与二级域名的对应关系,确保每个用户的二级域名都能正确映射到其专属内容。 ... [详细]
  • 本文全面解析了JavaScript中的DOM操作,并提供了详细的实践指南。DOM节点(Node)通常代表一个标签、文本或HTML属性,每个节点都具有一个nodeType属性,用于标识其类型。文章深入探讨了DOM节点的创建、查询、修改和删除等操作,结合实际案例,帮助读者更好地理解和掌握DOM编程技术。 ... [详细]
  • Angular Material 卡片组件的实现原理与技术解析 ... [详细]
  • 优化后的标题:利用 jQuery 实现高效树形结构元素选择与操作
    在Web前端开发中,DOM结构本质上是一种树形结构。通过优化后的jQuery选择器,可以高效地选择和操作DOM树中的节点。这些选择器不仅简化了代码编写,还提高了性能和可维护性。本文将详细介绍如何利用jQuery的树形选择器实现高效的元素选择与操作。 ... [详细]
  • 在HTML5应用中,Accordion(手风琴,又称抽屉)效果因其独特的展开和折叠样式而广泛使用。本文探讨了三种不同的Accordion交互效果,通过层次结构优化信息展示和页面布局,提升用户体验。这些效果不仅增强了视觉效果,还提高了内容的可访问性和互动性。 ... [详细]
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社区 版权所有