Vue.js Vue SSR将express server与Vue应用程序捆绑在一起,以便应用程序可以从复制到主机服务器的build dist文件夹运行

Vue.js Vue SSR将express server与Vue应用程序捆绑在一起,以便应用程序可以从复制到主机服务器的build dist文件夹运行,vue.js,server-side-rendering,Vue.js,Server Side Rendering,我已经创建了一个Vue SSR应用程序,我只读的所有说明/维基/博客都会告诉您如何在开发环境中运行该应用程序。它们不会告诉您如何在生产环境中运行应用程序 我以前在React SSR应用程序中编写过相同的应用程序。在该应用程序中,构建生成一个“dist”文件夹,其中包含捆绑包“server_bundle.js”。此捆绑包包含Express服务器(server.js)和React代码。我可以在“dist”文件夹中使用 node dist/server_bundle.js 在Vue SSR应用程序中

我已经创建了一个Vue SSR应用程序,我只读的所有说明/维基/博客都会告诉您如何在开发环境中运行该应用程序。它们不会告诉您如何在生产环境中运行应用程序

我以前在React SSR应用程序中编写过相同的应用程序。在该应用程序中,构建生成一个“dist”文件夹,其中包含捆绑包“server_bundle.js”。此捆绑包包含Express服务器(server.js)和React代码。我可以在“dist”文件夹中使用

node dist/server_bundle.js
在Vue SSR应用程序中,构建还生成一个“dist”文件夹。但是,它包含一个“vue ssr bundle.json”文件,其中不包括express服务器(server.js)。要在开发中运行应用程序,我必须使用位于项目根目录中的Express server文件,而不是从“dist”目录运行所有内容

node ./server.js
这在开发中是可以的,因为我在我的项目中工作,但在生产中这将不起作用,因为我必须从“dist”文件夹运行所有内容

构建和运行

该应用程序是使用以下命令构建的(在devi中添加“--watch”参数)

这些生成脚本创建并填充dist(服务器)和public(客户端)文件夹

要运行应用程序,我使用

node ./server.js"
请注意,我正在项目的根目录中使用“server.js”。它不在dist文件夹中

问题 那么,如果应用程序没有我的项目代码,它只有我的构建生成的“dist”和“public”文件夹,我将如何在生产环境中运行该应用程序?

我的代码

项目结构

project_root
  - src
      - assets
          - any static images, such as "myImage.jpg"
      - client
          - client_main.js
      - components
          - lots of files
      - pages
          - components which are top level pages
      - server 
          - server_main.js
      - vuex 
          - folders and files containing code pertaining to vuex
      - app.js
      - App.vue
      - router.js
  - index.html (the template html file to have content inserted into)
  - server.js (the Express Server file)
  - webpack.base.config.js          
  - webpack.client.config.js
  - webpack.server.config.js
src/client/client_main.js

import { createApp } from '../app'

const { app, router, store } = createApp()
... 
router.onReady(() => {
  app.$mount('#app')
})
src/server/server_main.js

import { createApp } from '../app'

export default context => {
  return new Promise((resolve, reject) => {
    const { app, router, store } = createApp()

    router.push(context.url)

    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()
      if (!matchedComponents.length) {
        return reject({ code: 404 })
      }

      resolve(app)
    }, reject)

  })
}
src/app.js

import Vue from 'vue'
import App from './App.vue'
import { createStore } from './vuex/store'
import { createRouter } from './router'
import { sync } from 'vuex-router-sync'

export function createApp () {
  const store = createStore()
  const router = createRouter()

  sync(store, router)

  const app = new Vue({
    router,
    store,
    render: h => h(App)
  })

  return { app, router, store }
}
server.js

const fs = require('fs');
const express = require('express');
const { createBundleRenderer } = require('vue-server-renderer');

const bundleRenderer = createBundleRenderer(
  require('./dist/vue-ssr-bundle.json'),
  {
    template: fs.readFileSync('./index.html', 'utf-8')
  }
);

const server = express();

server.use(express.static('public'));

server.get('*', (req, res) => {
    const context = { url: req.url }  

    bundleRenderer.renderToString(context, (err, html) => {
      if (err) {
        if (err.code === 404) {
          res.status(404).end('Page not found')
        } else {
          res.status(500).end('Internal Server Error')
        }
      } else {
        res.end(html)
      }
    })
});

server.listen(8080);
webpack.base.config.js

const webpack = require('webpack')

module.exports = {

  module: {
    rules: I wont put them all here to reduce noise, but i am using 'vue-loader', 'babel-loader''file-loader'       
  },

  resolve: {
    alias: {'vue$': 'vue/dist/vue.esm.js'},
    extensions: ['*', '.js', '.vue', '.json']
  },
  
  
  performance: {
    hints: false
  },
}
webpack.client.js

var path = require('path')
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.config.js');

const config = {
  entry: './src/client/main.js',      
  output: {
    filename: 'client-bundle.js',
    path: path.resolve(__dirname, 'public'),
  },
  module: {
    rules: I wont put them all here to reduce noise, but i am using 'vue-style-loader', 'css-loader'        
  },
  devServer: {
    historyApiFallback: true,
    noInfo: true,
    overlay: true
  },
  devtool: '#eval-source-map'
}

module.exports = merge(baseConfig, config);
webpack.server.js

const path = require('path')
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.config.js');
const webpackNodeExternals = require('webpack-node-externals');
const VueSSRPlugin = require('vue-ssr-webpack-plugin')

const config = {
  target: 'node',
  entry: './src/server/main.js',
  output: { 
    filename: 'server-bundle.js',
    path: path.resolve(__dirname, './dist'),
    libraryTarget: 'commonjs2'
  },
  externals: [webpackNodeExternals()],
  devtool: '#source-map',
  plugins: [
    new VueSSRPlugin(),
  ]
}

module.exports = merge(baseConfig, config);
我尝试的

我希望服务器构建会像React一样将Express server与Vue应用程序代码捆绑在一起,因此我只需要从dist文件夹中运行捆绑包。我认为这是一个更好更干净的解决方案

我尝试将Vue应用程序更改为像React应用程序一样构建和运行,但未成功

删除VueSSRPlugin 我删除了webpack.server.config.js中的VueSSRPlugin引用,并看到在“dist”文件中,我现在有了包和图像,就像React一样。但是,此捆绑包中仍然没有Express server。我仍然不知道如何在dist文件夹中获取express服务器

将express文件移到src/server文件夹中
我考虑将express文件(server.js)移动到我的项目源代码中,希望它能添加到包中。但是,我不知道如何更改express文件,因为它引用了express服务器文件最终所在的JSON文件。

我通过将以下内容复制到我的服务器,暂时解决了这个问题

- server-bundle.js
  • index.html
  • server.js
  • “dist”文件夹(包含服务器包)
  • “公共”文件夹(包含客户端包、样式表和其他文件)
然后在服务器上运行

node server.js
由于server.js和index.html已复制到服务器上


我仍然更喜欢React的构建方式,以便express server.js文件和index.html位于服务器捆绑包中,但至少这是可行的。

我使用React SSR来确定如何将所有Vue SSR应用程序(包括express server)捆绑到服务器捆绑包中

这个解决方案的缺点是我不能再使用“index.html”模板和“bundlerender”

此解决方案的优点是,express服务器现在可以使用WebPack配置中设置的变量

项目结构

我的项目结构如下。我将express服务器文件移到src/server文件夹中,并删除了“index.html”文件

网页包配置更改

“webpack.base.config.js”不再需要

devtool: '#source-map',
“webpack.server.config.js”不再需要“VueSSRPlugin”,因此可以删除以下内容

const VueSSRPlugin = require('vue-ssr-webpack-plugin');

plugins: [
  new VueSSRPlugin(),
],
将服务器条目更改为express服务器文件

entry: './src/server/server.js',
服务器主文件:src/Server/Server_main.js

返回VUEX应用商店和Vue应用程序。这是因为我们必须手动提取存储数据,序列化它并将其添加到返回给客户端的HTML中

import createApp from '../app';

export default (context) => new Promise((resolve, reject) => {
  const { app, router, store } = createApp();

  router.push(context.url);

  router.onReady(() => {
    const matchedComponents = router.getMatchedComponents();
    if (!matchedComponents.length) {
      return reject(new Error('404'));
    }

    context.rendered = () => {
      context.state = store.state;
    };

    return resolve({ app, store });
  }, reject);
});
import generateApp from './server_main.js';
import renderer from './renderer';

const express = require('express');
const vueServerRenderer = require('vue-server-renderer').createRenderer();

const server = express();

server.use(express.static('public'));

server.get('*', (req, res) => {
  const context = { url: req.url };

  generateApp(context).then((createdAppObj) => {
    const { app, store } = createdAppObj;

    vueServerRenderer.renderToString(
      app,
      (err, html) => {
        if (err) {
          if (err.code === 404) {
            res.status(404).end('Page not found');
          } else {
            res.status(500).end('Internal Server Error');
          }
        } else {
          // BUILD UP THE HTML TO RETURN
          const content = renderer(store, html);
          res.send(content);
        }
      },
    );
  });
});

server.listen(8080);
创建一个renderer.js文件,负责生成返回到客户端的HTML(即index.HTML的替换)

注意:您不必为此设置单独的文件,如果需要,可以将此代码包含在express server文件中

package.json更改

现在,当所有内容都构建好后,“dist”目录将包含以下内容

- server-bundle.js
这包含作为条目文件的express server文件,因此无法运行您执行的应用程序

node ./server.js
此解决方案的一个优点:Express Server可以使用在WebPack config中设置的变量

Vue支持使用模板插值将变量添加到“index.html”模板中,请参阅

然而,在我的例子中,我需要在我的网页配置中使用一个变量设置来放入“index.html”

我的网页包配置从“.env”文件设置变量,然后在我的Vue应用程序中使用该文件。
我需要在我的“index.html”中的这个“.env”文件中使用一个变量。由于Express Server没有被Web包解析和绑定,因此它不会设置变量。我必须读取Express Server文件中的“.env”文件。这意味着我还必须有“.env”express server文件所在的生产环境中的文件,以便从中读取

根据此评论中所述的更改,网页
import generateApp from './server_main.js';
import renderer from './renderer';

const express = require('express');
const vueServerRenderer = require('vue-server-renderer').createRenderer();

const server = express();

server.use(express.static('public'));

server.get('*', (req, res) => {
  const context = { url: req.url };

  generateApp(context).then((createdAppObj) => {
    const { app, store } = createdAppObj;

    vueServerRenderer.renderToString(
      app,
      (err, html) => {
        if (err) {
          if (err.code === 404) {
            res.status(404).end('Page not found');
          } else {
            res.status(500).end('Internal Server Error');
          }
        } else {
          // BUILD UP THE HTML TO RETURN
          const content = renderer(store, html);
          res.send(content);
        }
      },
    );
  });
});

server.listen(8080);
- server-bundle.js
node ./server.js