Vuejs, о котором вы могли не знать — лучшие практики (1)

внешний интерфейс Vue.js
Vuejs, о котором вы могли не знать — лучшие практики (1)

by yugasun from yugasun.com/post/there-ma…Эта статья может быть воспроизведена полностью, но должны быть сохранены первоначальный автор и источник.

Постельное белье с предыдущей статьей, я полагаю, что вы все дороже, новичок в разработке среднего размера упоминается о Vuejs, включая использование основных инструментов Eco-Vuejs (Vue-Router, Vuex) не является проблемой. Но в реальном процессе разработки проекта нам нужно сделать больше, чем просто работа, чтобы завершить наш бизнес-код, после завершения спроса нам также необходимо учитывать больше пост оптимизации, это фокусируется на оптимизированном уровне кода.

Вычисляемые свойства игнорируемых сеттеров

Вернемся к случаю управления состоянием из предыдущей статьи и воспользуемсяvuexспособ поделиться нашимmsgсвойства, сначала создайтеsrc/store/index.js:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const types = {
  UPDATE_MSG: 'UPDATE_MSG',
};

const mutations = {
  [types.UPDATE_MSG](state, payload) {
    state.msg = payload.msg;
  },
};

const actions = {
  [types.UPDATE_MSG]({ commit }, payload) {
    commit(types.UPDATE_MSG, payload);
  },
};

export default new Vuex.Store({
  state: {
    msg: 'Hello world',
  },
  mutations,
  actions,
});

затем в компонентеcomp1использовать его в:

<template>
  <div class="comp1">
    <h1>Component 1</h1>
    <input type="text" v-model="msg">
  </div>
</template>
<script>
export default {
  name: 'comp1',
  data() {
    const msg = this.$store.state.msg;
    return {
      msg,
    };
  },
  watch: {
    msg(val) {
      this.$store.dispatch('UPDATE_MSG', { msg: val });
    },
  },
};
</script>

такое же правоcomp2Внесите те же изменения. Конечно ещеsrc/main.jsПредставлен в:

import Vue from 'vue';
import App from './App';
import store from './store';

Vue.config.productionTip = false;

/* eslint-disable no-new */
new Vue({
  store,
  el: '#app',
  template: '<App/>',
  components: { App },
});

Если вы не знакомы с основами использования vuex, рекомендуется сначала прочитать официальную документацию.

Хорошо, мы достиглиmsgявляется общим, и вносятся изменения вwatch, когда поле ввода изменится, передать$store.dispatchчтобы вызвать соответствующийUPDATE_MSGдействия Действие для модификации состояния. Но вы найдете модификациюcomp1в поле ввода черезvue-devtoolsТакже см. Vuexstate.msgОн действительно изменился, ноcomp2Поле ввода не изменилось, конечно, это потому, что мы инициализировалиmsgКогда это прямое присвоение переменной, а не прослушивание$store.state.msgизменения, поэтому два компонента не могут быть синхронизированы.

Кто-то скажет это снова, добавьте ещеwatchсобственность, монитор$store.state.msgИзменить, переназначить компонент вmsgНет, это можно сделать, но не слишком ли элегантен этот код для простогоmsgдля синхронизации нам нужно датьdataНе слишком ли плохо добавлять свойства плюс два слушателя?

На самом деле это очень хорошо решается путем вычисления свойств, потому что компонент в компонентеmsgзависит$store.state.msg, мы определяем вычисляемые свойства напрямуюmsg, а затем вернуть нет.

хорошо, измениcomp1следующим образом:

<template>
  <div class="comp1">
    <h1>Component 1</h1>
    <input type="text" v-model="msg">
  </div>
</template>
<script>
export default {
  name: 'comp1',
  computed: {
    msg() {
      return this.$store.state.msg;
    },
  },
};
</script>

мы снова модифицируемcomp1В поле ввода откройте консоль, будет сообщено об ошибке:

vue.esm.js?efeb:591 [Vue warn]: Computed property "msg" was assigned to but it has no setter.
...

потому что мы используемv-modelсвязыватьmsgНа входе при изменении поля ввода он должен срабатыватьmsgизsetter(赋值)операция, но вычисляемое свойство поможет мне определить его по умолчаниюgetter, не определеноsetter, вот почему появляется вышеуказанное сообщение об ошибке, тогда мы настраиваем его сноваsetterБар:

<template>
  <div class="comp1">
    <h1>Component 1</h1>
    <input type="text" v-model="msg">
  </div>
</template>
<script>
export default {
  name: 'comp1',
  computed: {
    msg: {
      get() {
        return this.$store.state.msg;
      },
      set(val) {
        this.$store.dispatch('UPDATE_MSG', { msg: val });
      },
    },
  },
};
</script>

Как видите, мы можем простоsetter, то есть изменитьmsgКогда это того стоит, передайте его новое значение в нашvuex, это не убьет двух зайцев одним выстрелом. та же параcomp2Внесите те же изменения. Запустите проект, и вы родитесь,comp1 输入框的值,comp2 输入框的值а такжеstore 中的值Достигнуто синхронизированное обновление. И по сравнению с приведенной выше схемой, количество кода также значительно упрощено~

Настраиваемые часы

Давайте сначала посмотрим на код:

// ...
watch: {
    username() {
      this.getUserInfo();
    },
},
methods: {
  getUserInfo() {
    const info = {
      username: 'yugasun',
      site: 'yugasun.com',
    };
    /* eslint-disable no-console */
    console.log(info);
  },
},
created() {
  this.getUserInfo();
},
// ...

Здесь легко понять, что когда компонент создается, он получает информацию о пользователе, затем отслеживает имя пользователя и повторно получает информацию о пользователе после его изменения.Этот сценарий очень распространен в реальной разработке. Так можно ли еще оптимизировать?

Ответ положительный.其实,我们在 Vue 实例中定义watcherКогда свойство слушателя может быть объектом, оно содержит три свойства:deep,immediate,handler, когда мы обычно определяем его непосредственно в виде функции, Vue автоматически назначит функцию обратного вызоваhandler, а оставшиеся два значения свойства установлены вfalse. Сцена здесь может быть использованаimmediateсвойство, установите его вtrueпри создании компонентаhandlerОбратный вызов выполняется немедленно, поэтому мы можем сохранитьcreatedФункция вызывается снова, и реализация выглядит следующим образом:

watch: {
  username: {
    immediate: true,
    handler: 'getUserInfo',
  },
},
methods: {
  getUserInfo() {
    const info = {
      username: 'yugasun',
      site: 'yugasun.com',
    };
    /* eslint-disable no-console */
    console.log(info);
  },
},

Созданная проблема не может быть вызвана изменением URL-адреса, но компонент не изменяется.

Во-первых, маршрутизация проекта по умолчанию осуществляется черезvue-routerРеализовано, а дальше наша маршрутизация похожа на следующую:

// ...
const routes = [
  {
    path: '/',
    component: Index,
  },
  {
    path: '/:id',
    component: Index,
  },
];

общие компонентыsrc/views/index.vueкод показывает, как показано ниже:

<template>
  <div class="index">
    <router-link :to="{path: '/1'}">挑战到第二页</router-link><br/>
    <router-link v-if="$route.path === '/1'" :to="{path: '/'}">返回</router-link>
    <h3>{{ username }} </h3>
  </div>
</template>
<script>
export default {
  name: 'Index',
  data() {
    return {
      username: 'Loading...',
    };
  },
  methods: {
    getName() {
      const id = this.$route.params.id;
      // 模拟请求
      setTimeout(() => {
        if (id) {
          this.username = 'Yuga Sun';
        } else {
          this.username = 'yugasun';
        }
      }, 300);
    },
  },
  created() {
    this.getName();
  },
};
</script>

Два разных пути используют один и тот же компонентIndex, а затем в компоненте IndexgetNameфункция будет вcreatedПри выполнении вы заметите, что давайте переключим маршрут на/1, наша страница не изменилась,createdТоже не перезапустился.

Это потому чтоvue-routerОн распознает, что два маршрута используют один и тот же компонент, а затем повторно использует его, чтобы компонент не создавался заново, а затемcreatedПериодические функции естественно тоже не срабатывают.

Обычно решение состоит в том, чтобы добавитьwatcherмонитор$routeизменения, а затем повторно выполнитьgetNameфункция. код показывает, как показано ниже:

watch: {
  $route: {
    immediate: true,
    handler: 'getName',
  },
},
methods: {
  getName() {
    const id = this.$route.params.id;
    // 模拟请求
    setTimeout(() => {
      if (id) {
        this.username = 'Yuga Sun';
      } else {
        this.username = 'yugasun';
      }
    }, 300);
  },
},

хорошо, проблема решена, но что-то еще нужно изменитьindex.vueленивый способ?

просто дайrouter-viewдобавить одинkeyсвойств, так что даже для одного и того же компонента, но еслиurlИзменено, Vuejs воссоздает компонент. мы напрямую модифицируемsrc/App.vueсерединаrouter-viewследующим образом:

<router-view :key="$route.fullPath"></router-view>

Забытые $attrs

В большинстве случаев при передаче данных от родительских компонентов к дочерним компонентам мы передаемpropsреализовано, например, в следующем примере:

<!-- 父组件中 -->
<Comp3
  :value="value"
  label="用户名"
  id="username"
  placeholder="请输入用户名"
  @input="handleInput"
  >

<!-- 子组件中 -->
<template>
  <label>
    {{ label }}
    <input
      :id="id"
      :value="value"
      :placeholder="placeholder"
      @input="$emit('input', $event.target.value)"
    />
  </label>
</template>
<script>
export default {
  props: {
    id: {
      type: String,
      default: 'username',
    },
    value: {
      type: String,
      default: '',
    },
    placeholder: {
      type: String,
      default: '',
    },
    label: {
      type: String,
      default: '',
    },
  },
}
</script>

Такой компонент первого порядка реализовать очень просто, и это не проблема, нам нужно толькоpropsнапиши это вid, value, placeholder...Такое определение свойства прекрасно. Но если подкомпонент содержит подкомпоненты, а также нужно передатьid, value, placeholder...Шерстяная ткань? Как насчет третьего порядка, четвертого порядка...? Тогда нам нужноpropsОпределение повторялось много раз, как это можно терпеть?

тогдаvm.$attrsОн готов к дебюту, давайте сначала посмотрим на официальное объяснение:

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

Автор также подчеркивает在创建高级别的组件时非常有用, он должен решить проблему, которую я только что упомянул. Это не сложно, так что давайте использовать его быстро.Код модифицируется следующим образом:

<!-- 父组件中 -->
<Comp3
  :value="value"
  label="用户名"
  id="username"
  placeholder="请输入用户名"
  @input="handleInput"
  >

<!-- 子组件中 -->
<template>
  <label>
    {{ $attrs.label }}
    <input
      v-bind="$attrs"
      @input="$emit('input', $event.target.value)"
    />
  </label>
</template>
<script>
export default {
}
</script>

Разве это не выглядит намного чище, и даже если аналогичные подкомпоненты будут снова упоминаться в подкомпонентах, мы не боимся. потому что$attrs, где не закажешь, где...

Суммировать

Конечно, практические навыки Vuejs — это гораздо больше.Это просто краткое изложение того, с чем люди столкнулись в реальной разработке, и это место, которое многие друзья склонны игнорировать. Если у вас есть лучший метод практики, пожалуйста, прокомментируйте или отправьте мне электронное письмо, чтобы обменяться и учиться вместе.

Исходный код здесь

Тематический каталог

You-May-Not-Know-Vuejs