Практические навыки, Vue тоже умеет так писать

JavaScript Vue.js

Две иволги поют зеленые ивы, а стая жуков уходит в западное небо.

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

hookEvent`, оказывается можно прослушать жизненный цикл компонента вот так

1. Функция жизненного цикла внутреннего монитора

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

<template>
  <div class="echarts"></div>
</template>
<script>
export default {
  mounted() {
    this.chart = echarts.init(this.$el)
    // 请求数据,赋值数据 等等一系列操作...
    // 监听窗口发生变化,resize组件
    window.addEventListener('resize', this.$_handleResizeChart)
  },
  updated() {
    // 干了一堆活
  },
  created() {
    // 干了一堆活
  },
  beforeDestroy() {
    // 组件销毁时,销毁监听事件
    window.removeEventListener('resize', this.$_handleResizeChart)
  },
  methods: {
    $_handleResizeChart() {
      this.chart.resize()
    },
    // 其他一堆方法
  }
}
</script>

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

 大佬:这样写不是很好,应该将监听`resize`事件与销毁`resize`事件放到一起,现在两段代码分开而且相隔几百行代码,可读性比较差

 我:那我把两个生命周期钩子函数位置换一下,放到一起?

 大佬: `hook`听过没?

 我:`Vue3.0`才有啊,咋,咱要升级`Vue`?

Тогда технический босс проигнорировал меня и кинул мне кусок кода

export default {
  mounted() {
    this.chart = echarts.init(this.$el)
    // 请求数据,赋值数据 等等一系列操作...

    // 监听窗口发生变化,resize组件
    window.addEventListener('resize', this.$_handleResizeChart)
    // 通过hook监听组件销毁钩子函数,并取消监听事件
    this.$once('hook:beforeDestroy', () => {
      window.removeEventListener('resize', this.$_handleResizeChart)
    })
  },
  updated() {},
  created() {},
  methods: {
    $_handleResizeChart() {
      // this.chart.resize()
    }
  }
}

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

существуетVueкомпонент, можно использовать$on,$onceДля прослушивания всех функций ловушек жизненного цикла, таких как прослушивание компонентовupdatedФункция ловушки может быть записана какthis.$on('hook:updated', () => {})

2. Функция жизненного цикла внешнего слушателя

Сегодня коллега спросил в группе компании, хочу ли я контролировать функцию жизненного цикла компонента извне, есть ли способ?

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

<template>
  <!--通过@hook:updated监听组件的updated生命钩子函数-->
  <!--组件的所有生命周期钩子都可以通过@hook:钩子函数名 来监听触发-->
  <custom-select @hook:updated="$_handleSelectUpdated" />
</template>
<script>
import CustomSelect from '../components/custom-select'
export default {
  components: {
    CustomSelect
  },
  methods: {
    $_handleSelectUpdated() {
      console.log('custom-select组件的updated钩子函数被触发')
    }
  }
}
</script>

Небольшие проекты также используютVuex?использоватьVue.observableНаписать управление состоянием

В интерфейсных проектах есть много данных, которые необходимо пройти и совместно с различными компонентами. В это время требуется состояние государственного управления. Вообще мы будем использоватьVuex, но для небольших проектов, таких какVuexНа официальном сайте сказано: «Если вы не планируете разрабатывать большие одностраничные приложения, использование Vuex может быть утомительным и избыточным. Это правда — если ваше приложение достаточно простое, вам лучше не использовать Vuex». Тогда мы можем использоватьVue2.6Предоставляется новый APIVue.observableпостроить один вручнуюVuex

1. Создатьstore

import Vue from 'vue'

// 通过Vue.observable创建一个可响应的对象
export const store = Vue.observable({
  userInfo: {},
  roleIds: []
})

// 定义 mutations, 修改属性
export const mutations = {
  setUserInfo(userInfo) {
    store.userInfo = userInfo
  },
  setRoleIds(roleIds) {
    store.roleIds = roleIds
  }
}

2. Ссылка в компоненте

<template>
  <div>
    {{ userInfo.name }}
  </div>
</template>
<script>
import { store, mutations } from '../store'
export default {
  computed: {
    userInfo() {
      return store.userInfo
    }
  },
  created() {
    mutations.setUserInfo({
      name: '子君'
    })
  }
}
</script>

Для разработки глобальных компонентов вам может понадобиться знать оVue.extend

Vue.extendЭто глобальный API, обычно мы редко используем его при развитии нашего бизнеса, но иногда мы хотим разработать некоторые глобальные компоненты, такие какLoading,Notify,MessageПри ожидании компонента вы можете использовать его в это времяVue.extend.

студенты используютelement-uiизloading, в коде это может быть написано так

// 显示loading
const loading = this.$loading()
// 关闭loading
loading.close()

Наверное, ничего особенного в том, чтобы так писать, но если ты так пишешь

const loading = this.$loading()
const loading1 = this.$loading()
setTimeout(() => {
  loading.close()
}, 1000 * 3)

В это время вы обнаружите, что я вызывал загрузку дважды, но появился только один, и я только закрыл его.loading,ноloading1также был закрыт. Как это достигается? Сейчас мы используемVue.extend+ Шаблон Singleton для реализацииloading

1. Развитиеloadingкомпоненты

<template>
  <transition name="custom-loading-fade">
    <!--loading蒙版-->
    <div v-show="visible" class="custom-loading-mask">
      <!--loading中间的图标-->
      <div class="custom-loading-spinner">
        <i class="custom-spinner-icon"></i>
        <!--loading上面显示的文字-->
        <p class="custom-loading-text">{{ text }}</p>
      </div>
    </div>
  </transition>
</template>
<script>
export default {
  props: {
  // 是否显示loading
    visible: {
      type: Boolean,
      default: false
    },
    // loading上面的显示文字
    text: {
      type: String,
      default: ''
    }
  }
}
</script>

развитыйloadingПосле компонента, если вам нужно использовать его напрямую, вам нужно использовать его так

<template>
  <div class="component-code">
    <!--其他一堆代码-->
    <custom-loading :visible="visible" text="加载中" />
  </div>
</template>
<script>
export default {
  data() {
    return {
      visible: false
    }
  }
}
</script>

Но это использование не отвечает нашим потребностям

  1. Вы можете отобразить отключение, вызвав метод напрямую через js
  2. loadingМожет охватывать всю страницу

2. ПройтиVue.extendПреобразование компонента в глобальный компонент

1. Модернизацияloadingкомпонент, компонентpropsизменить наdata

export default {
  data() {
    return {
      text: '',
      visible: false
    }
  }
}

2. ПройтиVue.extendКомпоненты модернизации

// loading/index.js
import Vue from 'vue'
import LoadingComponent from './loading.vue'

// 通过Vue.extend将组件包装成一个子类
const LoadingConstructor = Vue.extend(LoadingComponent)

let loading = undefined

LoadingConstructor.prototype.close = function() {
  // 如果loading 有引用,则去掉引用
  if (loading) {
    loading = undefined
  }
  // 先将组件隐藏
  this.visible = false
  // 延迟300毫秒,等待loading关闭动画执行完之后销毁组件
  setTimeout(() => {
    // 移除挂载的dom元素
    if (this.$el && this.$el.parentNode) {
      this.$el.parentNode.removeChild(this.$el)
    }
    // 调用组件的$destroy方法进行组件销毁
    this.$destroy()
  }, 300)
}

const Loading = (options = {}) => {
  // 如果组件已渲染,则返回即可
  if (loading) {
    return loading
  }
  // 要挂载的元素
  const parent = document.body
  // 组件属性
  const opts = {
    text: '',
    ...options
  }
  // 通过构造函数初始化组件 相当于 new Vue()
  const instance = new LoadingConstructor({
    el: document.createElement('div'),
    data: opts
  })
  // 将loading元素挂在到parent上面
  parent.appendChild(instance.$el)
  // 显示loading
  Vue.nextTick(() => {
    instance.visible = true
  })
  // 将组件实例赋值给loading
  loading = instance
  return instance
}

export default Loading

3. Используйте загрузку на странице

import Loading from './loading/index.js'
export default {
  created() {
    const loading = Loading({ text: '正在加载。。。' })
    // 三秒钟后关闭
    setTimeout(() => {
      loading.close()
    }, 3000)
  }
}

С помощью приведенного выше преобразования загрузку можно использовать глобально. Если вам нужно сделать что-то вродеelement-uiсмонтирован на том жеVue.prototypeвыше, черезthis.$loadingвызов, его нужно изменить

4. Установите компонент наVue.prototypeнад

Vue.prototype.$loading = Loading
// 在export之前将Loading方法进行绑定
export default Loading

// 在组件内使用
this.$loading()

Пользовательские инструкции для решения проблем снизу вверх

Что такое заказ? Приказ заключается в том, что ваша девушка указывает на вас и говорит: «Стиральная доска вон там, встань на колени, это приказ!». Шучу, откуда у программиста могут быть девушки.

В предыдущем разделе мы разработалиloadingКомпоненты, после разработки другие разработчики выдвигают два требования при их использовании

  1. могуloadingУстановлен на элементе, теперь его можно использовать только в полноэкранном режиме
  2. Вы можете использовать директиву для монтирования на указанном элементеloading

Если будет необходимость, сделаем, нечего сказать

1. Развитиеv-loadingинструкция

import Vue from 'vue'
import LoadingComponent from './loading'
// 使用 Vue.extend构造组件子类
const LoadingContructor = Vue.extend(LoadingComponent)

// 定义一个名为loading的指令
Vue.directive('loading', {
  /**
   * 只调用一次,在指令第一次绑定到元素时调用,可以在这里做一些初始化的设置
   * @param {*} el 指令要绑定的元素
   * @param {*} binding 指令传入的信息,包括 {name:'指令名称', value: '指令绑定的值',arg: '指令参数 v-bind:text 对应 text'}
   */
  bind(el, binding) {
    const instance = new LoadingContructor({
      el: document.createElement('div'),
      data: {}
    })
    el.appendChild(instance.$el)
    el.instance = instance
    Vue.nextTick(() => {
      el.instance.visible = binding.value
    })
  },
  /**
   * 所在组件的 VNode 更新时调用
   * @param {*} el
   * @param {*} binding
   */
  update(el, binding) {
    // 通过对比值的变化判断loading是否显示
    if (binding.oldValue !== binding.value) {
      el.instance.visible = binding.value
    }
  },
  /**
   * 只调用一次,在 指令与元素解绑时调用
   * @param {*} el
   */
  unbind(el) {
    const mask = el.instance.$el
    if (mask.parentNode) {
      mask.parentNode.removeChild(mask)
    }
    el.instance.$destroy()
    el.instance = undefined
  }
})

2. Используйте директивы для элементов

<template>
  <div v-loading="visible"></div>
</template>
<script>
export default {
  data() {
    return {
      visible: false
    }
  },
  created() {
    this.visible = true
    fetch().then(() => {
      this.visible = false
    })
  }
}
</script>

3. Какие сцены в проекте можно настроить по инструкции

  1. добавить в компонентloadingЭффект

  2. Контроль разрешений на уровне кнопокv-permission

  3. Код скрыт, а инструкции определены в соответствии с типом операции.

  4. Поле ввода ввода автоматически получает фокус

  5. Другие и так далее. . .

глубинаwatchа такжеwatchНемедленно инициируйте обратный вызов, я могу следить за каждым вашим движением

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

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

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

<template>
  <!--此处示例使用了element-ui-->
  <div>
    <div>
      <span>搜索</span>
      <input v-model="searchValue" />
    </div>
    <!--列表,代码省略-->
  </div>
</template>
<script>
export default {
  data() {
    return {
      searchValue: ''
    }
  },
  watch: {
    // 在值发生变化之后,重新加载数据
    searchValue(newValue, oldValue) {
      // 判断搜索
      if (newValue !== oldValue) {
        this.$_loadData()
      }
    }
  },
  methods: {
    $_loadData() {
      // 重新加载数据,此处需要通过函数防抖
    }
  }
}
</script>

2. Немедленный триггер

С помощью приведенного выше кода теперь можно инициировать загрузку данных при изменении значения, но если мы хотим загрузить данные при инициализации страницы, нам также необходимоcreatedилиmountedВызывается снова в хуке жизненного цикла$_loadDataметод. Однако теперь вам не нужно писать так, настроивwatchСвойство немедленного триггера , может удовлетворить спрос

// 改造watch
export default {
  watch: {
    // 在值发生变化之后,重新加载数据
    searchValue: {
    // 通过handler来监听属性变化, 初次调用 newValue为""空字符串, oldValue为 undefined
      handler(newValue, oldValue) {
        if (newValue !== oldValue) {
          this.$_loadData()
        }
      },
      // 配置立即执行属性
      immediate: true
    }
  }
}

3. Глубокое слушание (я вижу каждое ваше движение внутри)

Для страницы формы требование надеется, что после того, как пользователь изменит любой элемент формы, страница формы должна быть переведена в измененное состояние. Если по приведенному выше примеруwatchнаписано, то нам нужно следить за каждым атрибутом формы, что слишком хлопотно, поэтому нам нужно использоватьwatchглубокий мониторингdeep

export default {
  data() {
    return {
      formData: {
        name: '',
        sex: '',
        age: 0,
        deptId: ''
      }
    }
  },
  watch: {
    // 在值发生变化之后,重新加载数据
    formData: {
      // 需要注意,因为对象引用的原因, newValue和oldValue的值一直相等
      handler(newValue, oldValue) {
        // 在这里标记页面编辑状态
      },
      // 通过指定deep属性为true, watch会监听对象里面每一个值的变化
      deep: true
    }
  }
}

Слушайте в любое время, отмените в любое время, узнайте$watch

Есть такое требование, есть форма, при редактировании нужно следить за изменением формы, если есть изменение, кнопка сохранения включена, иначе кнопка сохранения отключена. В это время для новой формы вы можете напрямую пройтиwatchдля прослушивания данных формы (при условии, что этоformData), как описано в приведенном выше примере, но для формы редактирования форма должна быть заполнена данными, которые будут изменены в это время.formDataзначение, вызоветwatch, невозможно точно определить, активировать ли кнопку сохранения. Теперь вам нужно знать$watch

export default {
  data() {
    return {
      formData: {
        name: '',
        age: 0
      }
    }
  },
  created() {
    this.$_loadData()
  },
  methods: {
    // 模拟异步请求数据
    $_loadData() {
      setTimeout(() => {
        // 先赋值
        this.formData = {
          name: '子君',
          age: 18
        }
        // 等表单数据回填之后,监听数据是否发生变化
        const unwatch = this.$watch(
          'formData',
          () => {
            console.log('数据发生了变化')
          },
          {
            deep: true
          }
        )
        // 模拟数据发生了变化
        setTimeout(() => {
          this.formData.name = '张三'
        }, 1000)
      }, 1000)
    }
  }
}

Как видно из приведенного выше примера, мы можем передатьthis.$watchотслеживать изменения данных. Итак, как отменить мониторинг, в приведенном выше примереthis.$watchвернул значениеunwatch, это функция, которая выполняется, когда ее нужно отменитьunwatch()отменить

Функциональные компоненты, функции являются компонентами?

Что такое функциональный компонент? Функциональные компоненты — это функции, которые являются компонентами, и создается впечатление, что вы играете словами. использовалReactОдноклассники не должны быть незнакомы с функциональными компонентами. Функциональные компоненты, мы можем понять, что нет внутреннего состояния, нет функции крючка жизненного цикла, нетthis(Никаких экземпляров компонентов не требуется).

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

1. Сначала идет код функционального компонента

export default {
  // 通过配置functional属性指定组件为函数式组件
  functional: true,
  // 组件接收的外部属性
  props: {
    avatar: {
      type: String
    }
  },
  /**
   * 渲染函数
   * @param {*} h
   * @param {*} context 函数式组件没有this, props, slots等都在context上面挂着
   */
  render(h, context) {
    const { props } = context
    if (props.avatar) {
      return <img src={props.avatar}></img>
    }
    return <img src="default-avatar.png"></img>
  }
}

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

2. Зачем использовать функциональные компоненты

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

3. Отличие функциональных компонентов от обычных компонентов

  1. Функциональные компоненты должны указывать функционал при объявлении компонента.
  2. Функциональные компоненты не нуждаются в создании экземпляров, поэтому нетthis,thisпройти черезrenderвторой параметр функции вместо
  3. Функциональные компоненты не имеют обработчиков жизненного цикла и не могут использовать вычисляемые свойства, часы и т. д.
  4. Функциональные компоненты не могут выставлять события через $emit, а вызовы событий могут выполняться только черезcontext.listeners.clickСпособ вызова внешних входящих событий
  5. Поскольку функциональные компоненты не создаются, они передаются извне.refПри разыменовании компонента фактическая ссылкаHTMLElement
  6. функциональный компонентpropsНет необходимости отображать оператор, поэтому нетpropsСвойства, объявленные внутри, автоматически и неявно разрешаются вprop, хотя все необъявленные свойства нормальных компонентов решаются в$attrsвнутрь и автоматически монтировать его на корневой элемент компонента (можно передатьinheritAttrsатрибут запрещен)

4. Я не хочу использоватьJSX, можно ли использовать функциональные компоненты?

существуетVue2.5Раньше использование функциональных компонентов было возможно только черезJSXтаким образом, после этого функциональные компоненты могут быть созданы с помощью синтаксиса шаблона

<!--在template 上面添加 functional属性-->
<template functional>
  <img :src="props.avatar ? props.avatar : 'default-avatar.png'" />
</template>
<!--根据上一节第六条,可以省略声明props-->

Эпилог

Не истощайте свое вдохновение и воображение, не будьте рабом своих моделей. - Винсент Ван Гог

❤️ Люблю тройное комбо

1. Если вы считаете, что эта статья неплохая, пожалуйста, подпишитесь, поставьте лайк и добавьте в закладки, чтобы ее увидело больше людей~

2. Обратите внимание на официальный аккаунт [Некоторые играют на фронтенде] и регулярно проталкивайте для вас свежие и галантерейные товары и хорошие статьи.

3. На спецучастках носите маску и пользуйтесь средствами индивидуальной защиты.