Биз жөнүндөБлогБайланыш
Frontend иштеп чыгуу2016-ж., 15-ноябрь 4 мин 118Жаңыланды: 2026-ж., 22-июнь

Vue.js менен биринчи долбоор: Бишкектеги тажрыйба (2016)

AunimedaAunimeda
📋 Мазмуну

Кыскача: Vue.js 2.0 орнотуу жөнөкөй: <script src="vue.min.js"> же npm install vue. Компоненттер Vue.component() аркылуу же .vue файлдары менен. Реактивдик маалымат: data() объектинен. Vuex - глобал state үчүн. React'ка салыштырмалуу 30% аз код.


Эмне үчүн Vue.js тандадык (2016)

Биздин командада 2016-жылы:

  • React жана Redux - татаал, жаңы башталган разработчикке кыйын
  • Angular 2 beta - нестабилдүү
  • Vue.js 1.x - жөнөкөй, бирок API көп өзгөрдү
  • Vue.js 2.0 (октябрь 2016) - туруктуу, React'тин иштеп чыгуу тезди, жеңил үйрөнүлгөн

Команданын 2 junior разработчики Vue.js'ти 1 апта ичинде үйрөнүштү. React: 3-4 апта.


Орнотуу

# npm аркылуу
npm install vue
npm install vue-cli -g
vue init webpack-simple my-vue-app
cd my-vue-app && npm install && npm run dev

# Же CDN аркылуу (быстрый прототип)
# <script src="https://unpkg.com/vue@2.0.5/dist/vue.min.js"></script>

Биринчи компонент: себет карточкасы

<!-- CartItem.vue - Vue Single File Component -->
<template>
  <div class="cart-item">
    <img :src="item.image" :alt="item.name" class="cart-item__image">
    
    <div class="cart-item__info">
      <h3 class="cart-item__name">{{ item.name }}</h3>
      <p class="cart-item__price">{{ formatPrice(item.price) }}</p>
    </div>
    
    <div class="cart-item__quantity">
      <button @click="decrease" :disabled="item.qty <= 1">−</button>
      <span>{{ item.qty }}</span>
      <button @click="increase">+</button>
    </div>
    
    <p class="cart-item__total">{{ formatPrice(item.price * item.qty) }}</p>
    
    <button class="cart-item__remove" @click="remove">✕</button>
  </div>
</template>

<script>
export default {
  name: 'CartItem',
  
  props: {
    item: {
      type: Object,
      required: true
    }
  },
  
  methods: {
    increase() {
      this.$emit('update-qty', this.item.id, this.item.qty + 1);
    },
    decrease() {
      if (this.item.qty > 1) {
        this.$emit('update-qty', this.item.id, this.item.qty - 1);
      }
    },
    remove() {
      this.$emit('remove', this.item.id);
    },
    formatPrice(price) {
      return new Intl.NumberFormat('ru-KG').format(price) + ' сом';
    }
  }
}
</script>

<style scoped>
.cart-item {
  display: flex;
  align-items: center;
  padding: 12px;
  border-bottom: 1px solid #eee;
}
.cart-item__image {
  width: 60px;
  height: 60px;
  object-fit: cover;
  margin-right: 12px;
}
</style>

Себет компоненти

<!-- Cart.vue - Себет (корзина) компоненти -->
<template>
  <div class="cart">
    <h2>Себет ({{ itemCount }} товар)</h2>
    
    <div v-if="items.length === 0" class="cart--empty">
      Себет бош. <router-link to="/catalog">Каталогго өтүңүз</router-link>
    </div>
    
    <template v-else>
      <cart-item
        v-for="item in items"
        :key="item.id"
        :item="item"
        @update-qty="updateQuantity"
        @remove="removeItem"
      />
      
      <div class="cart__summary">
        <div class="cart__total">
          Жалпы: <strong>{{ formatPrice(total) }}</strong>
        </div>
        
        <div class="cart__delivery">
          Жеткизүү: 
          <span v-if="total >= 3000" class="free">Бекер</span>
          <span v-else>{{ formatPrice(deliveryCost) }}</span>
        </div>
        
        <div class="cart__grand-total">
          Баардыгы: <strong>{{ formatPrice(grandTotal) }}</strong>
        </div>
        
        <button 
          class="checkout-btn"
          @click="checkout"
          :disabled="loading"
        >
          {{ loading ? 'Иштеп жатат...' : 'Заказ берүү' }}
        </button>
      </div>
    </template>
  </div>
</template>

<script>
import CartItem from './CartItem.vue';
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';

export default {
  name: 'Cart',
  components: { CartItem },
  
  data() {
    return {
      loading: false,
      deliveryCost: 200,  // Бишкек боюнча жеткизүү
    };
  },
  
  computed: {
    ...mapState('cart', ['items']),
    
    itemCount() {
      return this.items.reduce((sum, item) => sum + item.qty, 0);
    },
    
    total() {
      return this.items.reduce((sum, item) => sum + item.price * item.qty, 0);
    },
    
    grandTotal() {
      return this.total + (this.total >= 3000 ? 0 : this.deliveryCost);
    },
  },
  
  methods: {
    ...mapMutations('cart', ['removeItem', 'updateQuantity']),
    ...mapActions('cart', ['submitOrder']),
    
    async checkout() {
      this.loading = true;
      try {
        const order = await this.submitOrder();
        this.$router.push('/order/' + order.id + '/success');
      } catch (error) {
        alert('Ката кетти: ' + error.message);
      } finally {
        this.loading = false;
      }
    },
    
    formatPrice(price) {
      return new Intl.NumberFormat('ru-KG').format(price) + ' сом';
    }
  }
}
</script>

Vuex Store: себет статусу

// store/modules/cart.js

import api from '../../services/api';

const state = {
    items: JSON.parse(localStorage.getItem('cart') || '[]'),
};

const mutations = {
    addItem(state, product) {
        const existing = state.items.find(i => i.id === product.id);
        if (existing) {
            existing.qty++;
        } else {
            state.items.push({ ...product, qty: 1 });
        }
        localStorage.setItem('cart', JSON.stringify(state.items));
    },
    
    removeItem(state, productId) {
        state.items = state.items.filter(i => i.id !== productId);
        localStorage.setItem('cart', JSON.stringify(state.items));
    },
    
    updateQuantity(state, { productId, qty }) {
        const item = state.items.find(i => i.id === productId);
        if (item) {
            item.qty = qty;
            localStorage.setItem('cart', JSON.stringify(state.items));
        }
    },
    
    clearCart(state) {
        state.items = [];
        localStorage.removeItem('cart');
    },
};

const actions = {
    async submitOrder({ state, commit, rootState }) {
        const response = await api.post('/orders', {
            items: state.items.map(item => ({
                product_id: item.id,
                qty:        item.qty,
                price:      item.price,
            })),
            // Колдонуучунун дареги profile'дан
            address: rootState.user.profile?.address,
        });
        
        commit('clearCart');
        return response.data.order;
    },
};

export default { namespaced: true, state, mutations, actions };

main.js - Vue колдонмо башталышы

// main.js
import Vue    from 'vue';
import App    from './App.vue';
import router from './router';
import store  from './store';

Vue.config.productionTip = false;

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

React менен салыштыруу (2016 Бишкек тажрыйбасы)

Vue.js 2.0 React + Redux
Код сабы (CartItem) 45 сап 65 сап
Үйрөнүү убактысы (junior) 1 апта 3-4 апта
HTML шаблон .vue файлда JSX
Реактивдик маалымат Автомат (data()) setState() кол менен
Глобал state Vuex Redux
Экосистема Кичинерек Чоңураак

Биздин кичине команда үчүн Vue.js туура тандоо болду. React экосистемасы 2016-жылы ири компаниялар үчүн туура болчу.

Ошондой эле окуңуз

Kitty Girlfriend: Бишкектеги студия Google Play-га оюн чыгардыaunimeda
Оюн иштеп чыгуу

Kitty Girlfriend: Бишкектеги студия Google Play-га оюн чыгарды

Kitty Girlfriend — Бишкектеги Aunimeda командасы иштеп чыгып, Google Play-га жарыяланган мобилдик оюн. Unity кыймылдаткычы, дизайн жана монетизация жөнүндө айтып беребиз.

Auni Kitchen: Бишкектен тамак-аш темасындагы мобилдик оюнaunimeda
Оюн иштеп чыгуу

Auni Kitchen: Бишкектен тамак-аш темасындагы мобилдик оюн

Auni Kitchen — Aunimeda командасы тарабынан Бишкекте иштелип чыгылган жана Google Play-да жарыяланган казуалдык тамак-аш оюну. Unity, оюн дизайны жана монетизация жөнүндө айтып беребиз.

WebSockets vs SSE vs Long Polling: realtime технологиясын кантип тандоо керекaunimeda
Веб-иштеп чыгуу

WebSockets vs SSE vs Long Polling: realtime технологиясын кантип тандоо керек

Чат, кабарлар, заказ статусу - булардын баары реалдуу убакытта жаңыртууну талап кылат. WebSocket, Server-Sent Events жана Long Polling ар башка иштейт. Кайсын качан колдонуу керегин Node.js код мисалдары менен карайбыз.

Бизнесиңизге IT иштеп чыгуу керекпи?

Веб-сайттарды, мобилдик тиркемелерди жана AI чечимдерин иштеп чыгабыз. Акысыз консультация.

Веб-сайт иштеп чыгуу

Консультация алуу Бардык макалалар