夜听城嚣 夜听城嚣
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏

dwfrost

前端界的小学生
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏
  • HTML
  • JavaScript
  • 框架之Vue
    • 计算机网络
    • 前端面试题选题
    • 前端面试题汇总
    frost
    2021-09-22

    框架之Vue

    # Vue 基础

    # Vue 响应式原理(p4)

    Vue 采用属性劫持和发布订阅模式结合的方式,劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调,更新视图。主要包括几个部分:

    • Observer。对需要渲染的数据对象进行递归遍历,使用 Object.defineProperty() (Vue3 是 Proxy)来劫持其 setter 和 getter。当使用模板进行初始化渲染时,会访问到对象 (实例属性),从而触发 getter,此时实例化 Watcher,并添加到订阅器(dep);当程序修改实例属性,会触发 setter,此时订阅器会调用 notify 通知 Watcher,调用 update,从而更新视图。
    • Compiler。解析模板,将模板转为真实 DOM,同时解析 v-指令和表达式,对模板中的变量进行初始化渲染,同时实例化 Watcher 并传入更新回调,如果有事件绑定则监听元素对应 事件。
    • Watcher。订阅者,是 Observer 和 Compiler 之间的桥梁,调用自身的 update 方法后,可以触发更新回调,从而更新对应 DOM 节点的视图。
    # Vue 响应式中 Object.defineProperty()有什么缺点,怎么弥补(p4)

    比如通过下标方式修改数组或给对象新增属性,Object.defineProperty()是无法拦截的,从而导致视图不能更新。Vue 是通过重写实例属性中 Array 的原型方法,来做到响应式。如 pop(),当 vm.list.pop()触发时,Vue 一方面调用数组的 pop 方法并返回值,另一方面调用对应 dep 的 notify 方法,手动进行视图更新。

    # 为什么 Vue 中的 v-if 和 v-for 不建议一起用?(p4)

    在进行 if 判断的时候,v-for 是比 v-if 先进行判断

    最终结论:v-for 优先级比 v-if 高。

    因此:

    1. 永远不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)

    2. 如果要避免出现这种情况,则在外层嵌套 template(页面渲染不生成 dom 节点),在这一层进行 v-if 判断,然后在内部进行 v-for 循环

    <template v-if="isShow">
      <p v-for="item in items"></p>
    </template>
    
    1
    2
    3
    1. 如果条件出现在循环内部,可通过计算属性 computed 提前过滤掉那些不需要显示的项
    computed: { items: function() { return this.list.filter(function (item) { return item.isShow }) } }
    
    1
    # Computed 和 Watch 的区别(p3)

    computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值(data、props)发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。 属性有 get 和 set 方法,如果值是函数,默认使用 get 方法。

    watch 侦听器 : 观察一个值变化后的影响,类似于数据(data、props,computed)的监听回调,不支持缓存。通常包括 2 个参数,分别是新值和旧值。如果是对象写法,则有 immediate,deep,handler 等属性。

    computed 不支持异步操作,watch 支持异步监听。

    # slot 是什么,有几类,原理是什么(p3)

    slot 又名插槽,是 Vue 的内容分发机制,组件内部的模板引擎使用 slot 元素作为承载分发内容的出口。

    slot 分 2 类,普通插槽和作用域插槽

    • 普通插槽

      • 默认插槽。slot 没有指定 name 时默认显示,一个组件只有一个默认插槽。
      • 具名插槽。带有 name 的 slot,一个组件可以有多个具名插槽。
    • 作用域插槽。是默认插槽或具名插槽的变体,目的是父组件可以获取子组件传过来的(子作用域)数据,放到 slot 进行渲染。

    slot 原理:slot 本质上是返回 VNode 的函数。当子组件 vm 实例化时,获取到父组件传入的 slot 标签的内容,放到vm.$slot,如默认插槽为vm.$slot.default,具名插 槽为vm.$slot.xxx。当组件执行渲染函数时,遇到 slot 标签,使用$slot中的内容进行替换,此时可以为插槽传递数据,若存在数据,则称为作用域插槽。

    # v-if 和 v-show 的区别(p3)
    • v-if 是惰性渲染,当表达式为false时,组件渲染时不会渲染该节点,只有表达式为true时,才会更新渲染。切换过程中销毁和重建内部的事件监听和子组件。
    • v-show 在渲染组件时总会渲染该节点,它是通过 css 设置 display 属性来控制 DOM 的显示隐藏,如果涉及到频繁的显示隐藏切换,v-show 比 v-if 更合适。
    # v-model 是怎么实现的(p4)

    本质是一个父子组件通信的语法糖,通过 prop 和$.emit 实现。 因此父组件 v-model 语法糖本质上可以修改为:

    <child
      :value="message"
      @input="
        function(e) {
          message = e
        }
      "
    ></child>
    
    1
    2
    3
    4
    5
    6
    7
    8

    数据流还是单向传递的,只不过子组件会 emit input 事件,并将用户修改后的值抛出,父组件再将传入的值修改,从而达到双向绑定的效果。

    # data 为什么是一个函数而不是对象(p4)

    如果是一个对象,由于组件可以被复用,那么当一个组件被使用了多次,并且组件的实例属性被修改后,因为 js 中对象是按引用传递,那么一个组件的实例属性被修改后,另一个组 件的该属性也会改变,导致状态互相污染。实际上,在开发模式下,如果将 data 写成对象,是会编译报错的。

    而写成函数,并返回一个字面量对象的方式,则保证了,每次组件实例化时,都有各自私有的实例属性备份,不会污染其他组件的状态。

    # 如果要在多个组件之间来回切换,为提高性能,有什么方式?(p3)

    使用 keep-alive 组件

    追问:keep-alive 如何做到缓存组件,它背后的原理是什么?待整理

    # 使用过$nextTick 吗?它的原理是什么?(p3)

    它的本质是 Vue 对 EventLoop 的一种应用。原理是利用 js 的 Promise,MutationObserver 和 setTimeout 等方法来实现 Vue 内部的异步回调队列。

    相比原生 dom 渲染,vue 的异步队列更新机制性能更好,表现为:

    • 如果频繁更改实例属性,采用异步更新的方式会将多个更改操作进行合并,减少了 dom 的无用渲染。
    • 由于 vue 引入了虚拟 DOM,如果同步更新,那么在 dom 重新绘制过程中也会涉及到更多计算,导致性能降低。

    使 用$nextTick的场景是,当实例属性更新后,相关的dom即将进行更新。而如果立即获取dom的状态,这时由于视图没有立即更新,导致获取的dom状态不是最新的,此时可以通过vm.$nextTick(fn) 的方式来获取更新后的 dom 节点。

    关联问题:Vue data 中某一个属性的值发生改变后,视图会立即同步执行重新渲染吗?

    不会,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化, Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推 入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环 tick 中,Vue 刷新队列并执行实际(已去重的)工作。

    # vue tempalte render 的过程(p5)

    vue 的模版编译过程主要如下:template -> ast -> render 函数->生成虚拟 DOM。简述如下:

    在 beforeMount 之前执行编译过程,第一步通过 html-parser 将 template 解析成 ast 抽象语法树,第二步通过 optimize 优化 ast 并标记静态节点和静态根节点,第三步通过 generate 将 ast 抽象语法树编译成 render 字符串并将静态部分放到 staticRenderFns 中,最后通过 new Function(render)生成 render 函数。在 beforeMount 和 mounted 之 间执行 render 函数生成 VNode,然后通过 patch(VNode)生成 dom 树并挂载,调用 mounted。 https://blog.csdn.net/lyt_angularjs/article/details/105250391

    1. template->ast

      • 目标:把 tamplate 转换为 AST 树,它是一种用 JavaScript 对象的形式来描述整个模板。

      • 解析过程:利用正则表达式顺序解析模板,当解析到开始标签、闭合标签、文本的时候都会分别执行对应的 回调函数,来达到构造 AST 树的目的。

    2. 对静态节点做优化

      optimize(ast, options)
      
      1

      深度遍历 AST,分析出哪些是静态节点,给其打一个标记,为后续更新渲染可以直接跳过静态节点做优化。

    3. 生成 render 函数

      const code = generate(ast, options)
      
      1

      将 AST 编译成 render 字符串,再使用 new Function()生成 render 函数。

      生成 render 函数后,之后实例进行挂载和节点更新,都会调用 render 进行渲染。

    4. 生成虚拟 DOM

      Vue 实例挂载根节点(#app)或者在 beforeMount 之后,开始调用实例上的 render 函数,递归生成 VNode(就是虚拟 DOM)

    # Vue 是如何收集依赖的?(p5)

    Vue 在对数据(对象,数组,简单数据类型等)进行响应式化的过程中,需要进行依赖收集,目的是数据发生变动后,可以通知依赖(订阅者)进行响应式更新。

    这里涉及到几个类

    • Observer,接收一个 value,将其响应式化。

    • 订阅器-Dep,也叫依赖收集器

    • 订阅者-Watcher

    1. 响应式观察入口,实例化 Observer(data),data 是 vue 实例 data 函数返回的对象。

    2. 判断即将响应式的 value 是否数组。

      • 数组。遍历数组,对数组 item 项进行响应式观察。
      • 对象。遍历对象属性,对属性对应的值进行响应式化(调用 defineReactive)。下面分析 defineReactive 的过程。
    3. 实例化 Dep,得到该属性的订阅器

    4. 对属性对应的值继续响应式观察

    5. (响应式核心)使用Object.defineProperty劫持监听的对象及其属性,这里分 2 个方面

      • getter 劫持。此时 Watcher 实例已经生成,dep 将其添加到集合中。如果属性对应的值也是响应式对象,也会另外使用其订阅器进行收集。
      • setter 劫持。监听属性对应的值是否有更新,如果有则对新值响应式化,并调用 dep.notify()派发更新,进而通知订阅者调用其 update()触发更新回调,从而更新视图。

      注意:在每个实例属性被收集的过程中,可能一个实例属性有多个 watcher 生成,但同一时间只允许一个 watcher 被计算(即同一时间只有一个订阅者进行订阅),如果依赖已 经被添加过了,则下次不会再添加。

      大部分时间里,Dep.target 的值都为 null。当开始收集依赖时,实例化 Watcher,触发其 getter,此时 Dep.target 指向当前 watcher 实例,因此 dep 可以进行依赖收集,收 集完毕,Dep.target 指向 null,等待下一次订阅。

      先有订阅者进行订阅,才能收集依赖。这里的依赖就是指当时的订阅者。

    vue 依赖收集原理 (opens new window)

    # 对 React 和 Vue 的理解,它们的异同(p3)

    相似之处:

    • 都将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库;
    • 都有自己的构建工具,能让你得到一个根据最佳实践设置的项目模板;
    • 都使用了 Virtual DOM(虚拟 DOM)提高重绘性能;
    • 都有 props 的概念,允许组件间的数据传递;
    • 都鼓励组件化应用,将应用分拆成一个个功能明确的模块,提高复用性。

    不同之处 :

    1)数据流

    Vue 默认支持数据双向绑定,而 React 一直提倡单向数据流

    2)虚拟 DOM

    Vue2.x 开始引入"Virtual DOM",消除了和 React 在这方面的差异,但是在具体的细节还是有各自的特点。

    • Vue 宣称可以更快地计算出 Virtual DOM 的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
    • 对于 React 而言,每当应用的状态被改变时,全部子组件都会重新渲染。当然,这可以通过 PureComponent/shouldComponentUpdate 这个生命周期方法来进行控制,但 Vue 将此 视为默认的优化。

    3)组件化

    React 与 Vue 最大的不同是模板的编写。

    • Vue 鼓励写近似常规 HTML 的模板。写起来很接近标准 HTML 元素,只是多了一些属性。
    • React 推荐你所有的模板通用 JavaScript 的语法扩展——JSX 书写。

    具体来讲:React 中 render 函数是支持闭包特性的,所以 import 的组件在 render 中可以直接调用。但是在 Vue 中,由于模板中使用的数据都必须挂在 this 上进行一次中转, 所以 import 一个组件完了之后,还需要在 components 中再声明下。

    4)监听数据变化的实现原理不同

    • Vue 通过 getter/setter 以及一些函数的劫持,能精确知道数据变化,不需要特别的优化就能达到很好的性能
    • React 默认是通过比较引用的方式进行的,如果不优化(PureComponent/shouldComponentUpdate)可能导致大量不必要的 vDOM 的重新渲染。这是因为 Vue 使用的是可变数据,而 React 更强调数据的不可变。

    5)高阶组件

    react 可以通过高阶组件(HOC)来扩展,而 Vue 需要通过 mixins 来扩展。

    高阶组件就是高阶函数,而 React 的组件本身就是纯粹的函数,所以高阶函数对 React 来说易如反掌。相反 Vue.js 使用 HTML 模板创建视图组件,这时模板无法有效的编译,因此 Vue 不能采用 HOC 来实现。

    6)构建工具

    两者都有自己的构建工具:

    • React ==> Create React APP
    • Vue ==> vue-cli

    7)跨平台

    • React ==> React Native
    • Vue ==> Weex
    # vue 如何监听对象或者数组某个属性的变化(p4)

    这个问题也可以更详细的描述:加入 vue 的 data 中有个空数组,数组赋值渲染后,修改其中某一项的值,如何进行响应式更新?

    首先,由于 js 的限制,Object.defineProperty()无法监听到数组的属性值的变化。

    操作方法是使用 this.$set()或者调用 splice()等方法。

    this.$set 可以操作对象和数组,原理是:

    • 如果目标是数组,直接使用数组的 splice 方法触发响应式
    • 如果目标是对象,则调用 defineReactive 进行响应式化处理。
    # 对 SSR 的理解(p4)

    SSR 即服务端渲染,也就是将 Vue 在客户端把标签渲染成 HTML 的工作放在服务端完成,然后再把 html 直接返回给客户端

    SSR 的优势:

    • 更好的 SEO
    • 首屏加载速度更快

    SSR 的缺点:

    • 开发条件会受到限制,服务器端渲染只支持 beforeCreate 和 created 两个钩子;
    • 当需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于 Node.js 的运行环境;
    • 更多的服务端负载。
    # SPA 单页的优缺点是什么(p3)

    优点:

    • 页面资源只加载一次,公共模块可以共用,减小服务器压力,前端渲染也会更快
    • 多个页面视图在同一个应用下,可以统一进行状态管理
    • 使用前端路由,避免了页面重新加载

    缺点:

    • 项目资源要全部打包,导致打包慢,页面需要按需加载处理
    • 不利于 SEO,可以通过 SSR 解决

    # 生命周期

    # 说一下 Vue 的生命周期(p3)

    Vue 实例从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程。

    beforeCreate(创建前):数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能访问到 data、computed、watch、methods 上的方法和数据。

    created(创建后) :实例创建完成,实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM,所以不能访问到 $el 属性。

    beforeMount(挂载前):在挂载开始之前被调用,相关的 render 函数首次被调用。实例已完成以下的配置:编译模板,把 data 里面的数据和模板生成 html。此时还没有挂载 html 到页面上。

    mounted(挂载后):在 el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的 html 内容替换 el 属性指向的 DOM 对象。完成 模板中的 html 渲染到 html 页面中。此过程中进行 ajax 交互。

    beforeUpdate(更新前):响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染。

    updated(更新后) :在由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时,组件 DOM 已经更新,所以可以执行 依赖于 DOM 的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。

    beforeDestroy(销毁前):实例销毁之前调用。这一步,实例仍然完全可用,this 仍能获取到实例。

    destroyed(销毁后):实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被 调用。

    另外还有 keep-alive 独有的生命周期,分别为 activated 和 deactivated 。

    # Vue 子组件和父组件执行顺序(p3)

    加载渲染过程:

    1. 父组件 beforeCreate
    2. 父组件 created
    3. 父组件 beforeMount
    4. 子组件 beforeCreate
    5. 子组件 created
    6. 子组件 beforeMount
    7. 子组件 mounted
    8. 父组件 mounted

    更新过程:

    1. 父组件 beforeUpdate
    2. 子组件 beforeUpdate
    3. 子组件 updated
    4. 父组件 updated

    销毁过程:

    1. 父组件 beforeDestroy
    2. 子组件 beforeDestroy
    3. 子组件 destroyed
    4. 父组件 destoryed
    # 比较 created 和 mounted 的区别(p3)

    created 阶段实例已生成,但还未渲染视图。

    mounted,模板完成了渲染,挂载在 dom 上,这时可以获取到 dom 节点。

    推荐在 created 钩子中请求数据,理由如下

    • 请求时机更早,减少页面加载时间
    • created 更改数据后,mounted 阶段 dom 只会渲染一次数据;而如果是 mounted,因为 data 已经初始化一次(渲染过一次),这时再请求数据修改 data,则会触发二次渲染。

    # 组件通信

    # 常见的组件通信方式有哪些(p3)
    • props/$emit。数据是单向流动的,父组件通过props向子组件传递数据,子组件通过$emit向父组件通信。
    • eventBus($emit/$on)。适用于父子组件、非父子组件。通过实例化 Vue 生成事件总线,组件通过发送事件和监听事件来通信。使用方便,但不利于维护。
    • 依赖注入(provide/inject)。适用于父子、祖孙组件。provide 和 inject 是两个钩子,分别用来发送数据和接收数据。注意,依赖注入所提供的属性是非响应式的。
    • ref/$refs。适用于父组件访问子组件实例的数据和方法。
    • $parent/$children。其中$parent可以让子组件访问父组件的实例,而$children 可以访问到子组件,它是一个数组,不能保证顺序。
    • $attrs/$listeners。使用$attrs/$listeners可以优雅地实现组件之间的多属性数据通信。配合v-bind="$attrs"可以轻松实现跨代通信。
      • $attrs:继承所有的父组件属性(除了 prop 传递的属性、class 和 style ),一般用在子组件的子元素上
      • $listeners:该属性是一个对象,里面包含了作用在这个组件上的所有监听器,可以配合 v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素。(相 当于子组件继承父组件的事件)
    • vuex。数据状态管理模式,将公共的数据抽离出来,统一通过提交的方式进行修改,数据流动清晰,便于追踪。
    • pinia。vue3 推荐使用 pinia 进行状态管理,它是 vuex 的下一代状态管理工具。

    # 路由

    # Vue 路由模式有哪些,原理是什么(p3)

    Vue-Router 有两种模式:hash 模式和history 模式

    1. hash 模式

      比如https://abc.com/#/home,这里#/home就是 hash 值。

      hash 值的改变不会重新加载页面。

      主要原理是:window 监听 onhashchange()事件,根据变化的 hash 值加载对应的 vue 组件,这个过程不涉及到服务端请求,整个过程在浏览器完成。

    2. history 模式

      比如https://abc.com/home,它区别于 hash 模式的地方在于没有#,是利用 History API 来实现前端路由。具体是利用 pushState() 和 replaceState() 方法修改 url,同时加载 vue 组件。

      可能的风险点在于刷新页面或者直接输入 url 时,会返回 404,需要服务端进行配置:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面。

    前端路由的基本原理 (opens new window)

    # Vue-router 导航守卫有哪些(p3)

    按路由覆盖的范围可分为 3 类:

    • 全局路由钩子:beforeEach、beforeResolve、afterEach
    • 路由独享的守卫:beforeEnter
    • 组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

    # 虚拟 DOM

    # 虚拟 DOM 的解析过程(p4)
    • js 对象表示 DOM。首先对将要插入到文档中的 DOM 树结构进行分析,使用 js 对象将其表示出来,比如一个元素对象,包含 TagName、props 和 Children 这些属性。然后将 这个 js 对象树给保存下来,最后再将 DOM 片段插入到文档中。
    • diff 算法比较差异。当页面的状态发生改变,需要对页面的 DOM 的结构进行调整的时候,首先根据变更的状态,重新构建起一棵对象树,然后将这棵新的对象树和旧的对象树 进行比较,记录下两棵树的的差异。
    • 应用差异更新视图。最后将记录的有差异的地方应用到真正的 DOM 树中去,这样视图就更新了。
    # 为什么要用虚拟 DOM,它一定比真实 DOM 性能好吗(p4)
    1. 保证基本性能。虚拟 DOM 保证开发者不需要手动去进行优化,就可以提供不错的性能。
    • 真实 DOM∶ 生成 HTML 字符串+重建所有的 DOM 元素
    • 虚拟 DOM∶ 生成 vNode+ DOMDiff +必要的 dom 更新

    可以看到虚拟 DOM 多了一层虚拟 dom 计算和 diff 的过程,但是这是在 js 完成,相比后面 DOM 操作,消费起来便宜了太多。如果大量 DOM 中只需要局部更新一部分 dom,虚拟 DOM 会避免无效的渲染,优势更加明显。

    1. 提供跨平台能力。虚拟 DOM 本质是 js 对象,可以很方便的跨平台操作,比如 ssr,uniapp 等。
    # DIFF 算法的原理(p4)

    分为 3 步

    • 使用深度遍历标记新旧 DOM 树的节点,保证比较的时候比较出差异类型
    • 通过同层对比,比较差异。差异有几种类型,如节点类型改变,属性改变,文本改变,移动新增删除类型。同层保证了 O(n)的时间复杂度。
    • 将差异应用到真实 DOM。深度遍历真实 DOM,如果有差异,将该差异更新到真实 DOM 上。

    vue 核心之虚拟 DOM (opens new window)

    # Vue 中 key 的作用(p4)

    在虚拟 DOM 中,使用 key 来唯一的标识一个元素,在 diff 中可以准确地比较出节点的差异,然后高效地渲染 DOM。

    • 更准确:如果标记了 key,则相同类型的节点不会就地复用,避免旧节点状态的污染和低效率。
    • 更快速,因为 key 的唯一性,在 diff 中将 key 作为键,可以直接获取对应节点,比遍历更快。

    # 状态管理

    # 如何理解 Vuex(p3)

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

    Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

    改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样可以方便地跟踪每一个状态的变化。

    解读上图:

    • Vue Components 是 vue 组件,组件会触发(dispatch)一些事件或动作,也就是图中的 Actions;

    • 在组件中发出的动作,肯定是想获取或者改变数据的,但是在 vuex 中,数据是集中管理的,不能直接去更改数据,所以会把这个动作提交(Commit)到 Mutations 中;

    • 然后 Mutations 就去改变(Mutate)State 中的数据;

    • 当 State 中的数据被改变之后,就会重新渲染(Render)到 Vue Components 中去,组件展示更新后的数据,完成一个流程。

    # Vuex 中 action 和 mutation 的区别(p4)
    • Mutation 专注于修改 State,理论上是修改 State 的唯一途径;Action 业务代码、异步请求。

    • Mutation:必须同步执行;Action:可以异步,但不能直接操作 State。

    • 在视图更新时,先触发 actions,actions 再触发 mutation

    • mutation 的参数是 state,它包含 store 中的数据;store 的参数是 context,它是 state 的父级,包含 state、getters

    # Vuex 和 localStorage 的区别(p3)

    (1)最重要的区别

    • vuex 存储在内存中
    • localstorage 则以文件的方式存储在本地,只能存储字符串类型的数据,存储对象需要 JSON 的 stringify 和 parse 方法进行处理。 读取内存比读取硬盘速度要快

    (2)应用场景

    • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。vuex 用于组件之 间的传值。
    • localstorage 是本地存储,是将数据存储到浏览器的方法,一般是在跨页面传递数据时使用 。
    • Vuex 能做到数据的响应式,localstorage 不能

    (3)永久性

    刷新页面时 vuex 存储的值会丢失,localstorage 不会。

    # Vuex 有哪几种属性?(p3)

    有五种,分别是 State、 Getter、Mutation 、Action、 Module

    • state => 基本数据(数据源存放地)
    • getters => 从基本数据派生出来的数据
    • mutations => 提交更改数据的方法,同步
    • actions => 像一个装饰器,包裹 mutations,使之可以异步。
    • modules => 模块化 Vuex
    # Vuex 如何持久化

    使用相关插件比如vuex-persistedstate,它会监听vuex的数据变化,并在修改后存储到localStorage中,这样即使刷新页面也不会丢失状态了。

    # Vue3

    # Vue3.0 有什么更新(p3)
    • 基于 Proxy 实现响应式,而 vue2 是通过 ES5 的 Object.defineProperty 来实现。避免了对数组响应式更新做的 hack 和 vm.$set 的方式。
    • 组合式 API 的使用。
      • 通过 setup 选项,将统一逻辑关注点的代码集中在一起,便于维护。
      • 生命周期钩子和其他如 ref 等方法都可以按需引入。
      • 没有 this。
    • 模板不需要最外层的节点,即 template 下一级可以有多个元素或组件。
    • 移除 filter。
    • Teleport。可以将组件插入到任意 dom 节点。
    • Suspense。利用作用域插槽,可以方便显示异步组件加载前后的视图
    • 加强对 ts 的支持。
    # defineProperty 和 proxy 的区别(p4)

    Vue2 在实例初始化时遍历 data 中的所有属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。这样当追踪数据发生变化时,setter 会被自动调用。

    但是这样做有以下问题:

    1. 添加或删除对象的属性时,Vue 检测不到。因为添加或删除的对象没有在初始化进行响应式处理,只能通过$set 来调用Object.defineProperty()处理。
    2. 无法监控到数组下标和长度的变化。

    Vue3 使用 Proxy 来监控数据的变化。有以下特点:

    1. Proxy 直接代理整个对象而非对象属性,这样只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性。
    2. Proxy 可以监听数组的变化。
    3. 不需用使用 Vue.$set触发响应式。
    上次更新: 2024/09/17, 20:27:35
    JavaScript
    计算机网络

    ← JavaScript 计算机网络→

    最近更新
    01
    提交代码时修改commit消息
    04-09
    02
    如何快速定位bug
    02-20
    03
    云端web项目开发踩坑
    08-25
    更多文章>
    Theme by Vdoing | Copyright © 2021-2025 dwfrost | 粤ICP备2021118995号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式
    ×