Part3-4-1 搭建自己的SSR

构建同构渲染

构建流程

Part3-4-1 搭建自己的SSR_第1张图片

源码结构
我们需要使用 webpack 来打包我们的 Vue 应用程序。事实上,我们可能需要在服务器上使用
webpack 打包 Vue 应用程序,因为:
  • 通常 Vue 应用程序是由 webpack vue-loader 构建,并且许多 webpack 特定功能不能直接在Node.js 中运行(例如通过 file-loader 导入文件,通过 css-loader 导入 CSS)。
  • 尽管 Node.js 最新版本能够完全支持 ES2015 特性,我们还是需要转译客户端代码以适应老版浏览器。这也会涉及到构建步骤。
所以基本看法是,对于客户端应用程序和服务器应用程序,我们都要使用 webpack 打包 - 服务器需要「服务器 bundle 」然后用于服务器端渲染 (SSR) ,而「客户端 bundle」会发送给浏览器,用于混合静态标记。
现在我们正在使用 webpack 来处理服务器和客户端的应用程序,大部分源码可以使用通用方式编写,可以使用 webpack 支持的所有功能。同时,在编写通用代码时,有一些 事项 要牢记在心。

一个基本项目可能像是这样:

Part3-4-1 搭建自己的SSR_第2张图片

App.vue
 

 

app.js
app.js 是我们应用程序的「通用 entry 」。在纯客户端应用程序中,我们将在此文件中创建根 Vue 实 例,并直接挂载到 DOM 。但是,对于服务器端渲染 (SSR) ,责任转移到纯客户端 entry 文件。 app.js 简单地使用 export 导出一个 createApp 函数:
import Vue from 'vue' 
import App from './App.vue' 

// 导出一个工厂函数,用于创建新的 
// 应用程序、router 和 store 实例 
export function createApp () { 
    const app = new Vue({ 
        // 根实例简单的渲染应用程序组件。 
        render: h => h(App) 
    })
    return { app } 
}
entry-client.js
客户端 entry 只需创建应用程序,并且将其挂载到 DOM 中:
import { createApp } from './app' 

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

// 这里假定 App.vue 模板中根元素具有 `id="app"` 
app.$mount('#app')
entry-server.js
服务器 entry 使用 default export 导出函数,并在每次渲染中重复调用此函数。此时,除了创建和返回应用程序实例之外,它不会做太多事情 - 但是稍后我们将在此执行服务器端路由匹配 (server-side route matching) 和数据预取逻辑 (data pre-fetching logic)
import { createApp } from './app' 

export default context => { 
    const { app } = createApp() 
    return app 
}

构建配置

安装生产依赖
npm i vue vue-server-renderer express cross-env

Part3-4-1 搭建自己的SSR_第3张图片

安装开发依赖
npm i -D webpack webpack-cli webpack-merge 
webpack-node-externals @babel/core 
@babel/plugin-transform-runtime 
@babel/preset-env babel-loader 
css-loader url- loader file-loader 
rimraf vue-loader 
vue-template-compiler 
friendly-errors- webpack-plugin

Part3-4-1 搭建自己的SSR_第4张图片

配置文件及打包命令
(1 )初始化 webpack 打包配置文件

Part3-4-1 搭建自己的SSR_第5张图片

webpack.base.config.js  

/*** 公共配置 */
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const path = require('path')
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
const resolve = file => path.resolve(__dirname, file)
const isProd = process.env.NODE_ENV === 'production'

module.exports = {
  mode: isProd ? 'production' : 'development',
  output: {
    path: resolve('../dist/'),
    publicPath: '/dist/',
    filename: '[name].[chunkhash].js'
  },
  resolve: {
    alias: {
      // 路径别名,@ 指向 src 
      '@': resolve('../src/')
    },
    // 可以省略的扩展名 
    // 当省略扩展名的时候,按照从前往后的顺序依次解析
    extensions: ['.js', '.vue', '.json']
  },
  devtool: isProd ? 'source-map' : 'cheap-module-eval-source-map',
  module: {
    rules: [
      // 处理图片资源 
      {
        test: /\.(png|jpg|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            },
          },
        ],
      },
      // 处理字体资源 
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ['file-loader',],
      },
      // 处理 .vue 资源 
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      // 处理 CSS 资源 
      // 它会应用到普通的 `.css` 文件 
      // 以及 `.vue` 文件中的 `