Vue2原理

vue双向数据绑定原理

在这里插入图片描述
在这里插入图片描述
Observer:能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
Compile:对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
Watcher:作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图

参考:https://blog.csdn.net/yihanzhi/article/details/79731813
大致原理:https://segmentfault.com/a/1190000013294870

proxy

vue里代替defineproperty原因:

  1. Proxy无需一层层递归为每个属性添加代理,一次即可完成以上操作
  2. 性能上更好,并且原本的实现有一些数据更新不能监听到,但Proxy可以完美监听到任何方式的数据改变
  3. 缺点:浏览器兼容性

生命周期

vue生命周期

  • 创建前/后:在 beforeCreated 阶段,Vue 实例的挂载元素 el和数据对象data以及事件还未初始化。在created阶段,Vue实例的数据对象data以及方法的运算有了,el 和数据对象 data 以及事件还未初始化。
  • 载入前/后:在 beforeMount 阶段,render 函数首次被调用,Vue 实例的 $el 和 data 都初始化了,但还是挂载在虚拟的 DOM 节点上。在 mounted 阶段,Vue 实例挂载到实际的 DOM 操作完成,一般在该过程进行 Ajax 交互。
  • 更新前/后:在数据更新之前调用,即发生在虚拟 DOM 重新渲染和打补丁之前,调用 beforeUpdate。在虚拟 DOM 重新渲染和打补丁之后,会触发 updated 方法。
  • 销毁前/后:在执行实例销毁之前调用 beforeDestory,此时实例仍然可以调用。在执行 destroy 方法后,对 data 的改变不会再触发周期函数,说明此时 Vue 实例已经解除了事件监听以及和 DOM 的绑定,但是 DOM 结构依然存在。beforeDestroy 钩子函数的执行时机是在 $destroy 函数执行最开始的地方,接着执行了一系列的销毁动作,包括从 parent 的 $children 中删掉自身,删除 watcher,当前渲染的 VNode 执行销毁钩子函数等,执行完毕后再调用 destroy 钩子函数。在 $destroy 的执行过程中,它又会执行 vm.patch(vm._vnode, null) 触发它子组件的销毁钩子函数,这样一层层的递归调用,所以 destroy 钩子函数执行顺序是先子后父,和 mounted 过程一样
  • activated 和 deactivated 钩子函数是专门为 keep-alive 组件定制的钩子

vue路由原理

都可以经过修改URL,在不重新请求页面的情况下更新页面视图。
会对mode做一些校验:若浏览器不支持HTML5History方式(通过supportsPushState变量判断),则mode设为hash;若不是在浏览器环境下运行,则mode设为abstract;

hash:

  • 比如 ‘http://www.baidu.com/#/abc’ hash 的值为 ‘#/abc’
  • 虽然在地址里,但是不会在 HTTP 请求中,因此改变 hash 不会重新加载页面,会触发 onhashchange 事件

history:

  • 通过h5中新增的方法pushState() replaceState(),可以新增修改历史记录
  • 虽然改变了当前的 URL,但你浏览器不会立即向后端发送请求(重新加载会发送请求)
  • 通过onpopstate 事件,监听history变化

history优势:

  • pushState设置的新url可以是与当前url同源的任意url,而hash只可修改#后面的部分
  • pushState设置的新url可以与当前url一模一样,这样也会把记录添加到栈中,而hash设置的新值必须与原来不一样才会触发记录添加到栈中
  • pushState通过stateObject可以添加任意类型的数据记录中,而hash只可添加短字符串
  • pushState可额外设置title属性供后续使用

history模式的问题:
对于单页应用来说,理想的使用场景是仅在进入应用时加载index.html,后续在的网络操作通过ajax完成,不会根据url重新请求页面,但是如果用户直接在地址栏中输入并回车,浏览器重启重新加载等特殊情况。

hash模式仅改变hash部分的内容,而hash部分是不会包含在http请求中的(hash带#):所以hash模式下遇到根据url请求页面不会有问题

而history模式则将url修改的就和正常请求后端的url一样(history不带#)(http://oursite.com/user/id),如果这种向后端发送请求的话,后端没有配置对应/user/id的get路由处理,会返回404错误。

https://blog.csdn.net/weixin_58089129/article/details/120618550
https://blog.csdn.net/weixin_43953753/article/details/86912845
https://segmentfault.com/a/1190000014822765?utm_source=tag-newest#articleHeader1

vuex原理:

vuex规则
https://www.jianshu.com/p/d95a7b8afa06
https://mp.weixin.qq.com/s/igkif-J_BHd1q5mZ7TewCw
https://blog.csdn.net/weixin_44003190/article/details/104334140

vuex =>
	return {
	    Store: Store,
	    install: install,
	    version: '3.1.1',
	    mapState: mapState,
	    mapMutations: mapMutations,
	    mapGetters: mapGetters,
	    mapActions: mapActions,
	    createNamespacedHelpers: createNamespacedHelpers
	  }

vuex的store是如何注入到组件中的?
Vue.use(Vuex)
–> 有install方法就执行install,没有直接执行函数
–> 执行install(不重复install判断)
–> applyMixin() = Vue.mixin({ beforeCreate: vuexInit })

function vuexInit () {
	var options = this.$options;
   	// store injection
   	if (options.store) {
     	this.$store = typeof options.store === 'function'
       		? options.store() : options.store;
   	} else if (options.parent && options.parent.$store) {
   		this.$store = options.parent.$store;
   	}
}

vuex的state和getters是如何映射到各个组件实例中响应式更新状态呢?

const wrappedGetters = store._wrappedGetters
const computed = {}
forEachValue(wrappedGetters, (fn, key) => {
  	computed[key] = partial(fn, store) // fn(store)
  	Object.defineProperty(store.getters, key, {
		get: () => store._vm[key],
    	enumerable: true // for local getters
  	})
})
store._vm = new Vue({
	// new vue:state存入vue实例组件的data中,data是响应式的,实现双向数据绑定
    data: {
      	$$state: state
    },
    // getters:借助vue的计算属性computed实现数据实时监听
    computed
})

关于modules

getters

  • 是不能注册相同名称的方法
  • 如果没有注册命名空间,想获取a模块中的getters中的方法,用法为this.$store.getters.属性名
  • 如果注册了命名空间,想获取a模块中的getters中的方法,用法为this.$store.getters[‘a/xx’]

mutations

  • 可以注册相同名称的方法,相同方法会存入到数组中按顺序执行
  • 如果没有注册命名空间,想获取a模块中的mutations中的方法,用法为this.$store.commit(‘属性名’)
  • 如果注册了命名空间,想获取a模块中的mutations中的方法,用法为this.$store.commit[‘a/xx’]

actions
actions和mutations原理几乎一模一样,主要区别如下

  • 调用时接受的参数不是state而是一个经过处理的对象可以解构出state,commit等属性
  • 调用时没有命名空间是this.$ store.dispatch(xx) ,有命名空间是this.$store.dispatch(‘a/xx’)

computed

参考:https://segmentfault.com/a/1190000010408657
https://blog.csdn.net/NewTyun/article/details/104013076

function initComputed (vm, computed) {
    var watchers = vm._computedWatchers = Object.create(null);
    for (var key in computed) {
    	var userDef = computed[key];
    	var getter = typeof userDef === 'function' ? userDef : userDef.get;
    	{
	        if (getter === undefined) {
	            warn(("No getter function has been defined for computed property \"" + key + "\"."), vm);
	            getter = noop;
        	}
    	}
    	// create internal watcher for the computed property.
    	watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions);

    	// component-defined computed properties are already defined on the
    	// component prototype. We only need to define computed properties defined
    	// at instantiation here.
    	if (!(key in vm)) {
        	defineComputed(vm, key, userDef);
    	} else {
        	if (key in vm.$data) {
            	warn(("The computed property \"" + key + "\" is already defined in data."), vm);
        	} else if (vm.$options.props && key in vm.$options.props) {
            	warn(("The computed property \"" + key + "\" is already defined as a prop."), vm);
        	}
    	}
   	}
}
function defineComputed (target, key, userDef) {
    const shouldCache = !isServerRendering()
    if (typeof userDef === 'function') {
        sharedPropertyDefinition.get = createComputedGetter(key);
        sharedPropertyDefinition.set = noop;
    } else {
        sharedPropertyDefinition.get = userDef.get
            ? userDef.cache !== false
                ? createComputedGetter(key)
                : userDef.get
            : noop;
        sharedPropertyDefinition.set = userDef.set
            ? userDef.set
            : noop;
    }
    Object.defineProperty(target, key, sharedPropertyDefinition);
}

function createComputedGetter (key) {
    return function computedGetter () {
        var watcher = this._computedWatchers && this._computedWatchers[key];
        if (watcher) {
            if (watcher.dirty) {
                watcher.evaluate();
            }
            if (Dep.target) {
                watcher.depend();}
            return watcher.value
        }
    }
}

Watcher.prototype.evaluate = function evaluate () {
    this.value = this.get();
    this.dirty = false;
};
//顺藤摸瓜  再看get是怎么实现的
Watcher.prototype.get = function get () {
    pushTarget(this);
    var value;
    var vm = this.vm;
    try {
        value = this.getter.call(vm, vm);
    } catch (e) {
       if (this.user) {
           handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
       } else {
           throw e
       }
    } finally {
       // "touch" every property so they are all tracked as
       // dependencies for deep watching
       if (this.deep) {
           traverse(value);
       }
       popTarget();
       this.cleanupDeps();
    }
    return value
}

bus

vue中的bus事件,一般作为中央事件总线来使用
简单例子:比如在A,B组件为兄弟组件,现在A要调用B的中C事件
1.创建一个bus.js

import Vue from 'vue'
const Bus = new Vue()
export { Bus }

2.在A,B组件中引入bus.js

import { Bus } from 'bus'

3.在A组件中定义要调用B事件的bus事件名

例如:Bus.$emit('callC')

4.在B组件中调用C方法

Bus.$on('callC',this.C)
Bus.$on里有两个参数,第一个是在A组件定义的名字,第二个参数是B组件要调用的方法

vue和react对比

https://blog.csdn.net/m0_37631322/article/details/80719756
相同点:
    都是组件化开发,props传递
    都是虚拟dom
    配套框架
区别
    模板 vs JSX
    对象属性和状态管理

虚拟DOM Diff算法

  1. 虚拟 dom
  • 虚拟 dom 是利用 js 描述元素与元素的关系,用 js 对象来表示真实的 DOM 树结构,创建一个虚拟 DOM 对象
  • 由于在浏览器中操作 DOM 是很昂贵的。
  • 频繁的操作 DOM,会产⽣⼀定的性能问题.
  • 在组件渲染的时候会调用 render 函数,这个函数会生成一个虚拟 dom,再根据这个虚拟 dom 生成真实的 dom,然后这个真实的 dom 会挂载到我们的页面中。
  • 如果只是渲染一个页面后期不改动的话 那么虚拟 dom 其实成本更高 因为 都要渲染成真实的 dom
  • 如果组件内有响应的数据,数据发生改变的时候 render 函数会生成一个新的虚拟 dom ,新的虚拟 dom 树和旧的虚拟 dom 树进行对比,找到要要修改的虚拟 dom 的部分,去修改相对应部分的真实 dom
  1. diff 算法
  • diff 算法就是对虚拟 dom 进行对比,并返回一个 patch 对象,这个对象的作用是存储两个节点不同的地方,最后用 patch 里记录的信息去局部更新真实的 dom

  • diff 算法的步骤

    1. js 对象表示真实的 dom 结构,就是我们说的生成一个虚拟 dom,再用虚拟 dom 构建一个真的 dom 树,放到页面中。

    2. 状态改变的时候生成一个新的虚拟 dom 跟旧的进行对比,这个对比的过程就是 diff 算法,通过 patch 对象记录差异

    3. 把记录的差异用在第一个虚拟 dom 构建的真实的 dom 上,视图就更新了

Vue 的 diff 算法是平级⽐较,不考虑跨级⽐较的情况。内部采⽤深度递归的⽅式+双指针⽅式⽐较

原理简述:
(1)先去同级比较,然后再去比较子节点
(2)先去判断一方有子节点一方没有子节点的情况
(3)比较都有子节点的情况
(4)递归比较子节点
在这里插入图片描述

在这里插入图片描述
https://blog.csdn.net/qq_42072086/article/details/107965196

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值