Это то, что я научился организовыватьVue.jsПервая из серии статей, две другие посвящены оптимизации и принципам. Я надеюсь, что прочитав эти 3 статьи, вы сможетеVueиметь более глубокое понимание.
7 компонентных методов связи на выбор
Компонентная коммуникацияVueОсновные знания, освоить эти точки знаний, нет никаких проблем в развитии интервью.
props/@on+$emit
Используется для реализации связи между родительским и дочерним компонентами. пройти черезpropsВы можете передавать сообщения от родительских компонентов дочерним компонентам:
<!-- parent.vue -->
<child :title="title"></child>
// child.vue
props: {
title: {
type: String,
default: '',
}
}
Таким образомthis.titleПросто получите его непосредственно из родительского компонентаtitleстоимость. Обратите внимание, что вы не должны изменять непосредственно внутри дочерних компонентов.prop, я не буду вдаваться в подробности здесь,Вы можете непосредственно увидеть введение на официальном сайте.
и через@on+$emitКомпозиция позволяет дочерним компонентам передавать информацию родительским компонентам:
<!-- parent.vue -->
<child @changeTitle="changeTitle"></child>
// child.vue
this.$emit('changeTitle', 'bubuzou.com')
listeners
Vue_2.4добавлено в$attrs/$listenersВозможна межуровневая связь компонентов.$attrsсодержит бездействие в родительской областиpropПривязка свойства (classа такжеstyleКроме), это звучит немного непонятно? Все в порядке, посмотрите на код, чтобы узнать, что это значит:
<!-- 父组件 index.vue -->
<list class="list-box" title="标题" desc="描述" :list="list"></list>
// 子组件 list.vue
props: {
list: [],
},
mounted() {
console.log(this.$attrs) // {title: "标题", desc: "描述"}
}
родительский компонент вышеindex.vueВ мы даем дочерний компонентlist.vueПередаются 4 параметра, но внутри дочернего компонентаpropsВ нем определено только одноlist, то в это времяthis.$attrsКакова ценность? первым удалитьpropsбыл связан, а затем удалитьclassа такжеstyle, последний оставшийсяtitleа такжеdescРезультат такой же, как и напечатанный.
Основываясь на приведенном выше коде, мы даемlist.vueДобавьте подкомпонент к:
<!-- 子组件 list.vue -->
<detail v-bind="$attrs"></detial>
// 孙子组件 detail.vue
// 不定义props,直接打印 $attrs
mounted() {
console.log(this.$attrs) // {title: "标题", desc: "描述"}
}
В дочернем компоненте мы определяемv-bind="$attrs"Вы можете удалить параметры, переданные от родителяprops,classа такжеstyleПосле этого остальные продолжают передаваться на нижний уровень, реализуя таким образом межуровневую связь компонентов.
$attrsМожно передавать параметры между уровнями для обеспечения связи родитель-потомок; аналогично, через$listenersАналогичным образом события могут передаваться по уровням для обеспечения связи между детьми и родителями.$listenersсодержит родительскую область не содержит.nativeдекоративныйv-onпрослушиватель событий, черезv-on="$listeners"передается дочернему компоненту.
<!-- 父组件 index.vue -->
<list @change="change" @update.native="update"></list>
<!-- 子组件 list.vue -->
<detail v-on="$listeners"></detail>
// 孙子组件 detail.vue
mounted() {
this.$listeners.change()
this.$listeners.update() // TypeError: this.$listeners.update is not a function
}
предоставить / ввести комбо
provide/injectСозданные для того, чтобы компонент-предок мог внедрять зависимость во все его потомки, свойства и методы могут быть внедрены, что позволяет осуществлять межуровневую связь между родительскими и дочерними компонентами. Это особенно полезно при разработке компонентов более высокого порядка и библиотек компонентов.
// 父组件 index.vue
data() {
return {
title: 'bubuzou.com',
}
}
provide() {
return {
detail: {
title: this.title,
change: (val) => {
console.log( val )
}
}
}
}
// 孙子组件 detail.vue
inject: ['detail'],
mounted() {
console.log(this.detail.title) // bubuzou.com
this.detail.title = 'hello world' // 虽然值被改变了,但是父组件中 title 并不会重新渲染
this.detail.change('改变后的值') // 执行这句后将打印:改变后的值
}
provideа такжеinjectПривязки не являются реактивными для примитивных типов. Это сделано намеренно. Однако, если вы передаете прослушиваемый объект, свойства объекта по-прежнему доступны. Вот почему во внучатом компоненте изменилсяtitle, но причина, по которой родительский компонент не перерисовывается.
EventBus
Вышеупомянутые три метода могут обмениваться данными только между компонентами от родителя к дочернему или от дочернего к родительскому, и я более мощный 😀, я также могу обмениваться данными между одноуровневыми компонентами или даже между любыми двумя компонентами. использоватьVueэкземпляр реализуетEventBusПубликация и подписка на информацию могут реализовать связь между любыми двумя компонентами. Существует два способа записи для инициализацииeventBusОбъект:
-
путем экспорта
Vueinstance, а затем импортируйте его, где это необходимо:// eventBus.js import Vue from 'vue' export const EventBus = new Vue()использовать
EventBusПодписывайтесь и публикуйте сообщения:import {EventBus} from '../utils/eventBus.js' // 订阅处 EventBus.$on('update', val => {}) // 发布处 EventBus.$emit('update', '更新信息') -
существует
main.jsИнициализируйте глобальную шину событий в:// main.js Vue.prototype.$eventBus = new Vue()использовать:
// 需要订阅的地方 this.$eventBus.$on('update', val => {}) // 需要发布信息的地方 this.$eventBus.$emit('update', '更新信息')
Если вы хотите удалить прослушиватель событий, вы можете сделать это следующим образом:
this.$eventBus.$off('update', {})
Два способа написания представлены выше.Рекомендуется использовать второй способ глобального определения, который позволяет избежать импорта в нескольких местах.EventBusобъект. Этот метод связи компонентов теоретически может взаимодействовать между любыми двумя компонентами, если порядок подписки и публикации правильный, а имя события остается уникальным, что является весьма мощным. Однако, несмотря на то, что метод хорош, им не следует злоупотреблять. Его рекомендуется использовать только в простых и небольших проектах. Если этот метод используется бесконечно в большом и сложном проекте, это усложнит проект. поддерживать.
Vuex для глобального управления данными
Vuexэто специализированная службаVue.jsИнструмент управления состоянием приложения. Подходит для средних и крупных приложений.VuexЕсть некоторые проприетарные концепции, которые необходимо понять в первую очередь:
-
State: для хранения данных, даstoreЕдинственный источник данных в ; -
Getter: Подобно вычисляемым свойствам, правильноStateДанные в данных подвергаются вторичной обработке, такой как фильтрация и оценка нескольких данных; -
Mutation: подобное событие, изменениеStoreЕдинственный способ сохранить данные — выполнить операции синхронизации; -
Action:аналогичныйMutation, отправивMutationЧтобы изменить данные, а не напрямую манипулированиеState, может выполнять асинхронные операции; -
Module: Когда дело сложное, можно поставитьstoreРазделен на несколько модулей для удобства обслуживания;
Существуют различные соответствия этим понятиям.mapВспомогательные функции используются для упрощения операций, таких какmapState, следующие три способа написания на самом деле имеют одно и то же значение.stateПолучите от него данные и верните их компоненту для использования через вычисляемые свойства.
computed: {
count() {
return this.$store.state.count
},
...mapState({
count: state => state.count
}),
...mapState(['count']),
},
Другой примерmapMutations, функции, которые должны быть реализованы следующими двумя функциями, одинаковы, обе из которых должны отправитьmutationизменитьstateданные в:
methods: {
increment() {
this.$store.commit('increment')
},
...mapMutations(['increment']),
}
Далее мы будем использовать минимальный пример, чтобы показатьVuexУправление состоянием между любыми двумя компонентами в .
1. Новыйstore.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0,
},
mutations: {
increment(state) {
state.count++
},
decrement(state) {
state.count--
}
},
})
2. Создайте группуstoreизVueПример
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './utils/store'
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
3. Любой компонентAРеализовать увеличение количества кликов
<template>
<p @click="increment">click to increment:{{count}}</p>
</template>
<script>
import {mapState, mapMutations} from 'vuex'
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapMutations(['increment'])
},
}
</script>
4. Любой компонентBРеализовать декремент кликов
<template>
<p @click="decrement">click to decrement:{{count}}</p>
</template>
<script>
import {mapState, mapMutations} from 'vuex'
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapMutations(['decrement'])
},
}
</script>
Выше только самое простоеvuexКонфигурация для обеспечения связи компонентов, конечно, конфигурация в реальном проекте определенно будет более сложной, например, необходимостьStateДанные будут использованы для вторичной фильтрацииGetter, а затем, если вам нужно отправить асинхронно, вам нужно использоватьAction, например, если модулей много, можноstoreПодмодуль для управления состоянием. дляVuexДля более сложных операций рекомендуется см.Официальная документация Vuex, а затем напишите еще примеры.
Vue.observable реализует мини vuex
ЭтоVue2.6добавлено вAPI, используемый для того, чтобы сделать объект отзывчивым. Мы можем воспользоваться этой функцией для реализации небольшого менеджера состояний.
// store.js
import Vue from 'vue'
export const state = Vue.observable({
count: 0,
})
export const mutations = {
increment() {
state.count++
}
decrement() {
state.count--
}
}
<!-- parent.vue -->
<template>
<p>{{ count }}</p>
</template>
<script>
import { state } from '../store'
export default {
computed: {
count() {
return state.count
}
}
}
</script>
// child.vue
import { mutations } from '../store'
export default {
methods: {
handleClick() {
mutations.increment()
}
}
}
children/root
Путем определения подкомпонентовrefможно использовать свойства$refsдля прямого управления методами и свойствами дочерних компонентов.
<child ref="list"></child>
Например, дочерний компонент имеетgetListметод, который может быть вызван следующими способами для достижения связи родитель-потомок:
this.$refs.list.getList()
Кроме$refsКроме того, остальные три являются самостоятельными.VueСвойства, которые автоматически включаются после создания экземпляра, используйте аналогично приведенному выше.
6 категорий модификаторов, которые вы можете освоить
модификатор формы
Модификаторами класса формы являются оба иv-modelИспользуются вместе, например:v-model.lazy,v-model.trimтак же какv-model.numberЖдать.
-
.lazy: Задержка ответа на результат ввода формы, как правило, иv-modelДля использования с. Обычно вinputвход будет вpОтображается в реальном времени в ярлыке, но с добавлением.lazyЗатем вам нужно вызвать ответ, когда поле ввода теряет фокус.<input type="text" v-model.lazy="name" /> <p>{{ name }}</p> -
.trim: Отфильтруйте начальные и конечные пробелы входного содержимого, эта сумма напрямую получает строку, а затем проходитstr.trim()Удаление пробелов в начале и конце строки — это одно. -
.number: Если первый введенный символ является числом, можно вводить только числа, в противном случае это обычная строка.
модификатор события
VueМодификатор события предназначен специально дляv-onПредназначен для использования следующим образом:@click.stop="handleClick", а также может использоваться последовательно:@click.stop.prevent="handleClick".
<div @click="doDiv">
click div
<p @click="doP">click p</p>
</div>
-
.stop: предотвращение всплытия событий и встроенныйevent.stopPropagation()тот же эффект. Как указано выше, при нажатииpПри маркировке,divСобытие click также сработает, плюс.stopПосле того, как событие не будет передано родителю, событие родителя не будет запущено. -
.prevent: предотвращение событий по умолчанию и встроенныйevent.preventDefault()тот же эффект. напримерhrefСобытие клика добавляется к ссылке, тогда переход по ссылке также будет срабатывать при срабатывании события, но с.preventПосле этого переход по ссылке срабатывать не будет. -
.capture: поток событий по умолчанию: фаза захвата — целевая фаза — фаза всплытия, то есть событие запускается из наиболее конкретного целевого элемента, а затем всплывает вверх. и добавить.captureПоследнее наоборот, внешний элемент сначала запускает событие, а затем передает его глубокому слою. -
.self: события, которые запускаются только сами по себе, не доставляются родителю и.stopчем-то похож. -
.once: это событие будет запущено только один раз. -
.passive: всегда срабатывает при прокрутке страницыonScrollсобытие, это действительно имеет проблемы с производительностью, особенно на мобильной стороне, при добавлении.passiveПосле этого он будет срабатывать не так часто. -
.native: теперь используется в компонентеv-onБудут прослушиваться только пользовательские события (компоненты используют$emitтриггерные события). Если вы хотите прослушивать собственные события корневого элемента, вы можете использовать.nativeмодификаторы, такие как следующиеel-input, если не добавлено.nativeОн не сработает, когда карета вернетсяsearchфункция.<el-input type="text" v-model="name" @keyup.enter.native="search"></el-input>
При последовательном использовании модификаторов событий нужно обращать внимание на их порядок.Последовательно используются одни и те же два модификатора, но порядок разный, и результаты сильно отличаются.
@click.prevent.selfбудет блокировать все события щелчка, в то время как@click.self.preventПредотвращает клики только по своему элементу.
модификатор кнопки мыши
-
.left: щелчок левой кнопкой мыши; -
.right: Правый щелчок мыши; -
.middle: щелчок средней кнопкой мыши;
модификаторы клавиш клавиатуры
VueПриведены некоторые часто используемые коды клавиш:
.enter.tab-
.delete(захватывает клавиши «удалить» и «возврат») .esc.space.up.down.left.right
Кроме того, вы также можете напрямуюKeyboardEvent.keyЛюбое допустимое имя ключа преобразуется вkebab-caseВ качестве модификатора, например, вы можете использовать следующий код, чтобы увидеть, каково имя ключа определенного ключа:
<input @keyup="onKeyUp">
onKeyUp(event) {
console.log(event.key) // 比如键盘的方向键向下就是 ArrowDown
}
.точный модификатор
.exactМодификаторы позволяют вам контролировать, какие события вызываются точными комбинациями системных модификаторов.
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>
модификатор .sync
.syncМодификаторы часто используются для дочерних компонентов для обновления данных родительского компонента. Просто посмотрите на код ниже:
<!-- parent.vue -->
<child :title.sync="title"></child>
// child.vue
this.$emit('update:title', 'hello')
Подкомпоненты могут быть переданы напрямую черезupdate:titleдля обновления в форме, объявленной в родительском компоненте.syncизprop.
Надпись в вышеприведенном родительском компоненте на самом деле является сокращением для следующей записи:
<child :title="title" @update:title="title = $event"></child>
Обратите внимание, что v-bind с модификатором .sync нельзя использовать с выражениями.
Если вы хотите установить большеprop,Например:
<child :name.sync="name" :age.sync="age" :sex.sync="sex"></child>
в состоянии пройтиv-bind.syncСокращенно так:
<child v-bind.sync="person"></child>
person: {
name: 'bubuzou',
age: 21,
sex: 'male',
}
VueВнутренний анализ будет сделан самостоятельноpersonКаждое свойство в объекте действует как отдельноеpropПередайте его, добавьте каждый для обновленияv-onслушатель. Он остается прежним при обновлении из подкомпонентов, таких как:
this.$emit('update:name', 'hello')
6 способов написать многоразовые модули
Сегодня Demand рассмотрел требование и ему нужно было внедрить детальную страницу.Эта детальная страница доступна обычным пользователям и администраторам, но отображаемые данные немного отличаются, но большинство из них одинаковые, главное отличие в том, что детали для обычных пользователей.Пользователь чисто дисплейный,и от администратора требуется возможность редактирования,а то у администратора еще какие-то права на кнопки и тд. См. требования здесь.Если вы назначаете данные пользователя разработчику А, а данные администратора назначаете Б во время планирования, то в результате разработчик А пишет страницу сведений, а разработчик Б пишет Страница сведений создается, которая изменяется во время разработки. этап и после тестаbugКак на этапе, так и на более позднем этапе итерации необходимо поддерживать эти два файла одновременно, что приводит к потере времени и рабочей силы, поэтому вы можете осознать важность написания повторно используемых модулей.
а такжеVueЧтобы позволить разработчикам лучше писать повторно используемые модули, автор специально предоставляет множество средств, таких как: компоненты, пользовательские инструкции, функции рендеринга, плагины и фильтры.
компоненты
компонентVueНаиболее важной частью этого является также наиболее распространенный метод, который мы обычно используем для написания повторно используемых модулей.Однако, поскольку этот контент слишком длинный, он не будет здесь расширяться и будет подробно описан позже.
Используйте примеси
Что такое миксин? С точки зрения структуры кода микширование — это наполовину компонент, один компонент.VueКомпоненты могут включатьtemplate,scriptа такжеstyleТри части, и микширование на самом делеscriptсодержимое внутри. Объект миксина содержит произвольные параметры компонента, такие какdata,methods,computed,watch, крючки жизненного цикла или дажеmixinsПодождите, примеси предназначены для повышения гибкости кода и повторного использования.
Когда следует использовать миксины? Когда повторно используемая логика простоJSуровень кода, безtemplateВы можете рассмотреть возможность использования миксинов. Например, нам нужно зафиксировать время пребывания пользователя на странице, затем мы можем извлечь эту логику и поместить вmixinsвнутри:
// mixins.js
export const statMixin = {
methods: {
enterPage() {},
leavePage() {},
},
mounted() {
this.enterPage()
},
beforeDestroyed() {
this.leavePage()
}
}
Затем добавьте:
import { statMixin } from '../common/mixins'
export default {
mixins: [statMixin]
}
При использовании миксинов обратите внимание на правила слияния с параметрами компонентов, которые можно разделить на следующие три категории:
-
dataБудет выполнено рекурсивное слияние, и данные компонента будут преобладать при конфликтах имен ключей:// mixinA 的 data data() { obj: { name: 'hello', }, } // component A export default { mixins: [mixinA], data(){ obj: { name: 'bubuzou', age: 21 }, }, mounted() { console.log( this.obj ) // { name: 'bubuzou', 'age': 21 } } } -
Поскольку функции хуков жизненного цикла будут объединены в массив, хуки, смешанные с объектом, будут выполняться первыми:
// mixin A const mixinA = { created() { console.log( '第一个执行' ) } } // mixin B const mixinB = { mixins: [mixinA] created() { console.log( '第二个执行' ) } } // component A export default { mixins: [mixinB] created() { console.log( '最后一个执行' ) } } -
параметры, значение которых является объектом, например.
methods,componentsа такжеdirectives, будут объединены в один и тот же объект. Когда имена ключей двух объектов конфликтуют, берется пара ключ-значение объекта-компонента.
пользовательская директива
КромеVueНекоторые встроенные команды, такие какv-model,v-ifЖдать,VueТакже позволяет нам настроить директиву. существуетVue2.0В , основной формой повторного использования кода и абстракции являются компоненты. Однако бывают случаи, когда все же необходимоDOMЭлементы выполняют низкоуровневые операции, и в это время используются пользовательские инструкции. Например, мы можем управлять разрешениями кнопки, настроив инструкцию. Мы ожидаем разработать директиву следующего вида для управления разрешениями кнопок:
<button v-auth="['user']">提交</button>
Передав набор разрешений в инструкции кнопки, если кнопка имеет толькоadminМожно отправить только разрешение, и мы передаем другое разрешение, напримерuser, то кнопка не должна отображаться.
Далее переходим к регистрации глобальной директивы:
// auth.js
const AUTH_LIST = ['admin']
function checkAuth(auths) {
return AUTH_LIST.some(item => auths.includes(item))
}
function install(Vue, options = {}) {
Vue.directive('auth', {
inserted(el, binding) {
if (!checkAuth(binding.value)) {
el.parentNode && el.parentNode.removeChild(el)
}
}
})
}
export default { install }
Тогда нам нужноmain.jsЧтобы включить эту команду, установив плагин:
import Auth from './utils/auth'
Vue.use(Auth)
использовать функцию рендеринга
Здесь мы будем использовать функцию рендеринга для реализации кнопки разрешения, описанной выше.
Метод использования следующий: оберните кнопку, которая должна управлять разрешением, в компоненте разрешения.authorityВнутри, если у вас есть разрешение, оно будет отображаться, если нет, то не отображаться.
<authority :auth="['admin']">
<button>提交</button>
</authority>
Затем мы используем функцию рендеринга для реализацииauthorityКомпоненты:
<script>
const AUTH_LIST = ['admin', 'user', 'org']
function checkAuth(auths) {
return AUTH_LIST.some(item => auths.includes(item))
}
export default {
functional: true,
props: {
auth: {
type: Array,
required: true
}
},
render(h, context) {
const { props, scopedSlots} = context
return checkAuth(props.auth) ? scopedSlots.default() : null
}
}
</script>
Зарегистрируйте этот компонент глобально:
// main.js
import Authority from './components/authority'
Vue.component('authority', Authority)
использовать фильтр
VueПредусмотрена функция настраиваемых фильтров.Основной сценарий применения — когда вы хотите отобразить данные в определенном формате, а исходные данные этому формату не соответствуют. Например, есть набор данных о людях:
[{
name: '张茂',
population: 'young',
}, {
name: '王丽',
population: 'middle',
}, {
name: '郝鹏程',
population: 'child',
}]
Один из них о типах групп по возрастуpopulation, пока используетсяcodeДля идентификации мы надеемся, что при отображении он может отображаться в соответствующем китайском значении, напримерyoungПокажи как молодежь. Затем мы можем определить локальный фильтр следующим образом:
export default {
filters: {
popuFilters(value) {
if (!value) { return '未知' }
let index = ['child', 'lad', 'young', 'middle', 'wrinkly'].indexOf(value)
return index > 0 && ['儿童', '少年', '青年', '中年', '老年'][index] || '未知'
}
}
}
При использовании фильтра простоtemplateВы можете использовать его следующим образом:
<p>{{ item.population | popuFilters }}</p>
пользовательский плагин
В некоторых случаях содержание, которое мы инкапсулируем, возможно, не требуют, чтобы пользователи понять его внутреннюю структуру кода, им нужно только быть знакомыми с соответствующими методами и методами, предоставляемыми нами.apiВот и все, это требует от нас более систематической инкапсуляции общей части логики в плагины для добавления в проект глобальных функций, таких как общиеloadingфункция, всплывающее окно и т. д.
развиватьVueПлагин должен отображатьinstallметод. Первый параметр этого методаVueКонструктор, второй параметр является необязательным объектом опций. Плагин можно настроить четырьмя способами:
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或 property
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 3. 注入组件选项
Vue.mixin({
created: function () {
// 逻辑...
}
...
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}
Тогда вам нужно импортировать документы, такие какmain.jsЗарегистрируйте плагин в:
import MyPlugin from './plugins/plugins.js'
Vue.use(MyPlugin)
3 способа написать элегантный код вручную
При написании проекта мы обычно завершаем разработку, тестирование и доработку необходимых функций в первый раз.bugПодождите, затем с радостью дождитесь выпуска в производство, думая, что все остальное не имеет значения. На самом деле, когда мы оглядываемся назад и смотрим на код, который мы обычно пишем, мы можем обнаружить, что многие места заслуживают оптимизации, например, много часто повторяющихся кодов, например, некоторые сложные места. Элегантный код может превратить механизмы в автоматизацию, а сложность в простоту.Это как весенний ветерок, когда вы видите людей открытыми и у вас хорошее настроение. Вот некоторые из перечисленных вVueпроблемы, с которыми обязательно придется столкнуться, а затем решить элегантным способом.
Автоматически импортировать модули
При разработке чуть более крупного проекта вы будете использоваться для разделения маршрутов в соответствии с модулями, а затем может появиться следующий код:
// router.js
import Vue from 'vue'
import Router from 'vue-router'
// 导入了一大堆路由文件
import mediator from './mediator'
import judges from './judges'
import disputeMediation from './disputeMediation'
import onlineMediation from './onlineMediation'
import useraction from './useraction'
import organcenter from './organcenter'
import admin from './admin'
let routeList = []
routeList.push(mediator, judges, disputeMediation, onlineMediation, useraction, organcenter, admin)
export default new Router({
mode: 'history',
routes: routeList,
})
На самом деле, правда гораздо больше.Что касается моего локального проекта, то там более 20 файлов маршрутизации, и я написал много кода импорта, который очень раздут.Что еще более беспомощно, так это то, что всякий раз, когда мне нужно добавить модуль маршрутизации, мне также приходится делать это сноваimportопять такиpush, так есть ли способ решить эту проблему? Ответ, естественно, да.
использоватьwebpackизrequire.contextЭту проблему можно решить элегантно, используя следующий синтаксис:
require.context(
directory, // 搜索的目录
useSubdirectories = true, // 是否搜索子目录
regExp = /^\.\/.*$/, // 匹配的目标文件格式
mode = 'sync' // 同步还是异步
)
Используя этот синтаксис, мы можем легко написать следующий код:
import Vue from 'vue'
import Router from 'vue-router'
let routeList = []
let importAll = require.context('@/publicResource/router', false, /\.js$/)
importAll.keys().map(path => {
// 因为 index.js 也在 @/publicResource/router 目录下,所以需要排除
if (!path.includes('index.js')) {
//兼容处理:.default 获取 ES6 规范暴露的内容; 后者获取 commonJS 规范暴露的内容
let router = importAll(path).default || importAll(path)
routeList(router)
}
})
export default new Router({
mode: 'history',
routes: routeList,
})
На самом деле он используется не только для импорта модулей маршрутизации, этот метод можно использовать для решения любого места в проекте, где необходимо импортировать большое количество локальных модулей.
Плагин модульной регистрации
верить писатьVueстудентов знаютelement-uiЭта библиотека компонентов, при использовании этой библиотеки компонентов, использует только некоторые отдельные компоненты, поэтому в основном необходимые компоненты импортируются по запросу, а затем есть следующие кучиVue.use()код:
// main.js
import Vue from 'vue'
import {
Input,
Radio,
RadioGroup,
Checkbox,
CheckboxGroup,
Select
// 还有很多组件
} from 'element-ui'
Vue.use(Input)
Vue.use(Radio)
Vue.use(RadioGroup)
Vue.use(Checkbox)
Vue.use(CheckboxGroup)
Vue.use(Select)
В таком написании нет ничего плохого, но это недостаточно лаконично и удобно на вид, более элегантный способ — извлечь эту логику в файл, а затем использовать их, зарегистрировав плагины:
// elementComponent.js
import {
Input,
Radio,
RadioGroup,
Checkbox,
CheckboxGroup,
Select
// 还有很多组件
} from 'element-ui'
const components = {
Input,
Radio,
RadioGroup,
Checkbox,
CheckboxGroup,
Select
}
function install(Vue){
Object.keys(components).forEach(key => Vue.use(components[key]))
}
export default { install }
затем вmain.jsИспользуйте этот плагин в:
// main.js
import Vue from 'vue'
import elementComponent from './config/elementComponent'
Vue.use(elementComponent)
Элегантный интерфейс запроса на экспорт
Я не знаю, как вы определяете интерфейс запроса.Что касается моего текущего проекта, это делается так:
// api.js
import http from './config/httpServer.js'
/* 登入页面获取公钥 */
export const getPublicKey = (data) => {
return http({ url: '/userGateway/user/getPublicKey' }, data)
}
// 用户登录
export const login = data => {
return http({ url: '/userGateway/userSentry/login' }, data)
}
// 验证码登录
export const loginByCode = data => {
return http({ url: '/userGateway/userSentry/loginByCode' }, data)
}
Используйте интерфейс в компоненте:
<script>
import { getPublicKey } from './config/api.js'
export default {
mounted() {
getPublicKey().then(res => {
// xxx
}).catch(err => {
// xxx
})
}
}
</script>
Это все нормально, но в нашем проекте всего интерфейсов более 200. Согласно приведенному выше методу определения, определение интерфейса плюс пустая строка должны занимать 5 строк, поэтому, если все интерфейсы определены для этогоapi.jsЭто занимает около 1000 строк, что действительно неприятно видеть. Так что я думаю, что это место должно быть оптимизировано.
/userGateway/user/getPublicKey
Выше это бэкэнд к интерфейсному пути, косая черта делит этот путь на 3 подстроки, и последняя подстрока должна быть уникальной, чтобы мы могли сделать из нее возню. Итак, есть следующий код:
// api.js
const apiList = [
'/userGateway/user/getPublicKey', // 登入页面获取公钥
'/userGateway/userSentry/login', // 用户登录
'/userGateway/userSentry/loginByCode', // 验证码登录
]
let apiName, API = {}
apiList.forEach(path => {
// 使用正则取到接口路径的最后一个子串,比如: getPublicKey
apiName = /(?<=\/)[^/]+$/.exec(path)[0]
API[apiName] = (data) => {
return http({url: path}, data)
}
})
export { API }
Это примерно сокращает 5 строк, необходимых для определения интерфейса, до 1 строки, что значительно уменьшает содержимое файла. Также мое колесо мыши не вращается при просмотре этого файла.
Если вы определяете интерфейс таким образом, вам необходимо внести некоторые изменения при его использовании:
<script>
import { API } from './config/api.js'
export default {
mounted() {
API.getPublicKey().then(res => {
// xxx
}).catch(err => {
// xxx
})
}
}
</script>
4 способа передать параметры в $event
В реальных проектах часто бывает необходимо передавать параметры через события.Вот четыре сценария применения.
для компонентной связи
Например, подкомпоненты проходят$emitПри вызове метода родительского компонента вы можете использовать его в родительском компоненте$eventПолучите параметры, переданные от дочернего компонента:
<!-- 子组件 -->
<button @click="$emit('changeText', '18px')">点击加大字号</button>
<!-- 父组件 -->
<blog-post @changeText="changeText('article', $event)"></blog-post>
changeText(type, value) {
console.log(type, value) // 'article' '18px'
}
Если есть несколько параметров, переданных дочерним компонентом, используйте это время$eventЭто не очень хорошо, вы можете использовать его в это времяargumentsзаменять:
<!-- 子组件 -->
<button @click="$emit('changeText', 'red', '18px')">点击改变样式</button>
<!-- 父组件 -->
<blog-post @changeText="changeText(...arguments, 'article')"></blog-post>
changeText(...value) {
console.log( value ) // ['red', '18px', 'article']
}
Передайте собственные объекты событий DOM
Например, если нам нужно получить текущий элемент клика, мы можем передать его в событие клика.$eventпараметр:
<button @click="submit('first', $event)">提交</button>
submit(type, event) {
const target = event.target.tagName
}
Используется для обратных вызовов событий сторонних библиотек классов.
Например, компонент использует несколькоelement-uiкомпонент пагинации, каждая пагинация имеетcurrent-changeСобытия используются для обработки вещей после изменения страницы.В этом случае нам нужно написать несколько функций обратного вызова, но если мы используем следующие методы, мы также можем написать только одну функцию черезtypeчтобы определить, какой это обратный вызов пейджинга, и$eventиспользуется для передачиcurrent-changeОбратный вызов с параметрами по умолчанию:
<!-- 页面列表的分页 -->
<el-pagination
@current-change="changePage('main', $event)">
</el-pagination>
<!-- 弹窗A列表的分页 -->
<el-pagination
@current-change="changePage('modalA', $event)">
</el-pagination>
<!-- 弹窗B列表的分页 -->
<el-pagination
@current-change="changePage('modalB', $event)">
</el-pagination>
changePage(type, page) {
const types = ['main', 'modalA', 'modalB']
types[type] && (this[types[type]].pageIndex = page) && this.getList(type)
}
Работа со стрелочными функциями
Для третьего сценария, при использовании сторонних компонентов библиотеки классов, необходимо добавить дополнительные параметры в обратный вызов события.Если параметр обратного вызова по умолчанию только один, то мы можем использовать вышеуказанный метод, но если параметров обратного вызова несколько, использовать$eventРазобраться с этим непросто, можно использовать стрелочные функции. Например, при загрузке файла естьon-changeатрибут, когда файл изменится, будет запущен обратный вызов.В обычных обстоятельствах мы можем написать это так:
<el-upload :on-change="changeFile">
<el-button>上传</el-button>
</el-upload>
changeFile(file, fileList) {}
Но если в компоненте есть несколько загрузок файлов, и мы не хотим писать несколькоchangeFile, то нужно передать дополнительные параметрыtype:
<el-upload :on-change="(file, fileList) => changeFile('org', file, fileList)">
<el-button>上传</el-button>
</el-upload>
changeFile(type, file, fileList) {}
3 варианта использования углубленных часов
выполнить немедленно
watchдаVueСлушатель в , который может прослушиватьVueДанные об экземпляре, когда данные изменяются, прослушиватель будет запущен. Таким образом, его сценарий применения: вы можете использовать его, когда вам нужно что-то сделать после изменения данных.watchЛа.
дляwatch, мы обычно записываем большинство оценок следующим образом:
watch: {
list: function(val) {
this.getMsg()
}
}
Если мы хотим выполнить его один раз при инициализации компонентаgetMsgметод, вы можете напрямуюmountedвызов:
mounted() {
this.getMsg()
}
На самом деле, есть более простой способ записи, даваяwatchнастраиватьimmediate: true, ты сможешь:
watch: {
list: {
handler(val) { // 注意别写错成 handle
this.getMsg()
},
immediate: true
}
}
Глубокое слушание
Слушатель будет автоматически вызван один раз после изменения свойства, но он ограничен самим свойством. Если свойство свойства изменено, обратный вызов слушателя не будет запущен. Если вы хотите реализовать эту функцию, вы можете датьwatchПлюс «Глубокий: Правда»:
watch: {
obj: {
handler(val) { // do something },
deep: true
}
},
mounted() {
this.obj.name = 'bubuzou' // 将触发 handler
}
несколько обработчиков
Фактически,watchМожет быть установлен в массив, поддерживаемые типыString,Objectа такжеFunction. При срабатывании будет вызвано несколько функций-обработчиков.
watch: {
obj: [
'print',
{
handler: 'print',
deep: true
},
function(val, oldValue) {
console.log(val)
}
]
},
methods: {
print() {
console.log(this.obj)
}
}
5 других советов по развитию
владелецVueСоветы по разработке действительно полезны в некоторых конкретных сценариях. Вот несколько часто используемых советов.
Функциональные компоненты реализуют переменные нулевого времени
Когда мы используем слоты, мы знаем, что есть слот, называемыйpropЗнание , сегодня мы объединяем его с функциональными компонентами для реализации компонента переменной с нулевым временем:
// tempvar.vue
<script>
export default {
functional: true,
render(h, context) {
const { props, scopedSlots} = context
return scopedSlots.default && scopedSlots.default(props || {})
}
}
</script>
Как только функциональный компонент определен, мы можем импортировать и использовать его везде, где он нам нужен:
<template>
<tempvar
:var1="`hello ${user.name}`"
:var2="user.age ? user.age : '18'">
<template v-slot="{var1, var2}">
姓名: {{ var1 }}
年龄:{{ var2 }}
</template>
</tempvar>
</template>
<script>
import tempvar from '@/components/tempvar.vue'
export default {
data() {
return {
user: {
name: 'bubuzou',
age: 12,
},
}
}
components: {
tempvar
}
}
</script>
Может быть, его нашел осторожный маленький друг, вам нужно добавить имя перед именемhello, возраст по умолчанию установлен на18Можно ли использовать вычисляемые свойства? Зачем делать это таким сложным и использовать функциональный компонент для его реализации? На самом деле эта маленькая хитрость все-таки нужна.Когда у многих компонентов такой пересчет данных, если этот трюк не использовать, то нужно писать много вычисляемых свойств, а с функциональными компонентамиtempvarПосле этого нужно просто внедрить его в компонент, а затем написать слот. Это эквивалентно затратам усилий на написание вычисляемых свойств на написание слотов. В целом, оба метода могут реализовывать схожие функции расчета атрибутов, какой из них вы выберете по своему вкусу.
Шаблон отладки (не рекомендуется)
При разработке и отладке часто необходимо пройтиconsole.logраспечатать объект данных, чтобы увидеть его внутреннюю структуру или значения полей, но это, конечно, не обязательно вtemplateЕго более прямо вывести. Например, есть такие данные:
obj: {
name: 'bubuzou',
age: 21,
}
Показать в шаблоне:
<p>{{ obj }}</p>
Когда страница отобразится, вы увидите:
{ "name": "bubuzou", "age": 21 }
С такими результатами рендеринга проблем нет, но если этоobjЭто данные с глубокими слоями и множеством полей, при отображении куча данных будет свалена в кучу, а опыта чтения нет вообще.
Поэтому, исходя из этого фона, мы можемconsole.logПрикреплено кVueна прототипе экземпляра:
// main.js
Vue.prototype.$log = window.console.log
Затем вы можете с радостью использовать его в шаблоне:
<p>{{ $log( obj ) }}</p>
Это выведет текущий объект данных в консоль браузера, и эффект отображения будет таким же, какconsole.logПрямая печать ничем не отличается.
Но, сказав все это, используйтеVueДля разработки и отладки настоятельно рекомендуется официальныйvue-devtoolsИнструмент, кто знает, кто им пользуется.
Функция ловушки, которая слушает дочерние компоненты
Обычно, если мы хотим уведомить родительский компонент, когда срабатывает функция ловушки дочернего компонента, мы можем сделать это:
<!-- parent.vue -->
<child @mounted="doSomething"></child>
// child.vue
this.$emit('mounted')
На самом деле, есть более простой способ записи, то есть использованиеhookEvent:
<child @hook:mounted="doSomething"></child>
В дополнение к описанному выше использованию функция ловушки также может выполнять некоторые другие действия посредством динамической регистрации, например, освобождать ресурсы перед уничтожением компонента:
mounted() {
let setIntervalId = setInterval(() => {
console.log(888);
}, 1000)
this.$once("hook:beforeDestroy", () => {
clearInterval(setIntervalId)
setIntervalId = null
})
}
Разделение параметров маршрутизации
Разделение параметров, что это значит? Не волнуйтесь, давайте посмотрим, например, на такую строку маршрутов:
const router = [{
path: '/home/:type/:id',
name: 'Home',
component: Home,
}]
Путь к текущей страницеhttp://xxx/detail/preview/21?sex=male, обычно, когда мы пишем код, мы пишем такой код более или менее и используем его в компоненте$routeПередать параметры компоненту:
mounted() {
if (this.$route.params.type === 'preview') {
this.isPreview = true
} else {
this.isPreview = false
}
this.id = this.$route.params.id
this.sex = this.$route.query.sex
}
Нет ничего плохого в том, чтобы писать таким образом, но это сделает компонент и маршрутизацию сильно связанными, так что компонент может быть толькоURLиспользуется на странице, что ограничивает универсальность компонента. На самом деле, мы можемpropsПередайте параметры, чтобы отделить параметры маршрутизации, и измените приведенную выше конфигурацию маршрутизации на следующую:
const router = [{
path: '/home/:type/:id',
name: 'Home',
component: Home,
props: (route) => ({
type: route.params.type,
id: route.params.id,
sex: route.query.sex,
})
}]
затем в компонентеpropsДобавьте параметры:
props: ['type', 'id', 'sex']
При использовании параметров в компонентах вам не нужно использоватьthis.$route, а можно напрямуюthis.typeВот и все. Таким образом, компонент можно использовать где угодно.
Селектор действия глубины
когда даноstyleплюсscoped, когда страница будет отображена, она дастhtmlа такжеcssСелектор плюс хеш-значение используются для уникальности:
<div class="home" data-v-fae5bece>
<button data-v-fae5bece class="el-button el-button-primary">提交</button>
</div>
.home .el-button[data-v-fae5bece] {
font-size: 20px;
}
для вstyleбыл добавлен вscopedкомпонент, его стили будут работать только внутри компонента и не повлияют на его подкомпоненты. Например, есть такой компонент:
<!-- 父组件 -->
<div class="home">
<el-button type="primary">父按钮</button>
<child></child>
</div>
<style lang="scss" scoped>
.home .el-button {
font-size: 20px;
}
</style>
<!-- 子组件 -->
<div class="child">
<el-button type="primary">子按钮</button>
</div>
Когда страница отобразится, она будет выглядеть так:
<div class="home" data-v-fae5bece>
<button data-v-fae5bece class="el-button el-button-primary">父按钮</button>
<div class="child" data-v-fae5bece>
<button class="el-button el-button-primary">子按钮</button>
</div>
</div>
Согласно вышеизложенномуhtml,Мы видим, что.home .el-button[data-v-fae5bece]Этот селектор не работает с дочерними кнопкамиbutton.
В реальных проектах нам иногда нужно заставить стиль родительского компонента работать с дочерним компонентом, даже если стиль родительского компонентаstyleдобавленscoped, то нужно использовать селектор глубины>>>, например, в только что приведенном примере вы можете добавить селектор глубокого действия к стилю родительского компонента.
Селектор глубины будет
Vue Loaderпроцесс и может использоваться только там, где есть препроцессор. Из-за некоторых препроцессоров, таких какSassне может быть правильно разобран>>>, поэтому мы можем использовать его псевдоним:/deep/или::v-deepзаменить.
<style lang="scss" scoped>
.home {
/deep/ .el-button {
font-size: 20px;
}
}
</style>
После добавления селектора глубины действия селектор будет изменен с исходного:
.home .el-button[data-v-fae5bece] {}
становится следующим:
.home[data-v-fae5bece] .el-button {}
Справочная статья
- Шина событий (EventBus) в vue
- "Практика разработки Vue"
Спасибо за прочтение
Если эта статья была вам полезна, поставьте лайк этой статье Не можешь посмотреть? Вот несколько предыдущих статей:
- Vue-Test-Utils + Jest Unit Test Введение и практика
- Типы данных JS, необходимые для младших и средних фронтенд-интервью (подробная серия)
- Небольшое обновление программы Степпинг WEPY2