Vue.js основы для новичка

Jan 8, 2019

Нюансы

Внутри элемента template должен быть один корневой элемент.

Подключаем Vue.js

<script src="https://unpkg.com/vue"></script>

Инициализируем Vue.js

Инициализируем приложение Vue.js:

<div id="app"> .... </div">
new Vue({
    // указываем id элемента, куда будет вставлено Vue приложение,
    el: '#app',

    //...
});

// or

import Vue from 'vue'
import App from './app-complete/App.vue'

new Vue({
    el: '#app',
    render: h => h(App)
})

Интерполяция

{{ title }}

Свойства типа computed

Свойства типа computed (вычисляемые свойства) это такие свойства, которые должны вычисляться, например, сортировка постов в списке. То есть это те свойства, которые не хранятся в объекте данных, а высчитываются при рендеринге шаблона.

Такие свойства выглядят как обычные свойства, но работают как методы (что-то наподобие get):

new Vue({
    // указываем id элемента, куда будет вставлено Vue приложение,
    el: '#app',
    // добавляем данные в экземпляр Vue:
    data: {
        // по сути (submissions) это название переменой, которое мы будем использовать в шаблоне
        submissions: Seed.submissions
    },


    // вычисляемые свойства
    computed: {
        sortedSubmissions() {
            // this в данном случае ссылается на data
            return this.submissions.sort((a, b) => b.votes - a.votes);
        }
    }
});
    <article  v-for="submission in sortedSubmissions" v-bind:key="submission.id">
        <submission-component
                :submission="submission"
                :submissions="sortedSubmissions"
        ></submission-component>
    </article>

Директивы

v-bind

v-bind - используется для привязки данных. v-bind можно заменить на :.

Например, привяжем данные к атрибуту src:

<img
     v-bind:src="submissions[0].submissionImage">

v-bind:class

Ставим класс по условию:

<article class="media " v-bind:class="{ 'blue-border' : submission.votes >= 20 }"

v-for

v-for - перебирает элементы массива.

<article class="media" v-for="submission in submissions">
    <a href="#" class="has-text-info">{{ submission.title }}</a>

Обработка событий

Для обработки событий во Vue также используется директива - v-on. v-on можно заменить на @название_события: v-on:click === @click.

<span class="icon is-small" v-on:click="upvote(submission.id)">
    up
</span>
methods: {
    upvote(id) {
        // this в данном случае ссылается на data
        let submission = this.submissions.find((submission) =< {
            return submission.id === id;
        });
        submission.votes += 1;
    }
}

Компоненты

Регистрируем компонент

// 1-й - нерекомендуемый способ
// регистрируем компонент под именем submission-component
// 2-й параметр - объект с настройками компонента
Vue.components('submission-component', {

});

Любой компонент во Vue является экземпляром класса Vue.

Рекомендуемый способ регистрации компонента:

const submissionComponent = {
    template: `<div>submissionComponent</div>`
};
// но желательно писать код компонента в отдельном файле


new Vue({
    // указываем id элемента, куда будет вставлено Vue приложение,
    el: '#app',
    // добавляем данные в экземпляр Vue:
    data: {
        // по сути (submissions) это название переменой, которое мы будем использовать в шаблоне
        submissions: Seed.submissions
    },

    // регистрируем компонент во Vue приложении
    components: {
        // 'название тега' : название const компонента
        'submission-component' : submissionComponent
    },

Отображаем в шаблоне

<div>
    <submission-component></submission-component>
</div>

Входные параметры / props

Для этих целей существуют так называемый props - они позволяют передать данные дочернему компоненту от родительского компонента.

// родительский компонент (передаем значение из submission)
<submission-component
        v-bind:submission="submission"
></submission-component>

    <!-- имя переменной submission зависит от того,
        какое имя вы поставите в дочернем компоненте

        v-bind:submission="submission"
        можно заменить на :submission="submission"
    -->
// дочерний компонент
const submissionComponent = {
    template: `
        <div>
            {{ submission.id }}
        </div>
    `,
    // указываем входящие свойства (submission):
    // по сути объявляем переменную, которая будет доступна в шаблоне
    props: [
        'submission'
    ]
};

Обратите внимание на camelCase синтаксис для props:

    <note-count-component :note-count="noteCount"></note-count-component>
const noteCountComponent = {
    template: `<div class="note-count"
    >Всего заметок: {{noteCount}}</div>
    `,
    props: ['noteCount']
}

Подход к созданию компонента - single file components

Рассмотрим подход single file components. Этот подход подразумевает, что компонент состоит из одного файла и содержит: css, html, js (ES6).

Как передать данные приложению?

Данные от одного компонента можно передавать посредством props'ов.

Но есть такое понятие как - Управление состоянием (данными) приложения. Для Управление состоянием (данными) приложения используются специальные библиотеки, например, в react это Redux, а во Vue.js - Vuex.

Для упрощенной разработки можно использовать свой подход, например, можно созлать файл store.js:

import { seedData } from './seed';

export const store = {
    state: {
        seedData
    }
}

В файле store.js также пишется вся логика по взаимодействию с хранилищем (такой же подход у vuex):

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

export default {
        name: 'CalendarWeek',
        components: {
            CalendarDay
        },

        data() {
            return {
                state: store.state
            }
        }

    // в случае

    data: {
        state: store.state
    }
    // возникнет ошибка:
    /*
    The "data" option should be a function that returns a per-instance value in component definitions.

    Property or method "state" is not defined on the instance but referenced during render.
    Make sure that this property is reactive,
    either in the data option, or for class-based components, by initializing the property. See:
    */
const inputComponent = {
    template: `<input class="input is-small" type="text"
                      :placeholder="placeholder"
                      v-model="input"/>`,
    props: ['placeholder'],
    // указываем метод, так как нам нужно уникальное состояние для компонента
    // ибо если мы укажем объект, то у компонентов будет одинаковый state, а нам нужно
    // чтобы state был разный
    data() {
        return {
            input: ''
        }
    }

}

Формы

v-model

v-model - позволяет привязаться к значению поля (из свойства data компонента).

<input type="text"
       v-model="inputEntry"
       placeholder="New Event"/>
export default {
        name: 'CalendarEntry',

        data() {
            return {
                inputEntry: ''
            }
        },

События

<div class="day column" @click="setActiveDay(day.id)">
import { store } from '../store';
import CalendarEvent from './CalendarEvent.vue';

export default {
    name: 'CalendarDay',

    components: {
        CalendarEvent
    },

    props: [
        'day'
    ],

    methods: {
        setActiveDay(dayId) {
            store.setActiveDay(dayId);
        }
    }
}

События кастомные и DOM события (Обзор)

// DOM Event
const button = document.createElement('button');
button.addEventListener('click', event => {});


// Custom Event:

    // навешиваем событие
button.addEventListener('toggle', event => {
    console.log(event.detail);
});

    // регестрируем событие
const event = new CustomEvent('toggle', {
    detail: {
        value: false
    }
});

    // генерируем событие
button.dispatchEvent(event);

События во Vue.js

Во Vue.js используется система события близкая к Custom Event.

Данные (события) от родителя ребенку передаются через props. От ребенка родителю данные поступают посредством событий.

Пример использования событий

<div id="app">
    <div class="notes-section">
        <div class="columns">
            <div class="column has-text-centered">
                <strong>Notes</strong>
                <div v-for="note in notes" class="notes">
                    {{ note }}
                </div>
            </div>
            <div class="column has-text-centered">
                <strong>Timestamp</strong>
                <div v-for="timestamp in timestamps" class="timestamps">
                    {{ timestamp }}
                </div>
            </div>
        </div>

        <!-- add-note - событие компонента input-component,
                его значение (addNote) это метод у основного компонента;
                то есть таким образом мы подписываемся на событие из дочернего
                компонента -->
        <input-component
                @add-note="addNote"
                :placeholder="placeholder"></input-component>


    </div>
</div>
// дочерний компонент
const inputComponent = {
    template: `<input class="input is-small" type="text"
                      :placeholder="placeholder"
                      v-model="input"

                      v-on:keyup="handleKeyUp($event)"
                />

                <!-- OR  @keyup.enter="handleKeyUp" -->

                `,
    props: ['placeholder'],
    // указываем метод, так как нам нужно уникальное состояние для компонента
    // ибо если мы укажем объект, то у компонентов будет одинаковый state, а нам нужно
    // чтобы state был разный
    data() {
        return {
            input: ''
        }
    },
    methods: {
        handleKeyUp(event) {
            if (event.keyCode === 13) {
                // on enter
                console.log('event: ', event.target.value);

                // для того чтобы генерировать событие у vue есть метод $emit
                // метод $emit первым параметром принимает -  название события
                // второй - дополнитльные данные о событии
                this.$emit('add-note', {
                    note: event.target.value,
                    timestamp: new Date().toLocaleString()
                });

                this.input = '';
            }
        }
    }

}

// родительский компонент
new Vue({
    el: '#app',
    components: {
        'input-component': inputComponent
    },
    data: {
        notes: [],
        timestamps: [],
        placeholder: 'Имя заметки'

    },
    methods: {
        // срабатывает, когда компонент-ребенок эмитит событие add-note
        addNote(event) {
            console.log('event: ', event)
            this.notes.push(event.note);
            this.timestamps.push(event.timestamp);
        }
    }
})

Глобальная среда для событий (агрегатор событий)

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

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

Допустим у нас есть два дочерних компонента.

// создадим агрегатор событий;
// данный компонент потребуется лищь для того чтобы подписывать и агрегировать события
const EventBus = new Vue();
window.EventBus = EventBus;
// Так как EventBus это экземпляр от Vue, то у него есть:

// $emit - для генерации событий
// EventBus.$emit();

// $on - для подписки на событие
// EventBus.$on();

Дочерний компонент эмитит событие:

// дочерний компонент
const inputComponent = {
    template: `<input class="input is-small" type="text"
                      :placeholder="placeholder"
                      v-model="input"
                      v-on:keyup="handleKeyUp($event)"
                />`

    data() {
        return {
            input: ''
        }
    },
    methods: {
        handleKeyUp(event) {
            if (event.keyCode === 13) {
                // код работает через агрегатор событий
                EventBus.$emit('add-note', {
                    note: event.target.value,
                    timestamp: new Date().toLocaleString()
                });
                this.input = '';
            }
        }
    }

}

Другой компонент подписывается на событие посредством общего агрегатора событий:

new Vue({
    el: '#app',
    components: {
        'input-component': inputComponent,
        'note-count-component': noteCountComponent
    },
    data: {
        notes: [],
        timestamps: [],
        placeholder: 'Имя заметки'

    },
    // метод жизненного цикла
    created() {
        // подписываемся на событие 'add-note'
        EventBus.$on('add-note', event => {
            this.addNote(event);
        })
    },
    computed: {
        noteCount() {
            return this.notes.length;
        }
    },
    methods: {
        addNote(event) {
            console.log('event: ', event)
            this.notes.push(event.note);
            this.timestamps.push(event.timestamp);
        }
    }
})
Добавить комментарий