Введение
В этой статье мы рассмотрим основы Vue.js - прогрессивного фреймворка для создания пользовательских интерфейсов. Вы узнаете о ключевых концепциях, компонентном подходе и современных практиках разработки на Vue.js.
Что такое Vue.js?
Основные концепции
Особенности фреймворка
- Реактивность данных
- Компонентная архитектура
- Двустороннее связывание
- Директивы
Преимущества использования
- Простота изучения
- Гибкость
- Производительность
- Экосистема
Начало работы
1. Установка и настройка
Vue CLI
1
2
3
4
| npm install -g @vue/cli
vue create my-app
cd my-app
npm run serve
|
Структура проекта
1
2
3
4
5
6
7
8
9
10
11
12
| my-app/
├── node_modules/
├── public/
│ └── index.html
├── src/
│ ├── assets/
│ ├── components/
│ ├── views/
│ ├── App.vue
│ └── main.js
├── package.json
└── README.md
|
2. Основы синтаксиса
Шаблоны Vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| <template>
<div class="greeting">
<h1></h1>
<p v-if="isVisible">Это условный текст</p>
<button @click="handleClick">Нажми меня</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Привет, Vue!',
isVisible: true
}
},
methods: {
handleClick() {
this.isVisible = !this.isVisible
}
}
}
</script>
|
Компоненты
1. Создание компонентов
Однофайловые компоненты
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| <!-- Button.vue -->
<template>
<button @click="$emit('click')" :class="buttonClass">
<slot></slot>
</button>
</template>
<script>
export default {
name: 'Button',
props: {
variant: {
type: String,
default: 'primary'
}
},
computed: {
buttonClass() {
return `btn btn-${this.variant}`
}
}
}
</script>
<style scoped>
.btn {
padding: 8px 16px;
border-radius: 4px;
}
</style>
|
Использование компонентов
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| <template>
<div>
<Button variant="primary" @click="handleClick">
Нажми меня
</Button>
</div>
</template>
<script>
import Button from './Button.vue'
export default {
components: {
Button
},
methods: {
handleClick() {
console.log('Кнопка нажата')
}
}
}
</script>
|
2. Жизненный цикл
Хуки жизненного цикла
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| <script>
export default {
beforeCreate() {
console.log('Перед созданием')
},
created() {
console.log('Создан')
},
beforeMount() {
console.log('Перед монтированием')
},
mounted() {
console.log('Смонтирован')
},
beforeUpdate() {
console.log('Перед обновлением')
},
updated() {
console.log('Обновлен')
},
beforeDestroy() {
console.log('Перед уничтожением')
},
destroyed() {
console.log('Уничтожен')
}
}
</script>
|
Реактивность
1. Управление состоянием
Composition API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| <template>
<div>
<p>Счетчик: 9</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script>
import { ref, computed } from 'vue'
export default {
setup() {
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
}
function decrement() {
count.value--
}
return {
count,
doubled,
increment,
decrement
}
}
}
</script>
|
Options API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| <template>
<div>
<p></p>
<input v-model="firstName">
<input v-model="lastName">
</div>
</template>
<script>
export default {
data() {
return {
firstName: '',
lastName: ''
}
},
computed: {
fullName() {
return `${this.firstName} ${this.lastName}`
}
},
watch: {
fullName(newValue) {
console.log('Полное имя изменилось:', newValue)
}
}
}
</script>
|
Директивы
1. Встроенные директивы
Условный рендеринг
1
2
3
4
5
6
7
8
9
| <template>
<div>
<p v-if="type === 'A'">A</p>
<p v-else-if="type === 'B'">B</p>
<p v-else>C</p>
<p v-show="isVisible">Показать/Скрыть</p>
</div>
</template>
|
Циклы
1
2
3
4
5
6
7
| <template>
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ index + 1 }}. {{ item.name }}
</li>
</ul>
</template>
|
2. Пользовательские директивы
Создание директивы
1
2
3
4
5
6
7
8
9
10
11
| // main.js
Vue.directive('focus', {
mounted(el) {
el.focus()
}
})
// Использование в компоненте
<template>
<input v-focus>
</template>
|
Маршрутизация
1. Vue Router
Настройка маршрутов
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import('./views/About.vue')
},
{
path: '/user/:id',
name: 'User',
component: User,
props: true
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
|
Навигация
1
2
3
4
5
6
7
8
| <template>
<nav>
<router-link to="/">Главная</router-link>
<router-link :to="{ name: 'About' }">О нас</router-link>
</nav>
<router-view></router-view>
</template>
|
Управление состоянием
1. Vuex
Создание хранилища
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| import { createStore } from 'vuex'
export default createStore({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
},
getters: {
doubleCount: state => state.count * 2
}
})
|
Использование в компонентах
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| <template>
<div>
<p>9</p>
<p></p>
<button @click="increment">+</button>
<button @click="incrementAsync">+ Async</button>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
computed: {
...mapState(['count']),
...mapGetters(['doubleCount'])
},
methods: {
...mapMutations(['increment']),
...mapActions(['incrementAsync'])
}
}
</script>
|
Оптимизация
1. Производительность
Ленивая загрузка
1
2
3
4
5
6
7
| const AdminPanel = () => import('./components/AdminPanel.vue')
export default {
components: {
AdminPanel
}
}
|
Keep-alive
1
2
3
4
5
| <template>
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
</template>
|
2. Сборка
Vue CLI конфигурация
1
2
3
4
5
6
7
8
9
| // vue.config.js
module.exports = {
productionSourceMap: false,
chainWebpack: config => {
config.optimization.splitChunks({
chunks: 'all'
})
}
}
|
Тестирование
1. Unit тесты
Jest и Vue Test Utils
1
2
3
4
5
6
7
8
9
10
11
| import { mount } from '@vue/test-utils'
import Counter from '@/components/Counter.vue'
describe('Counter', () => {
test('increments count when button is clicked', async () => {
const wrapper = mount(Counter)
const button = wrapper.find('button')
await button.trigger('click')
expect(wrapper.vm.count).toBe(1)
})
})
|
Заключение
Vue.js - это мощный и гибкий фреймворк для создания современных веб-приложений. Начните с основ и постепенно изучайте более сложные концепции. Практика и работа над реальными проектами помогут вам стать опытным Vue-разработчиком.
Полезные ресурсы