Документация Vue3.0 и документация Vue2.x В чем отличия

Vue.js

Йода объявилVue3.0вошелКандидатский этап【Поздравляем】! ! ! Также возможно продвижениепроверять, который инициализирует проект напрямую через Vite.Vue3.0Бета-версия документации также была запущена, и заинтересованные друзья могут заглянуть внутрь. Кроме тогопоказать путь➡️ Часть руководства по API для Vue3, выпущенного ранее, которое включает в себя сложный и сложный системный API с реагированием и составной API.

Обзор

Новые функции Vue3.0

  • Composition API---- API композиции, включаяsetup()Ждать
  • Reactivity API---- API реактивной системы, в том числеref()Ждать
  • Teleport--- Встроенный компонент доставки
  • Fragments---templateВ Vue2.x может быть несколько корневых DOM (или компонентов), но может существовать только один
  • EmitsДобавлен настраиваемый параметр события в компонент
  • v-modelМодификацию , вы можете передать в параметрах, так что вы можете использовать несколькоv-model, так же можно настроить модификаторы, удалены.syncмодификатор.
  • createRenderer API from @vue/runtime-core to create custom renderers

удаленный контент

  • удаленныйkeyCodeмодификатор, вы можете использоватьkebab-caseимя вместо этого (например,.delete/.enter)
  • удалил экземпляр$on, $offа также$once метод
  • удаленныйфильтрFilters, Vue3.0 рекомендуется использоватьcomputedреализовать
  • удаленныйInline templates attributes
  • удаленный.syncмодификатор

Справочник по API / Новые и устаревшие API

---【Жирный курсив означает новые добавления, зачеркнутый шрифт означает удаление, а курсив означает изменения.]---

API экземпляров (отдельно от глобального API)

В Vue 3 API, который глобально изменил поведение Vue, был перемещен в экземпляр (черезcreateAppсоздан) в API. Они влияют только на этот экземпляр. следующим образом:mount,provide,unmount,directive(Хук жизненного цикла пользовательской инструкции был переименован),component,config,mixin,use

Глобальный API

createApp,h(render()псевдоним),defineComponent,defineAsyncComponent,resolveComponent(может использоваться только в функциях рендеринга),resolveDirective(может использоваться только в функциях рендеринга),withDirectives(может использоваться только в функциях рендеринга),createRenderer(может использоваться только в функциях рендеринга),nextTick,extend,filter,compile,observable(лоскутное одеялоreactiveальтернатива),version,set

Опции

Data

emits,data,props,computed,methods,watch,propsData

DOM

template,render,el,renderError

крючки жизненного цикла

beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,activated,deactivated,beforeUnmount(beforeDestroy),unmounted(destroyed),errorCaptured,renderTracked,renderTriggered

ресурс

directives,components,filters

комбинация

mixins,extends,provide / inject,setup,parent

разное

name,inheritAttrs,delimiters,functional,model,comments

свойства экземпляра

$data,$props,$el,$options,$parent,$root,$slots,$refs,$attrs,$children,$scopedSlots,$isServer,$listeners

Методы экземпляра (здесь также объединены жизненные циклы)

$watch,$emit,$forceUpdate,$nextTick,$set(обратите внимание, что это недоступно),$delete,$mount,destroy

инструкция

v-text,v-html,v-show,v-if,v-else,v-else-if,v-for,v-on,v-bind,v-model,v-slot,v-pre,v-cloak,v-once,v-is(Первоначально черезisвыполнить)

специальные свойства

key,ref,is,slot,slot-scope,scope(Эти 3 устарели в Vue 2.6 и выше)

встроенные компоненты

component,transition,transition-group,keep-alive,slot,teleport

[Новое] API адаптивной системы (API реактивности)

Основные реактивные API

reactive,readonly,isProxy,isReactive,isReadonly,toRaw,markRaw,shallowReactive,shallowReadonly

refs

ref,unref,toRef,toRefs,isRef,customRef,shallowRef

Computed and watch

computed,watchEffect,watch

[Новое] API композиции (API композиции)

setup,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,onErrorCaptured,onRenderTracked,onRenderTriggered,provide,inject

Вот мое сравнениеVue2.xНекоторые различия в учебной части документа версии приветствуются для дополнения или предоставления предложений по модификации (поскольку китайская версия еще не найдена, так что это просто мое собственное понимание, ссылка на эту часть документа была вставлена в точке знаний вы можете просмотреть исходный текст напрямую).

Основное использование

Как создать экземпляр Vue:

существуетVue2.xв, черезnew VueСоздайте экземпляр Vue и передайтеэль параметрсмонтировать DOM

<!-- Vue2.x 创建实例 -->
var vm = new Vue({
  // 选项
})

<!-- Vue2.x 挂载DOM -->
var vm = new Vue({
  el: '#app',
  data: {a:1}
})

существуетVue3.0в, черезcreateAppметод для создания экземпляра Vue. После создания экземпляра вы можете передать контейнер вmountспособ крепления

<!-- Vue3.0 创建实例 -->
Vue.createApp(/* options */)

<!-- Vue3.0 挂载DOM -->
Vue.createApp(/* options */).mount('#app')

Жизненный цикл

Жизненный цикл не сильно изменился, есть некоторые тонкие различия, связанные с изменением способа создания экземпляра.

Стоит отметить, что: вVue2.x, два хука для уничтожения экземпляраbeforeDestoryтак же какdestoryed, пока вVue3.0Измените имена этих двух хуков наbeforeUnmountа такжеunmounted.

Жизненный цикл Vue2.x

Жизненный цикл Vue3.0

Provide / inject

В Vue3.0 добавлена ​​поддержка руководств.Provide / injectизописывать

динамические компоненты

Vue2.x и Vue3.0 по-прежнему применяются путем добавления специального элемента во Vue.isсвойства для реализации

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

Но для анализа шаблонов DOM, таких как<ul>,<table>и т. д. ограничивают частный случай внутренних элементов по сравнению сVue2.xв привязкеisАтрибуты,Vue3.0при условии,v-isинструкция

<!-- Vue2.x 使用 is 属性 -->
<table>
  <tr is="blog-post-row"></tr>
</table>
<!-- Vue3.0 使用 v-is 指令 -->
<table>
  <tr v-is="'blog-post-row'"></tr>
</table>

пользовательское событие

Vue2.x и Vue3.0 все еще работают$emit('myEvent')триггерное событие,пройти черезv-on:myEventДля прослушивания событий разница в том, что Vue3.0 предоставляет в компонентеemitsсвойства для определения событий

<!-- Vue3.0 自定义事件 -->
app.component('custom-form', {
  emits: ['in-focus', 'submit']
})

Вы даже можете добавить проверку к пользовательским событиям, и в этом случае вам нужно поставитьemitsУстановите объект и назначьте имя события функции, которая получает$emitаргументы для вызова и возвращает логическое значение, чтобы указать, действительно ли событие

<!-- Vue3.0 为自定义事件添加校验 -->
app.component('custom-form', {
  emits: {
    // No validation
    click: null,

    // Validate submit event
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
  methods: {
    submitForm() {
      this.$emit('submit', { email, password })
    }
  }
})

Пользовательские компонентыv-model

существуетVue2.xсередина,v-modelПо умолчанию он будет использоватьvalueв качестве имени реквизита иinputкак имя инициированного события. Для особых сценариев вы также можете передатьmodelвозможность указать имя реквизита и имя события (обратите внимание, что в настоящее время этот реквизит все еще необходимо объявить в реквизитах)

<!-- Vue2.0 自定义 v-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)"
    >
  `
})

осторожность,существуетVue3.0середина,v-modelПо умолчанию он будет использоватьmodelValueв качестве имени реквизита иupdate:modelValueкак имя инициированного события.

поддержка каждогоv-modelпроходить впараметр, так что вы можете использовать несколько v-моделей на одном компоненте одновременно

<!-- Vue3.0 自定义 v-model 并且传入参数 -->
<my-component v-model:foo="bar" v-model:name="userName"></my-component>

можно даже установить для v-моделипользовательский модификатор, по умолчанию определяется в реквизитахmodelModifiersобъект для принятия модификаторов, поэтому вы можете использовать модификаторы для настройки различных механизмов запуска событий, которые вы хотите

<!-- Vue3.0 自定义修饰符默认接收方式 -->
<div id="app">
  <my-component v-model.capitalize="myText"></my-component>
  {{ myText }}
</div>

const app = Vue.createApp({
  data() {
    return {
      myText: ''
    }
  }
})

app.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  methods: {
    emitValue(e) {
      let value = e.target.value
      if (this.modelModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1)
      }
      this.$emit('update:modelValue', value)
    }
  },
  template: `<input
    type="text"
    v-bind:value="modelValue"
    v-on:input="emitValue">`
})

app.mount('#app')

Конечно, для переданного параметраv-model, вам нужно настроить его в реквизитахarg + "Modifiers"чтобы получить это параметризованноеv-modelмодификатор

<!-- Vue3.0 自定义参数的自定义修饰符 -->
<my-component v-model:foo.capitalize="bar"></my-component>

app.component('my-component', {
  props: ['foo', 'fooModifiers'],
  template: `
    <input type="text" 
      v-bind:value="foo"
      v-on:input="$emit('update:foo', $event.target.value)">
  `,
  created() {
    console.log(this.fooModifiers) // { capitalize: true }
  }
})

миксин

Vue2.x смешатьпуть черезVue.extend({mixins: [myMixin]})Определите компонент, который использует миксин

// 定义一个混入对象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// 定义一个使用混入对象的组件
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component() // => "hello from mixin!"

а такжеVue3.0похоже на создание экземпляра,Vue.createApp({mixins: [myMixin]})Определите компонент, который использует миксин

// 定义一个混入对象
const myMixin = {
  created() {
    this.hello()
  },
  methods: {
    hello() {
      console.log('hello from mixin!')
    }
  }
}

// 定义一个使用混入对象的组件
const app = Vue.createApp({
  mixins: [myMixin]
})

app.mount('#mixins-basic') // => "hello from mixin!"

пользовательская директива

Vue2.xОбъект определения директивы содержит5крючки:

  • bind: Вызывается только один раз, когда директива впервые привязывается к элементу. Здесь можно выполнить одноразовые настройки инициализации.
  • inserted: вызывается, когда связанный элемент вставляется в родительский узел (гарантируется существование только родительского узла, но не обязательно вставленного в документ).
  • update: Вызывается при обновлении VNode компонента, но может произойти до обновления его дочерних VNode. Значение инструкции может измениться, а может и не измениться. Но вы можете игнорировать ненужные обновления шаблона, сравнив значения до и после обновления.
  • componentUpdated: VNode компонента, в котором находится инструкцияи его дочерние VNodesВызывается после всех обновлений.
  • unbind: Вызывается только один раз, когда инструкция отсоединяется от элемента.

Vue3.0Объект директивы содержит6крючки:

  • beforeMount: вызывается, когда директива впервые привязывается к элементу. можно сделать здесьОдноразовыйначальные настройки.
  • mounted: вызывается, когда связанный элемент вставляется в родительский узел.
  • beforeUpdate: при обновлении VNode компонента, в котором он находитсяДопередача.
  • updated: VNode компонента, в котором находится инструкцияи его дочерние VNodesВызывается после всех обновлений.
  • beforeUnmount: Вызывается перед размонтированием родительского компонента связанного элемента. (Сравните Vue2.xновый)
  • unmounted: Вызывается только один раз, когда директива не привязана к элементу, а родительский компонент размонтирован.

В Vue3.0 из-за поддержки фрагментов компоненты могут иметь несколько корневых узлов, и использование пользовательских директив может вызвать проблемы. Крючки, содержащиеся в пользовательском объекте директивы, будут упакованы и внедрены в данные Vnode как хуки жизненного цикла Vnode.

<!-- Vue3.0 自定义指令对象包含的钩子包装后 -->
{
  onVnodeMounted(vnode) {
    // call vDemo.mounted(...)
  }
}

При использовании пользовательских директив в компонентах этиonVnodeXXXХуки будут передаваться непосредственно компоненту как несвязанные свойства, которые можно подключить непосредственно к жизненному циклу элемента в шаблоне, подобном этому.(这里不太明白,之后试验过再来更新)

<div @vnodeMounted="myHook" />

Когда дочерний компонент используется во внутреннем элементеv-bind="$attrs", он также применит к нему любые пользовательские директивы.

Встроенные компоненты доставкиTeleport

Встроенный Vue3.0<teleport>Компонент может отправить шаблон в другое место,

<!-- Vue3.0 <teleport>传送组件 -->
<body>
  <div id="app" class="demo">
    <h3>Move the #content with the portal component</h3>
    <div>
      <teleport to="#endofbody">
        <p id="content">
          This should be moved to #endofbody.
        </p>
      </teleport>
      <span>This content should be nested</span>
    </div>
  </div>
  <div id="endofbody"></div>
</body>

если<teleport>Включите компонент Vue, и он все равно будет<teleport>Логический дочерний элемент родительского компонента, то есть даже если дочерний элемент будет отрендерен в другом месте, он все равно будет дочерним элементом родителя и будет получать от родителяprop.

Использование нескольких транспортных компонентовБудет использовать логику накопления, вот так

<teleport to="#modals">
  <div>A</div>
</teleport>
<teleport to="#modals">
  <div>B</div>
</teleport>

<!-- 结果 B 渲染在 A 后面 -->
<div id="modals">
  <div>A</div>
  <div>B</div>
</div>

функция рендеринга

Vue2.xфункция рендерингаПараметрыcreateElement

Vue3.0функция рендерингапараметрыcreateVNode(Название ближе к тому, что оно на самом деле означает, возвращая виртуальный DOM)

будет такжеhВ качестве псевдонима в Vue3.0 можно напрямую передатьVue.hПолучать

const app = Vue.createApp({})

app.component('anchored-heading', {
  render() {
    const { h } = Vue

    return h(
      'h' + this.level, // tag name
      {}, // props/attributes
      this.$slots.default() // array of children
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

События и ключевые модификаторыVue2.x предоставляет соответствующие префиксы для модификаторов событий .passive, .capture и .once, которые можно использовать на:

модификатор приставка
.passive &
.capture !
.once ~
.capture.onceили
.once.capture ~!
<!-- Vue2.x 对修饰符使用前缀 -->
on: {
  '!click': this.doThisInCapturingMode,
  '~keyup': this.doThisOnce,
  '~!mouseover': this.doThisOnceInCapturingMode
}

Vue3.0 использует синтаксис объекта

<!-- Vue3.0 对修饰符使用对象语法 -->
render() {
  return Vue.h('input', {
    onClick: {
      handler: this.doThisInCapturingMode,
      capture: true
    },
    onKeyUp: {
      handler: this.doThisOnce,
      once: true
    },
    onMouseOver: {
      handler: this.doThisOnceInCapturingMode,
      once: true,
      capture: true
    },
  })
}

плагин

разработать плагинVue3.0 все еще необходимо предоставитьinstallметод, передать два параметра, первый параметр передаетсяVue.createAppСконструированный объект, второй необязательный параметр передается пользователемoptions

// plugins/i18n.js
export default {
  install: (app, options) => {
  // Plugin code goes here
  }
}

Выставляется в плагине черезapp.config.globalPropertiesглобальный метод регистрации собственности

// plugins/i18n.js
<!-- 通过 app.config.globalProperties 全局注入 translate 方法 -->
export default {
  install: (app, options) => {
    app.config.globalProperties.$translate = (key) => {
      return key.split('.')
        .reduce((o, i) => { if (o) return o[i] }, i18n)
    }
  }
}

также черезinjectпредоставить метод или свойство пользователю

// plugins/i18n.js
<!-- 这样组件里就可以通过 inject 访问 i18n 和 options -->
export default {
  install: (app, options) => {
    app.config.globalProperties.$translate = (key) => {
      return key.split('.')
        .reduce((o, i) => { if (o) return o[i] }, i18n)
    }

    app.provide('i18n')
  }
}

<!-- 然后就可以通过 inject['i18n'] 把 i18n 注入组件并访问 -->

Используйте плагинывсе еще черезuse()Метод может принимать два параметра, первый параметр — это подключаемый модуль, который будет использоваться, а второй параметр является необязательным и будет передан подключаемому модулю.

import { createApp } from 'vue'
import App from './App.vue'
import i18nPlugin from './plugins/i18n'

const app = createApp(App)
const i18nStrings = {
  greetings: {
    hi: 'Hallo!'
  }
}

app.use(i18nPlugin, i18nStrings)
app.mount('#app')

Отзывчивые принципы

Отзывчивая система

Как мы все знаем,Vue2.xчерезObject.definePropertyВ сочетании с моделью публикации / подписки для достижения.

Передайте экземпляр Vuedata, Vue будет проходитьdataобъект принадлежитpropertyи использоватьObject.definePropertyПреобразуйте все эти свойства вgetter/setter, чтобы отслеживать зависимости при доступе к свойствам и их изменении. Каждый экземпляр компонента соответствуетwatcherinstance, который записывает «затронутые» свойства данных как зависимости во время рендеринга компонента. когда зависимостиsetterПри срабатывании он уведомитwatcher, вызывая повторный рендеринг связанного с ним компонента.

, а такжеVue3.0используя ES6ProxyПрокси для перехвата доступа к целевому объекту. Передайте экземпляр Vuedata, Vue преобразует его вProxy. Это позволяет VUE выполняться при доступе или изменении свойств.отслеживание зависимостейтак же какуведомление об изменении. Каждое свойство считается зависимостью. После первоначального рендеринга компонент будет отслеживать зависимости (то есть свойства, к которым он обращался во время рендеринга). Другими словами, компонент становится свойством этих свойств.подписчик. Когда прокси перехватываетsetПри работе это свойство будет уведомлять своих подписчиков о повторном рендеринге.

const dinner = {
  meal: 'tacos'
}

const handler = {
  get(target, prop, receiver) {
    track(target, prop) // Track the function that changes it 依赖项跟踪
    return Reflect.get(...arguments)
  },
  set(target, key, value, receiver) {
    trigger(target, key) // Trigger the function so it can update the final value 更改通知
    return Reflect.set(...arguments)
  }
}

const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)

// intercepted!
// tacos

Стоит отметить, что исходный объект и прокси-объектне равныйиз

const obj = {}
const wrapped = new Proxy(obj, handlers)
console.log(obj === wrapped) // false

Основы реактивности

объявить реактивное состояниеreactive

reactiveМетод принимает обычный объект в качестве параметра, а затем возвращает реактивный прокси для обычного объекта (эквивалентно Vue2.xVue.observable()) для создания реактивного свойства. Реактивные преобразования являются «глубокими», и возвращаемый прокси-объект не равен оригиналу. Во время компиляции шаблонаrenderМетод использует эти реактивные свойства.

import { reactive } from 'vue'

// reactive state
const state = reactive({
  count: 0
})

Также можно создавать реактивные свойства только для чтения, тоже глубокие, и любые вложенные свойства внутри объекта также доступны только для чтения.

const original = reactive({ count: 0 })

const copy = readonly(original)

// mutating original will trigger watchers relying on the copy
original.count++

// mutating the copy will fail and result in a warning
copy.count++ // warning: "Set operation on key 'count' failed: target is readonly."

Создание автономных реактивных свойствrefs

refМетод принимает параметр примитивного значения, а также возвращает реактивный изменяемый объект ссылки. Если это значение типа-примитива, поскольку значение типа-примитива передается по значению, а не по ссылке, это будет похоже на обертывание значения типа-примитива в объект для обеспечения отклика, но этот объект содержит только уникальные свойства.value. А для ссылочных типов он вызоветreactiveметод глубокой реактивной трансформации.

import { ref } from 'vue'

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

когдаrefКак имущество, возвращенное в контексте рендеринга и доступа к шаблону, внутреннийvalue, поэтому нет необходимости использоватьxx.valueспособ получить к нему доступ, точно так же, как доступ к обычному свойству. хочу注意, автоматическая распаковкаvalueПроисходит только при вложенных реактивныхобъектПри входе из массива илиMapОн не будет автоматически распаковываться при доступе к собственному классу коллекции, и его по-прежнему необходимо.value.

<template>
  <div>
    <span>{{ count }}</span>
    <button @click="count ++">Increment count</button>
  </div>
</template>

<script>
  import { ref } from 'vue'
  export default {
    setup() {
      const count = ref(0)
      return {
        count
      }
    }
  }
</script>

Кроме того, если новыйrefназначить существующему свойству, которое заменит староеref

const otherCount = ref(2)

state.count = otherCount
console.log(state.count) // 2
console.log(count.value) // 1

рассчитать

computedметод

пройти черезcomputedметод может быть непосредственноСоздать вычисляемое значение, получитьgetterФункция принимает в качестве параметра и возвращает реактивный объект, который нельзя изменить вручную.

const count = ref(1)
const plusOne = computed(() => count.value++)

console.log(plusOne.value) // 2

plusOne.value++ // error

Или вы можете пройти вgetterа такжеsetterметод создания реактивного объекта, который можно изменить вручную

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

монитор

watchEffectметод

watchEffectметодОн может прослушивать зависимости, он немедленно запускает входящую функцию и отслеживает зависимости функции, а когда зависимости обновляются, немедленно выполняет функцию снова.

const count = ref(0)

watchEffect(() => console.log(count.value))
// -> logs 0

setTimeout(() => {
  count.value++
  // -> logs 1
}, 100)

когдаwatchEffectв компонентеsetup()Или при вызове в обработчике жизненного цикла слушатель автоматически связывается с жизненным циклом компонента и автоматически останавливается, когда компонент размонтирован. или явным вызовомwatchEffectвозвращаемое значение, чтобы прекратить прослушивание.

const stop = watchEffect(() => {
  /* ... */
})
Vue3
// 之后
stop()

Аннулирование побочного эффекта

иногдаwatchEffectМетод, выполняемый в, может быть асинхронным,watchEffectПереданная функция может получитьonInvalidateв качестве параметра для регистрации обратного вызова в случае сбоя очистки, он будетwatchEffectпри повторном выполнении илиwatchEffectпрекращается (еслиsetup()или生命周期钩子используется вwatchEffect, выполняется при удалении компонента).

watchEffect(onInvalidate => {
  const token = performAsyncOperation(id.value)
  onInvalidate(() => {
    // id 改变时 或 停止侦听时
    // 取消之前的异步操作
    token.cancel()
  })
})

Уведомлениеsetup()будет вызываться перед монтированием компонента, поэтому, если вы хотитеwatchEffectиспользуется вDOM(или компонент), пожалуйста, объявите об этом в установленном крюкеwatchEffect

onMounted(() => {
  watchEffect(() => {
    // access the DOM or template refs
  })
})

так же может бытьwatchEffectПередайте дополнительный объект в качестве параметра. Например, установивflushустанавливатьwatchEffectВыполнять ли асинхронно или до обновления компонента

// 同步运行
watchEffect(
  () => {
    /* ... */
  },
  {
    flush: 'sync'
  }
)

// 组件更新前执行
watchEffect(
  () => {
    /* ... */
  },
  {
    flush: 'pre'
  }
)

onTrack(вызывается при трассировке зависимостей) иonTrigger(сработало изменение зависимостиwatchEffectметод при срабатывании) параметры можно использовать для отладкиwatchEffectповедение

watchEffect(
  () => {
    /* side effect */
  },
  {
    onTrigger(e) {
      debugger
    }
  }
)

watchв сравнении сwatchEffect,watchОн инерт, но также явно изменял состояние, что запускает слушателя, чтобы повторно запустить, и может получить доступ до доступа до и после изменения значения свойства, слушающего.

// 侦听一个 getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// 直接侦听一个 ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})
// 侦听多个数据源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})

API композиции

В обычном бизнесе мы часто извлекаем повторно используемые компоненты, такие как функции фильтрации, функции поиска, бизнес-списки и так далее. Но когда компонент комбинируется очень большой, список логики, на которой следует сосредоточиться (входящие компоненты), соответственно увеличивается, что может затруднить чтение и понимание, особенно для тех, кто не писал их изначально. Итак, мы хотим составить код, связанный с логикой, что такжеComposition APIизиспользовать.

<!-- 没有 Composition API 时我们通常这样做 -->
<!-- 将逻辑关系标记为相同的数字 -->

// src/components/UserRepositories.vue

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: { type: String }
  },
  data () {
    return {
      repositories: [], // 1
      filters: { ... }, // 3
      searchQuery: '' // 2
    }
  },
  computed: {
    filteredRepositories () { ... }, // 3
    repositoriesMatchingSearchQuery () { ... }, // 2
  },
  watch: {
    user: 'getUserRepositories' // 1
  },
  methods: {
    getUserRepositories () {
      // using `this.user` to fetch user repositories
    }, // 2
    updateFilters () { ... }, // 3
  },
  mounted () {
    this.getUserRepositories() // 1
  }
}

настройка параметров компонента

setupПараметры нового компонента, используемые в сборке иComposition APIвходная точка.

setupМетод принимает 2 параметра. Первый параметрprops, это вsetupОн также отзывчив внутренне (будьте осторожны, чтобы неpropsИспользуйте назначение деструктурирования напрямую, что нарушит отзывчивость, но вы можете использоватьtoRefsдля достижения безопасного разрушения).

// MyBook.vue

import { toRefs } from 'vue'

setup(props) {
	const { title } = toRefs(props)

	console.log(title.value)
}

Второй параметрcontextЭто обычный объект (не отвечает) и обнажает 3 компонентных свойства.

// MyBook.vue

export default {
  setup(props, context) {
    // Attributes (Reactive Property)
    console.log(context.attrs)

    // Slots (Reactive Property)
    console.log(context.slots)

    // Emit Events (Method)
    console.log(context.emit)
  }
}

setupОн будет выполнен после инициализации реквизита до создания экземпляра компонента, поэтому доступ к нему возможен толькоprops,attrs,slots,emit, без доступа к внутренним компонентамdata,computed,methods. Уведомлениеsetup()ВнутреннийthisНе будет ссылкой на экземпляр Vue.

setupОбъект можно вернуть, все его свойства будут доступны для других опций компонента (computedхарактеристики,methods, хуки жизненного цикла и т. д.) и шаблоны компонентов. Также возможно вернуть функцию рендеринга, которая может напрямую использовать реактивное состояние, объявленное в той же области видимости:

Composition APIТакже включает хуки жизненного цикла, аналогичные параметрам компонента, но с префиксомonXXXимена какmountedсоответствуетonMounted, который принимает обратный вызов, который будет выполняться при вызове хука компонента.

Options API setupзацепить
beforeCreate ненужный
created ненужный
beforeMount onBeforeMountonMounted
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered

из-заsetupвокругbeforeCreateа такжеcreatedЗапускаются хуки жизненного цикла, то есть любой код, который будет написан в этих двух хуках, должен бытьsetupнаписано в.

Composition APIтакже содержитwatchметод, принимает 3 параметра, первый параметр — это реактивный объект, который мы хотим прослушать или который содержитgetterфункция, второй параметр — это обратный вызов, а третий параметр — необязательный параметр конфигурации.

Также упоминалось вышеComposition APIизcomputedметод для создания вычисляемого свойства вне компонента

Таким образом, мы можем переписать приведенный выше каштан следующим образом, всякий раз, когда мы вызываемgetUserRepositoriesметод,repositoriesИзменения будут внесены оперативно, и представление будет обновлено.

// src/composables/useUserRepositories.js

import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs } from 'vue'

export default function useUserRepositories(user) {
  // 数据列表(创建一个响应式对象)
  const repositories = ref([])
  
  // 更新数据列表的方法
  const getUserRepositories = async () => {
    repositories.value = await fetchUserRepositories(user.value)
  }

  onMounted(getUserRepositories)
  
  // 在 user 上设置一个侦听器
  watch(user, getUserRepositories)
  
  // 返回列表和方法,以在其他组件选项中访问它们
  return {
    repositories,
    getUserRepositories
  }
}


// src/components/UserRepositories.vue
// 在组件中引入 useUserRepositorie), useRepositoryNameSearch和 useRepositoryFilters
import { toRefs } from 'vue'
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import useRepositoryFilters from '@/composables/useRepositoryFilters'

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: { type: String }
  },
  setup(props) {
    const { user } = toRefs(props)

    const { repositories, getUserRepositories } = useUserRepositories(user)

    const {
      searchQuery,
      repositoriesMatchingSearchQuery
    } = useRepositoryNameSearch(repositories)
    
    const {
      filters,
      updateFilters,
      filteredRepositories
    } = useRepositoryFilters(repositoriesMatchingSearchQuery)

    return {
      // 只关心过滤后的结果,因此可以以 repositories 这样的名称暴露出去
      repositories: filteredRepositories,
      getUserRepositories,
      searchQuery,
      filters,
      updateFilters
    }
  }
}

существуетsetupтакже можно использовать вprovide / inject, четноеprovideРеактивное состояние, обратите внимание, что из-за одностороннего потока данных неinjectвводится как реактивное состояние, но вprovideпредоставляется в реактивном состоянии.

import { ref, reactive } from 'vue'

// in provider
setup() {
  const book = reactive({
    title: 'Vue 3 Guide',
    author: 'Vue Team'
  })
  const year = ref('2020') // 也可以提供一个响应式状态,尽量在provide时注入为响应式状态

  provide('book', book)
  provide('year', year) // 如果要提供多个值,可以之后再次调用 provide
}

// in consumer
setup() {
  const book = inject('book', 'Eloquent Javasctipt') /* 可选的参数默认值 */
  const year = inject('year') 

  return { book, year }
}

существуетsetupСредний, Отзывчивыйrefsи шаблонrefsявляется унифицированным, чтобы получить ссылку на экземпляр элемента или компонента в шаблоне, вы можетеsetupОбъявите Ref и верните его. Мы хотели бы этоrootэкспонируется в контексте рендеринга и передаетсяref="root"будет привязан кdivкак егоref. В алгоритме виртуального DOM, если ref виртуального узла соответствует ref контекста рендеринга, то этому ref будет присвоен экземпляр элемента или компонента, соответствующий виртуальному узлу, который выполняется при монтировании виртуального DOM или изменен, поэтому ссылка на шаблон доступна только после инициализации рендеринга.

<template>
  <div ref="root">This is a root element</div>
  <div v-for="(item, i) in list" :ref="el => { divs[i] = el }">
    {{ item }}
  </div>
</template>

<script>
  import { ref, onMounted } from 'vue'

  export default {
    setup() {
      const root = ref(null)
      const divs = ref([])

      onMounted(() => {
        // 在初始化后 DOM 元素将会被分配给 ref
        console.log(root.value) // <div>This is a root element</div>
      })
      // 在每次更新前重置引用
      onBeforeUpdate(() => {
        divs.value = []
      })

      return {
        root,
        divs
      }
    }
  }
</script>

Набор инструментов адаптивной системы

unref

если параметр являетсяrefВерни этоvalue, иначе возвращается сам параметр. этоval = isRef(val) ? val.value : valсинтаксический сахар.

function useFoo(x: number | Ref<number>) {
  const unwrapped = unref(x) // unwrapped 一定是 number 类型
}

toRef

toRefМожет использоваться для создания ссылки на свойство реактивного объекта. Этот ref может быть передан и остается отзывчивым. toRef пригодится, когда вы хотите передать свойство свойства как ссылку на комбинаторную логическую функцию.

const state = reactive({
  foo: 1,
  bar: 2,
})

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) // 2

state.foo++
console.log(fooRef.value) // 3

toRefs

Преобразование реактивного объекта в обычный объект, каждое свойство нормального объекта представляет собой ref , который соответствует свойству реактивного объекта один к одному. Использование toRefs полезно, когда вы хотите вернуть реактивные объекты из комбинаторной логической функции, этот API позволяет потребляющим компонентам деконструировать/расширять (используя оператор ...) возвращаемый объект без потери отклика.

isRef

Проверяет, является ли значение объектом ссылки.

isProxy

Проверяет, создан ли объектreactiveилиreadonlyметод создания прокси.

isReactive

Проверяет, создан ли объектreactiveСоздал реактивный прокси. Если этот прокси сделанreadonlyсоздан, но затемreactiveДругой созданный прокси оборачивает слой, а затем также возвращаетtrue.

isReadonly

Проверяет, создан ли объектreadonlyСоздал прокси только для чтения.

const state = reactive({
  foo: 1,
  bar: 2,
})

const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型如下:

{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// ref 对象 与 原属性的引用是 "链接" 上的
state.foo++
console.log(stateAsRefs.foo) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3

API расширенной адаптивной системы

customRef

customRefдля пользовательскогоref, вы можете явно управлять отслеживанием зависимостей и триггерным ответом, принимать фабричную функцию, для отслеживания используются два параметраtrackс тем, который использовался для запуска ответаtriggerи возвращает массив сgetа такжеsetобъект недвижимости Это каштан, который использует пользовательскую ссылку для реализации v-модели с функцией защиты от сотрясений.

<input v-model="text" />

function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()
        }, delay)
      },
    }
  })
}

export default {
  setup() {
    return {
      text: useDebouncedRef('hello'),
    }
  },
}

markRaw

Явно помечая объект как «никогда не превращаться в реактивный прокси», функция возвращает сам объект.

shallowReactive

Создавайте поверхностный реактивный прокси только для частных (первого уровня) свойств объекта, не делайте глубокий рекурсивный реактивный прокси для «свойств свойства», а просто оставьте его как есть.

shallowReadonly

Создайте неглубокий реактивный прокси только для чтения только для собственных свойств объекта (первого уровня), а также не будет выполнять глубокое, рекурсивное проксирование, а свойства глубокого уровня не доступны только для чтения.

shallowRef

Создаватьref, будет отслеживать его.valueизменить операцию, но это не влияет на измененную.valueВыполняйте реактивные преобразования прокси (т. е. изменения не вызываютreactive)

toRaw

вернулсяreactiveилиreadonlyМетоды конвертируются в простые объекты для реактивных прокси. Это метод возврата, который можно использовать для временного чтения, доступ не проксируется/отслеживается, а изменения не инициируются при записи. Не рекомендуется постоянно хранить ссылку на исходный объект. Пожалуйста, используйте с осторожностью.


А также добавить несколько моментов. первый,Vue3.0черезTypeScriptРефакторинг, проверка типов ts значительно снижает вероятность внесения неожиданных ошибок и снижает нагрузку на техническое обслуживание.

Vue3.0Оптимизация алгоритма виртуального DOM, при обнаружении всех узлов шаблон может быть разбит на вложенный блок, разделенный этими структурными инструкциями, для инструкции шаблона без динамического изменения структуры узла (например, использованиеv-ifилиv-for), структура узлов этого блока статична, и генерируемый им код будет удален из функции рендеринга. Каждый раз, когда эти объекты необходимо повторно отображать, их не нужно создавать повторно, что также увеличивает использование памяти и снижает частоту сборки мусора. На уровне элемента компилятор создает флаг оптимизации для элементов с динамическими привязками в зависимости от типа обновления, которое необходимо выполнить, которое среда выполнения выбирает и выбирает более быстрый путь.

а такжеVue3.0Перенесено через большинство глобальных API и помощниковmodule.exportsхарактеристики. такmodule bundlerМожет статически анализировать зависимости модуля, удалять код, связанный с неиспользуемыми атрибутами module.exports, и генерировать код, удобный для встряхивания дерева Когда функция фактически используется в шаблоне, вспомогательная функция для этой функции импортируется.

Несмотря на то, что было добавлено много новых функций,Vue3.0Соотношение сжатого размераVue2.xПоловина размера!


Выше приведены различия, обнаруженные при сравнении содержания учебника.Самое главное, чтоComposition APIа такжеReactivityЭто также самая сложная часть для меня. Кроме того, документация Vue3.0 все еще находится в стадии разработки, и в последующем могут быть некоторые изменения. Пожалуйста, обратитесь к документации. Мы не приступили к разработке напрямую, поэтому я не высказывал слишком много личных мнений. , только в сравнении с Vue2.x.