vuex vue-router 原理解析

vuex vue-router 原理解析

vuex 原理

vuex源码解析

使用:安装vuex插件

一开始是在beforeCreate声明周期函数前实例化\(store对象并注入到全局对象里,所以所有组件都能通过 this.\)store 访问到vuex中的数据;

如何实现修改的呢?

其本质就是将我们传入的state作为一个隐藏的vue组件的data,也就是说,我们的commit操作,本质上其实是修改这个组件的data值,结合上文的computed,修改被defineReactive代理的对象值后,会将其收集到的依赖的watcher中的dirty设置为true,等到下一次访问该watcher中的值后重新获取最新值。

这样就能解释了为什么vuex中的state的对象属性必须提前定义好,如果该state中途增加一个属性,因为该属性没有被defineReactive,所以其依赖系统没有检测到,自然不能更新。

vue的响应式原理,数据劫持+发布-订阅者模式

  • vue开始会初始化一个state对象,会调用initState方法,该方法主要会初始化 data、props、methods等属性;

  • initData就会初始化数据,每个组件会为自己的data属性创建一个observer实例;

  • 在创建observer实例的时候会调用一个walk方法遍历data中的每个数据并对其调用defineReactive方法;

  • defineReactive方法会为每个属性创建一个dep实例(相当于observer和watcher的桥梁),并且对属性的 set 和 get 进行劫持;

    • set的时候会调用 dep.notify() 函数来更新依赖的每一个watcher;
    • get的时候,会看 dep.target 是否存在,若存在则调用 dep.depend(),dep.target实际上就是当前的watcher,若存在我们就将这个watcher放入dep的subs数组里;
  • initComputed 会初始化计算属性,其会对每一个计算属性创建一个 Watcher实例,并对每个属性调用defineComputed 方法;

  • 创建watcher实例的时候会传入 {lazy: true;} 来声明是计算属性(和data不太一样,可以减少计算量)

    • 实例化的时候会有 this.dirty=this.lazy,由于lazy是true了因此一开始dirty也会是true,这就说明数据需要重新获取一下(调用evaluate函数,里面重新获取计算数据并且将dirty设为false)
    • watcher 的get里会先将当前 dep.target push进watcher再调用用户传入的方法
    • 调用用户传入的方法,就会要访问data里的数据 自然就会执行 get 里些的代码,之前说过,get会将当前的watcher放入data的dep中,这样依次下去就找到了该computed属性依赖的所有data(或被defineReactive包装过的对象),也就是依赖的data的dep里都有相应的watcher;

总体过程

  1. 初始化 data和computed,分别代理其set以及get方法, 对data中的所有属性生成唯一的dep实例。
  2. 对computed中的reversedMessage生成唯一watcher,并保存在vm._computedWatchers中
  3. 访问 reversedMessage,设置Dep.target指向reversedMessage的watcher,调用该属性具体方法reversedMessage
  4. 方法中访问this.message,即会调用this.message代理的get方法,将this.message的dep加入reversedMessage的watcher,同时该dep中的subs添加这个watcher
  5. 设置vm.message = 'World',调用message代理的set方法触发dep的notify方法'
  6. 因为是computed属性,只是将watcher中的dirty设置为true
  7. 最后一步vm.reversedMessage,访问其get方法时,得知reversedMessagewatcher.dirty为true,调用watcher.evaluate()方法获取新的值。

vue-router 原理

主要原理是,更新视图而不重新请求页面。有两种方式,hash和history(同时不是浏览器的话还有abstract)

vue-router原理

vue-router原理(包括 dispatch commit部分)

hash模式

  • hash(“#”)符号的本来作用是加在URL中指示网页中的位置:
  • hash虽然出现在URL中,但不会被包括在HTTP请求中。它是用来指导浏览器动作的,对服务器端完全无用,因此,改变hash不会重新加载页面

  • 可以为hash的改变添加监听事件:

    window.addEventListener("hashchange", funcRef, false)
    
  • 每一次改变hash(window.location.hash),都会在浏览器的访问历史中增加一个记录

1 $router.push() //调用方法

2 HashHistory.push() //根据hash模式调用,设置hash并添加到浏览器历史记录(添加到栈顶)(window.location.hash= XXX)

3 History.transitionTo() //监测更新,更新则调用History.updateRoute()

4 History.updateRoute() //更新路由

5 {app._route= route} //替换当前app路由

6 vm.render() //更新视图

上面的前五步都是路由更新的过程,第六步是视图更新,在插件安装的时候,会调用插件里的install函数,会在 beforeCreate 生命钩子前将router对象赋给全局的 _router 对象,并且对其调用 defineReactive 方法定义响应式的route属性,当router值发生变化就会触发相应的render操作。

  • 同时我们还需注意到用户直接在地址栏改变url时的操作,因此要监听 onhashchange 事件。

history模式

History interface是浏览器历史记录栈提供的接口,通过back(), forward(), go()等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。

从HTML5开始,History interface提供了两个新的方法:pushState(), replaceState()使得我们可以对浏览器历史记录栈进行修改:

window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)

这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前URL改变了,但浏览器不会立即发送请求该URL

  • 代码结构以及更新视图的逻辑与hash模式基本类似,只不过将对window.location.hash直接进行赋值window.location.replace()改为了调用history.pushState()和history.replaceState()方法。

  • 同时我们可以监听 onpopstate 事件来处理url地址栏直接修改的情况。

posted @ 2021-03-23 01:24  TRY0929  阅读(369)  评论(0编辑  收藏  举报