Руководство по созданию мультиязычного сайта с Nuxt.js — Блог о самом интересном.

От создателя: это управление создано для начинающих и экспертов, которые желают сделать настоящий многоязычный сайт с помощью Nuxt.js. С внедрением этого пошагового управления вы получите динамический сайт Nuxt.js, работающий на сервере now, с внедрением API для многоязычного контента.

Если вы торопитесь, то сможете просмотреть весь проект (nuxtblok.now.sh) на Github.

Настройка среды

Что для вас будет нужно:

Базисное осознание NuxtJs

VueJs и их CLI

NodeJS

NPM

NPX

CLI now.sh для хостинга

Акк на Storyblok.com для управления контентом

Если это еще не изготовлено, установите NodeJs, NPM и NPX. Начнем с инициализации проекта с помощью исходного шаблона nuxt.js.

JavaScript npx create-nuxt-app mywebsite cd mywebsite && npm build

npm run dev

123 npx create-nuxt-app mywebsitecd mywebsite && npm buildnpm run dev

Nuxt.js по умолчанию запускает собственный сервер на порту 3000, потому опосля пуска npm запустите dev и откройте браузер по адресу http://localhost:3000.

Так как мы будем применять SCSS для организации CSS, нам также необходимо установить sass-загрузчик.

JavaScript npm install —save-dev sass-loader node-sass css-loader

1 npm install —save-dev sass-loader node-sass css-loader

Чтоб выслеживать конфигурации, которые мы вносим с течением времени, мы также инициализируем репозиторий git.

JavaScript // Initialize git

git init && git add . && git commit -m ‘init’

12 // Initialize gitgit init && git add . && git commit -m ‘init’

Построение каркаса

Мы начнем строить основа для сайта. В конце у вас будет заголовок, разделы хэдера и футера и несколько нужных глобальных утилит CSS-классов.

Глобальный SCSS в Nuxt.js

На шаге 1 мы установили загрузчик SCSS, потому давайте сделаем некие глобальные стили и определим переменные scss. Мы сделаем папку для стилизации общих HTML-тегов assets/scss/elements/ и другую папку для служебного компонента assets/scss/components/.

JavaScript assets/ —| scss/ ——| elements/ ———| body.scss ———| … ——| components/ ———| util.scss ———| …

—| styles.scss

123456789 assets/—| scss/——| elements/———| body.scss———| …——| components/———| util.scss———| …—| styles.scss

Сделайте файл assets/scss/styles.scss и добавьте последующий в него код.

CSS $brand-color: #357F8A; $breakpoint-small: 480px; $breakpoint-medium: 768px; $breakpoint-large: 960px; $breakpoint-xlarge: 1220px; $breakpoint-mini-max: ($breakpoint-small — 1); $breakpoint-small-max: ($breakpoint-medium — 1); $breakpoint-medium-max: ($breakpoint-large — 1);

$breakpoint-large-max: ($breakpoint-xlarge — 1);

@import ‘elements/body.scss’;
@import ‘components/util.scss’;

123456789101112 $brand-color: #357F8A;$breakpoint-small: 480px;$breakpoint-medium: 768px;$breakpoint-large: 960px;$breakpoint-xlarge: 1220px;$breakpoint-mini-max: ($breakpoint-small — 1);$breakpoint-small-max: ($breakpoint-medium — 1);$breakpoint-medium-max: ($breakpoint-large — 1);$breakpoint-large-max: ($breakpoint-xlarge — 1); @import ‘elements/body.scss’;@import ‘components/util.scss’;

Заместо того, чтоб помещать стили всех HTML частей в один файл, я предпочитаю создавать отдельные файлы, чтоб создать проект структурированным и масштабируемым. Сделайте файл assets/scss/elements/body.scss для определения базисных стилей шрифтов.

CSS body { font-family: ‘Zilla Slab’, Helvetica, sans-serif; line-height: 1; font-size: 18px; color: #000; margin: 0; padding: 0;

}

12345678 body {  font-family: ‘Zilla Slab’, Helvetica, sans-serif;  line-height: 1;  font-size: 18px;  color: #000;  margin: 0;  padding: 0;}

В папке components мы управляем глобальными компонентами css и вспомогательными классами. Сделайте файл assets/scss/components/util.scss для определения глобальных служебных классов.

CSS .util__flex { display: flex;

}

.util__flex-col { flex: 0 0 auto;

}

.util__flex-eq { flex: 1;

}

.util__container { max-width: 75rem; margin-left: auto; margin-right: auto; padding-left: 20px; padding-right: 20px; box-sizing: border-box;

}

1234567891011121314151617181920 .util__flex {  display: flex;} .util__flex-col {  flex: 0 0 auto;} .util__flex-eq {  flex: 1;} .util__container {  max-width: 75rem;  margin-left: auto;  margin-right: auto;  padding-left: 20px;  padding-right: 20px;  box-sizing: border-box;}

Добавление Гугл font в Nuxt.js

В файле body.scss мы обусловили Zilla Slab как шрифт. Так как это не системный шрифт, мы должны добавить его в раздел заголовка документа. Тут в игру вступает файл конфигурации Nuxt.js. Откройте nuxt.config.js и добавьте таблицу стилей шрифта в раздел head.

JavaScript head: { … link: [ … { rel: ‘stylesheet’, href: ‘https://fonts.googleapis.com/css?family=Zilla+Slab:400,700’ } ] },

1234567891011 head: {    …    link: [      …      {        rel: ‘stylesheet’,        href: ‘https://fonts.googleapis.com/css?family=Zilla+Slab:400,700’      }    ]},…

Определение макета по умолчанию

Сейчас, когда у нас есть SCSS, нам необходимо добавить его в проект. Удостоверьтесь, что вы установили загрузчик sass на первом шаге и поменяйте код в layouts/default.vue на последующий.

import TopHeader from ‘~/components/TopHeader.vue’

import BottomFooter from ‘~/components/BottomFooter.vue’

export default { components: { TopHeader, BottomFooter } }

@import ‘../assets/scss/styles.scss’;

12345678910111213141516171819202122232425                            import TopHeader from ‘~/components/TopHeader.vue’import BottomFooter from ‘~/components/BottomFooter.vue’ export default {  components: {    TopHeader,    BottomFooter  }} @import ‘../assets/scss/styles.scss’;

Вы увидите сообщение о ошибке, гласящее, что составляющие TopHeader.vue и BottomFooter.vue еще не есть. Итак, давайте сделаем их.

Создание компонента заголовка

Направьте внимание на атрибут lang=»scss» в теге style. Он дозволяет применять SCSS в компонентах Vue.js.

  • Home
  • Blog

  • English
  • German

.top-header { justify-content: space-between; padding-top: 30px; padding-bottom: 30px;

}

.top-header__logo { text-align: center; position: absolute;

left: 50%;

img { position: relative; max-height: 60px; left: -50%; top: -15px; }

}

.top-header__second-navi { text-align: right; }

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152             

            

  •           Home        
  •         

  •           Blog        
  •       

                            

            

  •           English        
  •         

  •           German        
  •       

         .top-header {    justify-content: space-between;    padding-top: 30px;    padding-bottom: 30px;  }   .top-header__logo {    text-align: center;    position: absolute;    left: 50%;     img {      position: relative;      max-height: 60px;      left: -50%;      top: -15px;    }  }   .top-header__second-navi {    text-align: right;  }

Создание компонента футера

Добавьте в папку ./components файл BottomFooter.vue.

Sitemap

.bottom-footer { background: #e3f2ed; padding: 40px 0 120px 0; text-align: center;

}

.bottom-footer__link { color: #8ba19a; text-decoration: none; }

1234567891011121314151617181920             Sitemap       .bottom-footer {  background: #e3f2ed;  padding: 40px 0 120px 0;  text-align: center;} .bottom-footer__link {  color: #8ba19a;  text-decoration: none;}

В реальный момент веб-сайт должен смотреться приблизительно так, как показано на последующем снимке экрана. На последующем шаге я покажу для вас, как сделать домашнюю страничку с тизером и разделом функций.

Сейчас давайте передадим это в Git. Смотрите для справки мой коммит GitHub.

JavaScript $ git add . && git commit -m ‘creates the skeleton’

1 $ git add . && git commit -m ‘creates the skeleton’

Создание главной странички

Установите модуль Nuxt.js Storyblok. Модуль Storyblok установит $storyapi и $storyblok в экземпляр Vue.

JavaScript $ npm install storyblok-nuxt —save

1 $ npm install storyblok-nuxt —save

Опосля установки модуля для вас нужно инициализировать его токеном подготовительного просмотра вашего места Storyblok. Зарегайтесь либо войдите на app.storyblok.com и сделайте новое место. Добавьте последующее в файл nuxt.config.js и поменяйте PREVIEW_TOKEN на токен подготовительного просмотра.

JavaScript module.exports = { modules: [ [‘storyblok-nuxt’, {accessToken: ‘YOUR_PREVIEW_TOKEN’, cacheProvider: ‘memory’}] ],

12345 module.exports = {  modules: [    [‘storyblok-nuxt’, {accessToken: ‘YOUR_PREVIEW_TOKEN’, cacheProvider: ‘memory’}]  ],  …

Обновите компонент домашней странички

Сейчас поменяйте содержимое по умолчанию pages/index.vue последующим:

export default { data () { return { story: { content: {} } } }, mounted () { // Load the JSON from the API this.$storybridge.on([‘input’, ‘published’, ‘change’], (event) => { if (event.action == ‘input’) { if (event.story.id === this.story.id) { this.story.content = event.story.content } } else { window.location.reload() } }) }, asyncData (context) { return context.app.$storyapi.get(‘cdn/stories/home’, { version: ‘draft’ }).then((res) => { return res.data }).catch((res) => { if (!res.response) { console.error(res) context.error({ statusCode: 404, message: ‘Failed to receive content form api’ }) } else { console.error(res.response.data) context.error({ statusCode: res.response.status, message: res.response.data }) } }) } }

12345678910111213141516171819202122232425262728293031323334353637383940414243           export default {  data () {    return {      story: { content: {} }    }  },  mounted () {    // Load the JSON from the API    this.$storybridge.on([‘input’, ‘published’, ‘change’], (event) => {      if (event.action == ‘input’) {        if (event.story.id === this.story.id) {          this.story.content = event.story.content        }      } else {        window.location.reload()      }    })  },  asyncData (context) {    return context.app.$storyapi.get(‘cdn/stories/home’, {      version: ‘draft’    }).then((res) => {      return res.data    }).catch((res) => {      if (!res.response) {        console.error(res)        context.error({ statusCode: 404, message: ‘Failed to receive content form api’ })      } else {        console.error(res.response.data)        context.error({ statusCode: res.response.status, message: res.response.data })      }    })  }}

Способ asyncData загрузит JSON, который описывает, какие составляющие мы будем показывать на домашней страничке.

Создание компонент домашней странички

Чтоб показать полную домашнюю страничку, нам необходимо сделать несколько компонент. Добавьте файл components.js в папку plugins.

JavaScript import Vue from ‘vue’ import Page from ‘~/components/Page.vue’ import Teaser from ‘~/components/Teaser.vue’ import Grid from ‘~/components/Grid.vue’

import Feature from ‘~/components/Feature.vue’

Vue.component(‘page’, Page) Vue.component(‘teaser’, Teaser) Vue.component(‘grid’, Grid)

Vue.component(‘feature’, Feature)

12345678910 import Vue from ‘vue’import Page from ‘~/components/Page.vue’import Teaser from ‘~/components/Teaser.vue’import Grid from ‘~/components/Grid.vue’import Feature from ‘~/components/Feature.vue’ Vue.component(‘page’, Page)Vue.component(‘teaser’, Teaser)Vue.component(‘grid’, Grid)Vue.component(‘feature’, Feature)

Nuxt.js не выбирает файлы в плагинах автоматом, потому нам необходимо добавить их components.js в nuxt.config.js.

JavaScript module.exports = { plugins: [ ‘~/plugins/components’ ],

12345 module.exports = {  plugins: [    ‘~/plugins/components’  ],  …

Потом сделайте составляющие Vue снутри папки components.

Page.vue

export default { props: [‘blok’] }

1234567891011          export default {  props: [‘blok’]}

Teaser.vue

{{ blok.headline }}

export default { props: [‘blok’] }

1234567891011       {{ blok.headline }}   export default {  props: [‘blok’]}

Grid.vue

export default { props: [‘blok’] }

1234567891011          export default {  props: [‘blok’]}

Feature.vue

{{ blok.name }}

export default { props: [‘blok’] }

1234567891011       

{{ blok.name }}

   export default {  props: [‘blok’]}

При перезагрузке http://localhost:3000/ вы должны узреть последующее.

Сделайте собственный 1-ый блок в Storyblok

Мы лишь что загрузили демо контент Storyblok, и сейчас мы расширим компонент тизера интерактивными слайдами. Для этого начните с подключения среды к композитору Storyblok и вставьте хост разработки localhost:3000.

ВАЖНО: опосля вставки хоста для вас необходимо поменять поле настоящего пути (см. последующий шаг), в неприятном случае вы получите страничку 404.

Изменение поля настоящего пути

Сейчас вы должны узреть собственный веб-сайт на подготовительном просмотре. Но он покажет страничку 404, поэтому что Storyblok по умолчанию употребляет путь к домашней страничке /home. Чтоб это поменять, для вас необходимо будет перейти на вкладку Конфигурация и указать поле Настоящий путь — / .

Итак, давайте определим схему новейшего блока слайдов компонента /. Опосля прибавления схемы и содержимого в Storyblok нам необходимо добавить в проект компонент Vue.js slide. Сделайте файл components/Slide.vue со последующим содержимым.

export default { props: [‘blok’] }

.slide img { width: 100%; }

1234567891011121314151617          export default {  props: [‘blok’]} .slide img {  width: 100%;}

Добавьте новейший компонент в файл component.js.

JavaScript import Vue from ‘vue’ …

import Slide from ‘~/components/Slide.vue’


Vue.component(‘slide’, Slide)

123456 import Vue from ‘vue’…import Slide from ‘~/components/Slide.vue’ …Vue.component(‘slide’, Slide)

Естественно, мы не желаем показывать все слайды сразу. Итак, давайте расширим логику Teaser.vue , чтоб показать точечную навигацию. Вы сможете применять хоть какой плагин слайдера Vue.js, чтоб сделать наиболее продвинутый слайдер, но мы создадим это просто.

Next

export default {

props: [‘blok’],

data () { return { currentSlide: 0 }

},

computed: { slide () { let slides = this.blok.body.filter((slide, index) => { return this.currentSlide === index }) if (slides.length) { return slides[0] } return null }

},

methods: { handleDotClick (index) { this.currentSlide = index } } }

.teaser__pag { width: 100%; text-align: center; margin: 30px 0;

}

.teaser__pag-dot { text-indent: -9999px; border: 0; border-radius: 50%; width: 17px; height: 17px; padding: 0; margin: 5px 6px; background-color: #ccc; -webkit-appearance: none;

cursor: pointer;

&—current { background-color: #000; } }

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667                 Next       export default {  props: [‘blok’],   data () {    return {      currentSlide: 0    }  },   computed: {    slide () {      let slides = this.blok.body.filter((slide, index) => {        return this.currentSlide === index      })      if (slides.length) {        return slides[0]      }      return null    }  },   methods: {    handleDotClick (index) {      this.currentSlide = index    }  }} .teaser__pag {  width: 100%;  text-align: center;  margin: 30px 0;} .teaser__pag-dot {  text-indent: -9999px;  border: 0;  border-radius: 50%;  width: 17px;  height: 17px;  padding: 0;  margin: 5px 6px;  background-color: #ccc;  -webkit-appearance: none;  cursor: pointer;   &—current {    background-color: #000;  }}

Опосля сохранения у вас должен быть последующий итог.

Расширение раздела функций

Раздел функций в истинное время содержит лишь заголовок. Сейчас мы расширим его текстом описания и иконками. Нажмите на блок функций и добавьте поля description (с типом textarea) и icon (с типом изображения), нажав Найти схему.

Необычные случаи использования псевдо-элементов

Откройте компонент Feature (components/Feature.vue) и добавьте в него новейшие поля, также некие базисные стили CSS.

{{ blok.name }}

{{ blok.description }}

export default { computed: { resizedIcon () { if (typeof this.blok.icon !== ‘undefined’) { return ‘//img2.storyblok.com/80×80’ + this.blok.icon.replace(‘//a.storyblok.com’, ») } return null } }, props: [‘blok’] }

.feature { text-align: center; padding: 30px 10px 100px;

}

.feature__icon { max-width: 80px; }

12345678910111213141516171819202122232425262728293031323334           

{{ blok.name }}

          {{ blok.description }}       export default {  computed: {    resizedIcon () {      if (typeof this.blok.icon !== ‘undefined’) {        return ‘//img2.storyblok.com/80×80’ + this.blok.icon.replace(‘//a.storyblok.com’, »)      }      return null    }  },  props: [‘blok’]} .feature {  text-align: center;  padding: 30px 10px 100px;} .feature__icon {  max-width: 80px;}

Опосля того, как вы заполнили некий контент, у вас обязана быть на сто процентов редактируемая домашняя страничка.

Для построения динамического меню навигации у вас есть несколько способностей. Одним из их является создание глобального элемента контента, который содержит глобальные конфигурации. Иной метод — применять API-интерфейс ссылок для автоматического сотворения навигации из дерева контента. В этом руководстве мы реализуем 1-ый способ.

Так как мы создаем многоязычный сайт, нам необходимо сделать глобальную конфигурацию для всякого языка. Начнем с сотворения папки на британском языке en.

Создание элемента контента глобальных опций

Снутри папки en мы создаем элемент контента Settings с новеньким типом контента settings. Это будет элемент контента, в который мы помещаем элементы навигации и остальные глобальные конфигурации сайта.

Измените настоящий путь на / и сделайте схему для главный навигации, определяющую ключ main_navi с типом Blocks.
Добавьте блок для элемента навигации с типом name — Text и типом link — Link. В конце элемент контента Settings должен смотреться последующим образом:

Получение глобальных опций в хранилище Vuex

Так как Nuxt.js поставляется со интегрированной поддержкой Vuex, мы будем применять его для получения и сохранения конфигурации навигации, также текущего языка.

Опосля отправки деяния loadSettings в middleware у нас будут доступны элементы навигации по адресу $store.state.settings.main_navi.

JavaScript export const state = () => ({ cacheVersion: », language: ‘en’, settings: { main_navi: [] }

})

export const mutations = { setSettings(state, settings) { state.settings = settings }, setLanguage(state, language) { state.language = language }, setCacheVersion(state, version) { state.cacheVersion = version }

}

export const actions = { loadSettings({ commit }, context) { return this.$storyapi.get(`cdn/stories/${context.language}/settings`, { version: context.version }).then((res) => { commit(‘setSettings’, res.data.story.content) }) }

}

1234567891011121314151617181920212223242526272829 export const state = () => ({  cacheVersion: »,  language: ‘en’,  settings: {    main_navi: []  }}) export const mutations = {  setSettings(state, settings) {    state.settings = settings  },  setLanguage(state, language) {    state.language = language  },  setCacheVersion(state, version) {    state.cacheVersion = version  }} export const actions = {  loadSettings({ commit }, context) {    return this.$storyapi.get(`cdn/stories/${context.language}/settings`, {      version: context.version    }).then((res) => {      commit(‘setSettings’, res.data.story.content)    })  }}

Добавление middleware

Middleware в Nuxt.js дозволяет найти функцию, которая запускается перед отображением странички. Функция быть может асинхронной и возвращать промис, потому она совершенно подступает для загрузки опций из API.

JavaScript export default function ({ app, isServer, route, store, isDev }) { let version = route.query._storyblok || isDev ? ‘draft’ : ‘published’

let language = route.params.language || ‘en’

if (isServer) { store.commit(‘setCacheVersion’, app.$storyapi.cacheVersion)

}

if (!store.state.settings._uid || language !== store.state.language) {
store.commit(‘setLanguage’, language)

return store.dispatch(‘loadSettings’, {version: version, language: language}) }

}

1234567891011121314 export default function ({ app, isServer, route, store, isDev }) {  let version = route.query._storyblok || isDev ? ‘draft’ : ‘published’  let language = route.params.language || ‘en’   if (isServer) {    store.commit(‘setCacheVersion’, app.$storyapi.cacheVersion)  }   if (!store.state.settings._uid || language !== store.state.language) {    store.commit(‘setLanguage’, language)     return store.dispatch(‘loadSettings’, {version: version, language: language})  }}

Не считая того, middleware обязано быть записанно в nuxt.config.js.

JavaScript module.exports = { … router: { middleware: ‘languageDetection’

},

12345 module.exports = {  …  router: {    middleware: ‘languageDetection’  },

Сейчас в $store.state.settings.main_navi мы можем просто получить доступ к элементам навигации и циклически перемещать их, чтоб показать в components/TopHeader.vue.

  • {{ navitem.name }}

  • English
  • German

12345678910111213141516171819202122232425262728             

            

  •                       {{ navitem.name }}                  
  •       

                            

            

  •           English        
  •         

  •           German        
  •       

       …

Перезагрузив страничку, мы должны узреть заголовок навигации с настраиваемыми элементами навигации от Storyblok.

Создание раздела блога

Общей задачей при разработке веб-сайта является разработка обзорной странички коллекций, таковых как анонсы, сообщения в блогах либо продукты. В нашем примере мы сделаем обычный блог. В Nuxt.js вы сможете найти динамические маршруты, создавая папки с добавлением подчеркивания «_», и Nuxt автоматом конвертирует их в маршруты Vue.js.

Окончательный URL должен смотреться последующим образом /:language/blog/:slug, потому нам необходимо сделать последующую структуру папок.

JavaScript pages/ —| _language/ ——| blog/ ———| _slug.vue ———| index.vue

—| index.vue

123456 pages/—| _language/——| blog/———| _slug.vue———| index.vue—| index.vue

Добавление странички с описанием блога

Мы начнем со странички описания блога pages/_language/blog/_slug.vue, на которой будем извлекать контент из API, а потом выведем запись блога через markdown с внедрением в качестве парсера marked. Итак, поначалу нам необходимо установить парсер markdown.

JavaScript $ npm install marked —save

1 $ npm install marked —save

Потом мы сделаем файл pages/_language/blog/_slug.vue для динамического маршрута постов в блоге.

{{ story.content.name }}

import marked from ‘marked’

export default { data () { return { story: { content: { body: » } } } }, computed: { body () { return marked(this.story.content.body) } }, mounted () { // Load the JSON from the API this.$storybridge.on([‘input’, ‘published’, ‘change’], (event) => { if (event.action == ‘input’) { if (event.story.id === this.story.id) { this.story.content = event.story.content } } else { window.location.reload() } }) }, async asyncData (context) { return await context.app.$storyapi.get(‘cdn/stories/home’, { version: ‘draft’ }).then((res) => { return res.response }).catch((res) => { if (!res.response) { console.error(res) context.error({ statusCode: 404, message: ‘Failed to receive content form api’ }) } else { console.error(res.response.data) context.error({ statusCode: res.response.status, message: res.response.data }) } }) } }

.blog { padding: 0 20px; max-width: 600px;

margin: 40px auto 100px;

img { width: 100%; height: auto; }

}

.blog__body { line-height: 1.6; }

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970             

{{ story.content.name }}

                   import marked from ‘marked’ export default {  data () {    return {      story: { content: { body: » } }    }  },  computed: {    body () {      return marked(this.story.content.body)    }  },  mounted () {    // Load the JSON from the API    this.$storybridge.on([‘input’, ‘published’, ‘change’], (event) => {      if (event.action == ‘input’) {        if (event.story.id === this.story.id) {          this.story.content = event.story.content        }      } else {        window.location.reload()      }    })  },  async asyncData (context) {    return await context.app.$storyapi.get(‘cdn/stories/home’, {      version: ‘draft’    }).then((res) => {      return res.response    }).catch((res) => {      if (!res.response) {        console.error(res)        context.error({ statusCode: 404, message: ‘Failed to receive content form api’ })      } else {        console.error(res.response.data)        context.error({ statusCode: res.response.status, message: res.response.data })      }    })  }} .blog {  padding: 0 20px;  max-width: 600px;  margin: 40px auto 100px;   img {    width: 100%;    height: auto;  }} .blog__body {  line-height: 1.6;}

Основные ошибки, допускаемые разработчиками JavaScript

Создание странички обзора

Чтоб составить перечень записей в блоге, мы сделаем маршрут, /:language/blog, просто сохранив файл index.vue в папке blog.

API Storyblok может перечислять все элементы содержимого определенной папки с параметром starts_with. Количество возвращаемых частей содержимого по умолчанию составляет 25, но вы сможете поменять это с помощью параметра per_page и перейти к остальным страничкам с помощью параметра page.

{{ blogPost.content.name }}

{{ blogPost.published_at }}

{{ blogPost.content.intro }}

export default { data () { return { total: 0, data: { stories: [] } } }, asyncData (context) {

let version = context.query._storyblok || context.isDev ? ‘draft’ : ‘published’

return context.app.$storyapi.get(‘cdn/stories’, { version: version, starts_with: `${context.store.state.language}/blog`, cv: context.store.state.cacheVersion }).then((res) => { return res }).catch((res) => { context.error({ statusCode: res.response.status, message: res.response.data }) }) } }

.blog__overview { padding: 0 20px; max-width: 600px;

margin: 40px auto 60px;

p { line-height: 1.6; }

}

.blog__detail-link { color: #000; }

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354             

                  {{ blogPost.content.name }}              

              {{ blogPost.published_at }}            

        {{ blogPost.content.intro }}      

       export default {  data () {    return { total: 0, data: { stories: [] } }  },  asyncData (context) {    let version = context.query._storyblok || context.isDev ? ‘draft’ : ‘published’     return context.app.$storyapi.get(‘cdn/stories’, {      version: version,      starts_with: `${context.store.state.language}/blog`,      cv: context.store.state.cacheVersion    }).then((res) => {      return res    }).catch((res) => {      context.error({ statusCode: res.response.status, message: res.response.data })    })  }} .blog__overview {  padding: 0 20px;  max-width: 600px;  margin: 40px auto 60px;   p {    line-height: 1.6;  }} .blog__detail-link {  color: #000;}

Создание папки с содержимым блога

Опосля сотворения компонент Vue.js для отображения блога нам необходимо сделать в Storyblok новейшую папку для сотворения страничек блога. Сделайте папку en/blog и изберите для данной папки тип содержимого по умолчанию blog.

Создание статьи в блоге

Когда вы входите в папку блога и создаете новейший элемент контента, blog автоматом выбирается в качестве типа контента. Добавьте поля схемы intro (Textarea), name (Text) и body (Markdown) и сделайте некий демо контент.

В обзоре вы должны узреть перечень статей блога.

Создание карты сайта

Для сотворения карты сайта либо дерева навигации всех страничек с помощью Nuxt.js мы будем вызывать API ссылок Storyblok. API включает дела родитель-потомок через parent_id, и потому нам просто необходимо сгенерировать дерево, используя вычисляемое свойство.

Sitemap

                      

             export default {  data () {    return {      links: {}    }  },  computed: {    tree () {      let parentChilds = this.parentChildMap(this.links)       return this.generateTree(0, parentChilds)    }  },  asyncData (context) {    let version = context.query._storyblok || context.isDev ? ‘draft’ : ‘published’     return context.app.$storyapi.get(‘cdn/links’, {      version: version,      starts_with: context.store.state.language,      cv: context.store.state.cacheVersion    }).then((res) => {      return res.data    }).catch((res) => {      context.error(res)    })  },  methods: {    parentChildMap (links) {      let tree = {}      let linksArray = Object.keys(links).map(e => links[e])       linksArray.forEach((link) => {        if (!tree[link.parent_id]) {          tree[link.parent_id] = []        }         tree[link.parent_id].push(link)      })       return tree    },    generateTree (parent, items) {      let tree = {}       if (items[parent]) {        let result = items[parent]         result.forEach((cat) => {          if (!tree[cat.id]) {            tree[cat.id] = {item: {}, children: []}          }          tree[cat.id].item = cat          tree[cat.id].children = this.generateTree(cat.id, items)        })      }       return Object.keys(tree).map(e => tree[e])    }  }} .sitemap {  max-width: 600px;  margin: 20px auto 60px;}

Для карты сайта в виде дерева с нескончаемыми узлами мы создаем компонент SitemapItem.vue и включаем его при повторяющемся переходе по дочерним элементам дерева.

  • {{model.item.name}}
      0″>
  • export default { props: [‘model’] }

    .sitemap-item {

    padding: 5px 0;

    a { color: #8ba19a;

    }

    ul { margin-top: 10px; margin-bottom: 10px; } }

    1234567891011121314151617181920212223242526272829303132333435   

    •           {{model.item.name}}        
      • 0″>                

         export default {  props: [‘model’]} .sitemap-item {  padding: 5px 0;   a {    color: #8ba19a;  }   ul {    margin-top: 10px;    margin-bottom: 10px;  }}

    Не забудьте добавить новейший компонент SitemapItem в файл components.js.

    JavaScript …

    import SitemapItem from ‘~/components/SitemapItem.vue’


    Vue.component(‘sitemap-item’, SitemapItem)

    12345 …import SitemapItem from ‘~/components/SitemapItem.vue’ …Vue.component(‘sitemap-item’, SitemapItem)

    В конце у нас обязана получиться последующая страничка.

    Добавление другого языка

    Со Storyblok у вас есть два варианта сотворения многоязычных проектов — перевод на уровне поля и перевод с несколькими деревьями. Перевод на уровне поля — не плохое решение, если большая часть контента переведена. Задумайтесь о использовании перевода с несколькими деревьями, если дерево содержимого различается на любом языке. Если вы не убеждены, что избрать, прочитайте наше управление о i18n.

    Разворачивание в настоящей среде

    Сейчас настало время показать ваш проект миру. Для обычного, развертывание с нулевой настройкой вы сможете применять now. Опосля того, как вы скачали и установили их настольное приложение, вы сможете развернуть Nuxt.js с помощью одной команды.

    JavaScript now

    Вы получите неповторимый URL-адрес, который потом можно будет связать через now alias со своим пользовательским доменом.

    Заключение

    Сделать настоящий сайт с помощью Nuxt.js неописуемо просто, Nuxt.js владеет прекрасной экосистемой. Мне весьма нравится, как он абстрагирует общие задачки, которые вы обычно выполняете в конфигурации Webpack. Это мало похоже на Ruby on Rails, где соглашения идут поверх конфигурации. Для огромных проектов эти соглашения упрощают подбор новейших членов команды и делают проекты наиболее комфортными в обслуживании.

    Редакция: Команда webformyself.

    Источник

    Вам также может понравиться