vueSSR: 從0到1構建vueSSR項目 --- node以及vue-cli3的配置

前言

上一次作了路由的相關配置,本來計劃今天要作vuex部分,可是想了想,發現vuex單獨的客戶端部分穿插解釋起來很麻煩,因此今天改作服務端部分。
服務端部分作完,再去作vuex的部分,這樣就會很清晰。css

vue ssr是分兩個端,一個是客戶端,一個是服務端。
因此要作兩個cli3的配置。
那麼下面就直接開始作吧。html

修改package.json的命令

//package.json :client表明客戶端 :server表明服務端
//使用VUE_NODE來做爲運行環境是node的標識
//cli3內置  命令 --no-clean 不會清除dist文件
    "scripts": {
        "serve:client": " vue-cli-service serve",
        "build":"npm run build:server -- --silent && npm run build:client -- --no-clean --silent",
        "build:client": "vue-cli-service build",
        "build:server": "cross-env VUE_NODE=node vue-cli-service build",
        "start:server": "cross-env NODE_ENV=production nodemon nodeScript/index"
    }

修改vue.config.js配置

添加完相關腳本命令以後,咱們開始改造cli3配置。
首先要require('vue-server-renderer')
而後再根據VUE_NODE環境變量來決定編譯的走向以及生成不一樣的環境清單vue

先作cli3服務端的入口文件node

// src/entry/server.js
import {
    createApp
} from  '../main.js'

export default context => {

    return new Promise((resolve, reject) => {
        const {
            app,
            router
        } = createApp(context.data)
        //根據node傳過來的路由 來調用router路由的指向
        router.push(context.url)

        router.onReady(() => {
            //獲取當前路由匹配的組件數組。
            const matchedComponents = router.getMatchedComponents()
            //長度爲0就是沒找到該路由所匹配的組件
            //能夠路由設置重定向或者傳回node  node來操做也能夠
            if (!matchedComponents.length) {

                return reject({
                    code: 404
                })
            }

            resolve(app)

        }, reject)
    })
}

這裏是cli3的配置webpack

//vue.config.js

const ServerPlugin = require('vue-server-renderer/server-plugin'),//生成服務端清單
      ClientPlugin = require('vue-server-renderer/client-plugin'),//生成客戶端清單
      nodeExternals = require('webpack-node-externals'),//忽略node_modules文件夾中的全部模塊
      VUE_NODE = process.env.VUE_NODE === 'node',
      entry = VUE_NODE ? 'server' : 'client';//根據環境變量來指向入口

module.exports = {
    css: {
        extract: false//關閉提取css,不關閉 node渲染會報錯
    },
    configureWebpack: () => ({
        entry: `./src/entry/${entry}`,
        output: {
            filename: 'js/[name].js',
            chunkFilename: 'js/[name].js',
            libraryTarget: VUE_NODE ? 'commonjs2' : undefined
        },
        target: VUE_NODE  ? 'node' : 'web',
        externals: VUE_NODE ? nodeExternals({
            //設置白名單
            whitelist: /\.css$/
        }) : undefined,
        plugins: [//根據環境來生成不一樣的清單。
            VUE_NODE ? new ServerPlugin() : new ClientPlugin()
        ]
    }),
    chainWebpack: config => {
        config.resolve
            .alias
                .set('vue$', 'vue/dist/vue.esm.js')
        config.module
            .rule('vue')
                .use('vue-loader')
                    .tap(options => {
                        options.optimizeSSR = false;
                        return options;
                    });
        config.module
            .rule('images')
                .use('url-loader')
                    .tap(options => {
                        options = {
                            limit: 1024,
                            fallback:'file-loader?name=img/[path][name].[ext]'
                        }
                        return options;
                    });
    }
}

node相關配置

用於node渲染 必然要攔截get請求的。而後根據get請求地址來進行要渲染的頁面。git

官方提供了vue-server-renderer插件
大概的方式就是 node攔截全部的get請求,而後將獲取到的路由地址,傳給前臺,而後使用router實例進行pushgithub

再往下面看以前 先看一下官方文檔web

建立BundleRenderer
createBundleRenderer
將 Vue 實例渲染爲字符串。
renderToString
渲染應用程序的模板
template
生成所須要的客戶端或服務端清單
clientManifestvuex

先建立 服務端所須要的模板vue-cli

//public/index.nodeTempalte.html
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <link rel="icon" href="/favicon.ico">
        <meta charset="utf-8">
        <title>vuessr</title>
    </head>
    <body>
        <!--vue-ssr-outlet-->
    </body>
</html>

node部分

先建立三個文件
index.js //入口
proxy.js //代理
server.js //主要配置

//server.js
const fs = require('fs');
const { resolve } = require('path');
const express = require('express');
const app = express();
const proxy = require('./proxy');
const { createBundleRenderer } = require('vue-server-renderer')

//模板地址
const templatePath = resolve(__dirname, '../public/index.nodeTempalte.html')
//客戶端渲染清單
const clientManifest = require('../dist/vue-ssr-client-manifest.json')
//服務端渲染清單
const bundle = require('../dist/vue-ssr-server-bundle.json')
//讀取模板
const template = fs.readFileSync(templatePath, 'utf-8')
const renderer = createBundleRenderer(bundle,{
    template,
    clientManifest,
    runInNewContext: false
})


//代理相關
proxy(app);
//請求靜態資源相關配置
app.use('/js', express.static(resolve(__dirname, '../dist/js')))
app.use('/css', express.static(resolve(__dirname, '../dist/css')))
app.use('/font', express.static(resolve(__dirname, '../dist/font')))
app.use('/img', express.static(resolve(__dirname, '../dist/img')))
app.use('*.ico', express.static(resolve(__dirname, '../dist')))


//路由請求
app.get('*', (req, res) => {
    res.setHeader("Content-Type", "text/html")
    //傳入路由 src/entry/server.js會接收到  使用vueRouter實例進行push
    const context = {
        url: req.url
    }

    renderer.renderToString(context, (err, html) => {
        if (err) {
            if (err.url) {
                res.redirect(err.url)
            } else {
                res.status(500).end('500 | 服務器錯誤');
                console.error(`${req.url}: 渲染錯誤 `);
                console.error(err.stack)
            }
        }
        res.status(context.HTTPStatus || 200)
        res.send(html)
    })
})
module.exports = app;



//proxy.js

const proxy = require('http-proxy-middleware');

function proxyConfig(obj){
    return {
        target:'localhost:8081',
        changeOrigin:true,
        ...obj
    }
}
module.exports = (app) => {
    //代理開發環境
    if (process.env.NODE_ENV !== 'production') {
        app.use('/js/main*', proxy(proxyConfig()));
        app.use('/*hot-update*',proxy(proxyConfig()));
        app.use('/sockjs-node',proxy(proxyConfig({ws:true})));
    }
}


//index.js
const app = require('./server')

app.listen(8080, () => {
  console.log('\033[42;37m DONE \033[40;33m localhost:8080 服務已啓動\033[0m')
})

作完這一步以後,就能夠預覽基本的服務渲染了。
後面就只差開發環境的配置,以及到node數據的傳遞(vuex)

npm run build
    npm run start:server
    打開localhost:8080
    F12 - Network - Doc 就能夠看到內容

圖片描述

最終目錄結構

|-- vuessr
    |-- .gitignore
    |-- babel.config.js
    |-- package-lock.json
    |-- package.json
    |-- README.md
    |-- vue.config.js
    |-- nodeScript //node 渲染配置
    |   |-- index.js
    |   |-- proxy.js
    |   |-- server.js
    |-- public//模板文件
    |   |-- favicon.ico
    |   |-- index.html
    |   |-- index.nodeTempalte.html
    |-- src
        |-- App.vue
        |-- main.js
        |-- router.config.js//路由集合
        |-- store.config.js//vuex 集合
        |-- assets//全局靜態資源源碼
        |   |-- 備註.txt
        |   |-- img
        |       |-- logo.png
        |-- components//全局組件
        |   |-- Head
        |       |-- index.js
        |       |-- index.scss
        |       |-- index.vue
        |       |-- img
        |           |-- logo.png
        |-- entry//cli3入口
        |   |-- client.js
        |   |-- server.js
        |   |-- 備註.txt
        |-- methods//公共方法
        |   |-- 備註.txt
        |   |-- mixin
        |       |-- index.js
        |-- pages//源碼目錄
        |   |-- home
        |   |   |-- index.js
        |   |   |-- index.scss
        |   |   |-- index.vue
        |   |   |-- img
        |   |   |   |-- flow.png
        |   |   |   |-- head_portrait.jpg
        |   |   |   |-- logo.png
        |   |   |   |-- vuessr.png
        |   |   |-- vue
        |   |   |   |-- index.js
        |   |   |   |-- index.scss
        |   |   |   |-- index.vue
        |   |   |-- vueCli3
        |   |   |   |-- index.js
        |   |   |   |-- index.scss
        |   |   |   |-- index.vue
        |   |   |-- vueSSR
        |   |   |   |-- index.js
        |   |   |   |-- index.scss
        |   |   |   |-- index.vue
        |   |   |-- vuex
        |   |       |-- index.js
        |   |       |-- index.scss
        |   |       |-- index.vue
        |   |-- router//路由配置
        |   |   |-- index.js
        |   |-- store//vuex配置
        |       |-- all.js
        |       |-- gather.js
        |       |-- index.js
        |-- static//cdn資源
            |-- 備註.txt

項目github地址
項目公網地址

1) vueSSR: 從0到1構建vueSSR項目 --- 初始化
2) vueSSR: 從0到1構建vueSSR項目 --- 路由的構建
3) vueSSR: 從0到1構建vueSSR項目 --- node以及vue-cli3的配置
4) vueSSR: 從0到1構建vueSSR項目 --- vuex的配置(數據預取)
5) vueSSR: 從0到1構建vueSSR項目 --- 開發環境的部署
6) vueSSR: 從0到1構建vueSSR項目 --- 僞熱更新

相關文章
相關標籤/搜索