vue 知识点复盘

本文整理了常用的 vue 实战技巧及要点。对于 vue 初级开发者来说可能有些难度,但干货很足。

一.vue

1.vue 生命周期

vue生命周期大致分为:创建前后,挂载前后,更新前后,销毁前后四个阶段。

  • beforeCreate:在 beforeCreate 生命周期执行时,data 和 methods 中的数据还未初始化,所以此时不能使用 data 中的数据和 methods 中的方法
  • created:data 和 methods 初始化完毕,此时可以使用 methods 中的方法和 data 中的数据
  • beforeMount:template 模版已经编译好,但还未挂载到页面,此时页面还是上一个状态
  • mounted:此时 Vue 实例初始化完成了,DOM 挂载完毕,可以直接操作 dom 或者使用第三方 dom 库
  • beforeUpdate:此时 data 已更新,但还未同步页面
  • updated:data 和页面都已经更新完成
  • beforeDestory:Vue 实例进入销毁阶段,但所有的 data, methods,指令,过滤器等都处于可用状态
  • destroyed: 此时组件已经被销毁,data,methods 等都不可用

我们一般在 created 中发送 http 请求获取后端数据,在 mounted 中操作 dom

另外 3 个生命周期方法:

  • activated:被 keep-alive 缓存的组件激活时调用。
  • deactivated:被 keep-alive 缓存的组件停用时调用。
  • errorCaptured:当捕获一个来自子孙组件的错误时被调用。

使用 keep-alive 时,第一次进入页面的生命周期:beforeCreate/created => beforeMount/mounted => activated;再次进入时只执行 activated。

2.$on, $once 的使用

$on:监听当前实例上的自定义事件。
$once:监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除

示例:全局事件的绑定与解绑
普通版:

  mounted () {
    
    
    window.addEventListener('scroll', this.handleScroll)
  },
  beforeDestroy () {
    
    
    window.removeEventListener('scroll', this.handleScroll)
  }

升级版:

  mounted () {
    
    
    window.addEventListener('scroll', this.handleScroll)
    // 通过 hook 监听组件销毁钩子函数,并取消监听事件。
    this.$once('hook:beforeDestroy', () => {
    
    
       window.removeEventListener('scroll', this.handleScroll)
    })
  },

3.指令与修饰符

指令

  • v-text
  • v-html
  • v-show
  • v-if
  • v-else-if
  • v-else
  • v-for
  • v-on,缩写:@,用于绑定事件。绑定多个事件还可以这样写:
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
  • v-bind,缩写::,用于绑定属性
  • v-model,用于表单绑定
  • v-slot,缩写:#,插槽
  • v-pre,跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。例子:
<span v-pre>{
    
    {
    
     this will not be compiled }}</span>
  • v-cloak,这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。(用来避免页面加载时出现闪烁的问题)
[v-cloak] {
  display: none;
}

<div v-cloak>
  {
   
   { message }}
</div>
  • v-once,只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

常用修饰符

(1)事件修饰符(与 v-on 使用)

  • .stop:调用 event.stopPropagation(),阻止事件冒泡。
  • .prevent:调用 event.preventDefault(),阻止默认行为。
  • .self:只当事件是从侦听器绑定的元素本身触发时才触发回调。
  • .{keyCode | keyAlias}:只当事件是从特定键触发时才触发回调。
  • .native:监听组件根元素的原生事件。
  • .once:只触发一次回调。

(2)表单修饰符(与 v-model 使用)

  • .lazy:在输入框输入完内容,光标离开时才更新视图。
  • .number:输入字符串转为有效的数字。
  • .trim:输入首尾空格过滤。

(3)系统修饰符

  • .ctrl
  • .alt
  • .shift
  • .meta:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。

修饰符可以多个使用,比如 @click.prevent.self,修饰符的位置代表执行顺序。
系统修饰符的使用,如 @click.ctrl=“ctrlClick”,只有按住 ctrl 键再点击才会触发。当然,我们也可以在该元素上同时绑定 @click 事件。

4.computed,watch 的使用与比较

(1)computed 计算属性
计算属性的结果会被缓存,除非依赖的响应式 property 变化才会重新计算。
计算属性可以定义 get 和 set

var vm = new Vue({
    
    
  data: {
    
     a: 1 },
  computed: {
    
    
    // 读取和设置
    aPlus: {
    
    
      get: function () {
    
    
        return this.a + 1
      },
      set: function (v) {
    
    
        this.a = v - 1
      }
    }
  }
})
vm.aPlus   // => 2
vm.aPlus = 3
vm.a       // => 2

(2)watch 监听
watch 除了基础用法外,还有 立即触发监听,深度监听

var vm = new Vue({
    
    
  data: {
    
    
    c: {
    
    
      a: 1
    },
    d: 4,
    e: {
    
    
      f: {
    
    
        g: 5
      }
    }
  },
  watch: {
    
    
    // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
    c: {
    
    
      handler: function (val, oldVal) {
    
     /* ... */ },
      deep: true
    },
    // 该回调将会在侦听开始之后被立即调用
    d: {
    
    
      handler: 'someMethod',
      immediate: true
    },
    // 只监听对象某一个属性
    'e.f': function (val, oldVal) {
    
     /* ... */ }
  }
})

(3)computed 与 watch 的比较

  • 计算属性支持缓存,监听不缓存;
  • 计算属性是由一个或多个依赖计算出来的,所以适合一对一或多对一的关系;监听是一个属性变化从而执行一个或多个操作,所以适合一对一或一对多的关系;
  • 计算属性不支持异步操作,监听支持;若想要在计算属性中使用异步,可以考虑使用 vue-async-computed

5.组件间的通信方式

  1. 父子组件通信,通过属性与 $emit 事件触发的方式
  2. 通过事件总线(bus),即发布订阅的方式
  3. Vuex
  4. Vue.observable

Vuex 建议我们只在开发中大型项目时才使用它,这里我们可尝试用其他方式做状态管理。

使用 Vue.observable

(1)创建 store

import Vue from 'vue'

// 通过 Vue.observable 创建一个可响应的对象
export const store = Vue.observable({
    
    
  userInfo: {
    
    }
})

// 定义 mutations, 修改属性
export const mutations = {
    
    
  setUserInfo(userInfo) {
    
    
    store.userInfo = userInfo
  }
}

(2)组件中使用

<template>
  <div>
    {
   
   { userInfo.name }}
  </div>
</template>
<script>
import {
     
      store, mutations } from '../store'
export default {
     
     
  computed: {
     
     
    userInfo() {
     
     
      return store.userInfo
    }
  },
  created() {
     
     
    mutations.setUserInfo({
     
     
      name: '小明'
    })
  }
}
</script>

6.全局组件,局部组件,动态组件,异步组件,递归组件

(1)全局组件

通过 Vue.component 注册一个全局组件。如,在 main.js 中:

import MyPopover from '@/components/popover'

Vue.component('my-popover', MyPopover)

(2)局部组件

在一个组件或页面中引入另一个组件,则被引入组件就是局部组件,其只能在当前范围内使用。

(3)动态组件

有时我们需要在组件间做动态切换,这时就会用到动态组件。

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

currentTabComponent 可以包括:

  • 已注册组件的名字,或
  • 一个组件的选项对象

(4)异步组件

在做路由懒加载时我们会使用异步组件,以实现按需加载,优化单页应用的首屏时间。

(5)递归组件

当我们要在组件中调用自身时会使用递归组件。

<template>
  <div>
    <div
      class="item"
      v-for="(item, index) of list"
      :key="index"
    >
      <div class="item-title border-bottom">
        <span class="item-title-icon"></span>
        {
   
   {item.title}}
      </div>
      <div v-if="item.children" class="item-children">
        <!-- 调用自身 -->
        <detail-list :list="item.children"></detail-list>
      </div>
    </div>
  </div>
</template>
 
<script>
export default {
     
     
  name: 'DetailList',
  props: {
     
     
    list: Array
  }
}
</script>

7.单文件组件中 name 值的作用

<script>
export default {
     
     
  name: 'Home'
}
</script>
  • 作用一:给 <keep-alive> 设置 include 和 exclude 的值,这个值就是 name(当然,如果 name 选项不可用,则匹配它的局部注册名称);
  • 作用二:递归组件的调用
  • 作用三:在使用 Vue.js devtools 调试工具时,用来显示组件的名字(当然,如果 name 选项不可用,则匹配它的局部注册名称)。

8.v-show 与 v-if 的比较

  • v-show 有更高的初始渲染开销,v-if 有更高的切换开销。v-if 在切换时会对元素进行创建和销毁,v-show 只会在初始化时加载一次。
  • v-if 是惰性的,只有条件为真时才会进行渲染;v-show 不管条件真假,都会进行渲染,由 css 样式 display 进行显示隐藏。
  • 所以,v-show 适合频繁切换的元素,v-if 适合切换频率低的元素

9.key 的作用

key 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
当 key 值发生改变时,元素总是会被替换而不是被修改,因此,以下情景下可能会使用:

  • 完整地触发组件的生命周期钩子
  • 触发过渡

10.双向绑定原理

vue 数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。通过 Object.defineProperty() 来劫持各个属性的 setter 和 getter,当数据变化时会通知视图更新。下面,我们通过一个例子来简单实现其效果。

  <div id="harry"></div>
  <input id="trigger" type="text">
  <script>
    let harry = document.getElementById('harry')
    let trigger = document.getElementById('trigger')
    let key = 'name' // 属性键名
    let store = {
     
     }   // 辅助 get 取值
    let obj = {
     
           // 对象
      name: ''
    }
    Object.defineProperty(obj, key, {
     
     
      set (value) {
     
     
        harry.innerText = value // 修改视图
        store[key] = value
      },
      get () {
     
     
        return store[key]
      }
    })
    trigger.addEventListener('keyup', function () {
     
     
      obj[key] = this.value
      console.log(obj[key])
    })
  </script>

效果图:
在这里插入图片描述

11.默认插槽,具名插槽,作用域插槽

(1)默认插槽

  <div id="app">
    <slot-component>
      <span>hello</span>
    </slot-component>
  </div>

  <script>
    var SlotComponent = {
     
     
      template: `
        <div>
          <slot></slot>
        </div>
      `
    }
    new Vue({
     
     
      el: '#app',
      components: {
     
     
        SlotComponent
      }
    })
  </script>

渲染出的元素如下:
在这里插入图片描述
(2)具名插槽

顾名思义,就是给插槽加上名字。

  <div id="app2">
    <slot-component2>
      <template v-slot:header>
        <div>header</div>
      </template>
      <div>footer</div>
    </slot-component2>
  </div>

  <script>
    // 具名插槽
    var SlotComponent2 = {
     
     
      template: `
        <div>
          <slot name="header"></slot>
          <div>Other Content</div>
          <slot></slot>
        </div>
      `
    }
    new Vue({
     
     
      el: '#app2',
      components: {
     
     
        SlotComponent2
      }
    })
  </script>

这里,我们需要在一个<template> 元素上使用 v-slot 指令,以指明其名称。同时,任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容。
上述示例的渲染结果如下:
在这里插入图片描述
(3)作用域插槽

正常情况下,父级是无法直接访问插槽中的数据,如要实现这个效果,就需要使用作用域插槽。

<div id="app3">
  <slot-component3>
    <template v-slot:default="scope">
      {
   
   { scope.user.lastName }}
    </template>
  </slot-component3>
</div>

<script>
  // 作用域插槽
  var SlotComponent3 = {
     
     
    data () {
     
     
      return {
     
     
        user: {
     
     
          firstName: '1',
          lastName: '2'
        }
      }
    },
    template: `
      <div>
        <slot :user="user">{
      
      { user.firstName }}</slot>
      </div>
    `
  }
  new Vue({
     
     
    el: '#app3',
    components: {
     
     
      SlotComponent3
    }
  })
</script>

可以看到,在组件中原本是显示默认值 firstName,但在父作用域中,我们改为了 lastName。

12.混入(mixin)

我们在提炼组件间可复用功能时,往往会想到封装一个方法。而 mixin 相对而言会更加灵活,我们在使用 mixin 时需要掌握其合并策略。

  <script>
    var myMixin1 = {
     
     
      data () {
     
     
        return {
     
     
          msg: 'hi'
        }
      },
      created () {
     
     
        console.log('msg mixin1', this.msg) // hello
      }
    }
    var myMinin2 = {
     
     
      created () {
     
     
        console.log('msg mixin2', this.msg) // hello
      },
      methods: {
     
     
        sayName () {
     
     
          console.log('mixin name')
        }
      }
    }
    new Vue({
     
     
      el: '#app',
      mixins: [myMixin1, myMinin2],
      data: {
     
     
        msg: 'hello'
      },
      created () {
     
     
        console.log('msg', this.msg) // hello
        this.sayName() // component name
      },
      methods: {
     
     
        sayName () {
     
     
          console.log('component name')
        }
      }
    })
  </script>

最终打印的结果为:
// msg mixin1 hello
// msg mixin2 hello
// msg hello
// component name

由上,mixin 遵循以下规则:

  • 数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先;
  • 同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用;
  • 值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。

13.函数式组件

对于一个简单的数据展示组件,它不用管理任何状态,也不用监听任何传递给它的状态,也不用生命周期方法。这时,我们可以考虑使用函数式组件。

  <div id="app">
    <my-component :text="'Hello World!'"></my-component>
  </div>

  <script>
    var MyComponent = {
     
     
      // 声明函数式组件
      functional: true,
      props: {
     
     
        text: {
     
     
          type: String,
          default: ''
        }
      },
      // context 包含 props, slots 等字段
      render: function (createElement, context) {
     
     
        // console.log('content', context)
        return createElement('div', context.props.text)
      }
    }
    new Vue({
     
     
      el: '#app',
      components: {
     
     
        MyComponent
      }
    })
  </script>

函数式组件特点:

  • 无状态、无实例
  • 因为函数式组件只是函数,所以渲染开销也低很多

基于模板的函数式组件

如果不习惯用 JS 或 JSX 的方式写 html 模板,可以使用这种方式。

<template functional>
</template>

14.自定义指令(Vue.directive)

举个聚焦输入框的例子:

  <div id="app">
    <input type="text" v-focus >
  </div>

  <script>
    // 注册一个全局自定义指令 `v-focus`
    Vue.directive('focus', {
     
     
      // 当被绑定的元素插入到 DOM 中时……
      inserted: function (el) {
     
     
        // 聚焦元素
        el.focus()
      }
    })
    new Vue({
     
     
      el: '#app'
    })
  </script>

钩子函数:

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

关于钩子函数的参数相关介绍,见官网:钩子函数参数

15.$attrs 与 $listeners

  • $attrs : 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。
  • $listeners : 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。

如下例子,我们封装了一个表单的保存按钮。
父作用域:

<FormSaveButton :is-editForm="isEditForm" :disabled="loading" @click="submitForm('form')" />

子组件:

<template>
  <!-- 表单的保存按钮 -->
  <el-button
    :class="isEditForm ? 'warning-btn-text': ''"
    style="float: right; padding: 3px 0"
    type="text"
    v-bind="$attrs"
    v-on="$listeners"
  >{
   
   {$t('buttonText.save')}}
  </el-button>
</template>

<script>
export default {
     
     
  props: {
     
     
    isEditForm: Boolean
  }
}
</script>

这里我们通过 v-bind="$attrs" 将父作用域中的 :disabled=“loading” 绑定在了按钮上,通过 v-on="$listeners" 将点击事件绑定在了按钮上。注意:这里 $attrs 不包含 isEditForm 属性,因为其已经在 props 中进行了定义。

这样写的好处在于减少了 props, $emit 操作。

16. .sync 修饰符

在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。这时我们可以考虑使用 .sync 。

  <div id="app">
    <my-component :title="title" @update:title="title = $event"></my-component>
    <!-- 简写版 -->
    <!-- <my-component :title.sync="title"></my-component> -->
  </div>

  <script>
    var MyComponent = {
     
     
      props: {
     
     
        title: {
     
     
          type: String,
          default: ''
        }
      },
      template: `
        <div>{
      
      { title }}</div>
      `,
      created () {
     
     
        // 修改 title
        this.$emit('update:title', '更新标题')
      }
    }
    new Vue({
     
     
      el: '#app',
      components: {
     
     
        MyComponent
      },
      data: {
     
     
        title: '标题内容',
      }
    })
  </script>

除了使用 .sync 实现双向绑定外,v-model 也能实现双向绑定,不过两种之间存在差异:

  • 一个组件上可多个使用 .sync,而 v-model 目前只能使用一个。
  • v-model 默认触发 input 事件,常用于表单元素的双向绑定。

17.使用 Object.freeze 冻结对象或数组

vue 默认会对 data 中的数据做 getter 和 setter 转换,以支持响应式。
如果你有很大的数组或对象,并确信不会改变,这时可以考虑使用 Object.freeze 对其冻结。这样做能一定程度上提升性能。
例如:

  // 冻结 sourceArr 数组, 使 vue 不对其做 setter getter 转换
  this.myArr = Object.freeze(sourceArr)

18.watch 中使用防抖

使用场景:搜索框搜索,当用户输入内容后自动更新数据,这时,我们一般要加个防抖。

一般的防抖方法

function debounce (func, wait) {
    
    
  let timeout = null
  return function() {
    
    
    const context = this
    const args = arguments
    if (timeout) clearTimeout(timeout)
    timeout = setTimeout(() => {
    
    
      func.apply(context, args)
    }, wait)
  }
}

常见的使用防抖方式

  methods: {
    
    
    _addEvent () {
    
    
      window.addEventListener('resize', debounce(this.doSomething, 100))
    },
  }

我们给 resize 事件这样使用防抖是没问题的,但如果在监听中这样使用防抖

  watch: {
    
    
    searchVal: debounce(this._initData, 500)
  },

这时 this._initData 就会报错。那又如何改进呢?

修改防抖方法

/**
 * 防抖函数
 * @param {String} funcName 执行的方法名
 * @param {Number} wait 等待的时间
 * @return {Function} 执行函数
 */
function debounce (funcName, wait) {
    
    
  let timeout = null
  let _this = this
  return function() {
    
    
    if (timeout) clearTimeout(timeout)
    timeout = setTimeout(() => {
    
    
      // 监听防抖时用内层 this, 此时外层 this 为 undefined
      _this = _this || this
      _this[funcName]()
    }, wait)
  }
}

可以看到,这里将方法改为了函数名传递,然后再通过 this 进行访问。

在监听中使用

  watch: {
    
    
    searchVal: debounce('_initData', 500)
  },

由于修改了防抖方法,所以 resize 事件添加防抖的方式也要改变

  methods: {
    
    
    _addEvent () {
    
    
      window.addEventListener('resize', debounce.call(this, '_setCollapse', 100))
    },
  }

当然,若你有更好的方式欢迎在评论区提出。

19.v-model 的基础上再次封装

有如下需求,需要将下面的代码封装成全局组件:

<el-input
  v-model="searchVal"
  size="mini"
  :placeholder="$t('hintText.searchName')"
  class="vk-search-input"
/>

这个需求的难点在于 v-model,在进行封装前,我们需要知道 v-model 是语法糖,具体如下:

<input v-model="val">

等同于

<input
  v-bind:value="val"
  v-on:input="val = $event.target.value"
>

如果我们需要将代码如下模样,该如何实现呢?

<table-search-input v-model="searchVal" />

实现如下:

<template>
  <!-- 表格搜索输入框 -->
  <el-input
    v-model="val"
    size="mini"
    :placeholder="$t('hintText.searchName')"
    class="vk-search-input"
  />
</template>

<script>
export default {
     
     
  model: {
     
     
    prop: 'searchVal',
    event: 'input'
  },
  props: {
     
     
    searchVal: {
     
     
      type: String,
      default: ''
    }
  },
  data () {
     
     
    return {
     
     
      val: this.searchVal
    }
  },
  watch: {
     
     
    val (val) {
     
     
      this.$emit('input', val)
    }
  }
}
</script>

这里我们使用了 model 配置,其作用是——允许一个自定义组件在使用 v-model 时定制 prop 和 event。详情可见官网:API-model

二.vue-router

1.一些专业术语

  • 动态路由 (/user/:id)
  • 嵌套路由 (children: [])
  • 编程式导航 (push、replace、go)
  • 命名路由、命名视图
  • 重定向 (redirect) 和别名 (alias)
  • 路由组件传参 (props 解耦, 布尔模式、对象模式、函数模式)
  • H5 History 模式 (hash、history)
  • 导航守卫 (前置导航守卫 beforeEach、全局解析守卫 beforeResolve、全局后置钩子 afterEach、路由独享的守卫 beforeEnter、组件内的守卫 beforeRouteEnter beforeRouteUpdate beforeRouteLeave、完整的导航解析流程)
  • 路由元 (meta)
  • 过度效果 (transition)
  • 数据获取
  • 滚动行为 (scrollBehavior)
  • 路由懒加载
  • 导航故障

以上这些术语其实就是官网侧边栏目录,通过看这些词汇,我们脑海中应浮现出对应内容,如果没有,可能就是不太熟悉。

vue-router 官方文档

2.this.$route 与 this.$router 的区别

$route 是当前路由,$router 全局路由。
打印效果如下:

3.动态路由参数变化的注意点

比如有动态路由 /user/:id。当参数发生变化时,例如从 /user/0 导航到 /user/1原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用

对路由参数变化做出响应的方式:

方式一:监听 $route

  watch: {
    
    
    $route(to, from) {
    
    
      // 对路由变化作出响应...
    }
  }

方式二:使用 beforeRouteUpdate 导航守卫

const User = {
    
    
  template: '...',
  beforeRouteUpdate (to, from, next) {
    
    
    // 对路由变化作出响应...
    // 记得执行 next()
  }
}

4.编程式导航常用方法

  • router.push 导航到不同的 URL,会向 history 栈添加一个新的记录;
  • router.replace 它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录;
  • router.go 方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步。

三.vuex

1.流程图

在这里插入图片描述
图中,整个虚线部分就是Vuex,我们可以把它看成一个公共仓库 store。store 中有 Actions(行为)、Mutations(变动)和 State(状态)。整个的逻辑是组件通过 Dispatch 调用 Actions 中的方法,Actions 通过 Commit 调用 Mutations 中的方法,Mutatisons 改变 State 中的值。

注意:mutation 必须是同步函数,action 可以执行异步操作,action 也可省略。

2.解决刷新页面数据被重置为初始状态的问题

对于这个问题,我们一般通过 Vuex 与缓存结合使用来解决。

读取数据时

let defaultCity = ''
if (localStorage.getItem('city')) {
    
    
  defaultCity= localStorage.getItem('city')
}
 
const state = {
    
    
  city: defaultCity
}
 
export default state

改变数据时

const mutations = {
    
    
  changeCity (state, val) {
    
    
    localStorage.setItem('city', val)
    state.city= val
  }
}
 
export default mutations

四.vue3

vue3 正式版已发布,所以我们应对其有所了解。

vue3 官方文档

1.vue2 有哪些不足?

  • vue2.x 对数组对象的深层监听无法实现;
  • vue2.x 在模板编译过程中会涉及到许多不必要的CPU工作;
  • 随着功能的增长,复杂组件的代码变得难以维护;
  • vue2.x 是采用 Facebook 的 Flow 做类型检查,但在某些情况下推断有问题,且对 typescript 支持不太友好。

2.vue3 有哪些改动?

  • Object.defineProperty => Proxy
  • 使用 TS 重构
  • 重构了虚拟 DOM
  • OptionApi => Composition API

当然,以上写得并不全面具体,详细见官网。

猜你喜欢

转载自blog.csdn.net/qq_39025670/article/details/109049474