当前位置:   article > 正文

vue-router原理浅析_vueroute原理

vueroute原理

本文将浅析vue-router的具体实现,并且实现一个简易的vue-router。

一.前置知识

如果你很了解vue的插件应该怎么写,可以跳过此部分。

1.Vue.use( plugin) 安装 Vue.js 插件。

Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:

  1. MyPlugin.install = function (Vue, options) {
  2. // 1. 添加全局方法或属性
  3. Vue.myGlobalMethod = function () {
  4. // 逻辑...
  5. }
  6. // 2. 添加全局资源
  7. Vue.directive('my-directive', {
  8. bind (el, binding, vnode, oldVnode) {
  9. // 逻辑...
  10. }
  11. ...
  12. })
  13. // 3. 注入组件选项
  14. Vue.mixin({
  15. created: function () {
  16. // 逻辑...
  17. }
  18. ...
  19. })
  20. // 4. 添加实例方法
  21. Vue.prototype.$myMethod = function (methodOptions) {
  22. // 逻辑...
  23. }
  24. }

2.Vue.mixin( mixin )

全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。

  1. Vue.mixin({
  2. created: function () {
  3. var myOption = this.$options.myOption
  4. if (myOption) {
  5. console.log(myOption)
  6. }
  7. }
  8. })
  9. new Vue({
  10. myOption: 'hello!'
  11. })
  12. // => "hello!"

 

二.流程

1.使用一个current变量储存当前url

2.监听浏览器url变化,改变current

3.监听current,获取应该渲染的新组件

4.渲染新组件

下面细讲:

1.使用一个current变量储存当前url

  1. class HistoryRoute{
  2. constructor(){
  3. this.current=null;
  4. }
  5. }
  6. class vueRouter{
  7. constructor(options){
  8. this.mode=options.mode||'hash';
  9. this.routes=options.routes;
  10. this.history=new HistoryRoute;
  11. }
  12. }
  13. export default vueRouter;

在上述代码中,我们写了两个类,current用于保存当前路径,vueRouter则是我们要export出去的对象,所以他可以接受到一个options,这个options是从main.js(如果是vue-cli3则是router/index.js)中,new vueRouter时传进来的。(如下图,红框部分)

我们在构造函数中,取出mode,如果没有则默认是hash模式。把路由表保存到routes中。

2.监听浏览器url变化,改变current

  1. window.addEventListener('load',()=>{
  2. this.history.current=location.hash.slice(1);
  3. })
  4. window.addEventListener('hashchange',()=>{
  5. this.history.current=location.hash.slice(1);
  6. })

通过对load的监听,初始化history中current的值,通过对hashchange的监听,改变current的值。

 

3.监听current,获取应该渲染的新组件

current如何监听?vue提供了一个defineReactive的公共方法:

  1. // exposed util methods.
  2. // NOTE: these are not considered part of the public API - avoid relying on
  3. // them unless you are aware of the risk.
  4. Vue.util = {
  5. //warn: warn,
  6. //extend: extend,
  7. //mergeOptions: mergeOptions,
  8. defineReactive: defineReactive$$1
  9. };
  10. /**
  11. * Define a reactive property on an Object.
  12. */
  13. function defineReactive$$1 (
  14. obj,
  15. key,
  16. val,
  17. customSetter,
  18. shallow
  19. ) {
  20. var dep = new Dep();
  21. var property = Object.getOwnPropertyDescriptor(obj, key);
  22. if (property && property.configurable === false) {
  23. return
  24. }
  25. // cater for pre-defined getter/setters
  26. var getter = property && property.get;
  27. var setter = property && property.set;
  28. if ((!getter || setter) && arguments.length === 2) {
  29. val = obj[key];
  30. }
  31. var childOb = !shallow && observe(val);
  32. Object.defineProperty(obj, key, {
  33. enumerable: true,
  34. configurable: true,
  35. get: function reactiveGetter () {
  36. var value = getter ? getter.call(obj) : val;
  37. if (Dep.target) {
  38. dep.depend();
  39. if (childOb) {
  40. childOb.dep.depend();
  41. if (Array.isArray(value)) {
  42. dependArray(value);
  43. }
  44. }
  45. }
  46. return value
  47. },
  48. set: function reactiveSetter (newVal) {
  49. var value = getter ? getter.call(obj) : val;
  50. /* eslint-disable no-self-compare */
  51. if (newVal === value || (newVal !== newVal && value !== value)) {
  52. return
  53. }
  54. /* eslint-enable no-self-compare */
  55. if (customSetter) {
  56. customSetter();
  57. }
  58. // #7981: for accessor properties without setter
  59. if (getter && !setter) { return }
  60. if (setter) {
  61. setter.call(obj, newVal);
  62. } else {
  63. val = newVal;
  64. }
  65. childOb = !shallow && observe(newVal);
  66. dep.notify();
  67. }
  68. });
  69. }

有了这个方法,我们就可以把监听current了

  1. vueRouter.install=function(vue){
  2. vue.mixin({
  3. beforeCreate(){
  4. if(this.$options&&this.$options.router){
  5. this._root=this;
  6. this._router=this.$options.router;
  7. vue.util.defineReactive(this,'current',this._router.history);
  8. }else{
  9. this._root=this.$parent._root;
  10. }
  11. }
  12. })
  13. }

 

4.渲染新组件

我们能拿到当前hash了,也有一个router的路由可以对上了,如果我们把router改造成hash为对象的key的数组,这样取对应的组件的时候就容易多了

  1. createMap(routes){
  2. return routes.reduce((memo,current)=>{
  3. memo[current.path]=current.component;
  4. return memo
  5. },{})
  6. }

这样我们就可以通过memo[hash] 很快的取到对应的组件。

  1. vue.component('router-view',{
  2. render(h){
  3. let current=this._self._root._router.history.current;
  4. let routesMap=this._self._root._router.routesMap;
  5. return h(routesMap[current])
  6. }
  7. })

这样,我们就基本完工啦~

测试:

新建一个vue项目,在项目中新建一个my-router的文件夹,在文件夹中新建index.js,把VueRouter的引用指向新的文件

import VueRouter from '../myrouter'

在浏览器地址栏更改地址,成功跳转!

 

留个小问题:

请在myrouter/index.js中实现一个简单的<router-link>

答案:

  1. vue.component('router-link',{
  2. props:{
  3. to: String,
  4. },
  5. render(h){
  6. var getChildrenTextContent = function (children) {
  7. return children.map(function (node) {
  8. return node.children
  9. ? getChildrenTextContent(node.children)
  10. : node.text
  11. }).join('')
  12. }
  13. var headingId = getChildrenTextContent(this.$slots.default);
  14. return h(
  15. "div",
  16. [
  17. h('a', {
  18. attrs: {
  19. name: headingId,
  20. href: '#' + this.to
  21. }
  22. },this.$slots.default)
  23. ]
  24. )
  25. }
  26. })

完整代码已经上传到文章附件中

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/430383
推荐阅读
相关标签
  

闽ICP备14008679号