{"id":299685,"date":"2020-03-05T03:00:09","date_gmt":"2020-03-05T03:00:09","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=299685"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=299685","title":{"rendered":"\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441\u0438\u0432\u043d\u044b\u0439 PWA \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 2"},"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\/491018\/\">\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/bn\/s2\/ea\/bns2eafwvg8dzlyx7i2ng0wmpsw.jpeg\"><\/p>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u0447\u0430\u0441\u0442\u044c <a href=\"https:\/\/habr.com\/ru\/post\/490496\/\">\u0442\u0443\u0442 <\/a><\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043d\u0430\u0448\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0430. \u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0431\u0443\u0434\u0435\u0442:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u043f\u043e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0430\u0434\u0440\u0435\u0441\u0430\u043c<\/li>\n<li>\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0445\u043b\u0435\u0431\u043d\u044b\u0445 \u043a\u0440\u043e\u0448\u0435\u043a \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435<\/li>\n<li>\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0442\u043e\u0432\u0430\u0440\u0430<\/li>\n<li>\u0448\u0430\u043f\u043a\u0430<\/li>\n<li>\u0440\u0430\u0431\u043e\u0447\u0430\u044f \u043a\u043d\u043e\u043f\u043a\u0430 \u043a\u0443\u043f\u0438\u0442\u044c \u0441 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u043c\u0435\u0436\u0434\u0443 \u0432\u043a\u043b\u0430\u0434\u043a\u0430\u043c\u0438 (\u0438 \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438)<\/li>\n<\/ul>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<p>\u041a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0432\u044b\u0442\u0430\u0441\u043a\u0438\u0432\u0430\u0442\u044c \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0441 Unsplash \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0430\u044f \u0438\u0434\u0435\u044f. \u041b\u0443\u0447\u0448\u0435 \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0432 \u0442\u043e\u0432\u0430\u0440\u0430\u0445 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 (\u0434\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438). \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043d\u0443\u0436\u043d\u044b \u0430\u0434\u0440\u0435\u0441\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u0438 \u043f\u043e\u043b\u043e\u0436\u0438\u043c \u0438\u0445 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442.<\/p>\n<p>  <\/p>\n<h3 id=\"pishem-zagruzchik\">\u041f\u0438\u0448\u0435\u043c &quot;\u0437\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a&quot;<\/h3>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a.<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const products = require('..\/static\/mock\/products.json')  const got = require('got') const QS = require('querystring') const API_KEY = ''  const fs = require('fs') const { promisify } = require('util')  const writeFileAsync = promisify(fs.writeFile)  async function fetchApiImg (searchQuery) {   try {     const query = QS.stringify({ key: API_KEY, q: searchQuery, per_page: '3', image_type: 'photo' })     const resPr = got(`https:\/\/pixabay.com\/api\/?${query}`)     const json = await resPr.json()     if (json.hits &amp;&amp; json.hits.length &gt; 0 &amp;&amp; json.hits[0].largeImageURL &amp;&amp; json.hits[0].webformatURL) {       return {         imgXL: json.hits[0].largeImageURL,         imgL: json.hits[0].webformatURL       }     } else {       throw 'no image'     }   } catch (error) {     return {       imgXL: null,       imgL: null     }   } } async function getImagesUrls () {   const imagesUrl = []   await Promise.all(     products.map(async product =&gt; {       const productName = product.pName.split(' ')[0]       const imgUrls = await fetchApiImg(productName)       imagesUrl.push({ id: product.id, urls: imgUrls })     })   )   return imagesUrl } async function main () {   try {     const imagesUrls = await getImagesUrls()     await writeFileAsync('.\/static\/mock\/products-images.json', JSON.stringify(imagesUrls), { flag: 'w+' })   } catch (error) {     console.log(error)   } } main() <\/code><\/pre>\n<p>  <\/p>\n<p><strong>API_KEY<\/strong> \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u0432\u043e\u0439 (\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0435\u0433\u043e \u043e\u0442 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0437\u0430 1 \u043c\u0438\u043d).<br \/>  \u0427\u0442\u043e \u043f\u0440\u0438\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0442\u0430\u043a \u044d\u0442\u043e \u0442\u043e \u0447\u0442\u043e \u0431\u0435\u0437 \u043e\u0441\u043e\u0431\u043e\u0439 \u0432\u043e\u0437\u043d\u0438, \u043d\u0430 \u043c\u043e\u0435\u043c \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0435 \u044d\u0442\u043e\u0442 \u0441\u043a\u0440\u0438\u043f\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0437\u0430 1 \u0441\u0435\u043a, \u0430 \u044d\u0442\u043e 500 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 (\u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0442\u0430\u043a\u043e\u0439 ddos).<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0442\u0435\u0445 \u043a\u0442\u043e \u043d\u0435 \u043e\u0441\u043e\u0431\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442 \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441\u043a\u0440\u0438\u043f\u0442 \u0432\u043e\u0442 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0435 \u043e\u0431\u044a\u044f\u0441\u043d\u0435\u043d\u0438\u0435:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041f\u0440\u043e \u0441\u043a\u0440\u0438\u043f\u0442<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">  await Promise.all(     products.map(async product =&gt; {       const productName = product.pName.split(' ')[0]       const imgUrl = await fetchApiImg(productName)       imagesUrl.push({ id: product.id, url: imgUrl })     })   )<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0435 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0438\u0437 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u043c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 url \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 (\u0434\u0435\u043b\u0430\u044f \u0437\u0430\u043f\u0440\u043e\u0441), \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0433\u043e \u0432 \u043c\u0430\u0441\u0441\u0438\u0432 <strong>imagesUrl <\/strong>\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 (\u043d\u0435\u044f\u0432\u043d\u043e) Promise. <strong>await Promise.all<\/strong> \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0447\u0442\u043e \u043c\u044b \u0436\u0434\u0451\u043c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0445 \u043f\u0440\u043e\u043c\u0438\u0441\u043e\u0432 \u0438 \u0434\u0432\u0438\u0433\u0430\u0435\u043c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<p>  <\/p>\n<p><strong>product.pName.split(&#8216; &#8216;)[0]<\/strong> \u2014 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u043e\u0435 \u0441\u043b\u043e\u0432\u043e \u0438\u0437 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0442\u043e\u0432\u0430\u0440\u0430<br \/>  <strong>imagesUrl<\/strong> \u044d\u0442\u043e\u0442 \u043c\u0430\u0441\u0441\u0438\u0432 \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c id \u0442\u043e\u0432\u0430\u0440\u0430 \u0438 url \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0438 \u0434\u043b\u044f \u043d\u0435\u0433\u043e.<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">    const query = QS.stringify({ key: API_KEY, q: searchQuery, per_page: '3', image_type: 'photo' })     const resPr = got(`https:\/\/pixabay.com\/api\/?${query}`)     const json = await resPr.json()     if (json.hits &amp;&amp; json.hits.length &gt; 0 &amp;&amp; json.hits[0].largeImageURL &amp;&amp; json.hits[0].webformatURL) {       return {         imgXL: json.hits[0].largeImageURL,         imgL: json.hits[0].webformatURL       }     } else {       throw 'no image'     }<\/code><\/pre>\n<p>  <\/p>\n<p><strong>QS.stringify<\/strong> \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0430\u044f \u043d\u043e\u0434\u043e\u0432\u0441\u043a\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f <strong>querystring <\/strong> \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f (\u0438 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e) \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (\u043c\u043e\u0436\u043d\u043e \u0438 \u0440\u0443\u043a\u0430\u043c\u0438 \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c, \u043d\u043e \u0437\u0430\u0447\u0435\u043c?)<br \/>  <strong>got(<code>https:\/\/pixabay.com\/api\/?${query}<\/code>)<\/strong> \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0440\u043e\u043c\u0438\u0441 \u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430<br \/>  <strong> await resPr.json()<\/strong> \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0435\u0433\u043e, \u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u0430\u0440\u0441\u0438\u043c \u043a\u0430\u043a json<\/p>\n<\/div>\n<\/div>\n<p>  <\/p>\n<h3 id=\"pravim-store-dlya-kartinok\">\u041f\u0440\u0430\u0432\u0438\u043c Store \u0434\u043b\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a<\/h3>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u044d\u0442\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438. <\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">store\/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 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 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 } export const state = () =&gt; ({   categoriesList: [],   currentCategory: {},   currentProduct: {} }) 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   } } export const actions = {   async getCategoriesList ({ commit }) {     try {       await sleep(1000)       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 }, { route }) {     await sleep(1000)     const category = categories.find((cat) =&gt; cat.cSlug === route.params.CategorySlug)      const [products, productsImages] = await Promise.all(       [         await this.$axios.$get('\/mock\/products.json'),         await this.$axios.$get('\/mock\/products-images.json')       ]     )      await commit('SET_CURRENT_CATEGORY', addProductsToCategory(products, productsImages, category))   } } <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0422\u0443\u0442 \u043f\u0440\u0438\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u044d\u0442\u043e\u0442 \u043a\u0443\u0441\u043e\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">    const [products, productsImages] = await Promise.all(       [         await this.$axios.$get('\/mock\/products.json'),         await this.$axios.$get('\/mock\/products-images.json')       ]     )<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u043e \u0431\u0430\u0442\u0447\u0430\u0442\u0441\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a api. \u041c\u043e\u0436\u043d\u043e \u0445\u043e\u0442\u044c 20 \u0448\u0442\u0443\u043a \u0442\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438 \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e.<\/p>\n<p>  <\/p>\n<p>\u0418 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 Vue \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0447\u0443\u0442\u043e\u043a \u043f\u043e\u0434\u043f\u0440\u0430\u0432\u0438\u0442\u044c (\u043d\u0435 \u0431\u0443\u0434\u0443 \u0442\u0443\u0442 \u043f\u0438\u0441\u0430\u0442\u044c).<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u043c \u0432 \u0438\u0442\u043e\u0433\u0435 \u0447\u0442\u043e-\u0442\u043e \u0432\u0440\u043e\u0434\u0435:<br \/>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/tl\/tr\/en\/tltren2eawefry1vbt_-0worths.png\"><\/p>\n<p>  <\/p>\n<h3 id=\"davayte-dobavim--shapku\">\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0448\u0430\u043f\u043a\u0443<\/h3>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Header.vue<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">&lt;template&gt;   &lt;div :class=&quot;$style.wrapper&quot;&gt;     &lt;div :class=&quot;$style.header&quot;&gt;       &lt;n-link :class=&quot;$style.logo&quot; to=&quot;\/&quot;&gt;         &lt;p&gt;           \u0425\u0432\u043e\u0441\u0442\u0438\u043a\u0438         &lt;\/p&gt;       &lt;\/n-link&gt;     &lt;\/div&gt;   &lt;\/div&gt; &lt;\/template&gt;  &lt;script&gt; export default {  } &lt;\/script&gt;  &lt;style lang=&quot;scss&quot; module&gt; .wrapper {     background-color: $basic-bg-color;     height: 70px; } .header {   @include globalWrapper;   display: flex; } .logo {   font-size: 2.2em;   font-weight: 700;   opacity: 1;    color: #000;    text-decoration: none; } &lt;\/style&gt; <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u0432 default.vue<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">default.vue<\/b><\/p>\n<div class=\"spoiler_text\">   &lt;Header \/&gt;<\/p>\n<p>    &lt;nuxt \/&gt;<\/p>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<p>\u041d\u0430\u0432\u0435\u0440\u043d\u043e\u0435 \u0432\u044b \u0437\u0430\u043c\u0435\u0442\u0438\u043b\u0438 \u0447\u0442\u043e \u043f\u0440\u043e\u043f\u0430\u043b \u0441\u0442\u0438\u043b\u044c \u0434\u043b\u044f <strong>mainWrapper <\/strong>.<br \/>  \u042d\u0442\u043e \u043d\u0435 \u0441\u043f\u0440\u043e\u0441\u0442\u0430, \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u044f \u0432\u0435\u0441\u044c \u0448\u0430\u0431\u043b\u043e\u043d \u0432 \u043d\u0435\u043a\u0438\u0439 max-width \u043c\u044b \u043b\u0438\u0448\u0430\u0435\u043c \u0441\u0435\u0431\u044f \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u0438. \u041a\u0430\u043a \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u0438\u043a\u0441\u0438\u043d \u0432 \u0444\u0430\u0439\u043b\u0435 <strong>global-variables.scss<\/strong>.<br \/>  \u0412\u0438\u0434\u0430<\/p>\n<p>  <\/p>\n<pre><code class=\"css\">@mixin globalWrapper {   max-width: 1280px;   margin: 0 auto;   padding:  0 20px; } $basic-bg-color: #fcc000;<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u0430\u043b\u044c\u0448\u0435 \u043c\u043e\u0436\u043d\u043e \u0432 \u043d\u0443\u0436\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0435 \u043e\u0442\u0441\u0442\u0443\u043f\u044b \u043f\u043e \u0441\u0430\u0439\u0442\u0443. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 Header<\/p>\n<p>  <\/p>\n<pre><code class=\"css\">.header {   @include globalWrapper;   display: flex; }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412\u043e \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u0445 \u043d\u0443\u0436\u043d\u043e \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043c\u0438\u043a\u0441\u0438\u043d \u0432 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u043c\u0435\u0441\u0442\u0430\u0445.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0430\u043a\u0443\u044e \u0448\u0430\u043f\u043a\u0443 \u043d\u0430 \u0434\u0435\u0441\u043a\u0442\u043e\u043f\u0435:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/29\/nf\/-a\/29nf-adp_6vk1xbfbiqw4gncf-e.png\"><\/p>\n<p>  <\/p>\n<p>\u0418 \u043d\u0430 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u043c:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/le\/kx\/cy\/lekxcyrax3f8ov1en-l_ldc55ty.png\"><\/p>\n<p>  <\/p>\n<h3 id=\"sozdayom-stranicu-s-tovarom\">\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441 \u0442\u043e\u0432\u0430\u0440\u043e\u043c<\/h3>\n<p>  <\/p>\n<p>\u041f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435\u0439 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u0441\u043e\u0437\u0434\u0430\u0451\u043c <strong>_ProductSlug.vue<\/strong><\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">_ProductSlug.vue<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">&lt;template&gt;   &lt;div :class=&quot;$style.page&quot;&gt;     &lt;div :class=&quot;$style.topBlock&quot;&gt;       &lt;div :class=&quot;$style.topLeftBlock&quot;&gt;         &lt;a :href=&quot;product.images.imgXL&quot; target=&quot;_blank&quot;&gt;           &lt;img             v-lazy=&quot;product.images.imgL&quot;             :class=&quot;$style.image&quot;           \/&gt;         &lt;\/a&gt;       &lt;\/div&gt;       &lt;div :class=&quot;$style.topRightBlock&quot;&gt;         &lt;h1&gt;{{ product.pName }}&lt;\/h1&gt;         &lt;p&gt;\u0426\u0435\u043d\u0430: {{ product.pPrice }}&lt;\/p&gt;       &lt;\/div&gt;     &lt;\/div&gt;     &lt;h2&gt;\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435&lt;\/h2&gt;     &lt;p&gt;{{ product.pDesc }}&lt;\/p&gt;   &lt;\/div&gt; &lt;\/template&gt;  &lt;script&gt; import { mapState } from 'vuex' export default {   async asyncData ({ app, params, route, error }) {     try {       await app.store.dispatch('getCurrentProduct', { route })     } catch (err) {       console.log(err)       return error({         statusCode: 404,         message: '\u0422\u043e\u0432\u0430\u0440 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u0438\u043b\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d'       })     }   },   computed: {     ...mapState({       product: 'currentProduct'     })   },   head () {     return {       title: this.product.pTitle,       meta: [         {           hid: 'description',           name: 'description',           content: this.product.pMetaDescription         }       ]     }   } } &lt;\/script&gt; &lt;style lang=&quot;scss&quot; module&gt; .page {   @include globalWrapper; } .image {   width: 400px;   height: auto; } .topBlock {   padding-top: 2em;   display: flex;   .topLeftBlock {     display: flex;   }   .topRightBlock {     padding-left: 2em;     display: flex;     flex-direction: column;   } } &lt;\/style&gt; <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0418\u0437 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u0432\u0435 \u0447\u0442\u043e, \u0442\u043e \u0447\u0442\u043e \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u043a\u043b\u0438\u043a\u0430\u0431\u0435\u043b\u044c\u043d\u0430\u044f <\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">        &lt;a :href=&quot;product.images.imgXL&quot; target=&quot;_blank&quot;&gt;           &lt;img             v-lazy=&quot;product.images.imgL&quot;             :class=&quot;$style.image&quot;           \/&gt;         &lt;\/a&gt;<\/code><\/pre>\n<p>  <\/p>\n<p>\u0413\u0434\u0435 <strong>imgL<\/strong> \u0441\u0440\u0435\u0434\u043d\u0435\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435, \u0430 <strong>imgL<\/strong> \u0431\u043e\u043b\u044c\u0448\u043e\u0435.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/d3\/td\/o7\/d3tdo7imbo3pqjoimbuqizgbbi4.png\"><\/p>\n<p>  <\/p>\n<h3 id=\"sozdayom-hlebnye-kroshki\">\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u0445\u043b\u0435\u0431\u043d\u044b\u0435 \u043a\u0440\u043e\u0448\u043a\u0438<\/h3>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0444\u0438\u043b\u043e\u0441\u043e\u0444\u0438\u0438. \u0422\u0430\u043a \u043a\u0430\u043a nuxt \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 meta \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u0432 \u0440\u043e\u0443\u0442\u0430\u0445, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u044f \u0433\u043e\u0432\u043e\u0440\u044e \u0447\u0442\u043e \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 route \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">{                                                                                                                                                                                                 name: 'category-CategorySlug',   meta: [     {}   ],   path: '\/category\/dogs',   hash: '',   query: {},   params: {     CategorySlug: 'dogs'   },   fullPath: '\/category\/dogs',   matched: [     {       path: '\/category\/:CategorySlug?',       regex: \/^\\\/category(?:\\\/((?:[^\\\/]+?)))?(?:\\\/(?=$))?$\/i,       components: [Object],       instances: {},       name: 'category-CategorySlug',       parent: undefined,       matchAs: undefined,       redirect: undefined,       beforeEnter: undefined,       meta: {},       props: {}     }   ] }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u043e \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0432\u044b\u0442\u0430\u0449\u0438\u0442\u044c \u043e\u0442\u0441\u044e\u0434\u0430 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043a\u0440\u043e\u0448\u0435\u043a.<\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u043a\u0430\u043a \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0445, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u043a\u0440\u043e\u0448\u043a\u0438, \u043d\u043e \u0434\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u043e\u0439 \u043c\u0435\u0442\u043e\u0434, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e:<\/p>\n<p>  <\/p>\n<h4 id=\"privedyom-fayl-vuex-k-takomu-vidu\">\u041f\u0440\u0438\u0432\u0435\u0434\u0451\u043c \u0444\u0430\u0439\u043b Vuex \u043a \u0442\u0430\u043a\u043e\u043c\u0443 \u0432\u0438\u0434\u0443:<\/h4>\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 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 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: {},   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 = []   } } export const actions = {   async setBreadcrumbs ({ commit }, data) {     await commit('SET_BREADCRUMBS', data)   },   async getCategoriesList ({ commit }) {     try {       await sleep(300)       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(300)     const category = categories.find((cat) =&gt; cat.cSlug === route.params.CategorySlug)      const [products, productsImages] = await Promise.all(       [         await this.$axios.$get('\/mock\/products.json'),         await 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(300)     const productSlug = route.params.ProductSlug     const [products, productsImages] = await Promise.all(       [         await this.$axios.$get('\/mock\/products.json'),         await this.$axios.$get('\/mock\/products-images.json')       ]      )     const product = getProduct(products, productsImages, productSlug)     const crubms = getBreadcrumbs('product', route, product)     await dispatch('setBreadcrumbs', crubms)     await commit('SET_CURRENT_PRODUCT', product)   }  }<\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0422\u0443\u0442 \u043d\u0443\u0436\u043d\u043e \u0432\u0441\u0451 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u043e \u043f\u0443\u043d\u043a\u0442\u0430\u043c.<br \/>  \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u043a\u0440\u043e\u0448\u0435\u043a<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">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 }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c: \u043c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0435\u0451 1 \u0440\u0430\u0437 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0432 \u043d\u0435\u0451 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 <strong>route<\/strong>, <strong>data <\/strong>(\u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0432\u044b\u0442\u044f\u0433\u0438\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u0430-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0438 <strong>pageType<\/strong> \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0442\u0438\u043f\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u044d\u0442\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e.<\/p>\n<p>  <\/p>\n<p>\u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0442\u043e\u0432\u0430\u0440\u0430 \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0438 \u0441\u0430\u043c\u0443 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044e \u0432 \u043a\u0440\u043e\u0448\u043a\u0438 \u0438 \u0442\u043e\u0432\u0430\u0440 \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043d\u0430 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442.<\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0441\u0430\u043c \u0442\u043e\u0432\u0430\u0440 \u0434\u043e\u043b\u0436\u043d\u0430 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0441\u0432\u043e\u0438\u0445 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f\u0445 (\u0438 \u043e \u043f\u0440\u0430\u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f\u0445 \u0435\u0441\u043b\u0438 \u044d\u0442\u043e\u0442 \u0442\u043e\u0432\u0430\u0440 \u043b\u0435\u0436\u0438\u0442 \u0432 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 2-\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u0438 \u0442\u0434.)<\/p>\n<p>  <\/p>\n<p>\u0422\u043e \u0435\u0441\u0442\u044c \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0442\u043e\u0432\u0430\u0440\u0430 \u043c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u044d\u0442\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0435\u0449\u0451 \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u044b \u043e\u0442 api<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const crubms = getBreadcrumbs('product', route, product)<\/code><\/pre>\n<p>  <\/p>\n<p>\u0427\u0442\u043e \u043d\u0435 \u0441\u0430\u043c\u043e\u0435 \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043e \u0442\u043e\u0432\u0430\u0440\u0435, \u0430 \u043f\u043e\u0442\u043e\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0442\u043e\u0440\u043e\u0439 \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 \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u0442\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u043a\u0440\u043e\u0448\u043a\u0438 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435.<\/p>\n<p>  <\/p>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 \u043c\u044b \u0432 store \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0430\u043a\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443<br \/>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/pn\/eu\/ep\/pneuepgnrcgqdpg5tp06ntk8q9e.png\"><\/p>\n<p>  <\/p>\n<h3 id=\"sozdayom-komponent-hlebnyh-kroshek\">\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0445\u043b\u0435\u0431\u043d\u044b\u0445 \u043a\u0440\u043e\u0448\u0435\u043a<\/h3>\n<p>  <\/p>\n<p>\u0414\u0430\u043b\u044c\u0448\u0435 \u044d\u0442\u043e \u0434\u0435\u043b\u043e \u0442\u0435\u0445\u043d\u0438\u043a\u0438 \u0432\u044b\u0432\u0435\u0441\u0442\u0438 \u044d\u0442\u0443 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <strong>Breadcrumbs.vue<\/strong><\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Breadcrumbs.vue<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">&lt;template&gt;   &lt;div v-if=&quot;bredcrumbs &amp;&amp; bredcrumbs.length &gt; 0&quot; :class=&quot;$style.breadcrumbs&quot;&gt;     &lt;ul&gt;       &lt;li v-for=&quot;cr in bredcrumbs&quot; :key=&quot;cr.url&quot;&gt;         &lt;n-link :to=&quot;cr.url&quot;&gt;           {{ cr.title }}         &lt;\/n-link&gt;       &lt;\/li&gt;     &lt;\/ul&gt;   &lt;\/div&gt; &lt;\/template&gt;  &lt;script&gt; import { mapState } from 'vuex' export default {   computed: {     ...mapState({       bredcrumbs: 'bredcrumbs'     })   } } &lt;\/script&gt;  &lt;style lang=&quot;scss&quot; module&gt; \/* Style the list *\/ .breadcrumbs {   background-color: #eee;   padding: 10px 16px;   ul {     list-style: none;      @include globalWrapper;     li {       display: inline;       font-size: 18px;       + ::before {         padding: 8px;         color: black;         content: '\/\\00a0';       }       a {         color: #0275d8;         text-decoration: none;          &amp;:hover {           color: #01447e;           text-decoration: underline;         }       }     }   } } &lt;\/style&gt; <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u0432 \u043d\u0430\u0448 layout, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/hq\/qb\/u6\/hqqbu6xmzd6r9d38y84frtcykzs.png\"><\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ft\/c-\/97\/ftc-97pjrn38hchjcce0zctihro.png\"><\/p>\n<p>  <\/p>\n<p>\u0422\u0430\u043a \u0436\u0435 \u0438\u0437 \u043b\u043e\u0433\u0438\u043a\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u043c\u044b \u043d\u0435 \u0445\u043e\u0442\u0438\u043c \u0447\u0442\u043e\u0431\u044b \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0432\u044b\u0432\u043e\u0434\u0438\u043b\u0438\u0441\u044c \u044d\u0442\u0438 \u043a\u0440\u043e\u0448\u043a\u0438. \u0415\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432, \u043c\u044b \u0436\u0435 \u0443\u0434\u0430\u0440\u0438\u043c \u0432 \u043b\u043e\u0431 \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u0441\u0442\u044b\u043b\u044c.<\/p>\n<p>  <\/p>\n<h3 id=\"kostyl\">\u041a\u043e\u0441\u0442\u044b\u043b\u044c<\/h3>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c middlware <\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">export default async function ({ store, route }) {   if (route.matched.length &gt; 0) {     await Promise.all(route.matched.map(async ({ name }) =&gt; {       if (name === 'index') {         await store.commit('RESET_BREADCRUMBS')       }     }))   } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0435\u0433\u043e \u0432 nuxt.config.js<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">  router: {     middleware: ['resetBreacrumbs'],     prefetchLinks: false   },<\/code><\/pre>\n<p>  <\/p>\n<p>\u042d\u0442\u043e \u043d\u0435 \u0441\u0430\u043c\u043e\u0435 \u043b\u0443\u0447\u0448\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043d\u043e \u043a\u0430\u043a \u044f \u0443\u0436\u0435 \u0432\u044b\u0448\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u2014 \u043a\u043e\u0441\u0442\u044b\u043b\u044c. <\/p>\n<p>  <\/p>\n<p>\u0427\u0442\u043e \u044d\u0442\u043e \u0437\u0430 \u043c\u0430\u0433\u0430\u0437\u0438\u043d \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u043a\u0443\u043f\u0438\u0442\u044c, \u0441\u0440\u043e\u0447\u043d\u043e \u043f\u0438\u0448\u0435\u043c \u043a\u043e\u0440\u0437\u0438\u043d\u0443.<\/p>\n<p>  <\/p>\n<h3 id=\"korzina\">\u041a\u043e\u0440\u0437\u0438\u043d\u0430<\/h3>\n<p>  <\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0430\u0441 \u043d\u0435\u0442 api \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0441\u0435\u0441\u0441\u0438\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432\u0441\u0451 \u0432 <strong>localStorage<\/strong><br \/>  \u041c\u043e\u0436\u043d\u043e \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e, \u0442\u0430\u043a \u043a\u0430\u043a Vuex \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 watcher\u044b \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f. \u041c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u0442\u0435\u0439\u0442\u0430 \u0437\u0430\u043d\u043e\u0441\u0438\u0442\u044c \u0435\u0433\u043e \u0432 localStorage, \u043d\u043e \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u043c\u044b \u043f\u043e\u0442\u043e\u043c \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0430 \u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0431\u0443\u0434\u0435\u0442 \u043d\u0435\u0432\u0435\u0440\u043d\u0430\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f, \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u043b\u0438 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043f\u0438\u0441\u0430\u0442\u044c, \u0438\u043b\u0438 \u0432\u0435\u0440\u0441\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u0415\u0441\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c.<\/p>\n<p>  <\/p>\n<h3 id=\"nuxt-vuex-localstorage\">nuxt-vuex-localstorage<\/h3>\n<p>  <\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043f\u0440\u043e\u0435\u043a\u0442 <strong>nuxt-vuex-localstorage<\/strong> \u0438 \u0432 nuxt.config.js \u0432 \u043c\u043e\u0434\u0443\u043b\u044f\u0445 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0435\u0433\u043e<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">    ['nuxt-vuex-localstorage', {       ...(isDev &amp;&amp; {         mode: 'debug'       }),       localStorage: ['cart'] \/\/  If not entered, \u201clocalStorage\u201d is the default value     }]<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u044d\u0442\u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u044c \u043d\u0435 \u0445\u0438\u0442\u0440\u043e \u0448\u0438\u0444\u0440\u0443\u0435\u0442 localStorage \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 (\u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432 \u0446\u0435\u043b\u044f\u0445 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438, \u0447\u0442\u043e\u0431\u044b &quot;\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0435&quot; \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0442\u0430\u043a \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438), \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u044d\u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <strong>mode: &#8216;debug&#8217;<\/strong>. \u0422\u0430\u043a\u0436\u0435 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0430\u0448 \u043c\u043e\u0434\u0443\u043b\u044c Vuex \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u044d\u0442\u043e\u0442 \u043f\u043b\u0430\u0433\u0438\u043d \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c <strong>cart<\/strong>.<\/p>\n<p>  <\/p>\n<h3 id=\"sozdadim-novyy-vuex-modul\">\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u044b\u0439 Vuex \u043c\u043e\u0434\u0443\u043b\u044c<\/h3>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">cart.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))  export const state = () =&gt; ({   products: [],   version: '0.0.1'  }) export const mutations = {   ADD_PRODUCT (state, product) {     \/\/ if cart doesn't have product add it     if (!state.products.find(p =&gt; product.id === p.id)) {       state.products = [...state.products, product]     }   },   SET_PRODUCT (state, { productId, data }) {     state.products = [...state.products.filter(prod =&gt; prod.id !== productId), data]   },   REMOVE_PRODUCT (state, productId) {     state.products = Array.from(state.products.filter(prod =&gt; prod.id !== productId))   }  } export const actions = {   async addProduct ({ commit }, data) {     await sleep(300)     await commit('ADD_PRODUCT', data)   },   async removeProduct ({ commit }, productId) {     await sleep(300)     await commit('REMOVE_PRODUCT', productId)   }  } <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p><strong>version: &#8216;0.0.1&#8217;<\/strong> \u044d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0441\u0442\u0435\u0439\u0442\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u0439 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e storage \u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0443 \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0430\u0439\u0442\u0430. <\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043a\u0430 \u0447\u0442\u043e \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u043e\u0432\u0430\u0440\u044b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0442\u043e\u0432\u0430\u0440\u043e\u0432.<\/p>\n<p>  <\/p>\n<h3 id=\"sozdayom-knopku-kupit\">\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043a\u043d\u043e\u043f\u043a\u0443 \u043a\u0443\u043f\u0438\u0442\u044c<\/h3>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">BuyButton.vue<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">&lt;template&gt;   &lt;div v-if=&quot;product&quot;&gt;     &lt;button       v-if=&quot;!isProductAdded&quot;       :class=&quot;$style.buy&quot;       @click.prevent=&quot;buyClickHandler&quot;     &gt;       \u041a\u0443\u043f\u0438\u0442\u044c     &lt;\/button&gt;     &lt;a       v-else       :class=&quot;$style.added&quot;       href=&quot;#&quot;       @click.prevent=&quot;addedClickHandler&quot;     &gt;       \u0422\u043e\u0432\u0430\u0440 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435     &lt;\/a&gt;   &lt;\/div&gt; &lt;\/template&gt;  &lt;script&gt; import { mapActions, mapState } from 'vuex'  export default {   props: {     product: {       type: Object,       required: true     }   },   computed: {     ...mapState({       products: state =&gt; state.cart.products     }),     isProductAdded () {       return this.products.find(p =&gt; p.id === this.product.id)     }   },   methods: {     ...mapActions({       addProduct: 'cart\/addProduct',       removeProduct: 'cart\/removeProduct'     }),     buyClickHandler () {       this.addProduct(this.product)     },     addedClickHandler () {       this.removeProduct(this.product.id)     }   } } &lt;\/script&gt;  &lt;style lang=&quot;scss&quot; module&gt; .buy {   background-color: $basic-bg-color; \/* Green *\/   border: none;   color: #000;   padding: 15px 32px;   text-align: center;   text-decoration: none;   display: inline-block;   font-size: 16px;    &amp;:hover {     cursor: pointer;   } } .added {   text-decoration: none;   border-bottom: 2px dotted; } &lt;\/style&gt; <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u043c\u043e\u043c\u0435\u043d\u0442\u043e\u0432. <\/p>\n<p>  <\/p>\n<ul>\n<li>\u0432 mapActions \u043c\u044b \u0437\u0430\u0434\u0430\u0451\u043c \u043f\u0443\u0442\u044c \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u044f \u0438\u043c\u044f \u043c\u043e\u0434\u0443\u043b\u044f <strong>cart\/addProduct<\/strong><\/li>\n<li>\u0432 mapState \u0434\u0435\u043b\u0430\u0435\u043c \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435, \u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 state <strong>state =&gt; state.cart.products<\/strong><\/li>\n<li>\u0445\u0440\u0430\u043d\u0438\u043c \u0444\u043b\u0430\u0433 \u043d\u0430 \u0441\u043b\u0443\u0447\u0430\u0439 \u0435\u0441\u043b\u0438 \u0442\u043e\u0432\u0430\u0440 \u0443\u0436\u0435 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435 <strong>isProductAdded<\/strong><\/li>\n<li>\u0435\u0441\u043b\u0438 \u0442\u043e\u0432\u0430\u0440\u0430 \u043d\u0435\u0442 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435, \u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043a\u043d\u043e\u043f\u043a\u0430 \u043a\u0443\u043f\u0438\u0442\u044c, \u0435\u0441\u043b\u0438 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c, \u0442\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u043c \u0441\u0441\u044b\u043b\u043a\u0443 \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0442\u043e\u0432\u0430\u0440 \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0437 \u043a\u043e\u0440\u0437\u0438\u043d\u044b.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u044d\u0442\u043e\u0442 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0432 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0443 \u0442\u043e\u0432\u0430\u0440\u0430 \u0438 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0442\u043e\u0432\u0430\u0440\u0430.<\/p>\n<p>  <\/p>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0442\u0430\u043a\u043e\u0435 \u0432\u043e\u0442 \u0447\u0443\u0434\u043e<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ec\/qa\/wm\/ecqawm4ae1nwufqthwvlfmnhbeu.png\"><\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/wm\/nl\/p4\/wmnlp4m-gbzlplyetb_ort02b-m.png\"><\/p>\n<p>  <\/p>\n<p>\u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0432\u043a\u043b\u0430\u0434\u043a\u0443 \u0438\u043b\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u043a\u043b\u0430\u0434\u043e\u043a, \u0432\u0441\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0438 <strong>\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0430\u043c <\/strong>(\u0430 \u0442\u0430\u043a\u0436\u0435 \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043f\u0440\u043e\u0434\u0435).<\/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<\/ul>\n<p>  <\/p>\n<p>\u042d\u0442\u043e \u0432\u0442\u043e\u0440\u0430\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 \/>  \u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0445\u043e\u0447\u0443 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u044f\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, \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0438\u0445, \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0442\u043e\u0432\u0430\u0440\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u044b\u0435 \u043e\u043a\u043d\u0430.<\/p>\n<p>  <\/p>\n<p>\u0410 \u0442\u0430\u043a\u0436\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043a\u0430\u0437\u0430, \u0431\u043b\u043e\u043a\u0438 \u0441 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c\u044b\u043c\u0438 \u0442\u043e\u0432\u0430\u0440\u0430\u043c\u0438 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043c\u0438\u043a\u0440\u043e\u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0443.<\/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>\u0420\u0435\u0431\u044f\u0442\u0430, \u044f \u0441\u0442\u0430\u0440\u0430\u044e\u0441\u044c \u0434\u0435\u043b\u0430\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u043e \u043a\u0430\u043a \u043c\u043e\u0433\u0443, \u043d\u043e \u043d\u0430 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e \u0443\u0445\u043e\u0434\u0438\u0442 \u043c\u0438\u043d\u0438\u043c\u0443\u043c <strong>6<\/strong> \u0447\u0430\u0441\u043e\u0432 \u0432\u0440\u0435\u043c\u0435\u043d\u0438. \u041c\u043d\u0435 \u0431\u044b \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u043b\u0443\u0447\u0448\u0435 \u043f\u043e\u043d\u044f\u0442\u044c \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0430\u043a\u043e\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439.<\/p>\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\/491018\/\"> https:\/\/habr.com\/ru\/post\/491018\/<\/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\/491018\/\">\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/bn\/s2\/ea\/bns2eafwvg8dzlyx7i2ng0wmpsw.jpeg\"><\/p>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u0447\u0430\u0441\u0442\u044c <a href=\"https:\/\/habr.com\/ru\/post\/490496\/\">\u0442\u0443\u0442 <\/a><\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043d\u0430\u0448\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0430. \u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0431\u0443\u0434\u0435\u0442:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u043f\u043e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0430\u0434\u0440\u0435\u0441\u0430\u043c<\/li>\n<li>\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0445\u043b\u0435\u0431\u043d\u044b\u0445 \u043a\u0440\u043e\u0448\u0435\u043a \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435<\/li>\n<li>\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0442\u043e\u0432\u0430\u0440\u0430<\/li>\n<li>\u0448\u0430\u043f\u043a\u0430<\/li>\n<li>\u0440\u0430\u0431\u043e\u0447\u0430\u044f \u043a\u043d\u043e\u043f\u043a\u0430 \u043a\u0443\u043f\u0438\u0442\u044c \u0441 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u043c\u0435\u0436\u0434\u0443 \u0432\u043a\u043b\u0430\u0434\u043a\u0430\u043c\u0438 (\u0438 \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438)<\/li>\n<\/ul>\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-299685","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/299685","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=299685"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/299685\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=299685"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=299685"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=299685"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}