vue的两种服务器端渲染方案( 二 )


entry-server.js
import { createApp } from './app'export default context => {// 返回一个promise,服务器能够等待所有的内容在渲染前,已经准备就绪,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 })// 对所有匹配的路由组件调用 `asyncData()`Promise.all(matchedComponents.map(Component => {if (Component.asyncData) {return Component.asyncData({store,route: router.currentRoute})).then(() => {context.state = store.stateresolve(app)}).catch(reject)}, reject)(3)在根目录下创建server.js 文件
其中一个非常重要的api:createBundleRenderer,这个api上面有一个方法renderToString将代码转化成html字符串,主要功能就是把用webpack把打包后的服务端代码渲染出来 。具体了解可看官网bundle renderer指引 。
// server.jsconst app = require('express')()const { createBundleRenderer } = require('vue-server-renderer')const fs = require('fs')const path = require('path')const resolve = file => path.resolve(__dirname, file)const isProd = process.env.NODE_ENE === "production"const createRenderer = (bundle, options) => {return createBundleRenderer(bundle, Object.assign(options, {basedir: resolve('./dist'),runInNewContext: false,let renderer, readyPromiseconst templatePath = resolve('./src/index.template.html')if (isProd) {const bundle = require('./dist/vue-ssr-server-bundle.json')const clientManifest = require('./dist/vue-ssr-client-manifest.json')const template = fs.readFileSync(templatePath, 'utf-8')renderer = createRenderer(bundle, {template, // (可选)页面模板clientManifest // (可选)客户端构建 manifest} else {// 开发模式readyPromise = require('./config/setup-dev-server')(app, templatePath, (bundle, options) => {renderer = createRenderer(bundle, options)const render = (req, res) => {const context = {title: 'hello ssr with webpack',meta: `charset="UTF-8">`,url: req.urlrenderer.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)// 在服务器处理函数中……app.get('*', isProd ? render : (req, res) => {readyPromise.then(() => render(req, res))app.listen(8080) // 监听的是8080端口(4)接下来是config配置
在根目录新增config文件夹,然后新增四个配置文件:webpack.base.config,webpack.client.config,webpack.server.config,setup-dev-server(此方法是一个封装,为了配置个热加载,差点没搞明白,参考了好多)(官网传送门: 构建配置 )
大部分官网有示例代码,但是要在基础上进行一些更改
webpack.base.config
// webpack.base.configconst path = require('path')// 用来处理后缀为.vue的文件const { VueLoaderPlugin } = require('vue-loader')const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')// 定位到根目录const resolve = (dir) => path.join(path.resolve(__dirname, "../"), dir)// 打包时会先清除一下// const { CleanWebpackPlugin } = require('clean-webpack-plugin')const isProd = process.env.NODE_ENV === "production"module.exports = {mode: isProd ? 'production' : 'development',output: {path: resolve('dist'),publicPath: '/dist/',filename: '[name].[chunk-hash].js'},resolve: {alias: {'public': resolve('public')},module: {noParse: /es6-promise.js$/,rules: [test: /.vue$/,loader: 'vue-loader',options: {compilerOptions: {preserveWhiteSpace: false},test: /.js$/,loader: 'babel-loader',exclude: /node_modules/},test: /.(png|jpg|gif|svg)$/,loader: 'url-loader',options: {limit: 10000,name: '[name].[ext]?[hash]'},test: /.s(a|c)ss?$/,use: ['vue-style-loader', 'css-loader', 'sass-loader']},performance: {hints: false},plugins:[new VueLoaderPlugin(),// 编译后的友好提示,比如编译完成或者编译有错误new FriendlyErrorsWebpackPlugin(),// 打包时会先清除一下// new CleanWebpackPlugin()
webpack.client.config
// webpack.client.configconst {merge} = require('webpack-merge')const baseConfig = require('./webpack.base.config.js')const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')module.exports = merge(baseConfig, {entry: {app: './src/entry-client.js'},optimization: {// 重要信息:这将 webpack 运行时分离到一个引导 chunk 中,// 以便可以在之后正确注入异步 chunk 。// 这也为你的 应用程序/vendor 代码提供了更好的缓存 。splitChunks: {name: "manifest",minChunks: Infinity},plugins: [// 此插件在输出目录中// 生成 `vue-ssr-client-manifest.json` 。new VueSSRClientPlugin()


推荐阅读