亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue.js狀態管理及SSR解析怎么實現

發布時間:2022-09-14 09:59:48 來源:億速云 閱讀:143 作者:iii 欄目:開發技術

這篇文章主要介紹“Vue.js狀態管理及SSR解析怎么實現”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Vue.js狀態管理及SSR解析怎么實現”文章能幫助大家解決問題。

    前端狀態管理出現的意義及解決的問題

    隨著前端應用的逐步復雜,我們的組件中需要使用越來越多的狀態。有的時候我們需要使用子組件將狀態傳遞給父組件就會比較復雜,數據的向上傳遞過程我們可能會使用回調函數或是數據綁定的形式去處理,就會讓代碼晦澀難懂。

    Vue.js狀態管理及SSR解析怎么實現

    我們需要一種方式,能夠讓數據在所有組件中共享,同時能以簡單的方式進行傳遞,這種組織數據的方式就是狀態管理。我們很自然的就想到,把數據放到所有需要使用的組件的公共祖先上,在使用時自上而下傳遞即可。

    在 vue.js 中,我們主要說的狀態管理庫就是 vuex,當然,只要你能實現有條理的組織數據,那么它都可以認為是一種狀態管理庫。

    事實上,我們可以簡單的這樣理解【狀態管理】這個詞,vuex 實際上做的事情就是:

    • 在頂層實現一個數據管理的倉庫 store,將所有組件間需要共享的數據放置于此;

    • 同時組件也可以對這個 store 內的數據進行更新,同時更新完之后響應式更新所有使用此數據組件的視圖;

    Vue.js狀態管理及SSR解析怎么實現

    Vuex 源碼解讀

    Vuex 公共方法

    • 路徑:src\util.js

    export function find(list, f) {
      return list.filter(f)[0];
    }
    
    export function deepCopy(obj, cache = []) {
      if (obj === null || typeof obj !== 'object') {
        return obj;
      }
    
      const hit = find(cache, c => c.original === obj);
      if (hit) {
        return hit.copy;
      }
    
      const copy = Array.isArray(obj) ? [] : {};
      cache.push({
        original: obj,
        copy,
      });
    
      Object.keys(obj).forEach(key => {
        copy[key] = deepCopy(obj[key], cache);
      });
    
      return copy;
    }
    
    export function forEachValue(obj, fn) {
      Object.keys(obj).forEach(key => fn(obj[key], key));
    }
    
    export function isObject(obj) {
      return obj !== null && typeof obj === 'object';
    }
    
    export function isPromise(val) {
      return val && typeof val.then === 'function';
    }
    
    export function assert(condition, msg) {
      if (!condition) throw new Error(`[vuex] ${msg}`);
    }
    
    export function partial(fn, arg) {
      return function () {
        return fn(arg);
      };
    }

    Vuex 介紹及深入使用

    在說 vuex 之前,我們必須說一說 flux 架構,flux 架構可謂是狀態管理的鼻祖。

    flux 架構最早由 facebook 推出,主要是為了處理當時他們的 react 框架下狀態管理的問題,但在當時來講,整個設計比較復雜,后來人們簡化了其中的一些理念,但是保留了核心思想,繼而依據框架實現了很多不同的狀態管理庫,例如 reduxvuex 等等。其中 redux 大多數被用在了 react 項目中,而 vuex 就是在 vue 框架中實現的這么一個 flux 架構的狀態管理庫。

    **flux 架構約定,存放數據的地方稱為 storestore 內部的 state 是數據本身,我們必須通過 action 才能修改 store 里的 state。**這里的 action 指的意思是 行為,在大部分實現里面是一個函數,通過調用函數來更改 store 內部的 state

    Vue.js狀態管理及SSR解析怎么實現

    vuex 中,我們可以通過 mutation 來【同步】的改變 state,這時候就可以在組件中通過 commit 進行調用更改 state

    同樣的,我們也可以通過 action 來【異步】更改 state,不過在 action 中,我們還是需要調用 mutation

    Vuex 使用(官網)

    1、基本框架

    import Vue from 'vue';
    import Vuex from 'vuex';
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
      state: {},
      mutations: {},
      actions: {},
      modules: {},
    });

    2、基本使用

    • ./store/index.js

    import Vue from 'vue';
    import Vuex from 'vuex';
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
      state: {
        count: 0,
      },
      mutations: {
        increment(state, payload = 1) {
          state.count += payload;
        },
      },
      actions: {},
      modules: {},
    });
    • ./Home.vue

    <template>
      <div class="home">
        <div>count: {{ count }}</div>
        <button @click="increment">增加</button>
        <button @class="decrement">減少</button>
      </div>
    </template>
    
    <script>
    export default {
      name: 'Home',
      computed: {
        count() {
          return this.$store.state.count;
        },
      },
      methods: {
        increment() {
          // 使用 commit 派發 mutation 事件
          // this.$store.commit("increment");
          // 可以傳遞參數
          this.$store.commit('increment', 2);
        },
        decrement() {},
      },
    };
    </script>

    3、State

    可以使用計算屬性獲取 state 中的數據:

    computed: {
      count () {
        return this.$store.state.count
      }
    }
    3.1 mapState 輔助函數

    當一個組件需要獲取多個狀態的時候,將這些狀態都聲明為計算屬性會有些重復和冗余。為了解決這個問題,我們可以使用 mapState 輔助函數幫助我們生成計算屬性。

    import { mapState } from "vuex";
    
    computed: {
      ...mapState(["num"])
    }

    4、Getter

    Vuex 允許我們在 store 中定義 getter(可以認為是 store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變才會被重新計算。

    Getter 接受 state 作為其第一個參數:

    const store = new Vuex.Store({
      state: {
        todos: [
          { id: 1, text: 'foo', done: true },
          { id: 2, text: 'bar', done: false },
        ],
      },
      getters: {
        doneTodos: state => {
          return state.todos.filter(todo => todo.done);
        },
      },
    });
    4.1 通過屬性訪問

    Getter 會暴露為 store.getters 對象,你可以以屬性的形式訪問這些值:

    store.getters.doneTodos; // -> [{ id: 1, text: '...', done: true }]

    Getter 也可以接受其他 getter 作為第二個參數:

    getters: {
      // ...
      doneTodosCount: (state, getters) => {
        return getters.doneTodos.length;
      };
    }
    4.2 通過方法訪問

    你也可以通過讓 getter 返回一個函數,來實現給 getter 傳參。

    getters: {
      // ...
      getTodoById: state => id => {
        return state.todos.find(todo => todo.id === id);
      };
    }
    store.getters.getTodoById(2); // -> { id: 2, text: '...', done: false }

    【注意】:getter 在通過方法訪問時,每次都會去進行調用,而不會緩存結果

    4.3 mapGetters 輔助函數

    mapGetters 輔助函數僅僅是將 store 中的 getter 映射到局部計算屬性:

    import { mapGetters } from 'vuex';
    
    export default {
      computed: {
        // 使用對象展開運算符將 getter 混入 computed 對象中
        ...mapGetters(['doneTodosCount', 'anotherGetter']),
      },
    };

    如果你想將一個 getter 屬性另取一個名字,使用對象形式:

    ...mapGetters({
      // 把 this.doneCount 映射為 this.$store.getters.doneTodosCount
      doneCount: 'doneTodosCount'
    })

    5、Mutation

    更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似于事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是我們實際進行狀態更改的地方,并且它會接受 state 作為第一個參數:

    const store = new Vuex.Store({
      state: {
        count: 1,
      },
      mutations: {
        increment(state) {
          // 變更狀態
          state.count++;
        },
      },
    });

    你不能直接調用一個 mutation handler。這個選項更像是事件注冊:“當觸發一個類型為 increment 的 mutation 時,調用此函數。”要喚醒一個 mutation handler,你需要以相應的 type 調用 store.commit 方法:

    store.commit('increment');
    5.1 提交載荷(Payload)

    你可以向 store.commit 傳入額外的參數,即 mutation 的 載荷(payload

    mutations: {
      increment (state, n) {
        state.count += n
      }
    }
    
    // 使用方法
    store.commit('increment', 10)

    在大多數情況下,載荷應該是一個對象,這樣可以包含多個字段并且記錄的 mutation 會更易讀:

    mutations: {
      increment (state, payload) {
        state.count += payload.amount
      }
    }
    
    // 使用方法
    store.commit('increment', {
      amount: 10
    })

    對象風格的提交方式:

    提交 mutation 的另一種方式是直接使用包含 type 屬性的對象:

    store.commit({
      type: 'increment',
      amount: 10,
    });

    當使用對象風格的提交方式,整個對象都作為載荷傳給 mutation 函數,因此 handler 保持不變:

    mutations: {
      increment (state, payload) {
        state.count += payload.amount
      }
    }
    5.2 使用常量替代 Mutation 事件類型

    使用常量替代 mutation 事件類型在各種 Flux 實現中是很常見的模式。這樣可以使 linter 之類的工具發揮作用,同時把這些常量放在單獨的文件中可以讓你的代碼合作者對整個 app 包含的 mutation 一目了然:

    // mutation-types.js
    export const SOME_MUTATION = 'SOME_MUTATION';
    // store.js
    import Vuex from 'vuex'
    import { SOME_MUTATION } from './mutation-types'
    
    const store = new Vuex.Store({
      state: { ... },
      mutations: {
        // 我們可以使用 ES2015 風格的計算屬性命名功能來使用一個常量作為函數名
        [SOME_MUTATION] (state) {
          // mutate state
        }
      }
    })
    5.3 Mutation 必須是同步函數

    一條重要的原則就是要記住 mutation 必須是同步函數。為什么?請參考下面的例子:

    mutations: {
      someMutation (state) {
        api.callAsyncMethod(() => {
          state.count++
        })
      }
    }

    現在想象,我們正在 debug 一個 app 并且觀察 devtool 中的 mutation 日志。每一條 mutation 被記錄,devtools 都需要捕捉到前一狀態和后一狀態的快照。然而,在上面的例子中 mutation 中的異步函數中的回調讓這不可能完成:因為當 mutation 觸發的時候,回調函數還沒有被調用,devtools 不知道什么時候回調函數實際上被調用 &mdash;&mdash; 實質上任何在回調函數中進行的狀態的改變都是不可追蹤的。

    5.4 在組件中提交 Mutation

    你可以在組件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 輔助函數將組件中的 methods 映射為 store.commit 調用(需要在根節點注入 store)。

    import { mapMutations } from 'vuex';
    
    export default {
      // ...
      methods: {
        ...mapMutations([
          'increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')`
    
          // `mapMutations` 也支持載荷:
          'incrementBy', // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)`
        ]),
        ...mapMutations({
          add: 'increment', // 將 `this.add()` 映射為 `this.$store.commit('increment')`
        }),
      },
    };

    6、Action

    Action 類似于 mutation,不同在于:

    • Action 提交的是 mutation,而不是直接變更狀態。

    • Action 可以包含任意異步操作。

    讓我們來注冊一個簡單的 action

    const store = new Vuex.Store({
      state: {
        count: 0,
      },
      mutations: {
        increment(state) {
          state.count++;
        },
      },
      actions: {
        increment(context) {
          context.commit('increment');
        },
      },
    });

    Action 函數接受一個與 store 實例具有相同方法和屬性的 context 對象,因此你可以調用 context.commit 提交一個 mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters。當我們在之后介紹到 Modules 時,你就知道 context 對象為什么不是 store 實例本身了。

    實踐中,我們會經常用到 ES2015 的參數解構來簡化代碼(特別是我們需要調用 commit 很多次的時候):

    actions: {
      increment ({ commit, state, getters }) {
        commit('increment')
      }
    }

    7、分發 Action

    Action 通過 store.dispatch 方法觸發:

    store.dispatch('increment');

    乍一眼看上去感覺多此一舉,我們直接分發 mutation 豈不更方便?實際上并非如此,還記得 mutation 必須同步執行這個限制么?Action 就不受約束!我們可以在 action 內部執行異步操作:

    actions: {
      incrementAsync ({ commit }) {
        setTimeout(() => {
          commit('increment')
        }, 1000)
      }
    }

    Actions 支持同樣的載荷方式和對象方式進行分發:

    // 以載荷形式分發
    store.dispatch('incrementAsync', {
      amount: 10,
    });
    
    // 以對象形式分發
    store.dispatch({
      type: 'incrementAsync',
      amount: 10,
    });
    7.1 在組件中分發 Action

    你在組件中使用 this.$store.dispatch('xxx') 分發 action,或者使用 mapActions 輔助函數將組件的 methods 映射為 store.dispatch 調用(需要先在根節點注入 store):

    import { mapActions } from 'vuex';
    
    export default {
      // ...
      methods: {
        ...mapActions([
          'increment', // 將 `this.increment()` 映射為 `this.$store.dispatch('increment')`
    
          // `mapActions` 也支持載荷:
          'incrementBy', // 將 `this.incrementBy(amount)` 映射為 `this.$store.dispatch('incrementBy', amount)`
        ]),
        ...mapActions({
          add: 'increment', // 將 `this.add()` 映射為 `this.$store.dispatch('increment')`
        }),
      },
    };
    7.2 組合 Action

    Action 通常是 異步 的,那么如何知道 action 什么時候結束呢?更重要的是,我們如何才能組合多個 action,以處理更加復雜的異步流程?

    首先,你需要明白 store.dispatch 可以處理被觸發的 action 的處理函數返回的 Promise,并且 store.dispatch 仍舊返回 Promise

    actions: {
      actionA ({ commit }) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            commit('someMutation')
            resolve()
          }, 1000)
        })
      }
    }

    現在你可以:

    store.dispatch('actionA').then(() => {
      // ...
    });

    在另外一個 action 中也可以:

    actions: {
      // ...
      actionB ({ dispatch, commit }) {
        return dispatch('actionA').then(() => {
          commit('someOtherMutation')
        })
      }
    }

    最后,如果我們利用 async / await,我們可以如下組合 action

    // 假設 getData() 和 getOtherData() 返回的是 Promise
    actions: {
      async actionA ({ commit }) {
        commit('gotData', await getData())
      },
      async actionB ({ dispatch, commit }) {
        await dispatch('actionA') // 等待 actionA 完成
        commit('gotOtherData', await getOtherData())
      }
    }

    一個 store.dispatch 在不同模塊中可以觸發多個 action 函數。在這種情況下,只有當所有觸發函數完成后,返回的 Promise 才會執行。

    8、嚴格模式

    開啟嚴格模式,僅需在創建 store 的時候傳入 strict: true

    const store = new Vuex.Store({
      // ...
      strict: true,
    });

    在嚴格模式下,無論何時發生了狀態變更且不是由 mutation 函數引起的,將會拋出錯誤(但是數據還是會改變)。這能保證所有的狀態變更都能被調試工具跟蹤到。

    Vue.js狀態管理及SSR解析怎么實現

    8.1 開發環境與發布環境

    不要在發布環境下啟用嚴格模式! 嚴格模式會深度監測狀態樹來檢測不合規的狀態變更 &mdash;&mdash; 請確保在發布環境下關閉嚴格模式,以避免性能損失。

    類似于插件,我們可以讓構建工具來處理這種情況:

    const store = new Vuex.Store({
      // ...
      strict: process.env.NODE_ENV !== 'production',
    });

    vue.js 服務端渲染介紹

    大多數我們使用的 UI 框架如 vue 和 react,都是在客戶端進行渲染,也就是說每個用戶在加載進來我們所有的 html 文件和 js 文件之后,才開始渲染頁面的內容。

    但是這樣做會有兩個問題,一個是如果用戶網絡速度比較慢,如果我們渲染的內容比較多的話,就會產生一個延遲,造成不好的用戶體驗。另一個是某些爬蟲,例如百度的搜索收錄的爬蟲,在爬取你的頁面時,獲取不到你的頁面的真實內容,導致站點 SEO 權重變低。

    所以很多需要 SEO 的頁面,都需要在服務端提前渲染好 html 的內容,在用戶訪問時先返回給用戶內容,這楊對用戶和爬蟲都非常友好。

    我們可以通過直接在頁面上右擊查看網頁源代碼,來查看一個頁面是否有服務端渲染。

    1、客戶端渲染和服務端渲染

    客戶端渲染
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>客戶端渲染</title>
      </head>
      <body>
        <script>
          document.body.innerHTML = '<div>你好</div>';
        </script>
      </body>
    </html>

    在 Network 的中 Preview 中無數據,在 Response 中的沒有 DOM 標簽:

    Vue.js狀態管理及SSR解析怎么實現

    查看網頁源代碼:

    Vue.js狀態管理及SSR解析怎么實現

    2、服務端渲染

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>服務端渲染</title>
      </head>
      <body>
        <div>你好</div>
      </body>
    </html>

    在 Network 的中 Preview 中有數據,在 Response 中的有 DOM 標簽:

    Vue.js狀態管理及SSR解析怎么實現

    查看網頁源代碼:

    Vue.js狀態管理及SSR解析怎么實現

    客戶端路由

    在控制臺中可以看到,切換路由的時候并沒有發起 ajax 請求。

    3、服務端渲染實例

    vue.js 的服務端渲染非常簡單,我們只需要在 node.js 中通過 vue-server-renderer 模塊,調用對應服務端渲染的渲染器對組件渲染即可,他就會生成組件對應的 html 內容。渲染成功的 html 標簽,我們可以直接返回到客戶端作為初始請求 html 的返回值。

    • 安裝依賴

    yarn add express vue vue-server-renderer
    • ./index.js

    const Vue = require('vue');
    const createRenderer = require('vue-server-renderer').createRenderer;
    
    const vm = new Vue({
      data() {
        return {
          count: 100,
        };
      },
      template: `<div>{{ count }}</div>`,
    });
    
    const renderer = createRenderer();
    
    renderer.renderToString(vm, (err, html) => {
      console.log('html ==========', html); // <div data-server-rendered="true">100</div>
    });
    • ./index.js

    const Vue = require('vue');
    const createRenderer = require('vue-server-renderer').createRenderer;
    const express = require('express');
    const fs = require('fs');
    const path = require('path');
    
    const app = express();
    
    // ! 服務端路由
    app.get('*', function (req, res) {
      const vm = new Vue({
        data() {
          return {
            url: `服務端路由 ${req.url}`,
            count: 100,
          };
        },
        template: `<div>{{ url }} - {{ count }}</div>`,
      });
    
      const renderer = createRenderer({
        // 設置模板
        template: fs.readFileSync(path.resolve(__dirname, './index.template.html'), 'utf-8'),
      });
    
      renderer.renderToString(vm, (err, html) => {
        res.send(html);
      });
    });
    
    const PORT = 8080;
    app.listen(PORT, () => {
      console.log(`服務器啟動在 ${PORT} 端口`);
    });
    • ./index.template.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>服務端渲染Demo</title>
      </head>
      <body>
        <h2>這里是模板</h2>
        <!--! 中間不能帶空格 -->
        <!--vue-ssr-outlet-->
      </body>
    </html>
    • 執行 index.js 文件:node ./index.js

    我們需要注意的一點是,在服務端渲染組件,我們使用不了 windowlocation 等瀏覽器環境中的對象,所以如果組件內部使用了這種內容會報錯。

    同時,在服務端渲染時我們要注意,組件的生命周期也會只執行 beforeCreate 和 created 這兩個,所以在此聲明周期里面不能使用 window,但是可以在其他聲明周期比如 mounted 中使用。還有渲染的數據,對于服務端渲染的組件來說,我們不應該發請求獲取組件數據,而是應該直接渲染時使用數據進行渲染。

    路由也是如此,在 vue 客戶端使用路由的時候,我們也需要在服務端對路由進行匹配,從而得知具體需要渲染的組件是哪個。

    3、同構 - 客戶端渲染和服務端渲染

    • ./webpack.config.js

    /* 
      客戶端 webpack 配置
    */
    const path = require('path');
    const webpack = require('webpack');
    
    module.exports = {
      entry: './src/entry-client.js',
      output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'build.js',
      },
      module: {
        rules: [
          {
            test: /\.vue$/,
            loader: 'vue-loader',
          },
          {
            test: /\.js$/,
            loader: 'babel-loader',
            exclude: /node_modules/,
          },
        ],
      },
      resolve: {
        extensions: ['*', '.js', '.vue'],
      },
    };
    • ./webpack.server.config.js

    /* 
      服務端 webpack 配置
    */
    const path = require('path');
    const webpack = require('webpack');
    
    const merge = require('webpack-merge');
    const baseWebpackConfig = require('./webpack.config');
    
    const config = merge(baseWebpackConfig, {
      target: 'node',
      entry: {
        app: './src/entry-server.js',
      },
      output: {
        path: __dirname,
        filename: 'server.bundle.js',
        libraryTarget: 'commonjs2',
      },
    });
    
    console.log('config ============ ', config);
    module.exports = config;
    • ./package.json

    {
      "name": "06",
      "version": "1.0.0",
      "description": "",
      "main": "webpack.config.js",
      "dependencies": {
        "babel-core": "^6.26.3",
        "babel-loader": "^7.1.5",
        "babel-preset-env": "^1.7.0",
        "babel-preset-stage-3": "^6.24.1",
        "express": "^4.17.1",
        "vue": "^2.6.11",
        "vue-router": "^3.3.2",
        "vue-server-renderer": "^2.6.11",
        "vuex": "^3.4.0",
        "webpack": "^3.12.0",
        "webpack-merge": "^4.2.2"
      },
      "devDependencies": {
        "vue-loader": "^13.7.3",
        "vue-template-compiler": "^2.6.11"
      },
      "scripts": {
        "build-server": "webpack --config webpack.server.config.js",
        "build-client": "webpack --config webpack.config.js"
      },
      "keywords": [],
      "author": "",
      "license": "ISC"
    }
    • ./src/App.vue

    <template>
      <div>
        <h2>this is App.vue</h2>
    
        <router-link to="/">root</router-link>
        <router-link to="/about">about</router-link>
        <router-view></router-view>
      </div>
    </template>
    • ./src/Home.vue

    <template>
      <div>Home {{ $store.state.timestamp }}</div>
    </template>
    • ./src/About.vue

    <template>
      <div>about {{ $store.state.timestamp }}</div>
    </template>
    • ./index.js

    /* 
      entry-client 和 entry-server 共同的文件
    */
    import Vue from 'vue';
    import Router from 'vue-router';
    import Vuex from 'vuex';
    import Home from './Home';
    import About from './About';
    import App from './App';
    
    Vue.use(Router);
    Vue.use(Vuex);
    
    export function createApp() {
      const store = new Vuex.Store({
        state: {
          timestamp: new Date().getTime(),
        },
      });
    
      if (typeof window !== 'undefined' && window.store) {
        store.replaceState(window.store);
      }
    
      const router = new Router({
        mode: 'history',
        routes: [
          { path: '/', component: Home },
          { path: '/about', component: About },
        ],
      });
    
      const vm = new Vue({
        router,
        store,
        render: h => h(App),
      });
    
      return { vm, router, store };
    }
    • ./src/entry-server.js 第一種

    /* 
      服務端渲染 - 入口
    */
    const express = require('express');
    const fs = require('fs');
    const path = require('path');
    const renderer = require('vue-server-renderer').createRenderer();
    
    const { createApp } = require('./index');
    
    const app = express();
    
    app.use('/dist', express.static(path.join(__dirname, './dist')));
    
    app.get('/build.js', function (req, res) {
      const pathUrl = path.resolve(process.cwd(), './dist/build.js');
      console.log(pathUrl);
      res.sendFile(pathUrl);
    });
    
    app.get('*', function (req, res) {
      const url = req.url;
      const { vm, router } = createApp();
      router.push(url);
    
      /* 
        const matchedComponents: Array<Component> = router.getMatchedComponents(location?)
        返回目標位置或是當前路由匹配的組件數組 (是數組的定義/構造類,不是實例)。通常在服務端渲染的數據預加載時使用。
      */
      const matchedComponent = router.getMatchedComponents();
      if (!matchedComponent) {
        // 404 處理
      } else {
        renderer.renderToString(vm, function (err, html) {
          res.send(html);
        });
      }
    });
    
    const PORT = 8080;
    app.listen(PORT, () => {
      console.log(`服務器啟動在 ${PORT} 端口`);
    });
    
    /* 
      此時可以執行 yarn build-server 編譯 entry-server 文件,生成 server.bundle.js
      執行 node ./server.bundle.js 查看服務端路由的結果
    */
    • ./src/entry-server.js 第二種

    /* 
      服務端渲染 - 入口
    */
    const express = require('express');
    const fs = require('fs');
    const path = require('path');
    const renderer = require('vue-server-renderer').createRenderer({
      template: fs.readFileSync(path.resolve(process.cwd(), './index.template.html'), 'utf-8'),
    });
    
    const { createApp } = require('./index');
    
    const app = express();
    
    app.use('/dist', express.static(path.join(__dirname, './dist')));
    
    app.get('/build.js', function (req, res) {
      const pathUrl = path.resolve(process.cwd(), './dist/build.js');
      console.log(pathUrl);
      res.sendFile(pathUrl);
    });
    
    app.get('*', function (req, res) {
      const url = req.url;
      const { vm, router, store } = createApp();
      router.push(url);
    
      /* 
        const matchedComponents: Array<Component> = router.getMatchedComponents(location?)
        返回目標位置或是當前路由匹配的組件數組 (是數組的定義/構造類,不是實例)。通常在服務端渲染的數據預加載時使用。
      */
      const matchedComponent = router.getMatchedComponents();
      if (!matchedComponent) {
        // 404 處理
      } else {
        renderer.renderToString(vm, function (err, html) {
          res.send(html);
        });
      }
    });
    
    const PORT = 8080;
    app.listen(PORT, () => {
      console.log(`服務器啟動在 ${PORT} 端口`);
    });
    • ./src/entry-client.js

    /* 
      客戶端渲染 - 入口
    */
    import { createApp } from './index';
    
    const { vm } = createApp();
    
    vm.$mount('#app');
    • ./index.template.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <body>
        <div id="app">
          <h2>這里是模板</h2>
          <!--vue-ssr-outlet-->
        </div>
        <script src="/build.js"></script>
      </body>
    </html>

    執行 yarn build-client 編譯客戶端;

    執行 yarn build-server 編譯服務端;

    執行 node ./server.bundle.js 啟動服務器,打開瀏覽器輸入網址 http://localhost:8080/。

    關于“Vue.js狀態管理及SSR解析怎么實現”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    湖北省| 唐山市| 长白| 青州市| 博兴县| 乳源| 那曲县| 聊城市| 元朗区| 鄂州市| 沙雅县| 宝山区| 巴林右旗| 奎屯市| 海门市| 太白县| 平乡县| 岱山县| 蓬莱市| 株洲市| 连平县| 和政县| 新竹县| 砚山县| 广安市| 南宁市| 上杭县| 肥乡县| 时尚| 桂平市| 佳木斯市| 方正县| 东乌| 明水县| 荔浦县| 四子王旗| 峨眉山市| 屏南县| 蕉岭县| 疏附县| 景德镇市|