vue服务端转html,普通vue-cli初始项目转为服务端渲染SSR

1、第一步没啥好说的,利用vue-cli脚手架创建一个demo项目:

vue init webpack vue-ssr-demo

cd vue-ssr-demo

npm install

2、安装SSR依赖的模块:

npm i -D vue-server-renderer

3、随便加一个components/test.vue

Just a test page.

export default {

data () {

return {

}

}

}

4、src目录下创建俩js文件:

src

├── entry-client.js # 仅运行于浏览器

└── entry-server.js # 仅运行于服务器

5、修改router/index.js

import Vue from 'vue'

import Router from 'vue-router'

import HelloWorld from '@/components/HelloWorld'

Vue.use(Router)

export function createRouter () {

return new Router({

mode: 'history', // 注意这里也是为history模式

routes: [

{

path: '/',

name: 'Hello',

component: HelloWorld

}, {

path: '/test',

name: 'Test',

component: () => import('@/components/test') // 异步组件

}

]

})

}

6、修改main.js里路由的引入:

// The Vue build version to load with the `import` command

// (runtime-only or standalone) has been set in webpack.base.conf with an alias.

import Vue from 'vue'

import App from './App'

import { createRouter } from './router'

export function createApp () {

// 创建 router 实例

const router = new createRouter()

const app = new Vue({

// 注入 router 到根 Vue 实例

router,

render: h => h(App)

})

// 返回 app 和 router

return { app, router }

}

7、在entry_client.js里面写上如下内容:

import { createApp } from './main';

const {app, store, router} = createApp();

router.onReady(() => {

// 添加路由钩子函数,用于处理 asyncData.

// 在初始路由 resolve 后执行,

// 以便我们不会二次预取(double-fetch)已有的数据。

// 使用 `router.beforeResolve()`,以便确保所有异步组件都 resolve。

router.beforeResolve((to, from, next) => {

const matched = router.getMatchedComponents(to)

const prevMatched = router.getMatchedComponents(from)

// 我们只关心非预渲染的组件

// 所以我们对比它们,找出两个匹配列表的差异组件

let diffed = false

const activated = matched.filter((c, i) => {

return diffed || (diffed = (prevMatched[i] !== c))

})

if (!activated.length) {

return next()

}

// 这里如果有加载指示器 (loading indicator),就触发

Promise.all(activated.map(c => {

if (c.asyncData) {

return c.asyncData({ store, route: to })

}

})).then(() => {

// 停止加载指示器(loading indicator)

next()

}).catch(next)

})

app.$mount('#app')

})

8、在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) {

// eslint-disable-next-line

return reject({ code: 404 })

}

// Promise 应该 resolve 应用程序实例,以便它可以渲染

resolve(app)

}, reject)

})

}

9、在build文件夹下新增一个webpack.server.conf.js,写上:

const webpack = require('webpack')

const merge = require('webpack-merge')

const base = require('./webpack.base.conf.js')

const nodeExternals = require('webpack-node-externals')

const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')

module.exports = merge(base, {

target: 'node',

devtool: '#source-map',

entry: './src/entry-server.js',

output: {

filename: 'server-bundle.js',

libraryTarget: 'commonjs2'

},

resolve: {

alias: {

'create-api': './create-api-server.js'

}

},

// https://webpack.js.org/configuration/externals/#externals

// https://github.com/liady/webpack-node-externals

externals: nodeExternals({

// do not externalize CSS files in case we need to import it from a dep

allowlist: /\.css$/

}),

plugins: [

new webpack.DefinePlugin({

'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),

'process.env.VUE_ENV': '"server"'

}),

new VueSSRServerPlugin()

]

})

10、在webpack.prod.conf.js中加上:

const VueSSRClientPlugin = require('vue-server-renderer/client-plugin') // 开头引入这个

....

new webpack.DefinePlugin({

'process.env': env,

'process.env.VUE_ENV': '"client"' // 这里加一行process.env.VUE_ENV

}),

....

new VueSSRClientPlugin() // plugins数组里最后加一个这个

11、修改webpack.base.conf.js的entry:

entry: {

app: './src/entry-client.js' // 改为这个

},

12、配置package.json增加打包服务器端构建命令并修改原打包命令:

"build:client": "node build/build.js",

"build:server": "cross-env NODE_ENV=production webpack --config build/webpack.server.conf.js --progress --hide-modules",

"build": "rimraf dist && npm run build:client && npm run build:server"

13、根目录新建一个index.template.html模板页面(或者直接修改原index.html,只是这样要区分生产环境和开发环境,否则run dev的时候就不ok):

14、根目录下新建一个server.js(没安装express的先npm install express):

const express = require('express');

const fs = require('fs');

const path = require('path');

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

const app = express();

const serverBundle = require('./dist/vue-ssr-server-bundle.json');

const clientManifest = require('./dist/vue-ssr-client-manifest.json');

const template = fs.readFileSync(path.resolve('./index.template.html'), 'utf-8'); // 这里就看13步骤里你是哪个文件

const render = createBundleRenderer(serverBundle, {

runInNewContext: false, // 推荐

template, // (可选)页面模板

clientManifest

});

app.use(express.static('./dist',{index:false}))

app.get('*', (req, res) => {

const context = { url: req.url }

render.renderToString(context, (err, html) => {

console.log(html)

// 处理异常……

res.end(html)

})

})

const port = 3003;

app.listen(port, function() {

console.log(`server started at localhost:${port}`);

});

15、准备就绪了兄弟们,因为是本地运行,偷偷把"build:server"里的NODE_ENV先等于development,运行:

npm run build

之后可以看到生成了dist目录:

f251d195457d9b4114721f3e42086575.png

3318594e215257758f709fd39f4a9702.png

16、然后,运行:

node server

成功启动server started at localhost:3003

打开localhost:3003,在控制台NetWork里看到如下,SSR服务端渲染成功。

abfd46b5e30e6408a5ef71d23ebb26e0.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值