Vue(知识点总结)

358 阅读4分钟

Vue

对Vue的理解

Vue 是一个构建数据驱动的渐进性框架,目标是通过API实现 响应数据绑定视图更新

Vue的两大核心

  • 数据驱动
  • 组件系统

Vue生命周期

Vue实例从创建到销毁的过程

graph LR
开始创建 --> 初始化数据-->编译模板-->挂载DOM-->渲染-->更新-->渲染;更新-->卸载

生命周期的作用

生命周期中有多个事件钩子,能让开发者在控制整个vue实例的过程时更容易形成良好的逻辑判断

createdmounted

created有$data,mounted有$el

createdmounted
调用时机模板渲染成 HTML 前模板渲染成 HTML 后
常见作用初始化属性值,再渲染成视图操作DOM节点

定时器

beforeDestroy()清除

clearInterval(this.timer);
this.timer = null;

v-model

原理

用于表单数据的双向绑定,其实是语法糖,背后做了两个操作:

  1. v-bind绑定value属性
  2. v-on绑定input事件

实现

<input type="text"  :value="msg"   @input="msg=$event.target.value"  />
// 先给输入框的value绑定变量,这叫做属性绑定  
// 再给输入框绑定input事件,一旦输入,就将输入框的值赋给msg变量,从而实现双向绑定

自定义组件的v-model

组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的model 选项可以用来避免这样的冲突:

Vue.component('base-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
})

现在在这个组件上使用 v-model 的时候:

<base-checkbox v-model="lovingVue"></base-checkbox>

这里的 lovingVue 的值将会传入这个名为 checked 的 prop。同时当 <base-checkbox> 触发一个 change 事件并附带一个新的值的时候,这个 lovingVue 的 property 将会被更新

注意:仍然需要在组件的 props 选项里声明 checked 这个 prop

Vue响应式(双向数据)原理

通过 数据劫持 结合 发布订阅模式 的方式来实现

  • Vue2:通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调 Vue2.png
  • Vue3:将数据劫持的方式由Object.defineProperty更改为ES6的Proxy代理

为什么 Vue3 抛弃Object.defineProperty 采用Proxy

Object.defineProperty本身有一定的监控到数组下标变化的能力,但是在 Vue 中,从性能/体验的性价比考虑,尤大大就弃用了这个特性。为了解决这个问题,经过 vue 内部处理后可以使用以下几种方法来监听数组

push();
pop();
shift();
unshift();
splice();
sort();
reverse();

由于只针对了以上 7 种方法进行了 hack 处理,所以其他数组的属性也是检测不到的,还是具有一定的局限性。

Object.defineProperty是通过 递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象是才是更好的选择。

Proxy可以劫持整个对象,并返回一个新的对象:不仅可以代理对象,还可以代理数组,还可以代理动态增加的属性。

Vue3相对Vue2的优化

  1. 双向数据绑定方面: Vue2的object.defineProperty一次只能劫持对象的单个属性,从而需要通过递归+遍历对每个对象的每个属性进行数据监控;如果属性值是对象的话,还需要深度遍历 而Vue3.0中的proxy可以一次性地劫持对象的所有属性的settergetter,还可以代理数组,也可以代理动态添加的属性,有13种劫持操作
  2. 性能方面快1.2~2倍:

(1) diff方法优化

vue2中的虚拟dom是全量的对比(每个节点不论写静态还是动态的都会比较)

vue3新增了静态标记(patchflag)与上次虚拟节点对比时,只对比带有patch flag的节点(动态数据所在的节点);可通过flag信息得知当前节点要对比的具体内容

(2) 静态提升

vue2无论元素是否参与更新,每次都会重新创建然后再渲染 vue3对于不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用即可

(3) 时间侦听器缓存

默认情况下onClick会被视为动态绑定,所以每次都会追踪它的变化

但是因为是同一个函数,所以不用追踪变化,直接缓存起来复用即可

(4) ssr渲染

  1. 按需编译,体积比vue2.x更小
  2. 组合API(类似react hooks)
  3. 更好的Ts支持
  4. 暴露了自定义渲染API
  5. 更先进的组件
  6. Fragment:模板可以有多个根元素

虚拟DOM

虚拟DOM相对于浏览器所渲染出来的真实 DOM,在 React,Vue 等技术出现之前, 改变页面展示内容只能遍历查询 DOM 树找到需要修改的 DOM,然后修改样式行为或者结构,来更新UI,但是这种方式相当消耗计算资源,每次查询DOM几乎都需要遍历整棵DOM树,因此建立一个与真实DOM树对应的虚拟DOM对象(JavaScript对象),以对象嵌套的方式来表示DOM树,那么每次DOM的更改就变成了对象属性的更改,这样一来就能通过diff算法查找变化要比查询真实的DOM树的性能开销小

<template>渲染过程

graph TD
将template标签里面的内容编译成render函数 --> 挂载实例:根据render函数的根节点递归生成虚拟DOM树-->通过diff算法对比虚拟DOM,渲染到真实DOM-->新的DOM操作使得DOM树发生改变-->通过diff算法对比虚拟DOM,渲染到真实DOM

key的作用

为了高效的更新虚拟DOM

给虚拟DOM的每个节点 VNode 添加唯一 id,让其可以依靠 key,更快速准确地拿到 oldVnode 中对应的 VNode

【设置key不推荐使用index或者随机数,因为增删子项的时候损耗性能较大】

v-ifv-show

  • v-if在DOM层面决定元素是否存在,会引起重排重绘
  • v-show在CSS层面决定是否将元素渲染出来(实际上该元素一直存在)

v-ifv-for

v-ifv-for
Vue 2优先
Vue 3优先

解决方案 :

  1. 父元素使用v-if,子元素使用v-for
  2. 使用计算属性computed
computed() {
    list() {
        return [1, 2, 3].filter(item => item !== 2);
    }
}

data是个函数并且返回一个对象 / data为什么要用return

因为一个组件可能会多处调用,每次调用会执行data函数并返回新的数据对象,因此这样可以避免多处调用之间的数据污染

Vue 如何监听键盘事件

  1. @keyup.方法
  2. addEventListener

单向数据流

vue组件通信里面父子通信是自上而下的,不能直接修改props里面属性

组件通信

  • 父子的传参及方法调用:props / $emit、$parent / $children
  • 祖孙的传参:provide / inject API、$attrs / $listeners
  • 兄弟的传参:bus.js、Vuex
  • 路由的传参:query、params
父子祖孙/隔代兄弟
props / $emit
ref与 $parent / $children
$attrs / $listeners
provide / inject
EventBus($emit / $on)
Vuex

localStorage/Cookie等都可以

删除数组用 delete 和 Vue.delete 的区别

  • delete:只是被删除数组成员变为 empty / undefined,其他元素键值不变
  • Vue.delete:直接删了数组成员,并改变了数组的键值 (对象是响应式的,确保删除能触发更新视图,这个方法主要用于避开 Vue 不能检测到属性被删除的限制)

计算属性computed和属性检测watch

cpmputed:当且仅当计算属性依赖的 data 改变时才会自动计算

computedwatch
首次运行×
默认依赖深度(推荐使用)浅度
调用时在模板渲染只需修改元数据
合适性筛选、不可异步开销较大、异步操作
特征根据页面变化而变化(用于计算)监听页面状态而变化(用于监听)

v-for后使用this.$refs报错domundefined

ref:用来获取dom

组件初始化到第一次渲染完成的mounted周期里,只是渲染了组件模板的静态数据,并没有初始化动态绑定的dom,所以在mounted周期里面操作获取不到dom

解决方法:

  1. this.$nextTick放在获取到v-for绑定的数据并赋值之后,也就是触发响应式更新之后再进行操作
  2. 把操作dom的操作放到updated生命周期里,但是这样每次更新视图都会触发该操作

插槽

  • 默认插槽
  • 具名插槽
  • 作用域插槽

scoped的实现原理

  1. 通过PostCSS给所有dom都添加了唯一的动态属性
  2. 通过PostCSS也给css选择器额外添加对应的属性选择器,来选择组件中的dom

样式穿透

  1. scss:父元素 /deep/ 子元素
  2. stulus:父元素 >>> 子元素

Vue CLI

src目录每个文件夹和文件的用法

  1. assets文件夹是放静态资源;
  2. components是放组件;
  3. router是定义路由相关的配置;
  4. app.vue是一个应用主组件;
  5. main.js是入口文件

static和assets的区别

原理:webpack 如何处理静态资源

staticassets
内容类库项目资源
资源直接引用被 webpack 打包

(static放别人家的资源,assets放自己家的资源)

引入第三方库

  1. 绝对路径直接引入
  2. 在 webpack 中配置 alias [ˈeɪliəs]
  3. 在 webpack 中配置 plugins [ˈplu:genz]

Vue-Router

路由实现原理

  • 利用URL中的hash("#")
  • 利用History interface在HTML5中新增的方法

hash模式和history模式

hash模式history模式
浏览器支持版本IE、低版本HTML5新推出的API
刷新重新加载404
URL中是否带 #×
# 后面的hash变化不会重新加载,会触发hashchange事件渲染对应内容重新加载,会触发popState事件渲染对应内容

传参

  • name 传参
  • URL 传参
  • <router-link>的to传参
  • 用 path 匹配路由,通过 query 传参

二级路由 / 嵌套路由哦

使用路由导航<router-link>和路由容器<router-view>,配置children

路由守卫触发流程

  1. 不同组件(A组件跳转到B组件)
graph TD
触发导航-->调用A组件的路由守卫beforeRouteLeave-->调用全局路由前置守卫beforeEach-->调用B路由独享守卫beforeEnter-->解析异步路由组件B-->调用B的组件内路由守卫beforeRouteEnter-->调用全局路由解析守卫beforeResolve-->确认导航-->调用全局路由钩子afterEach-->渲染B组件DOM

keep-alive

keep-alive用于保存组件的渲染状态

不希望组件被重新渲染影响用户体验和降低性能,而是希望组件可以缓存下来,维持当前的状态,这时候就可以用到keep-alive组件

(通常搭配生命周期activateddeactived使用)

keep-alive是一个抽象组件:它自身不会渲染一个DOM元素,也不会出现在父组件链中;使用keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。

Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简单来说就是:应用遇到多个组件共享状态时,使用vuex。

vuex的流程

graph TD
页面通过&nbspmapAction&nbsp异步提交事件到&nbspaction-->action&nbsp通过&nbspcommit&nbsp把对应参数同步提交到&nbspmutation-->mutation&nbsp修改&nbspstate&nbsp中对应的值-->通过&nbspgetter&nbsp传递对应值-->在页面的&nbspcomputed&nbsp中通过&nbspmapGetter&nbsp来动态获取&nbspstate&nbsp中的值

vuex属性

mapAction:State , Getter , Mutation , Action , Module

  1. state:vuex的基本数据(state),用来存储变量
  2. getter:从基本数据(state)派生的数据,相当于state的计算属性
  3. mutation:提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数。
  4. action:和mutation的功能大致相同 不同之处:
  • Action 提交的是 mutation,而不是直接变更状态
  • Action 可以包含任意异步操作
  1. modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构清晰,方便管理。

ajax 写在 methods 中还是 actions 中

  1. 仅在请求组件内使用:写在组件的 methods 中
  2. 在其他组件复用:写在 vuex 的 actions 中 (包装成 promise 返回,在调用处用 async await 处理返回的数据)