學(xué)習(xí)記錄(只為方便自己回顧)

webpack.server.config.js

const webpack = require('webpack');
const merge = require('webpack-merge');

const base = require('./webpack.base.conf');
module.exports = merge(base,{
    target:"node",
    entry:"./src/entry-server.js",
    output:{
        filename:"bundle.server.js",
        libraryTarget:"commonjs2"
    },
    plugins:[
        
    ]
})

webpack.client.config.js

const webpack = require("webpack");
const vueLoaderConfig = require('./vue-loader.conf');
const utils = require('./utils')
const path  = require("path");
function resolve(dir){
    return path.join(__dirname,"..",dir);
}
module.exports = {
    entry: {
        app: './src/entry-client.js'
    },
    output: {
        path: path.resolve(__dirname,'../dist'),
        filename: 'bundle.client.js',
        publicPath: '/dist/'
    },
    plugins:[
        // new VueSSRServerPlugin();
    ],
    resolve: {
        extensions: ['.js', '.vue', '.json'],
        alias: {
        'vue$': 'vue/dist/vue.esm.js',
        '@': resolve('src')
        }
    },
    module: {
        rules: [
        {
            test: /\.vue$/,
            loader: 'vue-loader',
            options: vueLoaderConfig
        },
        {
            test: /\.js$/,
            loader: 'babel-loader',
            include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
        },
        {
            test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
            loader: 'url-loader',
            options: {
            limit: 10000,
            name: utils.assetsPath('img/[name].[hash:7].[ext]')
            }
        },
        {
            test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
            loader: 'url-loader',
            options: {
            limit: 10000,
            name: utils.assetsPath('media/[name].[hash:7].[ext]')
            }
        },
        {
            test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
            loader: 'url-loader',
            options: {
            limit: 10000,
            name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
            }
        }
        ]
    }
}

entry-server.js

import { createApp } from './main';

export default context => {
    
    return new Promise((resolve,reject) =>{
        const { app,router,store } = createApp()
        const url = context.url;
        router.push(url);
        router.onReady(()=>{
            const matchedComponents = router.getMatchedComponents();
            if(!matchedComponents.length){
                reject({code:404})
            }
            let asyncDataPromiseFns = [];
            matchedComponents.map( component => {
                if(component.serverRequest){
                    asyncDataPromiseFns.push(component.serverRequest(store));
                }   
            })
            //遍歷路由下所有的組件蜓堕,如果有需要服務(wù)端渲染的請求愿吹,則進行請求
            Promise.all(asyncDataPromiseFns).then(()=>{
                context.state = store.state;
                resolve(app);
            }).catch(reject)
        },reject)
    })
}

entry-client.js

//客戶端的入口文件
import { createApp } from './main';

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

if(window.__INITIAL_STATE__){
    store.replaceState(window.__INITIAL_STATE__);
}

window.onload = function(){
    app.$mount('#app');
}

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 router from './router'
import createRouter from "./router";
import createStore from './store'
Vue.config.productionTip = false

/* eslint-disable no-new */
export function createApp () {
  const router = createRouter();
  const store = createStore();
  const app = new Vue({
   router,
   store,
   // 根實例簡單的渲染應(yīng)用程序組件甥雕。
   render: h => h(App)
  })
  return { app,router,store,App }
 }

路由配置文件

import Vue from 'vue'
import Router from 'vue-router'
import home from '@/components/home'
import about from '@/components/about'


Vue.use(Router)

export default () => {
  return new Router({
    mode: 'history', //一定要是history模式
    routes: [
      {
        path: '/',
        name: 'home',
        component: home
      },
      {
        path: '/about',
        name: 'about',
        component: about
      }
    ]
  })
} 

Vuex配置文件

import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios';
Vue.use(Vuex);

export default function createStore(){
    let store = new Vuex.Store({
        state:{
            homeInfo:''
        },
        actions:{
            getHomeInfo({commit}){
                return new Promise((resolve,reject)=>{
                    axios.get("api地址").then(res=>{
                        commit('setHomeInfo',res.data);
                        resolve();
                    }).catch(reject);
                })
            }
        },
        mutations:{
            setHomeInfo(state,data){
                state.homeInfo = data;
            }
        }
    })
    return store;
}

請求數(shù)據(jù)的頁面

<template>
    <div class="home">
        <h1>這是首頁</h1>
        <div>{{homeInfo.message}}</div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
            
            }
        },
        serverRequest(store){
            return store.dispatch('getHomeInfo')
        },
        computed:{
            homeInfo(){
                return this.$store.state.homeInfo;
            }
        }
    }
</script>

package.json的scripts配置信息

 "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "build": "node build/build.js",
    "server": "webpack --config build/webpack.server.config.js",
    "client": "webpack --config build/webpack.client.config.js"
  }

服務(wù)器文件

const Vue = require('vue');
const Koa = require('koa');
const server = new Koa();
const path = require("path");
const koaStatic = require('koa-static');
const router = require('koa-router')();


//創(chuàng)建服務(wù)端渲染器
const renderer = require("vue-server-renderer").createRenderer();
const createApp = require('./dist/bundle.server.js')['default'];

//設(shè)置靜態(tài)資源目錄
server.use(koaStatic(path.resolve(__dirname,"./dist")))

const  clientBundleFileUrl = "/bundle.client.js";


router.get('/text',async ctx=>{
    ctx.body={
        code:1,
        message:"我是服務(wù)器返回的測試內(nèi)容!"
    }
})

router.get('*',async ctx=>{
    const context = { url:ctx.request.url };
    try{
        const app = await createApp(context);
        let state = JSON.stringify(context.state);
        const resHtml = await renderer.renderToString(app);
        ctx.body=`
            <!DOCTYPE html>
            <html>
            <head>
                <meta charset="utf-8">
                <meta name="viewport" content="width=device-width,initial-scale=1.0">
                <title>Vue2.0 SSR渲染頁面</title>
                <script>window.__INITIAL_STATE__=${state}</script>
                <script src="${clientBundleFileUrl}"></script>
            </head>
            <body>
                ${resHtml}
            </body>
            </html>        
        `;
    }catch(err){
        ctx.body="程序運行出錯栅受,請聯(lián)系管理員進行處理!"
    }
    
})

server.use(router.routes());
server.use(router.allowedMethods());

server.listen(3000,()=>{
    console.log("服務(wù)已啟動!");
});

利用到的插件

vue-server-renderer

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末果正,一起剝皮案震驚了整個濱河市秦躯,隨后出現(xiàn)的幾起案子忆谓,更是在濱河造成了極大的恐慌,老刑警劉巖踱承,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倡缠,死亡現(xiàn)場離奇詭異哨免,居然都是意外死亡,警方通過查閱死者的電腦和手機昙沦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門琢唾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盾饮,你說我怎么就攤上這事采桃。” “怎么了丘损?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵普办,是天一觀的道長。 經(jīng)常有香客問我号俐,道長泌豆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任吏饿,我火速辦了婚禮踪危,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘猪落。我一直安慰自己贞远,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布笨忌。 她就那樣靜靜地躺著蓝仲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪官疲。 梳的紋絲不亂的頭發(fā)上袱结,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機與錄音途凫,去河邊找鬼垢夹。 笑死,一個胖子當(dāng)著我的面吹牛维费,可吹牛的內(nèi)容都是我干的果元。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼犀盟,長吁一口氣:“原來是場噩夢啊……” “哼而晒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起阅畴,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤倡怎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诈胜,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡豹障,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了焦匈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片血公。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖缓熟,靈堂內(nèi)的尸體忽然破棺而出累魔,到底是詐尸還是另有隱情,我是刑警寧澤够滑,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布垦写,位于F島的核電站,受9級特大地震影響彰触,放射性物質(zhì)發(fā)生泄漏梯投。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一况毅、第九天 我趴在偏房一處隱蔽的房頂上張望分蓖。 院中可真熱鬧,春花似錦尔许、人聲如沸么鹤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒸甜。三九已至,卻和暖如春余佛,著一層夾襖步出監(jiān)牢的瞬間柠新,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工辉巡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留登颓,地道東北人。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓红氯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親咕痛。 傳聞我的和親對象是個殘疾皇子痢甘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容