Статьи NUXT о расширении возможностей изоморфизма Vue

Vue.js
Статьи NUXT о расширении возможностей изоморфизма Vue

В прошлый раз, когда я поделился с вами статьей VUE об изоморфном расширении прав и возможностей VUE SSR, ссылка здесь.
nuggets.capable/post/684490…

Сегодня следующая статья: NUXT
Прошлая статья может быть слишком сложной, в этот раз давайте сразу к теме
В последнем DEMO мы использовали NUXT для реализации этой страницы.
Адрес гитхаба в конце статьи.

Nuxt

Nuxt — это фреймворк для рендеринга на стороне сервера, который интегрирует Vue-Router, Vuex, webpack и другие фреймворки и компоненты на основе Vue ssr.
На мой взгляд, Nuxt — это обновленная версия Vue ssr, которая предустановляет различные конфигурации, необходимые для серверных приложений.
Но, соответственно, инвазивность Nuxt очень высока, нам нужно понять мышление Nuxt, чтобы воспользоваться им.

Структура каталогов

Во-первых, давайте взглянем на структуру каталогов проекта Nuxt.

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

Конкретную папку можно увидеть

assets - файлы ресурсов внешнего интерфейса
компоненты - интерфейсные общие компоненты
layouts — Компоненты макета внешнего интерфейса
store - vuex
статические - статические файлы

плагины - плагины
промежуточное ПО - промежуточное ПО

страницы - страницы внешнего интерфейса VUE

сервер - бэкенд

На самом деле столько файлов можно разделить на три:интерфейс, бэкэнд, ресурсы

соглашение о конфигурации

В Nuxt повсюду идея «конвенция важнее конфигурации», на это стоит обратить внимание.
Например, в простейшем случае имя вышеуказанной папки каталога нельзя изменить без дополнительной настройки.
Так же есть например: макеты в файле макета
default.vue — это страница макета по умолчанию.
error.vue — страница ошибки по умолчанию

Например, в папке front-end страницы pages согласовываются правила автоматической генерации маршрутизаторов.
Также в папке магазина index.js по умолчанию является деревом состояний по умолчанию,
файл todos.js будетАвтоматически генерировать модуль todos
так далее……

Эти особенности будут подробно рассмотрены ниже.

макет/маршрутизация

Теперь начните писать нашу демонстрацию, Начнем со страницы макета, нам нужно написать две страницы default.vue и error.vue
Страницу макета можно рассматривать как нашу обычную страницу входа app.vue в vue.
Но они разные.В подстранице Nuxt можно указать страницу макета (default.vue, если не указано)

default.vue

<template>
  <div>
    <header>
      <div class="head-content">
        <section class="head-left">
          一个帮助开发者成长的社区 |
          <nuxt-link to="/"> 首页</nuxt-link>
        </section>
        <section class="head-right">
          <nuxt-link to="/user/login"> 登录</nuxt-link>
          |
          <nuxt-link to="/user/register"> 注册</nuxt-link>
        </section>
      </div>
    </header>
    <nuxt />
  </div>
</template>

Макет настроек подстраницы

export default {
    // 默认就是default
    layout: "default",
}

и

Мы видим два компонента и на странице макета, что кажется немного знакомым.
Фактически это соответствующий ,
Таким образом, страница макета или наша вложенная страница маршрутизации должны использовать компонент .
Мы видим в исходном коде:

// nuxt
let routerView = [
  h('router-view', data)
]
if (props.keepAlive) {
  routerView = [
    h('keep-alive', { props: props.keepAliveProps }, routerView)
  ]
}
return h('transition', {
  props: transitionProps,
  on: listeners
}, routerView)

// nuxt-link
export default {
  name: 'NuxtLink',
  extends: Vue.component('RouterLink'),
}

является расширением vue-router
где принимает keep-alive и keep-alive-props
поможет нам расширить страницу разделения кода автоматической предварительной выборки.
Можно отключить с помощью атрибута no-prefetch.

<nuxt-link to="/user" no-prefetch>个人中心</nuxt-link>

Автоматически строить маршруты

Маршрутизация строится автоматически в Nuxt, что очень полезно и поможет нам сократить объем работы по настройке.
На самом деле во всех проектах SSR, на самом деле, для достижения изоморфизма внешнего и заднего конца, это зависит от маршрутизации, чтобы определить вход в выходной и задний конец, чтобы вывести выходной сигнал.
Давайте посмотрим на правила построения маршрутов в Nuxt.
Короче говоря, я подытожил три пункта:

  • Автоматически создавать маршруты с одинаковым именем на основе имен каталогов и файлов.
  • Создавайте динамические маршруты на основе _underscore
  • Создавайте вложенные маршруты на основе папки с тем же именем, что и файл vue.

Давайте посмотрим на нашу демонстрацию, нам нужен персональный центр и страница со списком
Итак, наша структура каталогов выглядит так

Окончательный сборник Router.js выглядит следующим образом:

    routes: [{
      path: "/user/login",
      component: _fade0492,
      name: "user-login"
    }, {
      path: "/user/register",
      component: _35d3a876,
      name: "user-register"
    }, {
      path: "/info/:pageIndex?",
      component: _287821ee,
      name: "info-pageIndex"
    }, {
      path: "/",
      component: _48b188ea,
      name: "index"
    }]

Как видите, страница со списком генерирует маршрут /info/:pageIndex?
Мы можем получить доступ к данным соответствующего номера страницы через /info/1 или /info/2

Давайте снова посмотрим на _pageIndex.vue

<template>
  <div>
    <vmenu type="info" />
    <div class="info-container">
      <div class="info-title">热门信息</div>
      <ul class="info-content">
        <li v-for="item in list" :key="item.title">
          <section class="info-content-r">{{ item.publishDate }}</section>
          <section class="info-content-l">{{ item.title }}</section>
        </li>
      </ul>
      <vpage :count="count" url="info" :pageIndex="pageIndex" />
    </div>
  </div>
</template>

page.vue

<template>
  <div class="page-container">
    <section>
      <router-link :to="{ name: toUrl, params: { pageIndex: 1 } }">
        第一页
      </router-link>
    </section>
    <section
      v-for="(i, ix) in pageCount"
      :key="i"
      :class="{ current: pageIndex == ix + 1 }"
    >
      <router-link :to="{ name: toUrl, params: { pageIndex: ix + 1 } }">
        {{ ix + 1 }}
      </router-link>
    </section>
    <section>
      <router-link :to="{ name: toUrl, params: { pageIndex: pageCount } }">
        最后一页
      </router-link>
    </section>
  </div>
</template>

<script>
export default {
  name: "page",
  props: {
    pageSize: { default: 10 },
    pageIndex: { default: 1 },
    count: { default: 100 },
    url: { default: "" }
  },
  computed: {
    toUrl() {
      return this.url + "-pageIndex";
    },
    pageCount() {
      return this.count % this.pageSize == 0
        ? this.count / this.pageSize
        : Math.floor(this.count / this.pageSize) + 1;
    }
  },
  mounted() {},
  created() {}
};
</script>

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

Асинхронные данные

Асинхронный сбор данных — необходимая функция проекта SSR, которая довольно проста в Nuxt.

asyncData

asyncData очень знаком, в VUR SSR есть тот же метод для загрузки асинхронных данных.
Но но
Метод asyncData в Nuxt совершенно другой.
Здесь можно не только получить асинхронные данные, но и монтировать конечные данные на данные.
Давайте посмотрим на демо здесь и получим список новостей из фона:

export default {
  async asyncData({
    isDev,
    route,
    store,
    env,
    params,
    query,
    req,
    res,
    redirect,
    error,
    $axios
  }) {
    let data = await $axios.post(`/api/news/list`, {
      page: params.pageIndex || 1
    });
    return { ...data.data };
  },
  components: {
    vmenu: Menu,
    vpage: page
  },
  // router.path是不一样的,不用监听
  // watchQuery: ["pageIndex"],
  data() {
    return {
      pageIndex: this.$route.params.pageIndex || 1
    };
  }
};

Здесь axios должен использовать официальный модуль Nuxt @nuxt/axios, который автоматически выполнит другие оптимизации, такие как заполнение заголовка.

Короче говоря, это так просто, мы получаем данные асинхронно, а затем можем их вернуть.
После этого компонент такой же, как мы обычно используем данные в данных.

fetch

Давайте посмотрим на выборку, На самом деле выборка очень похожа на asyncData в Vue SSR.
Он не будет привязан к данным, он используется только для работы с хранилищем. Использование заключается в следующем:

<template>
  <h1>Stars: {{ $store.state.stars }}</h1>
</template>

<script>
export default {
  fetch ({ store, params }) {
    return axios.get('http://my-api/stars')
    .then((res) => {
      store.commit('setStars', res.data)
    })
  }
}
</script>

По сравнению с VUESSR асинхронные данные в Nuxt более чистые и могут быть реализованы без помощи VUEX.
Магазин возвращается к сути разделения состояния

vuex

Давайте посмотрим, как vuex используется в Nuxt.
На самом деле, как я сказал в начале, Nuxt соглашается с тем, что index.js в каталоге хранилища является деревом состояний по умолчанию.
Для файлов с другими именами будут сгенерированы соответствующие модули хранилища
Итак, давайте напишем хранилище, которое сохраняет состояние входа в систему. магазин /user.js


export const state = () => {
    user: null
};

export const mutations = {
    // 设置登录用户
    set(state, user) {
        state.user = user;
    },
    loginOut(state) {
        state.user = null;
    }
}

nuxtServerInit

Один вопрос, когда мы записываем статус входа в магазин?
Думая о наших обычных приложениях, записывая файлы cookie или localstorage, это лучшее решение.
Но у нашего проекта SSR есть преимущество, мы можем тесно интегрироваться с бэкендом.
Мы можем использовать nuxtServerInit, чтобы записать статус входа клиента в sotre.

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

store/index.js

export const actions = {
    nuxtServerInit({ commit }, { req }) {
        if (req.session.user) {
            commit("user/set", req.session.user);
        }
    }
}

Таким образом, последний статус входа в систему клиента может поддерживаться все время (конечно, после успешного входа в систему он также должен быть записан в магазин).

middleware

Так как же интерфейсная страница определяет статус входа?
Лучше судить, когда переключается каждый маршрут, например, в router.beforeEach.
Но маршруты Nuxt строятся автоматически, и нет связанного интерфейса
Но не волнуйтесь, у нас есть промежуточное ПО получше
Промежуточное ПО может выполняться до того, как указанная нами страница будет загружена, и может выполнятьсяАсинхронное выполнение

Промежуточное ПО можно установить на странице макета, чтобы все страницы макета выполняли промежуточное ПО.

Давайте посмотрим на демонстрационный пример промежуточное ПО/default.js

export default function ({ route, store, redirect }) {
    // 如果用户不存在,跳到登录页面
    if (!store.state.user.user)
        redirect('/user/login?redirect=' + route.path);
}

Обратитесь к промежуточному программному обеспечению на странице _pageIndex.vue.

middleware: "default"

После ссылки, если вы не вошли в систему, вы автоматически перейдете на страницу входа

Модули/плагины

Nuxt имеет множество модулей, которые мы можем использовать
Официальные модули:
@nuxt/http: легкий и универсальный HTTP-запрос на основе ky-universal.
@nuxtjs/axios: безопасная и простая в использовании интеграция Axios с Nuxt.js для HTTP-запросов.
@nuxtjs/pwa: усовершенствуйте Nuxt с помощью проверенного, обновленного и стабильного решения PWA.
@nuxtjs/auth: Модуль аутентификации для Nuxt.js, предоставляющий различные схемы и стратегии аутентификации.

Есть еще модули сообщества:
GitHub.com/topics/гнев утихает…

Способ использования тоже очень простой, прямо в nuxt.config.js Например:

  modules: [
    ['@nuxtjs/axios', { baseURL: 'http://localhost:3000' }],
  ],

Конечно, мы по-прежнему будем ссылаться на некоторые плагины сами, мы можем использовать конфигурацию плагинов

 plugins: [
     { src: '@/plugins/element-ui', ssr: false }
  ],

Обратите внимание, что если наш плагин может работать только на стороне сервера, мы можем настроить ssr: false, и тогда внешний интерфейс также может использоваться с компонентом

Затем разверните каталог плагинов, где нам доступен объект Vue.

import Vue from 'vue'
import Element from 'element-ui'
import locale from 'element-ui/lib/locale/lang/en'

Vue.use(Element, { locale })

Жизненный цикл

На этом наша демонстрационная страница готова.
Стоит отметить различия в жизненном цикле Nuxt.
Nuxt extension asyncData, fetch, validate, промежуточное ПО будет выполняться как на переднем, так и на заднем концах.
nuxtServerInit, serverMiddleware будет выполняться на стороне сервера
Такие методы, как перенаправление и обнаружение ошибок, могут использоваться на переднем и заднем концах.
Но такие как req, res и тому подобное, внешний интерфейс не определен, на что также нужно обращать внимание в любое время.

тайник

Наконец, давайте посмотрим на кеш
Кэш компонентов такой же, как у VUESSR, элементы кеша настраиваем в nuxt.config.js

const LRU = require('lru-cache')
module.exports = {
  render: {
    bundleRenderer: {
      cache: LRU({
        max: 1000,
        maxAge: 1000 * 60 * 15
      })
    }
  }
}

Затем укажите serverCacheKey в компоненте, и все в порядке.

export default {
  props: ['type'],
  serverCacheKey: props => props.type
}

serverMiddleware

Кэширование на уровне страницы немного особенное, нам нужно использовать промежуточное ПО Nuxt на стороне сервера.
Здесь мы используем упомянутый выше redis-lru для повторного кэширования страниц.

const config = require('../nuxt.config.js')

module.exports = async function (req, res, next) {
    if (process.env.NODE_ENV === "development") {
        next();
        return;
    }

    const redis = require('redis').createClient(config.redis);
    const lru = require('redis-lru');
    const cache = lru(redis, 100);

    // 获取缓存的key值
    let key = req.originalUrl
    const value = await cache.get(key)
    if (value) {
        console.log('cached output', key);
        return res.end(value, 'utf-8')
    }
    res._end = res.end
    res.end = async function (data) {
        if (res._headers['content-type'].startsWith('text/html')
            && res.statusCode === 200) {
            await cache.set(key, data)
        }
        res._end(data, 'utf-8')
    }
    next();
}


Стоит отметить, что req и res в промежуточном программном обеспечении на стороне сервера являются объектами http-запроса Node.js.
Окончательный вывод должен вызывать исходный метод конца res (Даже если ваш внутренний сервер, настроенный Nuxt, является koa)

Эпилог

Хорошо, я закончил первую часть моей серии VUE SSR.
По сравнению с VUESSR, Nuxt является обновленной версией.Если вы новый стартап-проект, рекомендуется начать прямо с Nuxt.
Если вы хотите испытать весь процесс SSR, вы можете настроить VUESSR с нуля, и у вас будет другой опыт.

Демонстрационный адрес этого Nuxt находится здесь:GitHub.com/sleepy GitHub/ — это…