// Только для примера, в жизни для этой задачи использовать .map
[1, 2, 3].reduce((acc, el) => [...acc, el ** 2], [])
const o = {
importantField: “value”,
some: true,
random: false,
stuff: null
}
const { importantField, ...someRandomStuff } = o
// Не совсем тоже самое, но спред используется и здесь
function variadicFn(singleArg, ...arrayWithTheRestOfArgs) {}
const condition = true
const p = { ...condition && { text: “Contion is truthful” } }
<body>
<div id="app">
<p> {{ calculationResult | rub }}</p>
<!-- Плагин range slider инициализируется через директиву
В кавычка объект из binding.value из сигнатуры хука -->
<div v-range-slider="{ min: 0, max: 10 }"></div>
<!-- Подписка на события осуществляется прямо в html -->
<button @click="sendToChat">Отправить в чат</button>
<button @click="toggleChat">Открыть чат</button>
<button @click="toggleChat">Закрыть чат</button>
<!-- Чат изолирован от остальной разметки в отдельный компонент -->
<chat-app v-show="isChatOpen" :result="calculationResult" />
</div>
</body>
Vue.component('chat-app', {
// Шаблон это HTML + суперспособности vue, например ref
template: '<div ref="chat"></div>',
// Аргумненты компонента, позволяет делать
// - аннотации типов
// - валидация
// - дефолтные значения
props: {
result: { type: Number }
},
mounted() {
// Здесь нет смысла использовать директиву, инициализируем чат когда компонент окажется в ДОМе
// Вместо селекта элемента по например id, мы напрямую получаем из ранее созданной рефки
this.chatInstance = new SomeChatPlugin(this.$refs.chat)
},
watch: {
// При изменении пропа result, в текстовое поле чата будет добавлен текст
result(newValue) {
this.chatInstance.fillText(newValue)
}
}
})
new Vue({
el: '#app',
filters: {
// Фильтры это особый способ вызова функции
// rub(value) === value | rub
rub(v) {
const value = Math.round(v)
switch(value) {
case 1: return `${value} рубль`
case 2:
case 3:
case 4: return `${value} рубля`
default: return `${value} рублей`
}
}
},
directives: {
rangeSlider: {
// Директивы имеют свой жизненный цикл
// Хук inserted это ближайшее к $(document).ready() событие
/**
* @param {HTMLElement} el
* @param {Object} binding https://ru.vuejs.org/v2/guide/custom-directive.html#Аргументы-хуков
* @param {Vue} context vue instance
*/
inserted(el, binding, context) {
$(el).someFancyRangeSlide(binding.value)
.on('move', newValue => context.updateValue(newValue))
// События выпускаемые jquery будут обработаны vue
}
}
},
data() {
return {
value: 0,
calculationResult: 0,
isChatOpen: false,
}
},
methods: {
updateValue(newValue) {
this.value = newValue
},
thoggleChat() {
this.isChatOpen = !this.isChatOpen
},
sendToChat() {
this.calculationResult = this.value
}
}
})
<template>
<form>
<input v-model="text" />
<button :disabled="!isValid">Submit</button>
</form>
</template>
<script>
export default {
data() {
return {
text: ''
}
},
computed: {
isValid() {
return validator(this.text)
}
}
}
</script>
v-if
не отрендерит разметку никогда, если её биндинг не вернёт true
.v-show
отрендерит разметку, но в зависимости от своего биндинга установит display: hidden
на элемент.export default {
props: {
showModal:{ type: Boolean, default: false }
}
}
<template>
<store-modal-component :show-modal="showModal" />
</template>
selected
возвращает ложь, элемент удаляется из древа DOM и, следовательно, перестаёт работать jquery плагин инициализированный на нём. Чтобы это избежать, необходимо научиться понимать жизненный цикл Vue компонента. К тому же следует выделять код работающий с DOM но не являющийся неотъемлемой частью компонента в директивы. Например (для однофайловых компонентов):<template>
<div v-fancy-plugin="{ argumentObjectKey: 'value' }"></div>
</template>
<script>
import $ from 'jquery'
import fancyPlugin from 'fancy-plugin'
export default {
directives: {
fancyPlugin: {
inserted (element, argumentObject) {
$(element).fancyPluginInit(argumentObject)
}
}
}
</script>
// parent.vue
<template>
<parent>
<child @toggle-button="toggleButtonHandler"/>
</parend>
</template>
<script>
export default {
data: {
return {
buttonShow: false
}
},
methods: {
toggleButtonHandler () {
this.buttonShow = !this.buttonShow
}
}
}
</script>
// child.vue
<template>
<button @click="clickHandler">Hide Button</button>
</template>
<script>
export default {
methods: {
clickHandler () {
this.$emit('toggle-button')
}
}
}
</script>
$emit
принимает произвольные данные в качестве пейлоада события (второй аргумент обработчика).// store.js
export default {
state: {
buttonShow: false
},
mutations: {
buttonToggle: state => (state.buttonShow = !state.buttonShow)
}
}
clickHandler () {
this.$store.commit('buttonToggle')
}
@toggle-button
и больше не держит собственный стейт, а подписывается на стор:computed: {
buttonShow () {
return this.$store.state.buttonShow
}
}
const store = {
state: {
lockingPool: 0
},
getters: {
isUiLocked: state => state.lockingPool > 0
},
mutations: {
lockUi: state => state.lockingPool++,
unlockUi: state => state.lockingPool--
},
actions: {
async someAction ({ commit }) {
commit('lockUi')
const { data } = await http.get('/some-url')
commit('unlockUi')
}
}
}
<template>
<form>
<p v-if='errorMessage'>{{ errorMessage }}</p>
<input type='text' v-model='username'/>
<input type='password' v-model='password'/>
<button @click='send()'>Log in</button>
</form>
</template>
<script>
import someHttpClient from 'someHttpClient'
export default {
data: {
return {
username: '',
password: '',
errorMessage: ''
}
},
methods: {
send () {
var username, password, errorMessage = this
if (username && password) {
errorMessage = ''
someHttpClient.sendAndMaybeCipher({ username, password })
} else {
errorMessage = 'Form not filled properly'
}
}
}
}
</script>
export default {
// mounted неплохой вариант, если document должен быть ready
mounted () {
const $ = window.$
$('.main').onepage_scroll()
}
}
export default {
directives: {
// Произвольное имя директивы
onePageScroll: {
// На этом хуке компонент со всеми дочерними компонентами готов
componentUpdated (el) {
const $ = window.$
// Елемент не нужно «селектить» по классу/ИД т.к. он передан в аргументы директивы
$(el).onepage_scroll()
}
}
}
}
<div>
<section v-one-page-scroll />
</div>