Вам возможно не нужен vuex с vue 3

Что такое Vue.js?

Vue (произносится /vjuː/, примерно как view) — это прогрессивный фреймворк для создания пользовательских интерфейсов. В отличие от фреймворков-монолитов, Vue создан пригодным для постепенного внедрения. Его ядро в первую очередь решает задачи уровня представления (view), что упрощает интеграцию с другими библиотеками и существующими проектами. С другой стороны, Vue полностью подходит и для создания сложных одностраничных приложений (SPA, Single-Page Applications), если использовать его совместно с современными инструментами и .


Если вы хотите узнать больше о Vue перед тем как начать, мы с рассказом об основных принципах работы на примере проекта.

Если вы — опытный фронтенд-разработчик, и хотите узнать, чем Vue отличается от остальных библиотек или фреймворков, обратите внимание на сравнение с другими фреймворками

Emitting an event

Let’s now add a button to our modal allowing it to be closed. To do this, we’re going to add a element to the modal tempate with a click handler that emits an event .

src/Modal.vue

This event is will then be captured by the parent component and will toggle the value of , logically making it and causing the window to close.

src/App.vue

So far, this feature is identical as it would be in Vue 2. However, in Vue 3 it’s now recommended that you explicitly state a component’s events using the new component option. Just like with props, you can simply create an array of strings to name each event the component will emit.

src/Modal.vue

Reason for change

Imagine opening the file of a component that someone else wrote, and seeing its props and events explicitly declared. Immediately, you would understand the interface of this component i.e. what it’s meant to send and receive.

In addition to providing self-documenting code, you can also use the events declaration to validate your event payload, though I couldn’t find a reason to do that in this example.

Learn more: Emits Option RFC

Работа с пользовательским вводом

Чтобы пользователи могли взаимодействовать с вашим приложением, используйте директиву для отслеживания событий, указав метод-обработчик:

{{ message }}

Перевернуть сообщение

Обратите внимание, в методе мы просто обновляем состояние приложения, не затрагивая DOM — всю работу с DOM выполняет Vue, а вы пишете код, который занимается только логикой приложения. Vue также предоставляет директиву , позволяющую легко связывать элементы форм и состояние приложения:

Vue также предоставляет директиву , позволяющую легко связывать элементы форм и состояние приложения:

{{ message }}

Директивы

Директивы — это специальные атрибуты с префиксом . В качестве значения они принимают одно выражение JavaScript (за исключением , которую мы изучим далее). Директива реактивно применяет к DOM изменения при обновлении значения этого выражения. Вспомним пример из введения:

В этом случае директива удалит или вставит элемент в зависимости от истинности значения выражения .

Аргументы

Некоторые директивы могут принимать «аргумент», отделённый от названия директивы двоеточием. Например, директива используется для реактивного обновления атрибутов HTML:

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

Другим примером будет директива , которая отслеживает события DOM:

В данном случае аргументом является тип события. Подробнее на обработке событий мы остановимся позднее.

Динамические аргументы

Начиная с версии 2.6.0, можно использовать JavaScript-выражение в аргументе директивы, заключив его в квадратные скобки:

Здесь будет выполняться как выражение JavaScript, а его вычисленное значение использоваться в качестве финального значения аргумента. Например, если ваш экземпляр Vue имеет в данных свойство , значение которого равно , то это будет эквивалентно привязке .

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

В этом примере, когда значение будет , то обработчик будет эквивалентен .

Ограничения значений динамического аргумента

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

Ограничения динамического выражения

Для выражений динамических аргументов есть синтаксические ограничения, поскольку некоторые символы, такие как пробелы и кавычки, являются недопустимыми внутри имён атрибутов HTML. Например:

Обойти это ограничение можно использованием выражения без пробелов или кавычек, или заменой комплексного выражения на вычисляемое свойство.

При использовании шаблонов в DOM (шаблонов, написанных непосредственно в HTML-файле), следует избегать именования ключей заглавными буквами, поскольку браузеры будут приводить имена атрибутов к строчным:

Модификаторы

Модификаторы — особые постфиксы, добавляемые после точки, обозначающие, что директива должна быть связана каким-то определённым образом. Например, модификатор говорит директиве вызвать при обработке произошедшего события:


Больше примеров применения модификаторов вы увидите далее, когда будем подробнее разбирать и .

Creating a new Vue 3 app

Straight off the bat, the way we bootstrap a new Vue app has changed. Rather than using , we now need to import the new method.

We then call this method, passing our Vue instance definition object, and assign the return object to a variable .

Next, we’ll call the method on and pass a CSS selector indicating our mount element, just like we did with the instance method in Vue 2.

src/main.js

Reason for change

With the old API, any global configuration we added (plugins, mixins, prototype properties etc) would permanently mutate global state. For example:

src/main.js

This really shows up as an issue in unit testing, as it makes it tricky to ensure that each test is isolated from the last.

Under the new API, calling returns a fresh app instance that will not be polluted by any global configuration applied to other instances.

Learn more: Global API change RFC.

# Composing with Components

The component system is another important concept in Vue, because it’s an abstraction that allows us to build large-scale applications composed of small, self-contained, and often reusable components. If we think about it, almost any type of application interface can be abstracted into a tree of components:

In Vue, a component is essentially an instance with pre-defined options. Registering a component in Vue is straightforward: we create a component object as we did with objects and we define it in parent’s option:

12345678910

Now you can compose it in another component’s template:

1234

But this would render the same text for every todo, which is not super interesting. We should be able to pass data from the parent scope into child components. Let’s modify the component definition to make it accept a :

1234

Now we can pass the todo into each repeated component using :

123456789101112131415

1234567891011121314151617181920

See the Pen Intro-Components-1 by Vue (@Vue) on CodePen.

This is a contrived example, but we have managed to separate our app into two smaller units, and the child is reasonably well-decoupled from the parent via the props interface. We can now further improve our component with more complex template and logic without affecting the parent app.

In a large application, it is necessary to divide the whole app into components to make development manageable. We will talk a lot more about components later in the guide, but here’s an (imaginary) example of what an app’s template might look like with components:

1234567

Relation to Custom Elements

You may have noticed that Vue components are very similar to Custom Elements, which are part of the Web Components Spec. That’s because Vue’s component syntax is loosely modeled after the spec. For example, Vue components implement the Slot API and the special attribute. However, there are a few key differences:

  1. The Web Components Spec has been finalized but is not natively implemented in every browser. Safari 10.1+, Chrome 54+ and Firefox 63+ natively support web components. In comparison, Vue components work consistently in all supported browsers (IE11 with compatibility build and above). When needed, Vue components can also be wrapped inside a native custom element.
  1. Vue components provide important features that are not available in plain custom elements, most notably cross-component data flow, custom event communication and build tool integrations.

Although Vue doesn’t use custom elements internally, it has when it comes to consuming or distributing as custom elements. Vue CLI also supports building Vue components that register themselves as native custom elements.

Watchers

While computed properties are more appropriate in most cases, there are times when a custom watcher is necessary. That’s why Vue provides a more generic way to react to data changes through the option. This is most useful when you want to perform asynchronous or expensive operations in response to changing data.

For example:

Result:

Ask a yes/no question:

{{ answer }}

In this case, using the option allows us to perform an asynchronous operation (accessing an API), limit how often we perform that operation, and set intermediary states until we get a final answer. None of that would be possible with a computed property.

In addition to the option, you can also use the imperative .

##company## — ##description##

Edit this on GitHub! Netlify

# Getting Started

TIP

The official guide assumes intermediate level knowledge of HTML, CSS, and JavaScript. If you are totally new to frontend development, it might not be the best idea to jump right into a framework as your first step — grasp the basics then come back! Prior experience with other frameworks helps, but is not required.

The easiest way to try out Vue.js is using the Hello World example. Feel free to open it in another tab and follow along as we go through some basic examples.

The Installation page provides more options of installing Vue. Note: We do not recommend that beginners start with , especially if you are not yet familiar with Node.js-based build tools.

Teleporting content

If you’ve ever created a modal feature before, you’ll know that it’s commonly positioned just before the closing tag.

This is done because modals usually have a page-covering background (see the image at the beginning if you don’t know what I mean). To implement this with CSS, you don’t want to have to deal with parent elements positioning and z-index stacking context, and so the simplest solution is to put the modal at the very bottom of the DOM.


This creates an issue with Vue.js, though, which assumes the UI will be built as a single tree of components. To allow segments of the tree to be moved elsewhere in the DOM, a new component has been added in Vue 3.

To use the teleport, let’s first add an element to the page where we want our modal content to be moved to. We’ll go to and place a with ID adjacent to Vue’s mounting element.

index.html

Now, back in , we’re going to wrap the modal content in the component. We’ll also need to specify a attribute which will be assigned a query selector identifying the target element, in this case, .

src/App.vue

And that’s it. Any content within the will be rendered within the target element. However, it will still function like it was in it’s original position in the hierarchy (regarding props, events etc).

So after you’ve saved your code, reload the page, inspect the DOM in dev tools, and be amazed!

Learn more: Teleport RFC

Using a root component

If you go to the browser now and check the console, you’ll see the warning «Component is missing render function», since we haven’t yet defined a template for the root instance.

The best practice for Vue 2 is to create a minimal template for the root instance and create an component where the main app markup will be declared.

Let’s do that here, as well.

Now we can get the root instance to render that component. The difference is that with Vue 2, we’d normally use a render function for doing this:

src/main.js

We can still do that, but Vue 3 has an even easier way — making a root component. To do this, we can remove the root instance definition and instead pass the component.

src/main.js

This means the component is not just rendered by the root instance but is the root instance.

While we’re at it, let’s simply the syntax a little by removing the variable:

src/main.js

Moving over to the root component now, let’s re-add the state and method to this component:

src/App.vue

Let’s also make a new component for the modal feature:

For now, we’ll provide a minimal template including a slot for content. This ensures our modal is reusable. We’ll add more to this component later.

src/Modal.vue

Programmatic Event Listeners

So far, you’ve seen uses of , listened to with , but Vue instances also offer other methods in its events interface. We can:

  • Listen for an event with
  • Listen for an event only once with
  • Stop listening for an event with

You normally won’t have to use these, but they’re available for cases when you need to manually listen for events on a component instance. They can also be useful as a code organization tool. For example, you may often see this pattern for integrating a 3rd-party library:

This has two potential issues:

  • It requires saving the to the component instance, when it’s possible that only lifecycle hooks need access to it. This isn’t terrible, but it could be considered clutter.
  • Our setup code is kept separate from our cleanup code, making it more difficult to programmatically clean up anything we set up.

You could resolve both issues with a programmatic listener:

Using this strategy, we could even use Pikaday with several input elements, with each new instance automatically cleaning up after itself:

See this example for the full code. Note, however, that if you find yourself having to do a lot of setup and cleanup within a single component, the best solution will usually be to create more modular components. In this case, we’d recommend creating a reusable component.

To learn more about programmatic listeners, check out the API for .


Note that Vue’s event system is different from the browser’s EventTarget API. Though they work similarly, , , and are not aliases for , , and .

Новое решение

Общее состояние должно соответствовать двум критериям:

  • реактивность: при изменении состояния компоненты, использующие их, также должны обновляться
  • доступность: состояние может быть доступно в любом из компонентов

Реактивность

Vue 3 содержит свою систему реактивности через многочисленные функции. Вы можете создать реактивную переменную с функцией reactive (или альтернативной функции ref).

import { reactive } from 'vue';

export const state = reactive({ counter: 0 });

Объект, возвращаемый из функции reactive, является объектом Proxy, который может отслеживать изменения своих свойств. При использовании в шаблоне компонента, компонент перерисовывается при каждом изменении реактивного значения.

<template>
  <div>{{ state.counter }}</div>
  <button type="button" @click="state.counter++">Increment</button>
</template>

<script>
  import { reactive } from 'vue';

  export default {
    setup() {
      const state = reactive({ counter: 0 });
      return { state };
    }
  };
</script>

Доступность

Приведенный выше пример отлично подходит для одного компонента, но другие компоненты не могут получить доступ к состоянию. Чтобы преодолеть это, вы можете сделать любое значение доступным в приложении Vue 3 с помощью методов provide и inject.

import { reactive, provide, inject } from 'vue';

export const stateSymbol = Symbol('state');
export const createState = () => reactive({ counter: 0 });

export const useState = () => inject(stateSymbol);
export const provideState = () => provide(
  stateSymbol, 
  createState()
);

Когда вы передаете Symbol в качестве ключа и значение в метод provide, это значение будет доступно для любого дочернего компонента через метод inject. Ключ использует то же имя Symbol при предоставлении (providing) и получении (retrieving) значения.

Таким образом, если вы предоставите (provide) значение для самого верхнего компонента, оно будет доступно во всех компонентах. Кроме того, вы также можете вызвать provide в главном экземпляре приложения.

import { createApp, reactive } from 'vue';
import App from './App.vue';
import { stateSymbol, createState } from './store';

const app = createApp(App);
app.provide(stateSymbol, createState());
app.mount('#app');
<script>
  import { useState } from './state';

  export default {
    setup() {
      return { state: useState() };
    }
  };
</script>

Делаем это более надежным

Приведенное выше решение работает, но имеет недостаток: вы не контролируете любые изменения в состояние. Состояние может быть изменено напрямую, и нет никаких ограничений.

Вы можете защитить свое состояние, обернув его функцией readonly. Она передает переданную переменную в объект Proxy, который предотвращает любые изменения (при попытке выдается предупреждение). Мутации могут обрабатываться отдельными функциями, которые имеют доступ к доступному для записи хранилищу.

import { reactive, readonly } from 'vue';

export const createStore = () => {
  const state = reactive({ counter: 0 });
  const increment = () => state.counter++;

  return { increment, state: readonly(state) };
}

Внешний мир будет иметь доступ к state только для чтения, и только экспортированные внутренние функции могут изменять состояние записи.

Защищая состояние от нежелательных модификаций, новое решение становиться таким как Vuex.

Заключение

Используя систему реактивности и механизм внедрения зависимостей Vue 3, мы перешли от локального состояния к централизованному управлению состояниями, которое может заменить Vuex в небольших приложениях.

У нас есть объект состояния, который доступен только для чтения и реагирует на изменения в шаблонах. Состояние может быть изменено только с помощью специальных методов, точно таких же как actions/mutations в Vuex. Вы можете определить дополнительные геттеры с помощью вычисляемой (computed) функции.

Vuex имеет больше функционала, для обработки модулей, но чаще всего нам это не нужно.

Если вы хотите взглянуть на Vue 3 и попробовать этот подход к управлению состоянием, взгляните на мою песочницу Vue 3.

Spread the love

  • 3 Поделились

Столкновение пространства имен ⚔️

Представьте, что нам нужно добавить метод update в наш компонент:

// ...
<script>
export default {
    name: 'PostsPage',
    mixins: ,
    props: {
        id: Number,
        isLoading: Boolean,
        posts: Array,
        count: Number
    },
    methods: {
        update() {
            console.log('some update logic here')
        }
    }
}
</script>
// ...

Если вы снова откроете страницу и прокрутите ее, верхняя панель больше не будет отображаться. Это связано с перезаписью метода update в mixin. То же самое работает для HOCs. Если вы измените поле данных fetchedPosts на posts:

const withPostsHOC = WrappedComponent => ({
    props: WrappedComponent.props, // 
    data() {
        return {
            postsIsLoading: false,
            posts: [] // fetchedPosts -> posts
        }
    },
    // ...

… вы получите такие ошибки:

Причина этого заключается в том, что в обернутом компоненте уже указано свойство с именем posts.

Refactoring with Composition API

The flagship feature of Vue 3 is the Composition API. This new API allows you to define component functionality using a function rather than with properties you add to the component definition object.

Let’s now refactor our App component to use the Composition API.

Before I explain the code, be clear that all we’re doing is refactoring — the functionality of the component will be the same. Also note that the template is not changed as the Composition API only affects the way we define the component functionality, not the way we render it.

src/App.vue

method

Firstly, notice we import the function which allows us to define a reactive variable . This variable is equivalent to .

The method is just a plain JavaScript function. However, notice that to change the value of in the method body, we need to change its sub-property . That’s because reactive variables created using are wrapped in an object. This is necessary to retain their reactivity as they’re passed around.

Finally, we return and from the method, as these are the values that get passed to the template when it’s rendered.

Reason for change

Keep in mind that the Composition API is not a change as it’s purely optional to use. The main motivation is to allow for better code organization and the reuse of code between components (as mixins are essentially an anti-pattern).

You’d be correct in thinking that refactoring the component in this example to use the Composition API is unnecessary. But, if this were a much larger component, or we needed to share its features with other components, that’s when you’d see its usefulness.

Providing a more in-depth example is beyond the scope of this blog post, so if you’re interested to learn more about uses of the new API, check out my other article When To Use The New Vue Composition API (And When Not To).


С этим читают