前端Vue2知识体系的汇集及总结

一.Vue生命周期

vue从创建到销毁的整个过程就称为vue的生命周期
(1) vue的生命周期钩子是回调函数, 当创建组件实例的过程中会调用相应的钩子方法, 内部会对钩子进行处理, 将钩子函数维护成数组的形式
(2) 每个阶段都有两个生命周期的钩子函数

  • 创建阶段—beforeCreate, created
  • 挂载阶段—beforeMount, mounted
  • 运行阶段—beforeUpdate, updated
  • 销毁阶段—beforeDestroy, destroyed

1.beforeCreate
beforeCreate: 在初始化实例后触发的第一个钩子, 此时data, methods, computed以及watch上的数据和方法都未初始化不可访问
2.created
created: 在实例创建完成后被立即调用, 此时data和methods都初始化完成 此时可进行ajax请求异步数据的获取, 初始化数据
注:模板编译阶段, created之后, beforeMount之前的这段时间内, 判断用户是否在参数中提供了el选项, 如果提供了就自动开启编译与挂载阶段; 如果没有el选项, 则需要执行vm. $mount方法, 手动开启模板编译阶段, 最后将模板编译为渲染函数
3.beforeMount
beforeMount: 执行到这个钩子的时候也就是发生挂载之前, 虚拟Dom以及创建完成, 但是还没有挂载到页面中
4.mounted
mounted: 真实的Dom挂载完毕, 数据已经完成双向绑定, vuejs会开启watcher来持续追踪依赖变化, 想通过插件操作页面上的DOM节点, 可以在这个阶段进行
在这里插入图片描述
5.beforeUpdate
beforeUpdate: 在已经挂载的状态下, vuejs会持续追踪状态的变化, 当状态发生改变时, watcher会通知虚拟DOM, 在虚拟DOM重新渲染前被触发, 你可以在当前阶段更改数据, 不会重新渲染, 但会再次触发当前钩子函数
6.updated
updated: 发生在更新完成之后, 此时DOM已经更新, 不要在此期间改变数据, 因为可能导致无限循环的更新
在这里插入图片描述
7.beforeDestroy
beforeDestroy: 发生在实例销毁之前, 当实例上的 $destroy()方法被调用时, 会卸载追踪依赖, 子组件和事件监听器
8.destroyed
destroyed: 发生在实例销毁之后, 卸载后会触发此生命周期钩子
生命周期在真实场景下的应用
mounted: 挂载元素内dom节点的获取
nextTick: 针对单一事件更新数据后立即操作dom
updated: 任何数据的更新 做统一的业务逻辑处理
watch: 监听具体数据变化, 并做相应的处理

二.MVVM原理的理解

(1) MVVM是Model-view-viewmodel的简写, 包括model数据层, view视图层, viewmodel层, 各部分的通信是双向的, 采用双向数据绑定, MVVM在MVC的基础上, 把控制层隐藏掉了
在这里插入图片描述
Vue不是一个完全的MVVM框架, 它是一个视图层框架
(2) MVC和MVVM的区别
1.MVC各部分通信是单向的, 而MVVM是双向的;
2.MVVM通过数据来显示视图层; MVC通过DOM操作来显示视图层
3.MVC中的controller演变成了MVVM中的viewmodel
4.传统的MVC指用户操作会请求服务端路由, 路由会调用对应的控制器来处理, 控制器会获取数据, 将结果返回给前端, 让页面重新渲染;
MVVM不需要用户收到操作DOM元素, 将数据绑定到viewModel层上, 会自动将数据渲染到页面中, 视图变化会通知viewModel层更新数据

三.Vue响应式原理


说明:watcher通过回调函数更新view; observer观测data数据,通过get通知dep收集watcher, dep通过notify()通知watcher更新数据, watcher通过**addDep()**收集依赖

核心

  • object.defineProperty, 监听对象属性的改变
  • 发布订阅者模式

1.数据劫持:组件实例初始化的时候. 先通过Object.defineproperty() 给每一个Data属性都注册 getter, setter 所以vue封装了一个observer类, 通过递归将数据中的所有属性都检测到
2.依赖追踪:当组件实例挂载mount时创建一个Watcher实例, 组件挂载会执行render function, 进而通过get获取到data中的属性, 将依赖的属性进行收集跟踪
3.派发更新: 当数据变化时, 会触发相应的set, 通过Watcher实例通知订阅的属性进行视图更新, 也有可能会触发用户的某个回调
(1) Observer的作用
Vue2通过Object.defineproperty(obj,key.handle) 将我们代码中的data属性进行getter 和 setter的响应式转化, 这样data中的数据获取, 数据改变就会触发注册过的get set事件, 从而触发视图更新的其他操作, 这个Object.defineproperty()的过程, 就是有Observer实现的

function definReactive(obj,key,val) {
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: () => {
        console.log('数据获取时,被触发');
        return val;
      },
      set: newVal => {
        if (val === newVal) {
          return;
        }
        val = newVal;
        console.log('数据改变时,被触发');
      }
    })
  };

  let data = {
    test: '初始值',
  };

  //对data 上的test属性进行绑定
  definReactive(data,'test',data.test);

  console.log(data.test);//数据获取时,被触发

  data.test = 'hello';//数据改变时,被触发

(2)Dep的作用
data有很多种属性, 但是我们可能只是用部分属性, Dep就是用来收集获取data中属性对应的依赖, 然后当触发set时, 通过发布订阅模式, 通知执行收集各个依赖, 执行视图更新等操作

所谓的依赖即Watcher

//我们根据Observer中Object.defineProperty,当读取某个属性就会触发get方法,进而进行依赖收集
function defineReactive(obj,key,val) {
  let Dep;  //依赖对象

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: () => {
      console.log('数据被获取');
      //数据获取将进行依赖收集
      Dep.depend();//本次收集依赖
      return val
    },
    set: newVal => {
      if(val === newVal) {
        return
      }
      val = newVal;
      //当数据更新,通知收集的依赖去执行更新操作
      Dep.notify();//给订阅者发消息,执行更新操作
      console.log('数据改变,执行更新操作');
    }
  })
}

(3).Watcher的作用
Watcher就是被收集的依赖 Watcher中要有两个方法, 第一个是通知变化并执行更新操作实例; 第二个是将自身实例添加到Dep的依赖收集中

class Watcher {
	update() {
	}
	addDep() {
	}
}

(4). Compile(指令解析器)
Compile 主要做的事情就是解析模板指令, 将模板中变量替换成数据, 然后初始化渲染页面视图, 并将每个指令对于的节点绑定更新函数, 添加鉴定数据的订阅者, 一旦数据有变动, 收到通知, 更新视图.

四.Vue虚拟DOM以及Diff算法

1.操作虚拟DOM就是可以将DOM抽象成以js对象为节点的虚拟DOM树, 用VNode节点虚拟真实DOM节点
2.虚拟DOM不依赖真实平台环境, 可以实现跨平台
3.新旧虚拟DOM对比更新时通过Diff算法进行同级比较, 也就是深度优先算法
4.Diff对比流程:
当数据改变时, 会触发setter, 并通过Dep.notify()去通知所有的订阅者Watcher, 订阅者们就会调用patch方法, 给真实的DOM打补丁, 更新相应的视图

在这里插入图片描述
(1)patch方法:
对比当前层虚拟节点是否为同一种类型

  • 是:继续执行patchVnode方法进行深层比对
  • 否:直接将虚拟DOM节点的整个替换成新虚拟节点

(2)sameVnode方法
用此方法来判断节点是否属于同一类型 例如虚拟节点的key值, 标签名, 注释节点, 定义了data是否一样或存在
(3)patchVnode方法

  • 找到真实的DOM节点,称为el
  • 如果newVnodeoldVnode指向同一个对象则 return
  • 如果新旧虚拟节点都是文本节点且不相等 那么将el的文本节点更新新虚拟节点的文本
  • 对比newVnode和oldVnode,如果newVnode有子节点而oldVnode没有 则将newVnode的子节点真实化添加到el
  • 如果oldVnode有子节点而newVnode没有, 则删除el的子节点
  • 如果都有子节点则执行updateChildren函数比较子节点

(4)updateChildren方法
是为新旧虚拟节点的子节点对比: 首尾指针法 新的子节点集合和旧的子节点集合, 各有首尾两个指针 通过首尾指针的移动 将首尾指针指向的节点用sameVnode方法进行比较 sameVnode(old,new);
如果所有的互相比较都匹配不到, 那么就将所有的旧子节点的key 做一个映射到旧节点下标的key -> index 表, 然后用新 vnode 的key去找出在旧节点中可以复用的位置
在这里插入图片描述
(5)用index作key

<ul>
  <li key="0">a</li>
  <li key="1">b</li>
  <li key="2">c</li>
</ul>

<ul>
  <li key="0">GGB</li>
  <li key="1">a</li>
  <li key="2">b</li>
  <li key="3">c</li>
</ul>

在左边的初始数据中,插入一个GGB的li标签新节点如果使用index来当作key的话:

<ul>
  <li v-for="(item,index) in list" :key="index">{{item.title}}</li>
</ul>

<button @click="add">增加</button>

list: [
  {title: "a", id: "100"},
  {title: "b", id: "101"},
  {title: "c", id: "102"},
]

add() {
  this.list.unshift({title: "d", id: "103"})
}

点击按钮后 我们发现所有的li标签全部都更新了一遍(就地复用)
原因:因为diff算法对比的时候, 会将旧首节点和新首节点的sameNode进行对比, 所以新的节点对比key都是0了, 依次对比下来, 原来就有的c节点此时的key却变成了4, 而之前并没有key为4的节点, 所以都更新的原因就是将前三个都进行patchVnode来更新文本, 而最后一个新增了节点

改进的办法:用特有的id来当作key值:

<ul>
	<li v-for="item in list" :key="id">{{item.id}}</li>
</ul>

这样的话就不会出现全部更新的浪费性能的方式

五.观察者模式和发布订阅者模式

1.观察者模式: 当对象之间存在一对多依赖关系时, 其中一个对象的状态发生改变, 所有依赖它的对象都会收到通知, 这就是观察者模式, 在此模式中, 只有两种主体, 目标对象(Subject)观察者(Observer)
目标对象(Subject): 拥有方法: 添加/删除/通知 Observer
观察者(Observer): 拥有方法: 接收Subject状态变更通知并处理
2.发布订阅模式: 基于一个事件中心, 接收通知的对象是订阅者, 需要先订阅某个事件, 触发事件的对象发布者, 发布者通过触发事件,通知各个订阅者. js中事件绑定和vue中的事件总线就是使用的发布订阅模式
发布订阅模式用来处理不同系统组件的信息交流, 即使这些组件不知道对方的存在(第三方中介)

六.Vue的异步更新和渲染

(1).为了性能考虑, vue采用异步更新队列
Vue DOM更新是异步执行的, 即修改数据时, 视图不会立即更新, 而是会监听数据变化, 并缓存在同一事件循环中, 等所有的数据变化完成后, 才进行视图更改 就可以保证watcher最后也只执行一次渲染流程.
(2)Vue.$nextTick
$nextTick 方法将回调延迟到下次DOM更新循环之后执行 也就是等数据更新后我们拿到的就是最新的.
$nextTick核心如下:
Vue在内部对异步队列尝试使用原生的Promise.then, MutationObserver和setImmediate, 如果执行环境不支持, 则会采用setTimeout(fn, 0)代替
源码中三个参数的解析:

  • callback: 我们要执行的操作, 可以放在这个函数当中, 我们每执行一次$nextTick就会把回调函数放在一个异步队列当中;
  • pending: 标识, 用以判断在某个事件循环中是否为第一次加入, 第一次加入的时候才触发异步执行队列挂载
  • timerFunc: 用来触发执行回调函数, 也就是Promise.thenMutationObserversetImmediatesetTimeout 的过程
    总结:把回调函数放入callbacks等待执行, 将执行函数放到微任务或者宏任务中, 事件循环到了微任务或者宏任务, 执行函数依次执行callbacks中的回调
    这里推荐网址:https://cloud.tencent.com/developer/article/1633546很清晰

七.Vue的data是什么类型 说出具体的原因

组件中的data必须是一个函数. return一个对象.
data是挂载在原型对象上的. 一个组件被复用多次的话, 就会创建多个实例. 如果data是对象的话,对象属于引用类型, 会影响到所有的实例, 因此为了保证组件不同的实例之间data不冲突, data必须是一个函数. 而new Vue的实例, 是不会被复用的, 因此不存在引用对象的问题.

八.Computed和watch

  • computed是计算属性, 也就是基于Data或者父组件传递的props计算来得到数据, computed的值是在getter执行后进行缓存的, 只有它依赖的数据发生变化, 才会重新调用getter来计算
    computed不支持异步, 当computed内有异步操作时无效, 不能监听数据的变化
  • watch是监听属性, 可以监听data和props中的数据, 然后执行相应的操作, watch不支持缓存, 数据变会直接触发相应的操作
    watch支持异步操作,监听的函数接受两个参数, 第一个参数是最新的值, 第二个参数是输入之前的值
    总结: 具体需要返回值到页面上的用计算属性computed
    监听数据变化不需要返回值时使用侦听属性watch

九.手写事件总线EventBus

EventBus又称事件总线, 相当于一个全局的仓库, 任何组件都可以去这个仓库里获取事件

class EventBus {
  //所有事件的列表{
      //key: array
  //} array储存的是注册的回调函数
  constructor() {
    this.eventobj = {};//储存所有的订阅事件
  }
  //订阅事件.类似监听事件$on('key',()=>{})
  $on(name, callbcak) {
    //判断是否储存过
    if(!this.eventobj[name]) {
      this.eventobj[name] = [];
    }
    this.eventobj[name].push(callbcak);
  }
  //发布事件,类似触发事件$emit('key')
  $emit(name,...args) {
    //获取储存的事件回调函数数组
    const eventList = this.eventobj[name];
    //执行所有回调函数且传入参数
    for(const callbcak of eventList) {
      callbcak(...args);
    }
  }
  //初始化EventBus
}
let EB = new EventBus();

//订阅事件
EB.$on('key1',(name,age)=>{
  console.info('订阅事件A:',name,age);
})
EB.$on('key1',(name,age)=>{
  console.info('订阅事件B:',name,age);
})
EB.$on('key2',(name)=>{
  console.info('订阅事件C:',name);
})


//发布事件
EB.$emit('key1','小猪课堂',26);
EB.$emit('key2',"小猪课堂");

手写取消订阅版EventBus:待理解后完善

十.Vue组件通信方式

(1).props
prop通信是最常用的父子间通信类型, 我们可以直接在标签里面给子组件绑定属性和方法, 对于属性我们可以直接通过子组件声明的prop拿到, 对于父元素的方法, 可以通过this.$emit 自定义事件触发 父组件用v-on监听并接受参数
数据格式化: 可以用computed计算属性来接受props并格式化所需要的数据类型
子实例可以用this. $parent 访问父实例, 子实例被推入父实例的 $children数组中
Ref绑定在子组件上, 可以通过实例( $ref)直接调用子组件的方法或访问数据
(2)全局通信 事件总线 $bus: 配置全局总线(在main.js中配置) 在组件中用 $emit发送事件, 在通信的另一个组件中用 $on接受事件
(3)vuex跨级组件通信: vuex, $attrs, $listeners, Provide, inject

十一.Vue路由

路由就是通过不同的路径请求不同的资源

  • route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
  • router是“路由实例对象”,包括了路由的跳转方法**(push、replace)**,钩子函数等。
    1.Vue的单页面应用是基于组件和路由的, 路由用于设定访问路径, 并将路由和组件映射起来,前端路由的核心就是改变视图的同时不会向后端发起请求
    2.在一些特定场景里面我们会用到动态路由,所以这里给出了使用方式,动态路由主要通过两个功能实现
    router.addRoute()router.removeRoute()
    3.路由的两种模式:
  • hash: 兼容所有浏览器, 包括不支持HTML5 History Api的浏览器, hash的改变会触发hashchange事件, 通过监听hashchange事件来完成操作实现前端路由, hash值的变化不会让浏览器向服务器请求
// 监听hash变化,点击浏览器的前进后退会触发
window.addEventListener('hashchange', function(event){ 
    let newURL = event.newURL; // hash 改变后的新 url
    let oldURL = event.oldURL; // hash 改变前的旧 url
},false)
  • history: 兼容能支持HTML5 History Api的浏览器, 依赖HTML5 History API来实现前端路由, 没有#, 路由地址跟正常的url一样, 但是初次访问或者刷新都会向服务器请求, 如果没有请求到对应的资源就会返回404, 所以路由地址匹配不到任何静态资源, 则应该返回同一个index.html页面, 需要在 nginx中配置
  • Vue Router 是官方的路由管理器。它和 Vue.js 的核心深度集成,路径和组件的映射关系, 让构建单页面应用变得易如反掌。
  • router-link 实质上最终会渲染成a链接
  • router-view 子级路由显示
  • keep-alive 包裹组件缓存

十二.Babel编译

Babel就是一个Javascript编译器, 是一个工具链, 本质就是操作AST来完成代码的转译, AST是抽象语法树
大多数编译器工作过程可以分为三部分:
1.解析(Parse): 将与那代码转化成更加抽象的表示方法(例如抽象语法树), 包括词法分析和语法分析. 词法分析主要把字符流源代码(Char Stream)转换成令牌流(Token Stream) 词法分析主要是将令牌流转化成抽象语法树
2.转换(transform): 通过Babel的插件能力, 对抽象语法树做一些特殊处理, 将语法版本或者对AST的Node节点进行优化操作, 比如添加, 更新以及移除节点等
3.生成(Generate): 将AST转换成字符串形式的低版本代码, 同时也能创建Source Map映射
经过这三个阶段, 代码就被Babel转译成功了
在这里插入图片描述

十三.Vuex

  • 多个组件依赖于同一个状态
  • 来自不同组件的行为需要变更同一状态
    核心属性:state:保存着仓库中的变量, 通过this.$ store.state来访问state
    getters: 相当于VUE中的计算属性computed, 只有原状态改变派生状态才会改变, getters接受两个参数,第一个是state,第二个是getters(可以用来访问其他getter) 直接通过this.$ store.getters来访问
    mutation:是同步函数, 只能通过mutation来修改state 直接通过this.$ store.commit(‘mutationA’,data)提交模块中的mutation
    actions:是异步方法, 通过this.$ dispatch(‘actionA,data’)
    modules:状态树过大时可以将store分割成module, 每个module都有自己的state, mutation, action, getter
    在这里插入图片描述

十四.vue有哪些指令

v-once : 只会执行一次渲染,当数据发生改变时,不会再变化
v-show: v-show接受一个表达式或一个布尔值。相当于给元素添加一个display属性
v-if、v-else、v-else-if: v-if和v-show有同样的效果,不同在于v-if是重新渲染,而v-show使用display属性来控制显示隐藏。
v-text和v-html: v-text是渲染字符串 v-html是渲染为html。
v-on: v-on用于事件绑定
V-bind(缩写为:):动态绑定属性
v-model: 实现双向绑定

十五.keep-alive的实现

作用: 实现组件缓存, 保存这些组件的状态, 以避免反复渲染导致的性能问题, 需要缓存组件 频繁切换, 不需要重复渲染
场景: tabs标签页, 后台导航, vue性能优化
原理: Vue.js内部将DOM节点抽象成了VNode节点, keep-alive组件的缓存也是基于VNode节点的而不是直接储存DOM结构, 他将满足条件的组件在cache对象缓存起来, 在需要重新渲染的时候再将VNode节点从cache对象取出并渲染

十六.既然vue通过数据劫持可以精准的探测数据变化,为什么还要进行diff检测差异?

vue 的响应系统决定了通常绑定一个数据就需要一个Watcher, 一旦绑定的细粒度过高就会产生大量的Watcher, 会导致内存和依赖追踪的开销, 而细粒度过低会无法精确侦测变化, 因此Vue的设计选择中等细粒度的方式, 在组件级别进行push侦测的方式, 也就是响应式系统, 通常会第一时间检测到发生变化的组件, 然后在组件内部进行 Virtual Dom Diff 获取更具体的差异, 而Virtual Dom Diff则是pull操作, Vue是push+pull结合的方式来侦测变化的

十七.Webpack

webpack是一个现在的javaScript应用的静态模块化打包工具
它的核心就分为入口(entry),加载器(loader),插件(plugins),出口(output)
Entry:告诉webpack从哪里开始打包文件内容
Loader:webpack只能处理js模块,如果要处理其他类型的文件,就要用loader进行转换。(就比如添加了css模块,就需要用到style-loader和css-loader,是从右向左读的,css-loader加载css文件,style-loader负责将样式添加到DOM里)
**Plugins:**loader只能用于某些类型的模块,plugin的功能更强大;plugin可以对loader打包后的文件进行二次优化,比如代码压缩从而减小文件体积。
Output:告诉webpack在哪里输出它创建的bundle
vue内容和计网即待补充

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值