Создайте приложение SSR с помощью vue-cli3.

сервер JavaScript Vue.js Webpack

1. Граница

1.1 Что такое ССР

SSR (рендеринг на стороне сервера), как следует из названия, предназначен для отображения страницы непосредственно на стороне клиента после завершения рендеринга на стороне сервера.

1.2 Разница между рендерингом на стороне клиента и рендерингом на стороне сервера

традиционная спа-модель

то есть режим рендеринга на стороне клиента

  1. Приложение, созданное Vue.js, по умолчанию имеет страницу шаблона html, а затем оно упаковывается webpack для создания набора файлов js, css и других ресурсов. Затем вставьте его в index.html

  2. Пользователь вводит URL-адрес для доступа к странице -> сначала получить страницу шаблона html -> затем асинхронно запросить данные сервера -> получить данные сервера -> визуализировать в частичную страницу -> пользователь

режим ССР

Режим рендеринга на стороне сервера

  1. Пользователь вводит URL-адрес для доступа к странице -> сервер получает запрос -> отображает веб-страницу с данными, соответствующими запросу -> возвращает ее пользователю

1.3 Зачем использовать SSR?

преимущества сссрОфициальный сайтУже приведено два наиболее интересных преимущества:

  1. Лучше SEO. Потому что сканеры поисковых систем могут напрямую просматривать полностью обработанные страницы.
  2. Более быстрое время до контента

1.4, принцип ССР

Это официальная схема введения принципа SSR для vue.js Из этого изображения мы можем узнать, что нам нужно упаковать и сгенерировать два файла пакета через Webpack:

  • Клиентский пакет для браузеров. Аналогичен чистому интерфейсному проекту Vue Bundle
  • Пакет сервера, для использования сервера SSR, файл json

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

2. Начните создавать приложение SSR на основе vue-cli3.

Поняв принцип ssr, давайте начнем шаг за шагом создавать собственное приложение SSR.

  1. Установите vue-cli3

Установите леса vue-cli глобально

npm install @vue/cli -g --registry=https://registry.npm.taobao.org
  1. Создать vue-проект
vue create ssr-example 

Войдет в интерактивный интерфейс bash, выберите в соответствии с вашими потребностями

Входите шаг за шагом, выбирайте в соответствии с вашими потребностями

  1. запустить проект
npm run serve

Видеть эту подсказку означает, что половина успеха достигнута, а затем осуществляется вторая половина трансформации.

3. Выполните преобразование SSR

3.1 Установите необходимые пакеты

  1. Установить vue-сервер-рендерер
  2. Установить lodash.merge
  3. Установите webpack-node-externals
  4. установить кросс-оболочку
npm install vue-server-renderer lodash.merge webpack-node-externals cross-env --registry=https://registry.npm.taobao.org --save-dev

3.2 Интеграция в сервер

  1. Создайте новый server.js в корневом каталоге проекта.
  2. установить коа2
npm install koa koa-static --save --registry=https://registry.npm.taobao.org
  1. Интегрировать ssr в koa2
// server.js
// 第 1 步:创建一个 Vue 实例
const Vue = require("vue");

const Koa = require("koa");
const app = new Koa();
// 第 2 步:创建一个 renderer
const renderer = require("vue-server-renderer").createRenderer();


// 第 3 步:添加一个中间件来处理所有请求
app.use(async (ctx, next) => {
  const vm = new Vue({
    data: {
      title: "ssr example",
      url: ctx.url
    },
    template: `<div>访问的 URL 是: {{ url }}</div>`
  });
  // 将 Vue 实例渲染为 HTML
  renderer.renderToString(vm, (err, html) => {
    if(err){
      ctx.res.status(500).end('Internal Server Error')
      return
    }
    ctx.body = html
  });
});

const port = 3000;
app.listen(port, function() {
  console.log(`server started at localhost:${port}`);
});
  1. запустить server.js
node server.js

Это показывает, что простая сборка ssr прошла успешно.

Но до сих пор мы не рендерили файл .vue клиента через сервер, так как же совместить внешний файл .vue с внутренним узлом?

3.3 Изменить конфигурацию внешнего интерфейса для поддержки SSR

0. Новыйhtmlдокумент

существуетsrcДобавить новый в каталогindex.temp.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Document</title>
  </head>
  <body>
    <!--vue-ssr-outlet-->
  </body>
</html>
1. Измените структуру исходного кода
  • Создайте два новых файла в каталоге src, одинentry-client.jsработает только в браузере одинentry-server.jsработает только на сервере
  • Изменить main.js

main.js — это «общая запись» для нашего приложения. В чистом клиентском приложении мы бы создали корневой экземпляр Vue в этом файле и смонтировали его непосредственно в DOM. Однако при рендеринге на стороне сервера (SSR) ответственность переходит к чистому файлу записи на стороне клиента. app.js просто использует export для экспорта функции createApp:

// main.js
import Vue from 'vue'
import App from './App.vue'
import { createRouter } from "./router";

// 导出一个工厂函数,用于创建新的
// 应用程序、router 和 store 实例
export function createApp () {
  const router = createRouter();
  const app = new Vue({
    router,
    // 根实例简单的渲染应用程序组件。
    render: h => h(App)
  })
  return { app,router }
}
  • Изменить запись-client.js

Клиентская запись просто создает приложение и монтирует его в DOM.

import { createApp } from './main'

// 客户端特定引导逻辑……
const { app } = createApp()

// 这里假定 App.vue 模板中根元素具有 `id="app"`
app.$mount('#app')
  • Изменить файл entry-server.js

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

import { createApp } from "./main";

export default context => {
  // 因为有可能会是异步路由钩子函数或组件,所以我们将返回一个 Promise,
  // 以便服务器能够等待所有的内容在渲染前,
  // 就已经准备就绪。
  return new Promise((resolve, reject) => {
    const { app, router } = createApp();

    // 设置服务器端 router 的位置
    router.push(context.url);

    // 等到 router 将可能的异步组件和钩子函数解析完
    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents();
      // 匹配不到的路由,执行 reject 函数,并返回 404
      if (!matchedComponents.length) {
        return reject({
          code: 404
        });
      }
      // Promise 应该 resolve 应用程序实例,以便它可以渲染
      resolve(app);
    }, reject);
  });
};
  • Изменить router.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)

export function createRouter(){
  return new Router({
    mode: 'history', //一定要是history模式
    routes: [
      {
        path: '/',
        name: 'home',
        component: Home
      },
      {
        path: '/about',
        name: 'about',
        component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
      }
    ]
  })
}

2. Измените конфигурацию веб-пакета.

Проект vue, созданный в vue-cli3, не имеет предыдущих версий webpack.base.conf.js, webpack.dev.conf.js, webpack.prod.conf.js. Итак, как настроить веб-пакет?

существуетvue-cliОфициальный сайт также объясняет, как его использовать. Самый простой способ настроить вашу конфигурацию WebPack - предоставить объект в параметре ConfigureWebPack в Vue.config.js, который будет использоватьсяwebpack-mergeВключено в окончательную конфигурацию веб-пакета.

  1. В корневом каталоге проекта создайте новый файл vue.config.js.
// vue.config.js

const VueSSRServerPlugin = require("vue-server-renderer/server-plugin");
const VueSSRClientPlugin = require("vue-server-renderer/client-plugin");
const nodeExternals = require("webpack-node-externals");
const merge = require("lodash.merge");
const TARGET_NODE = process.env.WEBPACK_TARGET === "node";
const target = TARGET_NODE ? "server" : "client";


module.exports = {
  css: {
    extract: process.env.NODE_ENV === 'production'
  },
  configureWebpack: () => ({
    // 将 entry 指向应用程序的 server / client 文件
    entry: `./src/entry-${target}.js`,
    // 对 bundle renderer 提供 source map 支持
    devtool: 'source-map',
    target: TARGET_NODE ? "node" : "web",
    node: TARGET_NODE ? undefined : false,
    output: {
      libraryTarget: TARGET_NODE ? "commonjs2" : undefined
    },
    // https://webpack.js.org/configuration/externals/#function
    // https://github.com/liady/webpack-node-externals
    // 外置化应用程序依赖模块。可以使服务器构建速度更快,
    // 并生成较小的 bundle 文件。
    externals: TARGET_NODE
      ? nodeExternals({
          // 不要外置化 webpack 需要处理的依赖模块。
          // 你可以在这里添加更多的文件类型。例如,未处理 *.vue 原始文件,
          // 你还应该将修改 `global`(例如 polyfill)的依赖模块列入白名单
          whitelist: [/\.css$/]
        })
      : undefined,
    optimization: {
          splitChunks: TARGET_NODE ? 'commonjs2' : undefined
    },
    plugins: [TARGET_NODE ? new VueSSRServerPlugin() : new VueSSRClientPlugin()]
  }),
  chainWebpack: config => {
    config.module
      .rule("vue")
      .use("vue-loader")
      .tap(options => {
        merge(options, {
          optimizeSSR: false
        });
      });
      
    // fix ssr hot update bug
    if (TARGET_NODE) {
      config.plugins.delete("hmr");
    }
  }
};
  1. Измените пакет и добавьте три скрипта для создания bundle.json.
"build:client": "vue-cli-service build",
"build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build --mode server",
"build:win": "npm run build:server && move dist\\vue-ssr-server-bundle.json bundle && npm run build:client && move bundle dist\\vue-ssr-server-bundle.json",

  1. Выполнение заказа
npm run build:win

В каталоге dist будут сгенерированы два файла json.

3.4 Измените код server.js

const fs = require("fs");
const Koa = require("koa");
const path = require("path");
const koaStatic = require('koa-static')
const app = new Koa();

const resolve = file => path.resolve(__dirname, file);
// 开放dist目录
app.use(koaStatic(resolve('./dist')))

// 第 2 步:获得一个createBundleRenderer
const { createBundleRenderer } = require("vue-server-renderer");
const bundle = require("./dist/vue-ssr-server-bundle.json");
const clientManifest = require("./dist/vue-ssr-client-manifest.json");

const renderer = createBundleRenderer(bundle, {
  runInNewContext: false,
  template: fs.readFileSync(resolve("./src/index.temp.html"), "utf-8"),
  clientManifest: clientManifest
});

function renderToString(context) {
  return new Promise((resolve, reject) => {
    renderer.renderToString(context, (err, html) => {
      err ? reject(err) : resolve(html);
    });
  });
}
// 第 3 步:添加一个中间件来处理所有请求
app.use(async (ctx, next) => {
  const context = {
    title: "ssr test",
    url: ctx.url
  };
  // 将 context 数据渲染为 HTML
  const html = await renderToString(context);
  ctx.body = html;
});

const port = 3000;
app.listen(port, function() {
  console.log(`server started at localhost:${port}`);
});

3.5 Запуск server.js

node server.js

доступlocalhost:3000, вы можете видеть, что данные страницы возвращаются после завершения рендеринга на стороне сервера. На данный момент строительство ССР в основном завершено.

Если есть проблема, вы можете удалить файл index.html в каталоге dist. Избегайте прямого доступа к html-файлу.

4. Интегрируйте vuex

  • Изменить store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export function createStore() {
  return new Vuex.Store({
    state: {

    },
    mutations: {

    },
    actions: {

    }
  });
}

  • Изменить main.js
import Vue from "vue";
import App from "./App.vue";
import { createRouter } from "@/router";
import { createStore } from "@/store";
export function createApp() {
  const router = createRouter();
  const store = createStore()  // +
  const app = new Vue({
    router,
    store,      // +
    render: h => h(App)
  });
  return { app, router,store };
}
  • Запустите сборку скрипта еще раз
npm run build:win
node server.js

5. Код дела

Прикрепите исходный код кейсаGitHub.com/Len too/vUE-…приветственная звезда

6. Резюме

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

Нет публики

Добро пожаловать, чтобы обратить внимание на мой общедоступный номер"разработка кода", делитесь последней технической информацией каждый день. Подпишитесь, чтобы получать последние ресурсы