Запись разработки компонента Vue: три способа вызова компонентов

JavaScript Vue.js

Эта статья впервые появилась на моемблог. Добро пожаловать, чтобы щелкнуть для лучшего эффекта чтения ~

писал раньшеfj-service-system, столкнулся с некоторыми проблемами. То есть у меня есть некоторые компоненты, такие какDialog,MessageТаким компонентом является введение сторонней библиотеки компонентов, такой какelement-uiНравится это или реализовать самостоятельно? Хотя у них есть функции, представленные по запросу, общий стиль не соответствует всей моей системе. Таким образом, вы можете рассмотреть возможность реализации этих простых компонентов вручную.

Обычно, когда мы читаем некоторые статьи о Vue, мы обычно видим страницу разработки однофайлового компонента Vue. Статей по однокомпонентной разработке относительно мало. я делаюfj-service-systemКогда я работал над проектом, я обнаружил, что на самом деле очень интересно разрабатывать один компонент. Может писать и записывать. Потому что это не ui framework, это просто запись, репозитория на гитхабе нет, давайте посмотрим на код.

Существует три основных способа вызова компонентов:

  • v-modelили.syncЯвное управление отображением и скрытием компонента
  • Вызывается js-кодом
  • Вызывается через директиву Vue

При написании компонентов много текстов и вдохновения исходит отelement-ui,благодарный.

Dialog

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

dialog

Этот компонент подходит дляv-modelили.syncспособ явно контролировать появление и исчезновение. Его можно написать прямо на странице, а затем управлять данными — это тоже компонент, наиболее соответствующий дизайнерским идеям Vue.

Для этого мы можем написать компонент с именемDialog.vue

<template>
  <div class="dialog">
    <div class="dialog__wrapper" v-if="visble" @clcik="closeModal">
      <div class="dialog">
        <div class="dialog__header">
          <div class="dialog__title">{{ title }}</div>
        </div>
        <div class="dialog__body">
          <slot></slot>
        </div>
        <div class="dialog__footer">
          <slot name="footer"></slot>
        </div>
      </div>
    </div>
    <div class="modal" v-show="visible"></div>
  </div>
</template>

<script>
  export default {
    name: 'dialog',
    props: {
      title: String,
      visible: {
        type: Boolean,
        default: false
      }
    },
    methods: {
      close() {
        this.$emit('update:visible', false) // 传递关闭事件
      },
      closeModal(e) {
        if (this.visible) {
          document.querySelector('.dialog').contains(e.target) ? '' : this.close(); // 判断点击的落点在不在dialog对话框内,如果在对话框外就调用this.close()方法关闭对话框
        }
      }
    }
  }
</script>

Я не пишу CSS или что-то еще, и это не имеет ничего общего с самим компонентом. Однако стоит отметить, что вышеизложенноеdialog__wrapperЭтот класс также является полноэкранным и прозрачным, он в основном используется для получения событий щелчка и блокировки позиции щелчка.Node.contains()Метод для определения того, является ли позиция щелчка самим диалоговым окном, если оно щелкнуто за пределами диалогового окна, например, полупрозрачное.modalЗатем слой отправляет событие закрытия, чтобы закрыть диалоговое окно.

Когда мы хотим вызвать его извне, мы можем вызвать его следующим образом:

<template>
  <div class="xxx">
    <dialog :visible.sync="visible"></dialog> 
    <button @click="openDialog"></button>
  </div>
</template>

<script>
  import Dialog from 'Dialog'
  export default {
    components: {
      Dialog
    },
    data() {
      return {
        visible: false
      }
    },
    methods: {
      openDialog() {
        this.visible = true // 通过data显式控制dialog
      }
    }
  }
</script>

Чтобы открыть и закрыть диалог, вы можете попробовать добавить<transition></transition>Компонент сочетается с эффектом перехода, также хорошо будет смотреться простая анимация перехода.

Notice

Этот компонент похож наelement-uiизmessage(уведомление). Больше всего меня привлекает то, что он вызывается не через v-model явным образом прописывая html структуру компонента в странице, а через форму в js.this.$message()вызвать этот метод. Хотя этот метод идет вразрез с идеей Vue, основанной на данных. Но должен сказать, что в некоторых случаях это действительно удобно.

notice

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

Предыдущий подход состоял в том, чтобы написать файл Vue, а затем передатьcomponentsАтрибуты вводятся на страницу и явно записываются в вызываемый тег. Итак, как вызвать компонент через метод js?

Ключ здесь Vueextendметод.

Документация не дает подробностейextendМожно использовать так, просто как руководствоmountКонструктор компонентов Vue иллюстрирует это.

просмотревelement-uiИсходный код , можно рассматривать как понимание того, как реализовать вышеуказанные функции.

Первым остается создатьNotice.vueдокумент

<template>
  <div class="notice">
    <div class="content">
      {{ content }}
    </div>
  </div>
</template>

<script>
  export default {
    name: 'notice',
    data () {
      return {
        visible: false,
        content: '',
        duration: 3000
      }
    },
    methods: {
      setTimer() {
        setTimeout(() => {
          this.close() // 3000ms之后调用关闭方法
        }, this.duration)
      },
      close() {
        this.visible = false
        setTimeout(() => {
          this.$destroy(true)
          this.$el.parentNode.removeChild(this.$el) // 从DOM里将这个组件移除
        }, 500)
      }
    },
    mounted() {
      this.setTimer() // 挂载的时候就开始计时,3000ms后消失
    }
  }
</script>

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

Итак, вам также нужен файл js, чтобы взять на себя этот компонент и вызватьextendметод. В этом же каталоге можно создатьindex.jsдокумент.

import Vue from 'vue'

const NoticeConstructor = Vue.extend(require('./Notice.vue').default) // 直接将Vue组件作为Vue.extend的参数。新版vue-loader升级后要求加上`.default`,感谢评论指出~

let nId = 1

const Notice = (content) => {
  let id = 'notice-' + nId++

  const NoticeInstance = new NoticeConstructor({
    data: {
      content: content
    }
  }) // 实例化一个带有content内容的Notice

  NoticeInstance.id = id
  NoticeInstance.vm = NoticeInstance.$mount() // 挂载但是并未插入dom,是一个完整的Vue实例
  NoticeInstance.vm.visible = true
  NoticeInstance.dom = NoticeInstance.vm.$el
  document.body.appendChild(NoticeInstance.dom) // 将dom插入body
  NoticeInstance.dom.style.zIndex = nId + 1001 // 后插入的Notice组件z-index加一,保证能盖在之前的上面
  return NoticeInstance.vm
}

export default {
  install: Vue => {
    Vue.prototype.$notice = Notice // 将Notice组件暴露出去,并挂载在Vue的prototype上
  }
}

Мы можем видеть через этот файлNoticeConstructorМы можем управлять различными свойствами компонента через js. Наконец, мы регистрируем его в прототипе Vue, чтобы использовать его внутри страницы, напримерthis.$notice()метод, вы можете легко вызвать этот компонент, чтобы написать простой эффект подсказки уведомления.

Конечно, не забывайте, что это эквивалентно плагину Vue, поэтому вам нужно вызвать его в главном js.Vue.use()метод:

// main.js

// ...
import Notice from 'notice/index.js'

Vue.use(Notice)

// ...

Loading

смотреть вelement-ui, я также нашел очень интересный компонент, которыйLoading, который используется для применения слоя стилей загрузки к некоторым компонентам, которым необходимо дождаться загрузки данных. Удобнее всего вызывать эту загрузку черезv-loadingЭта инструкция, назначаяtrue/falseДля управления отображением и скрытием слоя «Загрузка». Такой способ вызова, конечно, очень удобен. И вы можете выбрать загрузку всей страницы или загрузки компонента. Такой опыт разработки, естественно, хорош.

loading

По сути, она аналогична идее Notice, но поскольку предполагаетdirective, поэтому логика будет относительно сложной.

Обычно, если это не связано с Vuedirectiveразвития, не может подвергатьсяmodifiers,bindingКонцепция равенства. Ссылаться наДокументация

Проще говоря, это выглядит:v-loading.fullscreen="true"Это предложение,v-loadingто естьdirective,fullscreenЭто оноmodifier,trueто естьbindingизvalueстоимость. Поэтому именно с помощью такого простого предложения добиться эффекта полноэкранной загрузки, а когда нетfullscreenПри использовании модификатора применяется элемент, которому принадлежит директива.loadingЭффект. компоненты проходятbindingизvalueзначение для контроляloadingвключить и выключить. (похожий наv-modelЭффект)

По сути, загрузка — это тоже собственно DOM-узел, но сделать из него удобную инструкцию не особо просто.

Сначала нам нужно написатьloadingVue-компонент. создать новыйLoading.vueдокумент

<template>
  <transition
    name="loading"
  	@after-leave="handleAfterLeave">
    <div
      v-show="visible"
      class="loading-mask"
      :class={'fullscreen': fullscreen}>
      <div class="loading">
        ...
      </div>
      <div class="loading-text" v-if="text">
        {{ text }}
      </div>
    </div>
  </transition>
</template>
<script>
export default {
  name: 'loading',
  data () {
    return {
      visible: true,
      fullscreen: true,
      text: null
    }
  },
  methods: {
    handleAfterLeave() {
      this.$emit('after-leave');
    }
  }
}
</script>
<style>
.loading-mask{
  position: absolute; // 非全屏模式下,position是absolute
  z-index: 10000;
  background-color: rgba(255,235,215, .8);
  margin: 0;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  transition: opacity .3s;
}
.loading-mask.fullscreen{
  position: fixed; // 全屏模式下,position是fixed
}
// ...
</style>

Ключом к загрузке является достижение двух эффектов:

  1. Полноэкранной загрузки можно добиться, вставив под тело, затем изменив положение загрузки на фиксированное и вставив его в тело.
  2. При загрузке элемента, где он находится, необходимо изменить положение текущего элемента: если нетabsolute, затем измените его наrelatvieи вставьте его под текущим элементом. В этот момент позиция Loading будет абсолютно позиционирована относительно текущего элемента.

Итак, в текущем каталоге создайтеindex.jsфайл, который объявляет нашdirectiveлогика.

import Vue from 'vue'
const LoadingConstructor = Vue.extend(require('./Loading.vue').default) // 新版vue-loader升级后要求加上`.default`,感谢评论指出~

export default {
  install: Vue => {
    Vue.directive('loading', { // 指令的关键
      bind: (el, binding) => {
        const loading = new LoadingConstructor({ // 实例化一个loading
          el: document.createElement('div'),
          data: {
            text: el.getAttribute('loading-text'), // 通过loading-text属性获取loading的文字
            fullscreen: !!binding.modifiers.fullscreen 
          }
        })
        el.instance = loading; // el.instance是个Vue实例
        el.loading = loading.$el; // el.loading的DOM元素是loading.$el
        el.loadingStyle = {};
        toggleLoading(el, binding);
      },
      update: (el, binding) => {
        el.instance.setText(el.getAttribute('loading-text'))
        if(binding.oldValue !== binding.value) {
          toggleLoading(el, binding)
        }   
      },
      unbind: (el, binding) => { // 解绑
        if(el.domInserted) {
          if(binding.modifiers.fullscreen) {
              document.body.removeChild(el.loading);
          }else {
            el.loading &&
            el.loading.parentNode &&
            el.loading.parentNode.removeChild(el.loading);
          }
        }
      }
    })

    const toggleLoading = (el, binding) => { // 用于控制Loading的出现与消失
      if(binding.value) { 
        Vue.nextTick(() => {
          if (binding.modifiers.fullscreen) { // 如果是全屏
            el.originalPosition = document.body.style.position;
            el.originalOverflow = document.body.style.overflow;
            insertDom(document.body, el, binding); // 插入dom
          } else {
            el.originalPosition = el.style.position;
            insertDom(el, el, binding); // 如果非全屏,插入元素自身
          }
        })
      } else {
        if (el.domVisible) {
          el.instance.$on('after-leave', () => {
            el.domVisible = false;
            if (binding.modifiers.fullscreen && el.originalOverflow !== 'hidden') {
              document.body.style.overflow = el.originalOverflow;
            }
            if (binding.modifiers.fullscreen) {
              document.body.style.position = el.originalPosition;
            } else {
              el.style.position = el.originalPosition;
            }
          });
          el.instance.visible = false;
        }
      }
    }

    const insertDom = (parent, el, binding) => { // 插入dom的逻辑
      if(!el.domVisible) {
        Object.keys(el.loadingStyle).forEach(property => {
          el.loading.style[property] = el.loadingStyle[property];
        });
        if(el.originalPosition !== 'absolute') {
          parent.style.position = 'relative'
        }
        if (binding.modifiers.fullscreen) {
          parent.style.overflow = 'hidden'
        }
        el.domVisible = true;
        parent.appendChild(el.loading) // 插入的是el.loading而不是el本身
        Vue.nextTick(() => {
          el.instance.visible = true;
        });
        el.domInserted = true;
      }
    }
  }
}

Аналогично, чтобы написать всю логику, нам нужно прописать ее под Vue в проекте:

// main.js

// ...
import Loading from 'loading/index.js'

Vue.use(Loading)

// ...

Пока мы можем использовать форму

<div v-loading.fullscreen="loading" loading-text="正在加载中">

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

Суммировать

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

Наконец, еще раз спасибоelement-uiИсходный код дает большое вдохновение.