Пост

Vue.js для начинающих: основы разработки современных веб-приложений

Vue.js для начинающих: основы разработки современных веб-приложений

Введение

В этой статье мы рассмотрим основы 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-разработчиком.

Полезные ресурсы

Авторский пост защищен лицензией CC BY 4.0 .

© Oleg Dobrynin. Некоторые права защищены.