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

(Посетите https://pitayan.com/posts/vue-techniques/, чтобы прочитать исходную статью. Исходный код выделен.)

1. Функциональный компонент

Функциональный компонент не имеет состояния и не имеет lifecycle или methods. Таким образом, он не может быть создан

Создать функциональный компонент очень просто. Все, что вам нужно сделать, это добавить свойство functional: true в SFC или добавить functional в шаблон. Поскольку он такой же легкий, как функция и не имеет ссылки на экземпляр, производительность рендеринга значительно улучшена.

Функциональный компонент полагается на context и видоизменяется вместе с данными внутри него.

<template functional>
  <div class="book">
    {{props.book.name}} {{props.book.price}}
  </div>
</template>
<script>
Vue.component('book', {
  functional: true,
  props: {
    book: {
      type: () => ({}),
      required: true
    }
  },
  render: function (createElement, context) {
    return createElement(
      'div',
      {
        attrs: {
          class: 'book'
        }
      },
      [context.props.book]
    )
  }
})
</script>

2. Глубокие селекторы

Иногда вам даже нужно использовать CSS-компоненты сторонних разработчиков, которые являются scoped стилями. Невозможно удалить scope или открыть новый стиль.

Теперь глубокие селекторы >>> /deep/ ::v-deep попадают в нужное место для помощи.

<style scoped>
>>> .scoped-third-party-class {
  color: gray;
}
</style>
<style scoped>
/deep/ .scoped-third-party-class {
  color: gray;
}
</style>
<style scoped>
::v-deep .scoped-third-party-class {
  color: gray;
}
</style>

3. Продвинутый «наблюдатель »

Выполнить немедленно

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

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

Или вы можете добавить свойство immediate к наблюдателю:

watch: {
    value: {
        handler: 'printValue',
        immediate: true
    }
},
methods : {
  printValue () {
    console.log(this.value)
  }
}

Глубокое слушание

Иногда опора наблюдателя - это Object. Но изменение его свойств не может вызвать обработчик наблюдателя. В этом случае добавление deep: true к наблюдателю может сделать обнаружимую мутацию его свойств.

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

data () {
  return {
    value: {
      one: {
        two: {
          three: 3
        }
      }
    }
  }
},
watch: {
  value: {
    handler: 'printValue',
    deep: true
  }
},
methods : {
  printValue () {
    console.log(this.value)
  }
}

Несколько обработчиков

На самом деле наблюдателя можно установить как Array. Поддерживаемые типы: String | Function | Object. Зарегистрированные обработчики наблюдателей будут вызываться один за другим при срабатывании триггера.

watch: {
  value: [
    'printValue',
    function (val, oldVal) {
      console.log(val)
    },
    {
      handler: 'printValue',
      deep: true
    }
  ]
},
methods : {
  printValue () {
    console.log(this.value)
  }
}

Подписаться на мутацию нескольких переменных

watcher не может прослушивать несколько переменных, но мы могли бы объединить цели вместе как новую computed и наблюдать за этой новой «переменной».

computed: {
  multipleValues () {
    return {
      value1: this.value1,
      value2: this.value2,
    }
  }
},
watch: {
  multipleValues (val, oldVal) {
    console.log(val)
  }
}

4. Аргумент события: $ event

$event - это специальная переменная объекта события. В некоторых сценариях он предоставляет дополнительные дополнительные возможности для сложных функций.

Родные события

В собственных событиях значение совпадает с событием по умолчанию (событие DOM или событие окна).

<template>
  <input type="text" @input="handleInput('hello', $event)" />
</template>
<script>
export default {
  methods: {
    handleInput (val, e) {
      console.log(e.target.value) // hello
    }
  }
}
</script>

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

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

<!-- Child -->
<template>
  <input type="text" @input="$emit('custom-event', 'hello')" />
</template>
<!-- Parent -->
<template>
  <Child @custom-event="handleCustomevent" />
</template>
<script>
export default {
  methods: {
    handleCustomevent (value) {
      console.log(value) // hello
    }
  }
}
</script>

5. Разделение параметров маршрутизатора

Я считаю, что это то, как большинство людей обрабатывают параметры маршрутизатора в компоненте:

export default {
  methods: {
    getRouteParamsId() {
      return this.$route.params.id
    }
  }
}

Использование $route внутри компонента создаст сильную связь для определенного URL. Это ограничивало гибкость компонента.

Правильное решение - добавить props к Router.

const router = new VueRouter({
  routes: [{
    path: '/:id',
    component: Component,
    props: true
  }]
})

Таким образом, компонент может получать params прямо из реквизита.

export default {
  props: ['id'],
  methods: {
    getParamsId() {
      return this.id
    }
  }
}

Кроме того, вы также можете передать функцию, возвращающую props для целей настройки.

const router = new VueRouter({
  routes: [{
    path: '/:id',
    component: Component,
    props: router => ({ id: route.query.id })
  }]
})

6. Двусторонняя привязка для пользовательских компонентов

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

v-model хорошо известен своей двусторонней привязкой. input - событие обновления по умолчанию. Значение можно обновить с помощью $emit. Единственное ограничение заключается в том, что компоненту требуется тег <input> для связывания с атрибутом value.

<my-checkbox v-model="val"></my-checkbox>
<template>
  <input type="checkbox" :value="value" @input="handleInputChange(value)" />
</template>
<script>
export default {
  props: {
    value: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    handleInputChange (val) {
      console.log(val)
    }
  }
}
</script>

Есть еще одно решение двусторонней привязки - модификатор sync. В отличие от v-model, он не требует, чтобы ваш компонент имел тег <input> и привязывал к нему значение. Он только запускает update:<your_prop>, чтобы изменить опору через систему событий.

<custom-component :value.sync="value" />

7. Крючок для жизненного цикла компонентов

Обычно вы можете прослушивать жизненный цикл дочернего компонента (например, mounted) следующим образом

<!-- Child -->
<script>
export default {
  mounted () {
    this.$emit('onMounted')
  }
}
</script>
<!-- Parent -->
<template>
  <Child @onMounted="handleOnMounted" />
</template>

Есть еще одно простое решение. Вместо этого вы можете использовать @hook:mounted. Он используется во внутренней системе Vue.

<!-- Parent -->
<template>
  <Child @hook:mounted="handleOnMounted" />
</template>

8. API прослушивателя событий

Например, добавление таймера при монтировании страницы, но таймер необходимо очистить при уничтожении. Выглядит хорошо.

Честно говоря, this.timer имеет смысл только тогда, когда используется в beforeDestroy для получения идентификатора таймера. Не будучи скупым, но чем меньше реактивных переменных, тем выше производительность.

export default {
  data () {
    return {
      timer: null
    }
  },
  mounted () {
    this.timer = setInterval(() => {
      console.log(Date.now())
    }, 1000)
  },
  beforeDestroy () {
    clearInterval(this.timer)
  }
}

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

export default {
  mounted () {
    let timer = null
    timer = setInterval(() => {
      console.log(Date.now())
    }, 1000)
    this.$once('hook:beforeDestroy', () => {
      clearInterval(timer)
    })
  }
}

9. Установите компоненты программно

В некоторых сценариях гораздо удобнее загружать компонент программно. Например, всплывающее окно или модальное окно можно открыть через глобальный контекст $popup() или $modal.open().

import Vue from 'vue'
import Popup from './popup'
const PopupCtor = Vue.extend(Popup)
const PopupIns = new PopupCtr()
PopupIns.$mount()
document.body.append(PopupIns.$el)
Vue.prototype.$popup = Vue.$popup = function () {
  PopupIns.open()
}

Element UI реализовал хорошо структурированный модальный компонент, который позволяет использовать пользовательские API для управления жизненным циклом экземпляра. Теория во многом аналогична той, что я демонстрировал выше.

Это 9 приемов о Vue 2.x. Надеюсь, что благодаря этому искусству вы сможете лучше понять, как использовать фреймворк. Если вы считаете эту статью отличной, поделитесь ею в других социальных сетях.

Спасибо, что прочитали!

"Использованная литература"

Изначально на pitayan.com