{"id":299750,"date":"2020-03-06T03:00:16","date_gmt":"2020-03-06T03:00:16","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=299750"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=299750","title":{"rendered":"\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u043c\u0430\u0433\u0430\u0437\u0438\u043d \u043d\u0430 Nuxt.js 2 \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u0427\u0430\u0441\u0442\u044c 3"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/491190\/\">\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/bz\/t1\/ds\/bzt1dsg3xirl4vj059ehw0wzhs4.jpeg\"><\/p>\n<p>  <\/p>\n<p>\u041a\u0430\u043a \u0438 \u043e\u0431\u0435\u0449\u0430\u043b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c.<\/p>\n<p>  <\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0431\u043b\u043e\u043a\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 &quot;\u0421 \u044d\u0442\u0438\u043c \u0442\u043e\u0432\u0430\u0440\u043e\u043c \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043a\u0443\u043f\u0430\u044e\u0442&quot; \u0438 &quot;\u0418\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u0442\u043e\u0432\u0430\u0440\u044b&quot;<\/li>\n<li>\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0438\u043a\u043e\u043d\u043a\u0443 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0441 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0442\u043e\u0432\u0430\u0440\u043e\u0432<\/li>\n<li>\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u043d\u043e \u0441 \u0442\u043e\u0432\u0430\u0440\u0430\u043c\u0438 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435<\/li>\n<li>\u043f\u0435\u0440\u0435\u043f\u0438\u0448\u0435\u043c \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 store<a name=\"habracut\"><\/a><\/li>\n<\/ul>\n<p>  <\/p>\n<h3 id=\"sozdadim-bloki-tovarov-tipa-s-etim-tovarom-takzhe-pokupayut\">\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0431\u043b\u043e\u043a\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0442\u0438\u043f\u0430: &quot;\u0421 \u044d\u0442\u0438\u043c \u0442\u043e\u0432\u0430\u0440\u043e\u043c \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043a\u0443\u043f\u0430\u044e\u0442&quot;<\/h3>\n<p>  <\/p>\n<p>\u0422\u0443\u0442 \u043d\u0435\u043c\u043d\u043e\u0436\u043a\u043e \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e\u0431 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445 \u0432 Nuxt. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0439 \u0431\u043b\u043e\u043a \u0441 \u0442\u043e\u0432\u0430\u0440\u0430\u043c\u0438. \u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u0443\u043c\u0435\u0442\u044c \u043a\u0430\u043a-\u0442\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0438 \u043c\u044b \u0435\u0433\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043c, \u043d\u043e \u0432\u043e\u043f\u0440\u043e\u0441 \u043a\u0443\u0434\u0430?<\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0442\u0438\u043f\u0430 <strong>AlsoBuyList <\/strong>\u0442\u043e \u0443 \u043d\u0435\u0433\u043e \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u0430 <strong>asyncData<\/strong>, \u0438 \u043c\u044b \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u043c \u0435\u0433\u043e \u043e\u0442\u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435. \u0415\u0441\u043b\u0438 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u0434\u043b\u044f \u0432\u0430\u0441 \u044d\u0442\u043e \u043d\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u0447\u0442\u043e \u0432 html \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u0434\u0430\u0451\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u044d\u0442\u0438\u0445 \u0431\u043b\u043e\u043a\u043e\u0432, \u0442\u043e \u0442\u043e\u0433\u0434\u0430 \u0432\u043c\u0435\u0441\u0442\u043e asyncData \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <strong>mounted<\/strong> (\u0438\u043b\u0438 \u043e\u0442\u043b\u043e\u0436\u0438\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0434\u043e \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u043f\u043e\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0431\u0443\u0434\u0435\u0442 \u0432 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438).<\/p>\n<p>  <\/p>\n<p>\u041d\u043e \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u043c\u044b \u0445\u043e\u0442\u0438\u043c True SSR, \u0442\u043e\u0433\u0434\u0430 \u043a\u0430\u043a \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u043c asyncData \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b. \u042d\u0442\u043e \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0435\u0442 \u043f\u0440\u043e\u0435\u043a\u0442, \u0442\u0430\u043a \u043a\u0430\u043a \u0433\u0434\u0435 \u0431\u044b \u043c\u044b \u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u0438 \u043e\u0442\u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0442\u043e\u0432\u0430\u0440\u043e\u0432, \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c \u0434\u043e\u043b\u0436\u043d\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0438 \u0441\u043f\u0438\u0441\u043a\u0438 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0434\u043b\u044f \u043d\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0435. \u0423\u0436\u0430\u0441\u043d\u043e, \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e, \u043d\u0435\u0443\u0434\u043e\u0431\u043d\u043e, \u043d\u043e \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0442\u0440\u0443SSR. <\/p>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0435\u0445\u0430\u043b\u0438 \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u0432\u043e\u0434\u0438\u043c \u043d\u0430\u0448 Vuex \u043a \u0442\u0430\u043a\u043e\u043c\u0443 \u0432\u0438\u0434\u0443:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">index.js<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">\/\/ function for Mock API const sleep = m =&gt; new Promise(r =&gt; setTimeout(r, m)) const sampleSize = require('lodash.samplesize') const categories = [   {     id: 'cats',     cTitle: '\u041a\u043e\u0442\u0438\u043a\u0438',     cName: '\u041a\u043e\u0442\u0438\u043a\u0438',     cSlug: 'cats',     cMetaDescription: '\u041c\u0435\u0442\u0430 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435',     cDesc: '\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435',     cImage: 'https:\/\/source.unsplash.com\/300x300\/?cat,cats',     products: []   },   {     id: 'dogs',     cTitle: '\u0421\u043e\u0431\u0430\u0447\u043a\u0438',     cName: '\u0421\u043e\u0431\u0430\u0447\u043a\u0438',     cSlug: 'dogs',     cMetaDescription: '\u041c\u0435\u0442\u0430 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435',     cDesc: '\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435',     cImage: 'https:\/\/source.unsplash.com\/300x300\/?dog,dogs',     products: []   },   {     id: 'wolfs',     cTitle: '\u0412\u043e\u043b\u0447\u043a\u0438',     cName: '\u0412\u043e\u043b\u0447\u043a\u0438',     cSlug: 'wolfs',     cMetaDescription: '\u041c\u0435\u0442\u0430 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435',     cDesc: '\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435',     cImage: 'https:\/\/source.unsplash.com\/300x300\/?wolf',     products: []   },   {     id: 'bulls',     cTitle: '\u0411\u044b\u0447\u043a\u0438',     cName: '\u0411\u044b\u0447\u043a\u0438',     cSlug: 'bulls',     cMetaDescription: '\u041c\u0435\u0442\u0430 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435',     cDesc: '\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435',     cImage: 'https:\/\/source.unsplash.com\/300x300\/?bull',     products: []   } ] function getProductsByIds (products, productsImages, ids) {   const innerProducts = products.filter(p =&gt; p.id === ids.find(id =&gt; p.id === id))   if (!innerProducts) return null   return innerProducts.map(pr =&gt; {     return {       ...pr,       images: productsImages.find(img =&gt; img.id === pr.id).urls,       category: categories.find(cat =&gt; cat.id === pr.category_id)     }   }) } function getProduct (products, productsImages, productSlug) {   const innerProduct = products.find(p =&gt; p.pSlug === productSlug)   if (!innerProduct) return null   return {     ...innerProduct,     images: productsImages.find(img =&gt; img.id === innerProduct.id).urls,     category: categories.find(cat =&gt; cat.id === innerProduct.category_id)   } } function addProductsToCategory (products, productsImages, category) {   const categoryInner = { ...category, products: [] }   products.map(p =&gt; {     if (p.category_id === category.id) {       categoryInner.products.push({         id: p.id,         pName: p.pName,         pSlug: p.pSlug,         pPrice: p.pPrice,         image: productsImages.find(img =&gt; img.id === p.id).urls       })     }   })   return categoryInner } function getBreadcrumbs (pageType, route, data) {   const crumbs = []   crumbs.push({     title: '\u0413\u043b\u0430\u0432\u043d\u0430\u044f',     url: '\/'   })   switch (pageType) {     case 'category':       crumbs.push({         title: data.cName,         url: `\/category\/${data.cSlug}`       })       break     case 'product':       crumbs.push({         title: data.category.cName,         url: `\/category\/${data.category.cSlug}`       })       crumbs.push({         title: data.pName,         url: `\/product\/${data.pSlug}`       })        break      default:       break   }   return crumbs } export const state = () =&gt; ({   categoriesList: [],   currentCategory: {},   currentProduct: {     alsoBuyProducts: [],     interestingProducts: []   },   bredcrumbs: [] }) export const mutations = {   SET_CATEGORIES_LIST (state, categories) {     state.categoriesList = categories   },   SET_CURRENT_CATEGORY (state, category) {     state.currentCategory = category   },   SET_CURRENT_PRODUCT (state, product) {     state.currentProduct = product   },   SET_BREADCRUMBS (state, crumbs) {     state.bredcrumbs = crumbs   },   RESET_BREADCRUMBS (state) {     state.bredcrumbs = []   },   GET_PRODUCTS_BY_IDS () {} } export const actions = {   async getProductsListByIds ({ commit }) {     \/\/ simulate api work     const [products, productsImages] = await Promise.all(       [         this.$axios.$get('\/mock\/products.json'),         this.$axios.$get('\/mock\/products-images.json')       ]      )     commit('GET_PRODUCTS_BY_IDS')     const idsArray = (sampleSize(products, 5)).map(p =&gt; p.id)     return getProductsByIds(products, productsImages, idsArray)   },   async setBreadcrumbs ({ commit }, data) {     await commit('SET_BREADCRUMBS', data)   },   async getCategoriesList ({ commit }) {     try {       await sleep(50)       await commit('SET_CATEGORIES_LIST', categories)     } catch (err) {       console.log(err)       throw new Error('\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u044f\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u0441\u043e\u043e\u0431\u0449\u0438\u0442\u0435 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0443')     }   },   async getCurrentCategory ({ commit, dispatch }, { route }) {     await sleep(50)     const category = categories.find((cat) =&gt; cat.cSlug === route.params.CategorySlug)     \/\/ simulate api work     const [products, productsImages] = await Promise.all(       [         this.$axios.$get('\/mock\/products.json'),         this.$axios.$get('\/mock\/products-images.json')       ]     )     const crubms = getBreadcrumbs('category', route, category)     await dispatch('setBreadcrumbs', crubms)      await commit('SET_CURRENT_CATEGORY', addProductsToCategory(products, productsImages, category))   },   async getCurrentProduct ({ commit, dispatch }, { route }) {     await sleep(50)     const productSlug = route.params.ProductSlug     \/\/ simulate api work     const [products, productsImages, alsoBuyProducts, interestingProducts] = await Promise.all(       [         this.$axios.$get('\/mock\/products.json'),         this.$axios.$get('\/mock\/products-images.json'),         dispatch('getProductsListByIds'),         dispatch('getProductsListByIds')       ]      )     const product = getProduct(products, productsImages, productSlug)     const crubms = getBreadcrumbs('product', route, product)     await dispatch('setBreadcrumbs', crubms)     await commit('SET_CURRENT_PRODUCT', { ...product, alsoBuyProducts, interestingProducts })   }  } <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p><strong>getProductsByIds<\/strong> \u0441\u0438\u043c\u0443\u043b\u044f\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430 api \u0441\u0435\u0440\u0432\u0435\u0440<br \/>  <strong>getProductsListByIds<\/strong> action \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0438\u043c\u0443\u043b\u0438\u0440\u0443\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u043d \u0440\u0430\u043d\u0434\u043e\u043c\u043d\u043e \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442 5 \u0442\u043e\u0432\u0430\u0440\u043e\u0432<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">    await sleep(50)     const productSlug = route.params.ProductSlug     \/\/ simulate api work     const [products, productsImages, alsoBuyProducts, interestingProducts] = await Promise.all(       [         this.$axios.$get('\/mock\/products.json'),         this.$axios.$get('\/mock\/products-images.json'),         dispatch('getProductsListByIds'),         dispatch('getProductsListByIds')       ]      )     const product = getProduct(products, productsImages, productSlug)     const crubms = getBreadcrumbs('product', route, product)     dispatch('setBreadcrumbs', crubms)     commit('SET_CURRENT_PRODUCT', { ...product, alsoBuyProducts, interestingProducts })<\/code><\/pre>\n<p>  <\/p>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043e\u0434 \u0440\u0430\u043d\u044c\u0448\u0435 \u0443 \u043d\u0430\u0441 \u043f\u043e\u043b\u0443\u0447\u0430\u043b \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0444\u0443 \u043e \u0442\u043e\u0432\u0430\u0440\u0435. \u0422\u0435\u043f\u0435\u0440\u044c \u0436\u0435 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0438\u043d\u0444\u0443 \u0438 \u043f\u0440\u043e \u044d\u0442\u0438 \u0431\u043b\u043e\u043a\u0438 <strong>alsoBuyProducts<\/strong> <strong>interestingProducts<\/strong> \u0438 \u043a\u043b\u0430\u0441\u0442\u044c \u0435\u0451 \u0432 \u0442\u043e\u0432\u0430\u0440.<br \/>  \u0422\u0443\u0442 \u043c\u043e\u0436\u043d\u043e \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0447\u0442\u043e \u043c\u044b \u0431\u0430\u0442\u0447\u0438\u043c \u0441\u0440\u0430\u0437\u0443 \u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 actions, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u043b\u0430\u044e\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b. \u042d\u0442\u043e \u043b\u0443\u0447\u0448\u0435 \u0447\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0441\u0451 \u0432 \u0441\u0442\u0438\u043b\u0435:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">        const products = await this.$axios.$get('\/mock\/products.json')         const productsImages = await this.$axios.$get('\/mock\/products-images.json')         const alsoBuyProducts = await dispatch('getProductsListByIds')         const interestingProducts = await dispatch('getProductsListByIds')<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0438\u0445 \u043f\u043e \u043e\u0447\u0435\u0440\u0435\u0434\u0438.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0442\u043e\u0432\u0430\u0440\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0438\u0437 \u0441\u0442\u043e\u0440\u0430:<br \/>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/kw\/xy\/lm\/kwxylmtd0b22ypzha-u82steboy.png\"><br \/>  \u0414\u0430\u043b\u044c\u0448\u0435 \u0434\u0435\u043b\u043e \u0442\u0435\u0445\u043d\u0438\u043a\u0438, \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0442\u043e\u0432\u0430\u0440\u0430 \u0432\u044b\u0432\u043e\u0434\u0438\u043c<\/p>\n<p>  <\/p>\n<pre><code class=\"html\">    &lt;h2&gt;\u0421 \u044d\u0442\u0438\u043c \u0442\u043e\u0432\u0430\u0440\u043e\u043c \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043a\u0443\u043f\u0430\u044e\u0442&lt;\/h2&gt;     &lt;ProductsList :products=&quot;product.alsoBuyProducts&quot; \/&gt;     &lt;h2&gt;\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0432\u0430\u043c \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e&lt;\/h2&gt;     &lt;ProductsList :products=&quot;product.interestingProducts&quot; \/&gt;<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <strong>ProductsList <\/strong><\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">ProductsList.vue<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"html\">&lt;template&gt;   &lt;div :class=&quot;$style.wrapper&quot;&gt;     &lt;div v-for=&quot;product in products&quot; :key=&quot;product.id&quot;&gt;       &lt;nuxt-link :to=&quot;`\/product\/${product.pSlug}`&quot;&gt;         &lt;p&gt;{{ product.pName }}&lt;\/p&gt;         &lt;img           v-lazy=&quot;product.images.imgL&quot;           :class=&quot;$style.image&quot;         \/&gt;       &lt;\/nuxt-link&gt;       &lt;p&gt;\u0426\u0435\u043d\u0430 {{ product.pPrice }}&lt;\/p&gt;       &lt;BuyButton :product=&quot;product&quot; \/&gt;     &lt;\/div&gt;   &lt;\/div&gt; &lt;\/template&gt;  &lt;script&gt; import BuyButton from '~~\/components\/common\/BuyButton' export default {   components: {     BuyButton   },   props: {     products: {       type: Array,       default: () =&gt; []     }   } } &lt;\/script&gt;  &lt;style lang=&quot;scss&quot; module&gt; .wrapper {   display: flex;   flex-wrap: wrap;   &gt; div {     margin: 1em;    }   p {     max-width: 270px;     height: 35px;   } } .image {   width: 300px;   height: 300px;   object-fit: cover; } &lt;\/style&gt; <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u041e\u043d \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043c\u0430\u0441\u0441\u0438\u0432 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0438 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442 \u0435\u0433\u043e. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/bd\/8f\/iw\/bd8fiwbqfc0h-i-bbcirv-3-9ji.png\"><\/p>\n<p>  <\/p>\n<h2 id=\"vy-gotovy-k-nastoyaschemu-hardkoru\">\u0412\u044b \u0433\u043e\u0442\u043e\u0432\u044b \u043a \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u043c\u0443 \u0445\u0430\u0440\u0434\u043a\u043e\u0440\u0443?<\/h2>\n<p>  <\/p>\n<p>\u0427\u0435\u0441\u0442\u043d\u043e \u0433\u043e\u0432\u043e\u0440\u044f, \u043f\u0435\u0440\u0432\u044b\u0439 \u0440\u0430\u0437 \u0434\u0435\u043b\u0430\u044e \u043a\u043e\u0440\u0437\u0438\u043d\u0443 client-side only, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u043e\u0441\u0442\u0438\u0442\u0435 \u043c\u043e\u0438 \u043e\u0448\u0438\u0431\u043a\u0438 (\u0431\u0443\u0434\u0443 \u0440\u0430\u0434 \u0441\u043e\u0432\u0435\u0442\u0430\u043c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445). \u041d\u043e \u0447\u0438\u0441\u0442\u043e \u0441 \u0430\u043a\u0430\u0434\u0435\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b (\u0434\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438) \u0434\u0443\u043c\u0430\u044e, \u0442\u0435\u0440\u043f\u0438\u043c\u043e.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043c\u043e\u0434\u0430\u043b\u043a\u0438 \u0441 \u043a\u043e\u0440\u0437\u0438\u043d\u043e\u0439 \u043c\u043d\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u043b\u043e\u0441\u044c \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0432 \u043e\u0431\u0449\u0435\u0439 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438 24 \u0444\u0430\u0439\u043b\u0430!<\/p>\n<p>  <\/p>\n<p>\u0421\u0445\u0435\u043c\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a\u0430\u044f:<\/p>\n<p>  <\/p>\n<ol>\n<li>\u0425\u0440\u0430\u043d\u0438\u043c \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 id \u0438 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0432\u0430\u0440\u0430 \u0438 \u0438\u0445 \u043f\u043e\u0440\u044f\u0434\u043a\u043e\u0432\u044b\u0439 \u043d\u043e\u043c\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0443.<\/li>\n<li>\u041f\u0438\u0448\u0435\u043c action, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 id \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0438 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u0430-\u0438\u043d\u0444\u0443 \u043e \u0442\u043e\u0432\u0430\u0440\u0430\u0445 (\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435, \u0446\u0435\u043d\u0443, \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0438 \u0442\u0434.).<\/li>\n<li>\u041f\u0438\u0448\u0435\u043c getter, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u044a\u0435\u0434\u0435\u043d\u044f\u0442\u044c \u0442\u043e\u0432\u0430\u0440 \u0438\u0437 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0438 \u043c\u0435\u0442\u0430-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e.<\/li>\n<li>\u041d\u0430\u0448 vuex store \u0438 localstorage \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0443\u0436\u0435 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u043e\u0432\u0430\u043d, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u043d\u0438\u0433\u0434\u0435 \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043a api \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u0440\u0437\u0438\u043d\u044b.<\/li>\n<li>\u041f\u0438\u0448\u0435\u043c 100500 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0430.<\/li>\n<li>\u0422\u0430\u043a \u043a\u0430\u043a \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e \u043a\u043e\u0440\u0437\u0438\u043d\u0435, \u0432\u0441\u0451 \u0447\u0442\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0438\u0437 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u043c\u044b \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0432 &lt;client-only&gt;, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0431\u044b\u043b\u043e \u043e\u0448\u0438\u0431\u043a\u0438 \u0447\u0442\u043e DOM \u043d\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 (\u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0435).<\/li>\n<\/ol>\n<p>  <\/p>\n<h3 id=\"teper-o-realizacii\">\u0422\u0435\u043f\u0435\u0440\u044c \u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/h3>\n<p>  <\/p>\n<p>\u0412 \u043c\u043e\u0434\u0443\u043b\u0435 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0431\u0443\u0434\u0435\u0442\u0435 2 \u043c\u0430\u0441\u0441\u0438\u0432\u0430:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u041f\u0435\u0440\u0432\u044b\u0439 <strong>products<\/strong> \u0441 \u0442\u0430\u043a\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439 { qty, productId, order }<\/li>\n<li>\u0412\u0442\u043e\u0440\u043e\u0439 <strong>metaProducts<\/strong> \u0437\u0434\u0435\u0441\u044c \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0442\u043e\u0432\u0430\u0440\u0430\u0445 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u0430\u044f \u043e\u0442 api (\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435, \u0446\u0435\u043d\u0430, \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0438 \u0442\u0434.)<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0432\u044b\u0439 \u0438 \u0432\u0442\u043e\u0440\u043e\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 \u0441\u0430\u043c\u0438 \u043f\u043e \u0441\u0435\u0431\u0435 \u0431\u0435\u0441\u043f\u043e\u043b\u0435\u0437\u043d\u044b \u0434\u043b\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430, \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c 1 \u043c\u0430\u0441\u0441\u0438\u0432 \u0433\u0434\u0435 \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u0435 \u0441\u0440\u0430\u0437\u0443 \u0431\u0443\u0434\u0435\u0442 \u0432\u0441\u044f \u043d\u0443\u0436\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u043f\u043e\u0434\u043e\u0439\u0434\u0451\u0442 getter. \u0413\u0435\u0442\u0435\u0440\u044b \u044d\u0442\u043e \u0430\u043d\u0430\u043b\u043e\u0433 computed \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u0445, \u0440\u0435\u0430\u0433\u0438\u0440\u0443\u044e\u0442 \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u043d\u0438 \u043f\u043e\u043b\u0430\u0433\u0430\u044e\u0442\u0441\u044f. <\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u0433\u0435\u0442\u0442\u0435\u0440<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">  getProductsInCart: state =&gt; {     const products = []     state.products.map(p =&gt; {       const metaProduct = state.metaProducts.find(mp =&gt; mp.id === p.productId)       if (metaProduct) {         products.push({ ...p, meta: metaProduct })       }     })     return products.sort((a, b) =&gt; a.order - b.order)   }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u043d \u0431\u0435\u0440\u0451\u0442 \u043f\u0435\u0440\u0432\u044b\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 \u0438 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u043f\u043e \u043d\u0435\u043c\u0443. \u0415\u0441\u043b\u0438 \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u043c \u043c\u0430\u0441\u0441\u0438\u0432\u0435 \u0435\u0441\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043c\u0435\u0442\u0430-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f, \u0442\u043e \u043c\u044b \u0437\u0430\u043d\u043e\u0441\u0438\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0432\u0438\u0434\u0430<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">{ ...p, meta: metaProduct }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0433\u0434\u0435 <strong>p<\/strong> \u044d\u0442\u043e { qty, productId, order }, \u0432 <strong>const products<\/strong><\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043e\u0431\u0445\u043e\u0434\u0430 \u0432\u0441\u0435\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 products, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u043e <strong>order<\/strong> \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435 \u0437\u0430\u0434\u0430\u0451\u0442\u0435\u0441\u044c \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u043c, \u0437\u0430\u0447\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u043d\u044b\u0439 <strong>merge<\/strong>. \u0414\u0435\u043b\u043e \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043c\u0435\u0442\u0430\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u0442\u044f\u0433\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0442 api \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c, \u043f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043c\u0430\u0441\u0441\u0438\u0432\u0430 <strong>products<\/strong>. \u0410 \u044d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u0442\u0430\u043a\u0430\u044f \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f, \u043a\u043e\u0433\u0434\u0430 \u0432 \u043f\u0435\u0440\u0432\u043e\u043c \u043c\u0430\u0441\u0441\u0438\u0432\u0435 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0442\u043e\u0432\u0430\u0440, \u0430 \u043c\u0435\u0442\u0430\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0435\u0449\u0451 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430. \u042d\u0442\u043e \u043b\u0435\u0433\u043a\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043e\u0448\u0438\u0431\u043a\u0430\u043c \u0440\u0435\u043d\u0434\u0435\u0440\u0430, \u0442\u0430\u043a \u043a\u0430\u043a \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0437\u043d\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u043e\u0436\u0438\u0434\u0430\u044e\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u043e\u0432\u0430\u0440 \u0438 \u0435\u0433\u043e \u043c\u0435\u0442\u0430-\u0434\u0430\u043d\u043d\u044b\u0435. <\/p>\n<p>  <\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0442\u043e\u043b\u044c\u043a\u043e, \u043a\u043e\u0433\u0434\u0430 \u043c\u0435\u0442\u0430-\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043e\u0442 api, \u044d\u0442\u043e\u0442 \u0433\u0435\u0442\u0442\u0435\u0440 \u0441\u0434\u0435\u043b\u0430\u0435\u0442 merge \u0434\u0432\u0443\u0445 \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u0432. \u042d\u0442\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043e\u0431\u043b\u0435\u0433\u0447\u0430\u0435\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0440\u0435\u043d\u0434\u0435\u0440.<\/p>\n<p>  <\/p>\n<h3 id=\"pishem-mutations\">\u041f\u0438\u0448\u0435\u043c mutations<\/h3>\n<p>  <\/p>\n<pre><code class=\"javascript\">export const mutations = {   ADD_PRODUCT (state, productId) {     \/\/ if cart doesn't have product add it     if (!state.products.find(p =&gt; productId === p.productId)) {       state.products = [...state.products, { productId: productId, qty: 1, order: findMax(state.products, 'order') + 1 }]     }   },   REMOVE_PRODUCT (state, productId) {     state.products = Array.from(state.products.filter(prod =&gt; prod.productId !== productId))   },   SET_PRODUCT_QTY (state, { productId, qty }) {     state.products = [       ...state.products.filter(prod =&gt; prod.productId !== productId),       { ...state.products.find(prod =&gt; prod.productId === productId), qty }     ]   },   SET_PRODUCTS_BY_IDS (state, products) {     state.metaProducts = products   }  }<\/code><\/pre>\n<p>  <\/p>\n<p><strong>ADD_PRODUCT<\/strong> \u2014 \u0443\u0431\u0435\u0436\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0442\u043e\u0432\u0430\u0440\u0430 \u0435\u0449\u0451 \u043d\u0435\u0442, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0433\u043e \u0441 <strong>qty = 1 <\/strong> (\u0442\u043e \u0435\u0441\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0432\u0430\u0440\u0430 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e 1 \u0448\u0442.) \u0438 \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u0442 \u0435\u043c\u0443 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u044f\u0434\u043a\u043e\u0432\u044b\u0439 \u043d\u043e\u043c\u0435\u0440 <strong>+ 1<\/strong>.<br \/>  \u0413\u0434\u0435 \u0437\u0430 \u043f\u043e\u0438\u0441\u043a \u044d\u0442\u043e\u0433\u043e \u043d\u043e\u043c\u0435\u0440\u0430 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const findMax = (array, field) =&gt; {   if (!array || array.lenght === 0) return 1   return Math.max(...array.map(o =&gt; o[field]), 0) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043c\u0430\u0441\u0441\u0438\u0432 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0431\u043e\u0440\u0430 \u0438 \u043f\u043e\u043b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0442\u044c \u0441 \u043e\u0434\u043d\u043e\u0438\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043c\u0430\u0441\u0441\u0438\u0432\u0430.<br \/>  <strong>REMOVE_PRODUCT<\/strong> \u2014 \u0443\u0431\u0438\u0440\u0430\u0435\u0442 \u0442\u043e\u0432\u0430\u0440<br \/>  <strong>SET_PRODUCT_QTY <\/strong> \u2014 \u043c\u0435\u043d\u044f\u0435\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0432\u0430\u0440\u0430 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435<br \/>  <strong>SET_PRODUCTS_BY_IDS <\/strong> \u2014 \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u0442 \u043c\u0435\u0442\u0430-\u0438\u043d\u0444\u0443<\/p>\n<p>  <\/p>\n<h3 id=\"pishem-actions\">\u041f\u0438\u0448\u0435\u043c actions<\/h3>\n<p>  <\/p>\n<pre><code class=\"javascript\">export const actions = {   async setProductsListByIds ({ commit, state }) {     \/\/ simulate api work     await sleep(50)     const [products, productsImages] = await Promise.all(       [         this.$axios.$get('\/mock\/products.json'),         this.$axios.$get('\/mock\/products-images.json')       ]      )     const productsIds = state.products.map(p =&gt; p.productId)     await commit('SET_PRODUCTS_BY_IDS', mock.getProductsByIds(products, productsImages, productsIds))   },   async addProduct ({ commit, dispatch }, productId) {     \/\/ simulate api work     await sleep(50)     await commit('ADD_PRODUCT', productId)     await dispatch('setProductsListByIds')   },   async removeProduct ({ commit, dispatch }, productId) {     \/\/ simulate api work     await sleep(50)     await commit('REMOVE_PRODUCT', productId)     await dispatch('setProductsListByIds')   },   async setProductQuantity ({ commit, dispatch }, { productId, qty }) {     \/\/ simulate api work     await sleep(50)     await commit('SET_PRODUCT_QTY', { productId, qty })     await dispatch('setProductsListByIds')   }  }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0442\u043e\u0432\u0430\u0440\u0430 \u043c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c <strong>await dispatch(&#8216;setProductsListByIds&#8217;)<\/strong> \u0442\u043e \u0435\u0441\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u043c\u0435\u0442\u0430-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0442 api.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u0447\u0438\u043d\u0430 \u043f\u043e\u0447\u0435\u043c\u0443 \u043f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0430 api \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0446\u0435\u043d, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0442\u043e\u0432\u0430\u0440\u0430 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435, \u0441\u0443\u043c\u043c\u044b \u0437\u0430\u043a\u0430\u0437\u0430, \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043f\u043e\u0437\u0438\u0446\u0438\u0439 \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u0418 \u0434\u0430\u0436\u0435 \u043d\u0435 \u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0441\u0435\u0439\u0447\u0430\u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043a\u043e\u0440\u0437\u0438\u043d\u0435, \u044d\u0442\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0442\u0430\u043a \u0437\u0430\u0434\u0435\u043b \u043d\u0430 \u0431\u0443\u0434\u0443\u0449\u0435\u0435.<\/p>\n<p>  <\/p>\n<h3 id=\"sozdayom-modalnoe-okno\">\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u043d\u043e<\/h3>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u043b\u0430\u0433\u0438\u043d vue-modal.js<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import Vue from 'vue' import VModal from 'vue-js-modal'  export default async (context, inject) =&gt; {   Vue.use(VModal) } <\/code><\/pre>\n<p>  <\/p>\n<p>\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0435\u0433\u043e \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u043d\u0430 \u0432\u0435\u0441\u044c \u043f\u0440\u043e\u0435\u043a\u0442 \u0432 <strong>nuxt.config.js<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">{ src: '~~\/plugins\/vue-modal.js', mode: 'client' },<\/code><\/pre>\n<p>  <\/p>\n<h4 id=\"sozdayom-komponent\">\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442<\/h4>\n<p>  <\/p>\n<p><strong>mode: &#8216;client&#8217; <\/strong> \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0435\u0433\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435<\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043f\u0430\u043f\u043a\u0443 components\/modals \u0438 \u0444\u0430\u0439\u043b CastomerCartModal.vue<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">CastomerCartModal.vue<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">&lt;template&gt;   &lt;div&gt;     &lt;client-only&gt;       &lt;modal         name=&quot;customer-cart&quot;         transition=&quot;pop-out&quot;         height=&quot;95%&quot;         width=&quot;95%&quot;         :max-width=&quot;960&quot;         :adaptive=&quot;true&quot;         :scrollable=&quot;true&quot;         :pivot-y=&quot;0.5&quot;         :reset=&quot;true&quot;         classes=&quot;v--modal-customer-cart&quot;         @before-open=&quot;beforeOpen&quot;       &gt;         &lt;div class=&quot;modal-wrapper content-padding&quot;&gt;           &lt;div class=&quot; header-block&quot;&gt;             &lt;p class=&quot;h1-header&quot;&gt;               Cart             &lt;\/p&gt;             &lt;div class=&quot;close&quot; @click=&quot;$modal.hide('customer-cart')&quot;&gt;               &lt;CloseOrDeleteButton \/&gt;             &lt;\/div&gt;           &lt;\/div&gt;           &lt;div v-if=&quot;getProductsInCart.length === 0&quot; class=&quot;&quot;&gt;             &lt;p&gt;               \u0422\u043e\u0432\u0430\u0440\u043e\u0432 \u043f\u043e\u043a\u0430 \u043d\u0435\u0442, \u043d\u043e \u044d\u0442\u043e \u043b\u0435\u0433\u043a\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c :)             &lt;\/p&gt;           &lt;\/div&gt;           &lt;template v-else&gt;             &lt;div class=&quot;wrapper&quot;&gt;               &lt;template v-if=&quot;getAddedProduct&quot;&gt;                 &lt;p class=&quot;added-product &quot;&gt;                   You've added                 &lt;\/p&gt;                 &lt;ProductsList class=&quot;&quot; :products-from-cart=&quot;getAddedProduct&quot; \/&gt;                 &lt;p v-if=&quot;getProducts.length &gt; 0&quot; class=&quot;added-product &quot;&gt;                   Previously added products                 &lt;\/p&gt;               &lt;\/template&gt;               &lt;ProductsList class=&quot;products&quot; :products-from-cart=&quot;getProducts&quot; \/&gt;             &lt;\/div&gt;             &lt;div&gt;Total: {{ getAmount | round }}&lt;\/div&gt;             &lt;div class=&quot;bottom&quot;&gt;               &lt;a class=&quot;button color-grey close-button&quot; @click.prevent=&quot;$modal.hide('customer-cart')&quot;&gt;                 Close               &lt;\/a&gt;               &lt;div class=&quot;amount-block&quot;&gt;                 &lt;nuxt-link                   to=&quot;\/checkout&quot;                   class=&quot;button color-primary checkout-button&quot;                 &gt;                   To checkout                 &lt;\/nuxt-link&gt;               &lt;\/div&gt;             &lt;\/div&gt;           &lt;\/template&gt;         &lt;\/div&gt;       &lt;\/modal&gt;     &lt;\/client-only&gt;   &lt;\/div&gt; &lt;\/template&gt;  &lt;script&gt; import { mapGetters } from 'vuex' import ProductsList from '~~\/components\/cart\/ProductsList.vue' import CloseOrDeleteButton from '~~\/components\/common\/input\/CloseOrDeleteButton.vue' import round from '~~\/mixins\/round.js' export default {   components: {     ProductsList,     CloseOrDeleteButton   },   mixins: [round],   data () {     return {       addedProduct: null,       defaults: {         addedProduct: null       }     }   },    computed: {     ...mapGetters({       getProductsInCart: 'cart\/getProductsInCart'     }),     getAddedProduct () {       const product = this.getProductsInCart.find(         prod =&gt; prod.productId === this.addedProduct       )       if (product) {         return [product]       } else {         return null       }     },     getAmount () {       let amount = 0       if (this.getProductsInCart &amp;&amp; this.getProductsInCart.length &gt; 0) {         this.getProductsInCart.forEach(product =&gt; {           amount +=             parseFloat(product.meta.pPrice) *             parseInt(product.qty)         })         return amount       } else {         return 0       }     },     getProducts () {       if (this.addedProduct) {         return this.getProductsInCart.filter(           prod =&gt; prod.productId !== this.addedProduct         )       } else {         return this.getProductsInCart       }     }    },    watch: {     $route: function () {       this.$modal.hide('customer-cart')     },     getProductsInCart: function (newVal, oldVal) {       if (oldVal.length &gt; 0) {         if (this.getProductsInCart.length === 0) {           this.$modal.hide('customer-cart')         }       }     }   },   methods: {     beforeOpen (event) {       if (!event.params) {         event.params = {}       }       if (event.params.addedProduct) {         this.addedProduct = event.params.addedProduct       } else {         this.addedProduct = this.defaults.addedProduct       }     }   } } &lt;\/script&gt; &lt;style lang=&quot;scss&quot;&gt; &lt;\/style&gt; &lt;style lang=&quot;scss&quot; scoped&gt; .submit-error {   font-weight: 500;   color: #de0d0d;   \/\/ font-weight: 300;   font-size: 0.7em; } .modal-wrapper {   \/\/ border: 3px solid $accent-border-color;   background: #fff;   overflow-y: scroll;   \/\/ margin-top: 20px;   height: 100%;    display: flex;   flex-direction: column;   align-items: stretch; } .bottom {   flex-shrink: 0;   margin-bottom: 30px;   display: flex;   justify-content: flex-start;   \/\/ align-items: flex-start;   flex-direction: column;   margin-top: 16px;   @media screen and (min-width: 1024px) {     justify-content: space-between;     flex-direction: row;     align-items: flex-end;     padding-bottom: 50px;   }   .close-button {     display: none;   @media screen and (min-width: 1024px) {       display: block;     }   }   .amount-block {     .checkout-button {       margin-top: 5px;       width: 100%;       display: flex;         @media screen and (min-width: 640px) {         width: auto;       }     }   }    button.bttn-material-flat {     font-size: 1rem;   @media screen and (min-width: 1024px) {       font-size: 1.4rem;     }   } } p.added-product {   font-size: 1.6rem;   margin-bottom: 1rem; }  .wrapper {   \/\/ height: 100%;   flex-grow: 1;   position: relative; } .header-block {   flex-shrink: 0;   \/\/ padding: 10px 20px;   margin-top: 20px;   \/\/ background: #f8fafb;   \/\/ font-size: 1.6rem;   position: relative;   margin-bottom: 1rem;   .close {     position: absolute;     right: 12px;     top: 0;   } }  .pop-out-enter-active, .pop-out-leave-active {   transition: all 0.5s; } .pop-out-enter, .pop-out-leave-active {   opacity: 0;   transform: translateY(24px); } &lt;\/style&gt; <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435.<br \/>  \u0412\u0435\u0441\u044c \u043a\u043e\u0434 \u043e\u0431\u0432\u0451\u0440\u043d\u0443\u0442 \u0432 <strong>&lt;client-only&gt;<\/strong>, \u0447\u0442\u043e\u0431\u044b Vue \u043d\u0435 \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u0435\u0433\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435.<\/p>\n<p>  <\/p>\n<h4 id=\"modal\">modal<\/h4>\n<p>  <\/p>\n<ul>\n<li><strong>name=&quot;customer-cart&quot;<\/strong> \u0438\u043c\u044f \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u043a\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0442\u044c\/\u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u043e\u043a\u043d\u043e \u0438\u0437 \u043b\u044e\u0431\u043e\u0433\u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 (\u0442\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0440\u0430\u043d\u0435\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b\u0438 \u044d\u0442\u043e\u0442 \u043f\u043b\u0430\u0433\u0438\u043d \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e)<\/li>\n<li><strong>:adaptive=&quot;true&quot;<\/strong> \u0434\u0435\u043b\u0430\u0435\u0442 \u0435\u0433\u043e \u0448\u0438\u0440\u0438\u043d\u0443 \u043d\u0435 95%, \u0430 \u0430\u0434\u0430\u043f\u0442\u0438\u0432\u043d\u043e\u0439 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u0438 \u0448\u0438\u0440\u0438\u043d\u044b \u044d\u043a\u0440\u0430\u043d\u0430 (\u043d\u043e \u043a\u043e\u0433\u0434\u0430 \u044d\u043a\u0440\u0430\u043d \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439, \u043e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 95%)<\/li>\n<li><strong>:scrollable=&quot;true&quot; <\/strong> \u043b\u043e\u0447\u0438\u0442\u044c body \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u044f \u0442\u0435\u043c \u0441\u0430\u043c\u044b\u043c \u0441\u043a\u043e\u0440\u043b\u043b \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.<\/li>\n<li><strong>:pivot-y=&quot;0.5&quot;<\/strong> \u0434\u0435\u043b\u0430\u0435\u043c \u043e\u043a\u043d\u043e \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443<\/li>\n<li><strong>:reset=&quot;true&quot;<\/strong> \u043f\u0435\u0440\u0435\u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f (\u0443\u0441\u0442\u0440\u0430\u043d\u044f\u0435\u0442 \u0431\u0430\u0433\u0438 \u0432\u0441\u044f\u043a\u043e\u0433\u043e \u0440\u043e\u0434\u0430)<\/li>\n<li><strong>classes=&quot;v&#8212;modal-customer-cart&quot;<\/strong> \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0441\u0432\u043e\u0439 \u0441\u0442\u0438\u043b\u044c<\/li>\n<li><strong>@before-open=&quot;beforeOpen&quot;<\/strong> \u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0435\u0440\u0435\u0434 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435\u043c \u043c\u043e\u0434\u0430\u043b\u043a\u0438 (\u043e\u0431 \u044d\u0442\u043e\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435)<\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"xml\">&lt;div class=&quot;close&quot; @click=&quot;$modal.hide('customer-cart')&quot;&gt;   &lt;CloseOrDeleteButton \/&gt; &lt;\/div&gt;<\/code><\/pre>\n<p>  <\/p>\n<p><strong>CloseOrDeleteButton <\/strong> \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e svg \u0438\u043a\u043e\u043d\u043a\u0430 (\u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0441\u043f\u0440\u044f\u0442\u0430\u043d\u0430 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442.<br \/>  <strong><a href=\"https:\/\/habr.com\/ru\/users\/click\/\" class=\"user_link\">click<\/a>=&quot;$modal.hide(&#8216;customer-cart&#8217;)&quot;<\/strong> \u043f\u043e \u043a\u043b\u0438\u043a\u0443 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0437\u0430\u043a\u0440\u044b\u0442\u044c, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u043d\u043e \u043f\u043e \u0438\u043c\u0435\u043d\u0438<\/p>\n<p>  <\/p>\n<pre><code class=\"xml\">&lt;template v-if=&quot;getAddedProduct&quot;&gt;   &lt;p class=&quot;added-product &quot;&gt;     You've added   &lt;\/p&gt;   &lt;ProductsList class=&quot;&quot; :products-from-cart=&quot;getAddedProduct&quot; \/&gt;   &lt;p v-if=&quot;getProducts.length &gt; 0&quot; class=&quot;added-product &quot;&gt;     Previously added products   &lt;\/p&gt; &lt;\/template&gt; &lt;ProductsList class=&quot;products&quot; :products-from-cart=&quot;getProducts&quot; \/&gt;<\/code><\/pre>\n<p>  <\/p>\n<p><strong>ProductsList <\/strong> \u044d\u0442\u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0441 \u0442\u043e\u0432\u0430\u0440\u0430\u043c\u0438 \u0434\u043b\u044f \u043a\u043e\u0440\u0437\u0438\u043d\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043f\u043e\u043a\u0430 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043b\u0438.<br \/>  \u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u043a\u0443\u043f\u0438\u0442\u044c, \u0442\u043e\u0432\u0430\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438, \u0441\u0442\u043e\u0438\u0442 \u043e\u0441\u043e\u0431\u043d\u044f\u0447\u043a\u043e\u043c \u043e\u0442 \u0432\u0441\u0435\u0445 \u0441 \u043d\u0430\u0434\u043f\u0438\u0441\u044c\u044e <strong>You&#8217;ve added<\/strong>, \u0430 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0442\u043e\u0432\u0430\u0440\u044b \u0438\u0434\u0443\u0442 \u043f\u043e\u0442\u043e\u043c \u043f\u043e\u0441\u043b\u0435 <strong>Previously added products<\/strong>. \u041d\u043e \u0435\u0441\u043b\u0438 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0430\u0436\u0430\u043b\u0438 \u043d\u0430 \u0438\u043a\u043e\u043d\u043a\u0443 \u043a\u043e\u0440\u0437\u0438\u043d\u044b (\u0442\u043e \u0435\u0441\u0442\u044c \u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u043b\u0438 \u0442\u043e\u0432\u0430\u0440, \u0430 \u0437\u0430\u0445\u043e\u0442\u0435\u043b\u0438 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u0442\u043e <strong>getAddedProduct<\/strong> \u0431\u0443\u0434\u0435\u0442 <strong>null<\/strong> \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0431\u0435\u0437 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043b\u0438\u0448\u043d\u0438\u0445 \u043d\u0430\u0434\u043f\u0438\u0441\u0435\u0439 \u043c\u044b \u0432\u043e\u0432\u043e\u0434\u0438\u043c \u0432\u0441\u0435 \u0442\u043e\u0432\u0430\u0440\u044b.<\/p>\n<p>  <\/p>\n<p><strong>getAddedProduct<\/strong> \u0438 <strong>getProducts<\/strong> \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u043e\u0431\u0432\u0451\u0440\u0442\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 <strong>addedProduct<\/strong> \u043c\u0435\u043d\u044f\u044e\u0442 \u0441\u0432\u043e\u0451 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043d\u0430 \u0443\u043c\u0435\u0441\u0442\u043d\u043e\u0435.<\/p>\n<p>  <\/p>\n<pre><code class=\"xml\">&lt;div&gt;Total: {{ getAmount | round }}&lt;\/div&gt;<\/code><\/pre>\n<p>  <\/p>\n<p>\u042d\u0442\u043e\u0442 \u0441\u0442\u0440\u0430\u043d\u043d\u044b\u0439 \u0441\u0438\u043c\u0432\u043e\u043b | (\u043f\u0430\u0439\u043f) \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u0433\u043e\u0432\u043e\u0440\u0438\u0442, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e round (\u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u043a\u0440\u0443\u0433\u043b\u044f\u0435\u0442 \u0447\u0438\u0441\u043b\u043e \u0434\u043e \u0434\u0432\u0443\u0445 \u0437\u043d\u0430\u043a\u043e\u0432) \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.<\/p>\n<p>  <\/p>\n<p>\u042d\u0442\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u044b \u0432\u044b\u0434\u0435\u043b\u0438\u043c \u0432 \u043c\u0438\u043a\u0441\u0438\u043d <strong>mixins\\round.js<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">export default {   filters: {     round (num) {       return Math.round((num + Number.EPSILON) * 100) \/ 100     }   } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043c\u0438\u043a\u0441\u0438\u043d \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 <\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import round from '~~\/mixins\/round.js' ... mixins: [round]<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0438 \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u043a\u043d\u0430 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">this.$modal.show('customer-cart', { addedProduct: this.product.id })<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u0431\u044a\u0435\u043a\u0442 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0432\u0442\u043e\u0440\u044b\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u0432 \u043c\u0435\u0442\u043e\u0434 <strong>beforeOpen<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">  methods: {     beforeOpen (event) {       if (!event.params) {         event.params = {}       }       if (event.params.addedProduct) {         this.addedProduct = event.params.addedProduct       } else {         this.addedProduct = this.defaults.addedProduct       }     }   }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0413\u0434\u0435 \u043c\u044b, \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u043c \u0438\u0445 \u0432 <strong>data()<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">  data () {     return {       addedProduct: null,       defaults: {         addedProduct: null       }     }   },<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u043e\u043a\u043d\u0430 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 store.<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { mapGetters } from 'vuex' ...   computed: {     ...mapGetters({       getProductsInCart: 'cart\/getProductsInCart'     }), ...<\/code><\/pre>\n<p>  <\/p>\n<p>\u0413\u0434\u0435 \u043a\u0430\u043a \u0438 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 actions \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0438\u043c\u044f \u043c\u043e\u0434\u0443\u043b\u044f \u0447\u0435\u0440\u0435\u0437 \u0441\u043b\u0435\u0448.<br \/>  \u0418\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e \u0447\u0442\u043e \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c &quot;\u0445\u0438\u0442\u0440\u0443\u044e&quot; \u043b\u043e\u0433\u0438\u043a\u0443 \u0434\u043b\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 (\u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0442\u043e\u0433\u043e, \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0442\u043e\u0432\u0430\u0440 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0438\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u043a\u0440\u044b\u043b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c) \u043c\u044b \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <strong>getProductsInCart<\/strong> \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e, \u0430 \u043f\u0438\u0448\u0435\u043c \u0434\u0432\u0435 \u043e\u0431\u0432\u0451\u0440\u0442\u043a\u0438.<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">    getAddedProduct () {       const product = this.getProductsInCart.find(         prod =&gt; prod.productId === this.addedProduct       )       if (product) {         return [product]       } else {         return null       }     },     getProducts () {       if (this.addedProduct) {         return this.getProductsInCart.filter(           prod =&gt; prod.productId !== this.addedProduct         )       } else {         return this.getProductsInCart       }     }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u043c\u044b \u043d\u0430\u0436\u043c\u0451\u043c \u043d\u0430 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0442\u043e\u0432\u0430\u0440, \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u043d\u043e \u0437\u0430\u043a\u0440\u044b\u043b\u043e\u0441\u044c (\u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0435 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u044d\u0442\u043e\u0433\u043e \u0442\u043e\u0432\u0430\u0440\u0430). \u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u044d\u0442\u043e \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">  watch: {     $route: function () {       this.$modal.hide('customer-cart')     }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u043e \u0435\u0441\u0442\u044c \u043f\u0440\u0438 \u0441\u043c\u0435\u043d\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <strong>route<\/strong> \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043c\u043e\u0434\u0430\u043b\u043a\u0443.<\/p>\n<p>  <\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043c\u044b \u0441\u043b\u0435\u0434\u0438\u043c \u0437\u0430 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0432 \u043a\u043e\u0440\u0438\u0437\u043d\u0435, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0443\u0434\u0430\u043b\u0438\u043b \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0442\u043e\u0432\u0430\u0440, \u0442\u043e \u043c\u044b \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043c\u043e\u0434\u0430\u043b\u043a\u0443.<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">  watch: { ...     getProductsInCart: function (newVal, oldVal) {       if (oldVal.length &gt; 0) {         if (this.getProductsInCart.length === 0) {           this.$modal.hide('customer-cart')         }       }     } ...<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"sozdayom-productslist\">\u0421\u043e\u0437\u0434\u0430\u0451\u043c ProductsList<\/h3>\n<p>  <\/p>\n<p>\u0412 \u043d\u0451\u043c \u043d\u0443\u0436\u043d\u044b \u0442\u0430\u043a\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0442\u043e\u0432\u0430\u0440\u0430<\/li>\n<li>\u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0442\u043e\u0432\u0430\u0440\u0430 (\u0441 debounce \u0442\u043e \u0435\u0441\u0442\u044c \u0441 \u0443\u043c\u043d\u043e\u0439 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u043e\u0439, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0447\u043d\u0451\u0442 \u0442\u044b\u043a\u0430\u0442\u044c \u043f\u043e \u0441\u0442\u0440\u0435\u043b\u043a\u0435 \u0432\u0432\u0435\u0440\u0445)<\/li>\n<li>\u043f\u043e\u0434\u0441\u0447\u0435\u0442 \u0441\u0443\u043c\u043c\u044b<\/li>\n<\/ul>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">ProductsList.vue<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"xml\">&lt;template&gt;   &lt;div v-if=&quot;productsFromCart.length &gt; 0&quot; :class=&quot;$style.wrapper&quot;&gt;     &lt;div       v-for=&quot;product in productsFromCart&quot;       :key=&quot;product.productId&quot;       :class=&quot;$style.product&quot;     &gt;       &lt;template&gt;         &lt;CloseOrDeleteButton           :class=&quot;$style.remove&quot;           button-type=&quot;delete&quot;           @click.native=&quot;onRemoveClickHandler(product)&quot;         \/&gt;         &lt;nuxt-link :to=&quot;`\/product\/${product.meta.pSlug}`&quot;&gt;           &lt;img             v-lazy=&quot;product.meta.images.imgL&quot;             :class=&quot;$style.image&quot;           \/&gt;         &lt;\/nuxt-link&gt;         &lt;nuxt-link :class=&quot;$style.pName&quot; :to=&quot;`\/product\/${product.meta.pSlug}`&quot;&gt;           &lt;p&gt;{{ product.meta.pName }}&lt;\/p&gt;         &lt;\/nuxt-link&gt;         &lt;div&gt;           &lt;p&gt;Price: &lt;\/p&gt;           &lt;p&gt;{{ product.meta.pPrice }}&lt;\/p&gt;         &lt;\/div&gt;         &lt;div&gt;           &lt;p&gt;Quantity:&lt;\/p&gt;           &lt;input             :value=&quot;product.qty&quot;             :class=&quot;$style.input&quot;             type=&quot;number&quot;             :min=&quot;1&quot;             :max=&quot;1000&quot;             @change.prevent=&quot;onQuantityChangeHandler($event, product)&quot;           \/&gt;         &lt;\/div&gt;         &lt;div&gt;           &lt;p&gt;Amount:&lt;\/p&gt;           &lt;p&gt;{{ (product.meta.pPrice * product.qty) | round }}&lt;\/p&gt;         &lt;\/div&gt;       &lt;\/template&gt;     &lt;\/div&gt;   &lt;\/div&gt; &lt;\/template&gt;  &lt;script&gt; import CloseOrDeleteButton from '~~\/components\/common\/input\/CloseOrDeleteButton.vue' import round from '~~\/mixins\/round' import { mapActions } from 'vuex' import debounce from 'lodash.debounce' export default {   components: {     CloseOrDeleteButton   },   mixins: [round],   props: {     productsFromCart: {       type: Array,       default: () =&gt; []     }   },   methods: {     ...mapActions({       setProductQuantity: 'cart\/setProductQuantity',       removeProduct: 'cart\/removeProduct'     }),     onRemoveClickHandler (product) {       this.removeProduct(product.productId)     },     onQuantityChangeHandler: debounce(function onQuantityChangeHandler (e, product) {       const qty = e.target.value       this.setProductQuantity({ productId: product.productId, qty })     }, 400)    } } &lt;\/script&gt;  &lt;style lang=&quot;scss&quot; module&gt; .input {   height: 20px; }     .remove {       top: -15px;       position: absolute;       left: -30px;       z-index: 1;     } .wrapper {   display: flex;   flex-wrap: wrap;   flex-direction: column;   .product {     position: relative;     margin: 1em;     display: flex;     flex-direction: row;      * {       margin-right: 10px;     }     .pName {       width: 150px;     }   }    p {     max-width: 270px;     height: 35px;   } } .image {   width: 75px;   height: 75px;   object-fit: cover; } &lt;\/style&gt; <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u044b 2 actions \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">    ...mapActions({       setProductQuantity: 'cart\/setProductQuantity',       removeProduct: 'cart\/removeProduct'     }),<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c <\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">    onQuantityChangeHandler: debounce(function onQuantityChangeHandler (e, product) {       const qty = e.target.value       this.setProductQuantity({ productId: product.productId, qty })     }, 400)<\/code><\/pre>\n<p>  <\/p>\n<p>\u041a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <strong>e.target.value<\/strong> \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <strong>setProductQuantity<\/strong>. \u0418 \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u0432\u0441\u0451 \u043e\u0431\u0432\u0435\u0440\u043d\u0451\u043c \u0432 <strong>debounce<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044f \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043a\u0430\u0447\u0430\u043b \u0438\u0437 lodash <\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import debounce from 'lodash.debounce'<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0430\u043a \u0436\u0435 \u0442\u0443\u0442 \u043c\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043c\u0438\u043a\u0441\u0438\u043d <strong>round<\/strong> \u0434\u043b\u044f \u043e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u044f \u0441\u0443\u043c\u043c\u044b \u0438 \u043c\u043e\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <strong>CloseOrDeleteButton<\/strong><\/p>\n<p>  <\/p>\n<p>\u041a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">CloseOrDeleteButton.vue<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"xml\">&lt;template&gt;   &lt;div class=&quot;svg-icon-block&quot;&gt;     &lt;SvgClose :class=&quot;{'svg-icon-close': buttonType === 'close', 'svg-icon-delete': buttonType === 'delete'}&quot; \/&gt;   &lt;\/div&gt; &lt;\/template&gt;  &lt;script&gt; import SvgClose from '~~\/assets\/svg\/baseline-close-24px.svg?inline' export default {   components: {     SvgClose   },   props: {     buttonType: {       type: String,       default: 'close'     }   } } &lt;\/script&gt;  &lt;style lang=&quot;scss&quot; scoped&gt; .svg-icon-delete {   \/\/ background: #ddd;   fill: #ffb2a9;   border: 3px solid #ffb2a9;   transition: all .3s ease;     width: 20px;   height: 20px;   &amp;:hover {     \/\/ background: #ddd;     fill: #fb3f4c;     border-color: #fb3f4c;   }     @media (--mobile) {     width: 20px;     height: 20px;     border-width: 3px;   } } .svg-icon-close {   background: hsl(0, 0%, 60%);   fill: #fff;   border: 8px solid hsl(0, 0%, 60%);     width: 20px;   height: 20px;   &amp;:hover {     background: hsl(0, 0%, 33%);     fill: #fff;     border-color :hsl(0, 0%, 33%);   } } .svg-icon-block {   display: block;   cursor: pointer; } svg {   border-radius: 100%;   opacity: 0.7;   line-height: 0;    box-sizing: content-box;   \/\/ \/\/ noselect   \/\/ -webkit-user-select: none; \/* webkit (safari, chrome) browsers *\/   \/\/ -moz-user-select: none; \/* mozilla browsers *\/   \/\/ -khtml-user-select: none; \/* webkit (konqueror) browsers *\/   \/\/ -ms-user-select: none; \/* IE10+ *\/  } &lt;\/style&gt; <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435, \u0447\u0442\u043e \u0442\u0443\u0442 \u043f\u0440\u0438\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u2014 \u044d\u0442\u043e \u0442\u043e \u043a\u0430\u043a \u043c\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c svg<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import SvgClose from '~~\/assets\/svg\/baseline-close-24px.svg?inline'<\/code><\/pre>\n<p>  <\/p>\n<p><strong>?inline<\/strong> \u0433\u043e\u0432\u043e\u0440\u0438\u0442, \u0447\u0442\u043e \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u044d\u0442\u043e\u0442 svg \u0432 \u0432\u0438\u0434\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430, \u0447\u0442\u043e \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0432 html \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044f \u043d\u0430\u043c \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0442\u0438\u043b\u0438 (\u0442\u043e \u0435\u0441\u0442\u044c \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043c\u0435\u043d\u044f\u0442\u044c \u0446\u0432\u0435\u0442 \u044d\u0442\u043e\u0433\u043e svg).<\/p>\n<p>  <\/p>\n<h3 id=\"v-itoge\">\u0412 \u0438\u0442\u043e\u0433\u0435<\/h3>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/9c\/_k\/vo\/9c_kvoonhk1jp8ergjwgz3socqw.png\"><\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u043d\u043e \u0441 \u0442\u043e\u0432\u0430\u0440\u0430\u043c\u0438. \u0411\u043e\u043d\u0443\u0441\u043e\u043c \u0438\u0434\u0451\u0442 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u0432\u043a\u043b\u0430\u0434\u043a\u0430\u043c\u0438 (\u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u044c 2 \u0432\u043a\u043b\u0430\u0434\u043a\u0438 \u0438 \u043c\u0435\u043d\u044f\u0442\u044c \u043c\u043e\u0434\u0430\u043b\u043a\u0443, \u0432\u0441\u0451 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0437\u0430 \u0441\u0447\u0435\u0442 \u043e\u0431\u0449\u0435\u0433\u043e LocalStorage).<\/p>\n<p>  <\/p>\n<h3 id=\"dobavim-ikonku-korziny\">\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0438\u043a\u043e\u043d\u043a\u0443 \u043a\u043e\u0440\u0437\u0438\u043d\u044b<\/h3>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u0444\u0430\u0439\u043b<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">components\\header\\CartButton.vue<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"xml\">&lt;template&gt;   &lt;div :class=&quot;$style.block&quot;&gt;     &lt;client-only&gt;       &lt;a         :href=&quot;'#'&quot;         :class=&quot;$style.cartButton&quot;         :disabled=&quot;!productsQuantity &gt; 0 &quot;         @click.prevent=&quot;onClickHandler&quot;       &gt;         &lt;div v-if=&quot;productsQuantity &gt; 0&quot; :class=&quot;$style.quantity&quot;&gt;           {{ productsQuantity }}         &lt;\/div&gt;         &lt;CartSvg :class=&quot;$style.svg1&quot; \/&gt;       &lt;\/a&gt;     &lt;\/client-only&gt;   &lt;\/div&gt; &lt;\/template&gt; &lt;script&gt; import { mapState } from 'vuex' import CartSvg from '~~\/assets\/svg\/shopping-cart.svg?inline' export default {   components: {     CartSvg   },   computed: {     ...mapState({       products: state =&gt; state.cart.products     }),     productsQuantity () {       if (this.products) {         return this.products.length       } else return 0     }   },   methods: {     onClickHandler () {       this.$modal.show('customer-cart')     }   } } &lt;\/script&gt;  &lt;style lang=&quot;scss&quot; module&gt; .block {   position: relative; } .cartButton {   position: relative;   display: flex;   flex-direction: row;   justify-content: center;   align-items: center;   width: 68px;   height: 72px;   text-align: center;   transition: all 0.3s ease-in-out; } .svg1 {   margin-right: 3px;   width: 40px;   fill: #000;   \/\/ noselect   -webkit-user-select: none; \/* webkit (safari, chrome) browsers *\/   -moz-user-select: none; \/* mozilla browsers *\/   -khtml-user-select: none; \/* webkit (konqueror) browsers *\/   -ms-user-select: none; \/* IE10+ *\/ } .quantity {     position: absolute;     right: 5px;     top: 5px;     border-radius: 50px;     background-color: #fb3f4c;     width: 20px;     color: #fff;     height: 20px;     text-align: center;     line-height: 20px;     font-size: .8rem;     font-weight: 600;   \/\/ noselect   -webkit-user-select: none; \/* webkit (safari, chrome) browsers *\/   -moz-user-select: none; \/* mozilla browsers *\/   -khtml-user-select: none; \/* webkit (konqueror) browsers *\/   -ms-user-select: none; \/* IE10+ *\/ } &lt;\/style&gt; <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0418 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0435\u0433\u043e \u0432 \u043d\u0430\u0448 Header. <\/p>\n<p>  <\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0432\u0430\u0440\u0430 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435 (\u0431\u0435\u0437 \u043c\u0435\u0442\u0430-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438) \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u043e\u0432\u0430\u0440\u044b \u043d\u0435 \u0447\u0435\u0440\u0435\u0437 \u0433\u0435\u0442\u0442\u0435\u0440, \u0430 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">    ...mapState({       products: state =&gt; state.cart.products     }),<\/code><\/pre>\n<p>  <\/p>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442, \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0432\u0430\u0440\u0430 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435 \u0438 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u043d\u0430\u0448\u0443 \u043c\u043e\u0434\u0430\u043b\u043a\u0443 \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043e\u043d \u0442\u0430\u043a<br \/>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ln\/2r\/yw\/ln2rywtf74mqq-isiwaguwmvgx0.png\"><\/p>\n<p>  <\/p>\n<h3 id=\"itogi\">\u0418\u0442\u043e\u0433\u0438<\/h3>\n<p>  <\/p>\n<ul>\n<li><strong>\u041a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/strong>: \u043d\u0430 Github <a href=\"https:\/\/github.com\/AntonMoskalchenko\/nuxt-ecommerce\" rel=\"nofollow\">\u0442\u044b\u0446<\/a>.<\/li>\n<li><strong>\u041f\u043e\u0442\u044b\u043a\u0430\u0442\u044c <\/strong>: <a href=\"https:\/\/nuxt-ecommerce.herokuapp.com\/\" rel=\"nofollow\">\u0442\u044b\u0446<\/a>.<\/li>\n<li><a href=\"https:\/\/habr.com\/ru\/post\/490496\/\">1 \u0447\u0430\u0441\u0442\u044c<\/a><\/li>\n<li><a href=\"https:\/\/habr.com\/ru\/post\/491018\/\">2 \u0447\u0430\u0441\u0442\u044c<\/a><\/li>\n<li><a href=\"https:\/\/habr.com\/ru\/post\/491190\/\">3 \u0447\u0430\u0441\u0442\u044c<\/a><\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u042d\u0442\u043e \u0442\u0440\u0435\u0442\u044c\u044f \u0447\u0430\u0441\u0442\u044c \u043c\u043e\u0435\u0433\u043e \u043d\u0435\u0437\u0430\u043c\u044b\u0441\u043b\u043e\u0432\u0430\u0442\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f Nuxt.<br \/>  \u041c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u0431\u043b\u043e\u043a\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432, \u043f\u0435\u0440\u0435\u0434\u0435\u043b\u0430\u043b\u0438 \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u043e\u0440\u0437\u0438\u043d\u043e\u0439, \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043c\u043e\u0434\u0430\u043b\u043a\u0438.<br \/>  \u0422\u0435\u043f\u0435\u0440\u044c \u044d\u0442\u043e \u0447\u0443\u0434\u043e \u0432\u0441\u0451 \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u043c\u0430\u0433\u0430\u0437\u0438\u043d.<br \/>  \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e \u0432 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0443\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0442\u0430\u043a\u043e\u0439 \u043e\u0431\u044a\u0435\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u044b\u043b \u0437\u0430\u0434\u0443\u043c\u0430\u043d \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043a\u0430\u0437\u0430 (\u0438 \u0432\u0441\u0451 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435) \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u0443\u0436\u0435 \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438.<\/p>\n<p>  <\/p>\n<h3 id=\"posleslovie\">\u041f\u043e\u0441\u043b\u0435\u0441\u043b\u043e\u0432\u0438\u0435<\/h3>\n<p>  <\/p>\n<p>\u0411\u0443\u0434\u0443 \u0440\u0430\u0434 \u0432\u044b\u0441\u043b\u0443\u0448\u0430\u0442\u044c \u0432\u0430\u0448\u0438 \u043f\u043e\u0436\u0435\u043b\u0430\u043d\u0438\u044f \u0447\u0442\u043e \u043f\u0440\u0438\u043a\u0440\u0443\u0442\u0438\u0442\u044c \u043a \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0443, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u0430 \u0442\u0430\u043a \u0436\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b \u0438 \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043f\u043b\u044e\u0441\u0438\u043a\u0430\u043c\u0438, \u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u0437\u0430\u0448\u043b\u043e.<\/p>\n<p>  <\/p>\n<p>\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0447\u0442\u0435\u043d\u0438\u0435!<\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/491190\/\"> https:\/\/habr.com\/ru\/post\/491190\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/491190\/\">\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/bz\/t1\/ds\/bzt1dsg3xirl4vj059ehw0wzhs4.jpeg\"><\/p>\n<p>  <\/p>\n<p>\u041a\u0430\u043a \u0438 \u043e\u0431\u0435\u0449\u0430\u043b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c.<\/p>\n<p>  <\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0431\u043b\u043e\u043a\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 &quot;\u0421 \u044d\u0442\u0438\u043c \u0442\u043e\u0432\u0430\u0440\u043e\u043c \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043a\u0443\u043f\u0430\u044e\u0442&quot; \u0438 &quot;\u0418\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u0442\u043e\u0432\u0430\u0440\u044b&quot;<\/li>\n<li>\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0438\u043a\u043e\u043d\u043a\u0443 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0441 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0442\u043e\u0432\u0430\u0440\u043e\u0432<\/li>\n<li>\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u043d\u043e \u0441 \u0442\u043e\u0432\u0430\u0440\u0430\u043c\u0438 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435<\/li>\n<li>\u043f\u0435\u0440\u0435\u043f\u0438\u0448\u0435\u043c \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 store<br \/>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-299750","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/299750","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=299750"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/299750\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=299750"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=299750"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=299750"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}