Управление локальным состоянием в компонентах Vue.js

Щелкните k здесь, чтобы опубликовать эту статью в LinkedIn »

Обычно, когда мы слышим Vue.js и управление состоянием, мы сразу же вспоминаем Vuex или другое надежное решение для управления состоянием. Однако большая часть нашего государственного управления не происходит в таких масштабах. Вместо этого он спрятан внутри наших компонентов Vue.

Если вы все еще ломаете голову над тем, о чем я говорю, давайте взглянем на пример местного государства в действии.

<template>
    <div>
        <div v-if="state === 'default'">
            <h1>Default State</h1>
        </div>
        <div v-else>
            <h1>Every Other State</h1>        
        </div>
    </div>
</template>
<script>
    export default {
        data: function () {
            return {
                state: 'default'
            }
        }
    }        
</script>

В нашем data у нас есть свойство state, которое мы используем для отслеживания различных состояний нашего компонента. Довольно просто, правда?

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

<template>
    <div>
        <div v-if="state.visbility">
            <h1>Default State</h1>
        </div>
        <div v-else>
            <h1>Every Other State</h1>        
        </div>
    </div>
</template>
<script>
    export default {
        data: function () {
            return {
                state: {
                    visiblity: true
                }
            }
        }
    }        
</script>

Оба эти метода работают довольно хорошо, но давайте подробнее рассмотрим каждый из них и рассмотрим их плюсы и минусы.

Установка

Прежде чем мы начнем, давайте настроим небольшой компонент уведомлений о загрузке. Чтобы все заработало как можно быстрее, мы будем использовать Vue CDN вместо полной сборки.

Начнем с пустого index.html файла с нашим Vue CDN.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Local State Management</title>
</head>
<body>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
</body>
</html>

Теперь давайте добавим CDN Tailwind CSS и Font Awesome, чтобы получить более красивый пример.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Local State Management</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css" rel="stylesheet">
</head>
<body>
    <script defer src="https://use.fontawesome.com/releases/v5.0.9/js/all.js" integrity="sha384-8iPTk2s/jMVj81dnzb/iFR2sdA7u06vHJyyLlAd4snFpCl/SnyUjRrbdJsw1pGIl" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
</body>
</html>

Большой! Теперь мы можем создать немного HTML. В этом примере нам нужна простая карта пользовательского интерфейса с небольшим компонентом загрузчика в заголовке.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Local State Management</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css" rel="stylesheet">
</head>
<body>
    <div>
        <div>
          <h1>A Cool Card</h1>
          <h4 id="loader">
            <i class="fas fa-circle-notch fa-spin"></i> Loading...
          </h4>
        </div>
        <div>
          <p>Lorem Khaled Ipsum is a major key to success. They will try to close the door on you, just open it. Give thanks to the most high. Eliptical talk. Celebrate success right, the only way, apple. Another one. The weather is amazing, walk with me through the pathway of more success. Take this journey with me, Lion! Watch your back, but more importantly when you get out the shower, dry your back, it’s a cold world out there.</p>
        </div>
    </div>
    
    <script defer src="https://use.fontawesome.com/releases/v5.0.9/js/all.js" integrity="sha384-8iPTk2s/jMVj81dnzb/iFR2sdA7u06vHJyyLlAd4snFpCl/SnyUjRrbdJsw1pGIl" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
</body>
</html>

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

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Local State Management</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="flex item-center justify-center h-screen bg-blue-lightest font-sans">
    <div class="p-6 bg-white rounded shadow max-w-md m-auto">
        <div class="flex justify-between items-center mb-4">
          <h1 class="text-black">A Cool Card</h1>
          <h4 id="loader" class="font-light text-blue">
            <i class="fas fa-circle-notch fa-spin mr-1"></i> Loading...
          </h4>
        </div>
        <div class="text-grey-dark leading-normal">
          <p>Lorem Khaled Ipsum is a major key to success. They will try to close the door on you, just open it. Give thanks to the most high. Eliptical talk. Celebrate success right, the only way, apple. Another one. The weather is amazing, walk with me through the pathway of more success. Take this journey with me, Lion! Watch your back, but more importantly when you get out the shower, dry your back, it’s a cold world out there.</p>
        </div>
    </div>
    <script defer src="https://use.fontawesome.com/releases/v5.0.9/js/all.js" integrity="sha384-8iPTk2s/jMVj81dnzb/iFR2sdA7u06vHJyyLlAd4snFpCl/SnyUjRrbdJsw1pGIl" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
</body>
</html>

Удивительно, что немного CSS может сделать для пользовательского интерфейса.

Наконец, давайте создадим код Vue.js, который мы можем использовать совместно с методами String и Boolean State.

Мы начнем с инициализации экземпляра Vue, установки el, преобразования шаблона для управления данными и исключения data и вычисляемого свойства, которое нам понадобится.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Local State Management</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="flex item-center justify-center h-screen bg-blue-lightest font-sans">
    <div class="p-6 bg-white rounded shadow max-w-md m-auto">
        <div class="flex justify-between items-center mb-4">
          <h1 class="text-black">A Cool Card</h1>
          <h4 v-if="state'" id="loader" class="font-light text-blue">
            <i v-show="state" class="fas fa-circle-notch fa-spin mr-1"></i>
            <i v-show="state" class="far fa-check-circle mr-1"></i>
            {{ header }}
          </h4>
        </div>
        <div class="text-grey-dark leading-normal">
          <p>Lorem Khaled Ipsum is a major key to success. They will try to close the door on you, just open it. Give thanks to the most high. Eliptical talk. Celebrate success right, the only way, apple. Another one. The weather is amazing, walk with me through the pathway of more success. Take this journey with me, Lion! Watch your back, but more importantly when you get out the shower, dry your back, it’s a cold world out there.</p>
        </div>
    </div>
    <script defer src="https://use.fontawesome.com/releases/v5.0.9/js/all.js" integrity="sha384-8iPTk2s/jMVj81dnzb/iFR2sdA7u06vHJyyLlAd4snFpCl/SnyUjRrbdJsw1pGIl" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script>
      var shoppingList = new Vue({
        el: '#loader',
        data: {
          state: null
        },
        computed: {
          header() {
            return '';
          }
        }
      })
    </script>
</body>
</html>

Bada bing bada boom, у нас есть отличный компонент для использования. Теперь давайте посмотрим на различные способы управления нашим государством.

Логический метод

Во-первых, логический метод. Это был первый способ управления состоянием, который я понял, и он имеет смысл. Я обнаружил, что обычно использую состояния для работы с условными директивами Vue, поэтому часто вижу, как переключаю логические значения состояния.

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

data: {
    state: {
        default: true,
        loading: false,
        done: false
    }
}

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

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

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

computed: {
  header() {
       if(this.state.loading) {
      return 'Loading...';
    }
    if(this.state.done) {
      return 'Done!';
    }
    return '';
  }
}

Затем мы можем обновить наш шаблон, чтобы отобразить соответствующий значок.

<h4 v-if="!state.default" id="loader" class="font-light text-blue">
    <i v-show="state.loading" class="fas fa-circle-notch fa-spin mr-1"></i>
    <i v-show="state.done" class="far fa-check-circle mr-1"></i>
    {{ header }}
</h4>

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

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

Теперь давайте посмотрим, как метод String сравнивается с методом Object.

Строковый метод

С методом String мы не будем использовать Object для поддержки списка всех возможных комбинаций состояний. Вместо этого мы просто установим для нашего состояния значение String, чтобы отслеживать текущее состояние.

Давайте посмотрим, как это сделать с помощью нашего компонента. Начнем с обновления наших данных до String.

data: {
    state: 'default',
}

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

Давайте подробнее рассмотрим это, завершив работу над нашим компонентом. И снова мы начнем с обновления нашего вычисляемого свойства.

computed: {
  header() {
       if(this.state === 'loading') {
      return 'Loading...';
    }
    if(this.state === 'done') {
      return 'Done!';
    }
    return '';
  }
}

И теперь мы можем обновить наш шаблон.

<h4 v-if="state !== 'default'" id="loader" class="font-light text-blue">
    <i v-show="state === 'loading'" class="fas fa-circle-notch fa-spin mr-1"></i>
    <i v-show="state === 'done'" class="far fa-check-circle mr-1"></i>
    {{ header }}
</h4>

Как я уверен, вы уже поняли, у метода String есть противоположные плюсы и минусы по сравнению с методом Object. Он отлично показывает текущее состояние компонента, но трудно увидеть все возможные состояния.

Это можно облегчить, добавив комментарий к списку всех состояний, но это тоже не лучшее решение. Кроме того, использование метода String подразумевает, что у вас есть только одно «активное» состояние, о котором нужно беспокоиться в вашем компоненте.

Хотя это может быть верно для простого компонента, такого как наш загрузчик, это не всегда так. Если вы думаете о компоненте формы, нам, возможно, придется отслеживать состояние отправки и ошибки независимо. С этим может быть сложно справиться с помощью одной струны.

Заключение

И Object, и String - это жизнеспособные варианты управления нашим локальным состоянием. Хотя объектный подход превосходно дает нам большую гибкость и видимость всех возможных состояний, может быть трудно управлять и видеть, каково текущее состояние в любой данный момент.

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

В моих проектах мое текущее практическое правило - всегда начинать с метода String. Таким образом, я могу быстро перебирать состояния, которые потребуются моему компоненту. Если я обнаружу, что полагаюсь на множество различных состояний, то я рассмотрю возможность рефакторинга метода Object или посмотреть, смогу ли я разделить свой компонент на более мелкие части.

Когда вы освоитесь с этими подходами, я уверен, что вы придумаете свой собственный поток. Может быть, вы придумаете еще лучший подход! Как всегда, не стесняйтесь задавать мне любые вопросы в Твиттере. И до следующего раза, удачных исследований!