Vue2实用知识

2022/7/15 Vue

# 1. 杂记

# 数据驱动 MVVM

  • MVVM 表示的是 Model-View-ViewModel:【1】Model:模型层,负责处理业务逻辑以及和服务器端进行交互;【2】View:视图层:负责将数据模型转化为 UI 展示出来,可以简单的理解为HTML页面;【3】ViewModel:视图模型层,用来连接 Model 和 View,是 Model 和 View 之间的通信桥梁。
  • 双向绑定:ViewModel 它的主要职责就是:数据变化后更新视图、视图变化后更新数据。

# 双向绑定的原理

  • vue2 是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。vue3 中则采用 Proxy,它可以监听到数组内的数据变化。

# 生命周期

  • 生命周期 Life Cycle 的概念应用很广泛,特别是在政治、经济、环境、技术、社会等诸多领域经常出现,其基本涵义可以通俗地理解为 “从摇篮到坟墓” Cradle-to-Grave 的整个过程。在 Vue 中实例从创建到销毁的过程就是生命周期,即指从创建、初始化数据、编译模板、挂载 Dom→渲染、更新→渲染、卸载等一系列过程。
  • Vue生命周期总共可以分为 8 个阶段:创建前后、载入前后、更新前后、销毁前后,以及一些特殊场景的生命周期(activated、deactivated、errorCaptured)。
  • 数据请求建议放在 created 生命周期当中。

# v-show 和 v-if

  • 控制手段不同:v-show 隐藏则是为该元素添加 css--display:nonedom 元素依旧还在。v-if 显示隐藏是将 dom 元素整个添加或删除。
  • 编译过程不同:v-if 切换有一个局部编译 / 卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show 只是简单的基于 css 切换。
  • 编译条件不同:v-if 是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-if 的变化会触发组件的生命周期,v-show 的变化则不会触发组件的生命周期。
  • 性能消耗不同:v-if 有更高的切换消耗;v-show 有更高的初始渲染消耗。

# 组件 vs 插件

  • 组件的定义:组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在 Vue 中每一个 .vue 文件都可以视为一个组件。组件的优势:降低整个系统的耦合度、调试方便、提高可维护性。
  • 插件是什么:插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制,一般有下面几种:【1】添加全局方法或者属性。如 vue-custom-element;【2】添加全局资源:指令 / 过滤器 / 过渡等。如 vue-touch;【3】通过全局混入来添加一些组件选项。如 vue-router;【4】添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
  • 区别:编写形式、注册形式、使用场景(组件 (Component) 是用来构成你的 App业务模块,它的目标是 App.vue;插件 (Plugin) 是用来增强你的技术栈的功能模块,它的目标是 Vue 本身)。

# 为什么 data 属性是一个函数而不是一个对象

  • 根实例对象 data 可以是对象也可以是函数(根实例是单例),不会产生数据污染情况。
  • 组件实例对象 data 必须为函数,目的是为了防止多个组件实例对象之间共用一个 data,产生数据污染。采用函数的形式,initData 时会将其作为工厂函数都会返回全新 data 对象。

# 计算属性注意事项

  • 计算属性值会基于其响应式依赖被缓存
  • 计算属性仅会在其响应式依赖更新时才重新计算。
// 解释了为什么下面的计算属性永远不会更新,因为 Date.now() 并不是一个响应式依赖
const now = computed(() => Date.now())
1
2
  • 在计算属性中使用 reverse()sort() 的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:
- return numbers.reverse()
+ return [...numbers].reverse()
1
2

# key 的作用

  • diff 算法简单的说就是新旧虚拟 DOM 的比较,如果有差异就以新的为准,然后再插入的真实的 DOM 中,重新渲染。
  • key 的作用:主要是为了更高效的对比虚拟 DOM 中每个节点是否是相同节点。
  • key 的取值:使用数据库中的 id、自增 id 函数、使用 uuid 库。

# Vue 实例挂载的过程

  • new Vue 的时候:【1】调用会调用 _init 方法:定义 $set$get$delete$watch 等方法;定义 $on$off$emit$off等事件;定义 _update$forceUpdate$destroy生命周期。【2】调用 $mount 进行页面的挂载,挂载的时候主要是通过 mountComponent 方法。【3】定义 updateComponent 更新函数。【4】执行 render 生成虚拟 DOM。【5】_update 将虚拟 DOM 生成真实 DOM 结构,并且渲染到页面中。

# nextTick

  • 是什么:官方对其的定义,在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOMVue 在更新 DOM 时是异步执行的。当数据发生变化,Vue 将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新。

# mixin

  • Mixin 是面向对象程序设计语言中的类,提供了方法的实现。其他类可以访问 mixin 类的方法而不必成为其子类。Mixin 类通常作为功能模块使用,在需要该功能时 “混入”,有利于代码复用又避免了多继承的复杂。
  • Vue 中的 mixin:mixin(混入),提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。本质其实就是一个 js 对象,它可以包含我们组件中任意功能选项,如 datacomponentsmethodscreatedcomputed 等等。全局混入常用于插件的编写
  • 注意事项:当组件存在与 mixin 对象相同的选项的时候,进行递归合并的时候,组件的选项会覆盖 mixin 的选项;但是如果相同选项为生命周期钩子的时候,会合并成一个数组,先执行 mixin 的钩子,再执行组件的钩子。
  • 几种混入策略:【1】替换型策略propsmethodsinjectcomputed,就是将新的同名参数替代旧的参数;【2】合并型策略data,通过 set 方法进行合并和重新赋值;【3】队列型策略有生命周期函数和 watch,原理是将函数存入一个数组,然后正序遍历依次执行;【4】叠加型策略componentdirectivesfilters,通过原型链进行层层的叠加。

# slot

  • 使用场景:通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理
  • 分类:默认插槽、具名插槽、作用域插槽。

# observable

  • Vue 中的定义:Vue.observable让一个对象变成响应式数据Vue 内部会用它来处理 data 函数返回的对象。返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器
Vue.observable({ count : 1})
// 其作用等同于
new vue({ count : 1})
1
2
3
  • Vue 2.x 中,被传入的对象会直接被 Vue.observable 变更,它和被返回的对象是同一个对象;在 Vue 3.x 中,则会返回一个可响应的代理,而对源对象直接进行变更仍然是不可响应的。
  • 使用场景:在非父子组件通信时,可以使用通常的 bus 或者使用 vuex,但是实现的功能不是太复杂,而使用上面两个又有点繁琐。这时,observable 就是一个很好的选择。

# keep-alive

  • keep-alive 是 vue 中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染 DOM。keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。可以设置以下 props 属性:【1】include - 字符串或正则表达式。只有名称匹配的组件会被缓存;【2】exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存;【3】max - 数字。最多可以缓存多少组件实例。
<keep-alive include="a, b">
  <component :is="view"></component>
</keep-alive>

<div id="app" class="wrapper">
  <keep-alive>
    <!-- 需要缓存的视图组件 --> 
    <router-view v-if="$route.meta.keepAlive"></router-view>
  </keep-alive>
    
  <!-- 不需要缓存的视图组件 -->
  <router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 缓存后如何获取数据:beforeRouteEnter、actived。

# 修饰符

  • 在程序世界里,修饰符是用于限定类型以及类型成员的声明的一种符号。在 Vue 中,修饰符处理了许多 DOM 事件的细节。vue 中修饰符分为以下五种:表单修饰符、事件修饰符、鼠标按键修饰符、键值修饰符、v-bind 修饰符。

# 自定义指令

  • 指令系统是计算机硬件的语言系统,也叫机器语言,它是系统程序员看到的计算机的主要属性。因此指令系统表征了计算机的基本功能决定了机器所要求的能力。在 vue 中提供了一套为数据驱动视图更为方便的操作,这些操作被称为指令系统。
  • 如何实现:注册一个自定义指令有全局注册(Vue.directive)与局部注册(directive)。
  • 自定义指令也像组件那样存在钩子函数:【1】bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。【2】inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。【3】update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。【4】componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。【5】unbind:只调用一次,指令与元素解绑时调用。
  • 所有的钩子函数的参数都有以下:【1】el:指令所绑定的元素,可以用来直接操作 DOM。【2】binding:一个对象,包含一些 property。【3】vnodeVue 编译生成的虚拟节点。【4】oldVnode:上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。(注意:除了 el 之外,其它参数都应该是只读的,切勿进行修改)

# 过滤器

  • 过滤器的实质不改变原始数据,只是对数据进行加工处理后返回过滤后的数据再进行调用处理,我们也可以理解其为一个纯函数。(ps: Vue3 中已废弃filter
  • vue 中的过滤器可以用在两个地方:双花括号插值和 v-bind 表达式,过滤器应该被添加在 JavaScript表达式的尾部,由 “管道” 符号指示:
// 在双花括号中
{{ message | capitalize }}

// 在 `v-bind` 中
<div v-bind:id="rawId | formatId"></div>
1
2
3
4
5
  • 应用场景:比如单位转换、数字打点、文本格式化、时间格式化之类的等。

# 虚拟 DOM

  • 虚拟 DOM (Virtual DOM )这个概念相信大家都不陌生,从 ReactVue ,虚拟 DOM 为这两个框架都带来了跨平台的能力(React-NativeWeex)。实际上它只是一层对真实 DOM 的抽象,以 JavaScript 对象 (VNode 节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上。
  • 为什么需要虚拟 DOM:DOM 是很慢的,其元素非常庞大,页面的性能问题,大部分都是由 DOM 操作引起的,哪怕一个最简单的 div 也包含着很多属性。【1】很多人认为虚拟 DOM 最大的优势是 diff 算法,减少 JavaScript 操作真实 DOM 的带来的性能消耗。虽然这一个虚拟 DOM 带来的一个优势,但并不是全部。【2】虚拟 DOM 最大的优势在于抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是小程序,也可以是各种GUI。

# diff 算法

  • diff 算法是一种通过同层的树节点进行比较的高效算法。其有两个特点:比较只会在同层级进行, 不会跨层级比较;在 diff 比较的过程中,循环从两边向中间比较。(同层比较、深度优先)
  • 原理分析:当数据发生改变时,set 方法会调用 Dep.notify 通知所有订阅者 Watcher,订阅者就会调用 patch 给真实的 DOM 打补丁,更新相应的视图。diff 的过程就是调用 patch 函数,比较新旧节点,一边比较一边给真实的 DOM 打补丁。

# SPA(单页应用)首屏加载速度慢怎么解决

  • 首屏加载:首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容。首屏加载可以说是用户体验中最重要的环节。
  • 关于计算首屏时间:利用 performance.timing 提供的数据;通过 DOMContentLoad 或者 performance 来计算出首屏时间。
  • 加载慢的原因:网络延时问题、资源文件体积是否过大、资源是否重复发送请求去加载了、加载脚本的时候,渲染内容堵塞了。
  • 解决方案:减小入口文件积、静态资源本地缓存、UI 框架按需加载、图片资源的压缩、组件重复打包、开启 GZip 压缩、使用 SSR。

# vue 项目布署到服务器后刷新 404 问题

  • 为什么 hash 模式下没有问题:router hash 模式我们都知道是用符号 # 表示的,如website.com/#/login,hash 的值为 #/login。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对服务端完全没有影响,因此改变 hash 不会重新加载页面。hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 website.com/#/login 只有 website.com 会被包含在请求中 ,因此对于服务端来说,即使没有配置location,也不会返回404错误。

  • 为什么 history 模式下有问题:Vue 是属于单页应用(single-page application),而 SPA 是一种网络应用程序或网站的模型,所有用户交互是通过动态重写当前页面,不管我们应用有多少页面,构建物都只会产出一个 index.html

  • 解决方案:只需要配置将任意页面都重定向到 index.html,把路由交由前端处理。

server {
  listen  80;
  server_name  www.xxx.com;

  location / {
    index  /data/dist/index.html;
    try_files $uri $uri/ /index.html;
  }
}
1
2
3
4
5
6
7
8
9

# 2. 项目实践

# 配置全局主题样式

  • 依赖的包是 style-resources-loader、vue-cli-plugin-style-resources-loader,目的是为了在单文件组件中使用时不再需要每次都去做引入操作。
// 安装对应的依赖
npm i style-resources-loader vue-cli-plugin-style-resources-loader --save-dev

// vue.config.js 中配置
pluginOptions: {
  "style-resources-loader": {
    preProcessor: "less",
    patterns: ["./src/styles/var.less"]
  }
}

// 重启项目,文件中使用变量文件
<style lang='less'>
// 可以不用单个引用了,直接使用即可
// @import '@/styles/var.less'
.test {
  color: @primary-color;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 自动化注册(批处理脚本)

  • require.context(directory, useSubdirectories, regExp):directory -- 表示检索的目录;useSubdirectories -- 表示是否检索子文件夹;regExp -- 匹配文件的正则表达式,一般是文件名。
  • 使用 require.context 实现一些自动注册脚本,减少重复劳动
Last Updated: 2023/04/22, 23:39:26
彩虹
周杰伦