Vue 서버 렌더링(Vue SSR)

9385 단어
서버 렌더링(SSR)이란 무엇입니까?
Vue.js는 클라이언트 응용 프로그램을 구축하는 프레임워크이지만, 같은 구성 요소를 서버의 HTML 문자열로 렌더링하여 브라우저에 직접 보내고, 마지막으로 정적 표시를 클라이언트에서 완전히 상호작용할 수 있는 응용 프로그램으로 '활성화' 할 수도 있다.서버가 렌더링하는 Vue.js 응용 프로그램도'동구'또는'통용'이라고 할 수 있으며 프로그램의 대부분 코드는 서버와 클라이언트에서 실행될 수 있다.
서버 렌더링이 필요합니까?
기존 SPA에 비해 SSR의 주요 장점은 다음과 같습니다.
  • SEO 향상
  • 더 빠른 내용 도착 시간(time-to-content)
  • 주의해야 할 점:
  • 개발 조건에 한계가 있다. 동적 업데이트가 없기 때문에 서버 렌더링 과정에서 beforeCreatecreated 갈고리 함수만 호출된다. 이 두 생명주기 함수에서 전역 부작용이 발생하는 코드를 피해야 한다. 예를 들어 그 중에서 사용setInterval으로timer를 설정하는 것을 피해야 한다.
  • 구축 설정 및 배포에 대한 요구 사항이 더 많습니다.서버 렌더링 응용 프로그램은 Node에 있어야 합니다.js 서버 실행 환경입니다.
  • 더 많은 서버 측 워크로드높은 유량 환경에서 사용할 것으로 예상되면 서버 부하를 준비하고 캐시 정책을 현명하게 적용해야 한다.

  • 서버 렌더링이 필요하다면 다음 사용법을 계속 보십시오.
    기본용법
    설치하다.
    npm install vue vue-server-renderer --save
    

    참고:
  • Node.js버전 6+
  • vue-servier-renderervue 버전 일치 필요
  • 빌드 단계
    1. 포털 파일 만들기
    클라이언트 응용 프로그램과 서버 응용 프로그램 모두 웹 패키지로 두 개의 버블을 포장해야 한다. 서버는 서버 렌더링에 서버 버블을 필요로 하고 클라이언트 버블은 브라우저에 보내서 정적 표시를 혼합하는 데 사용한다.
    하나의 기본 항목은 다음과 같다.
    src
    ├── components
    │   ├── Foo.vue
    │   └── Baz.vue
    ├── App.vue
    ├── app.js #    entry(universal entry)
    ├── index.template.html
    ├── entry-client.js #        
    └── entry-server.js #        
    

    단일 루틴 메커니즘으로 인해 서버 렌더링에서 단일 루틴과 유사한 조작이 있기 때문에 모든 요청이 이 단일 루틴의 조작을 공유하기 때문에 공장 함수를 사용하여 모든 요청 간의 독립성을 확보해야 한다.app.js는 주로 exportcreateApp 함수이다.유사하게 storerouter는 모두 이런 공장 함수를 내보내야 한다.
    # app.js
    import Vue from 'vue';
    import App from './App.vue';
    import { createStore } from './store';
    import { createRouter } from './router';
    
    export const createApp = () => {
      const store = createStore();
      const router = createRouter();
      const app = new Vue({
        router,
        store,
        render: h => h(App)
      });
      return { app, router, store };
    };
    
    

    클라이언트 entry에서 응용 프로그램을 만들고 DOM에 마운트합니다.
    # entry-client.js
    import { createApp } from './app';
    const { app, router, store } = createApp();
    
    if (window.__INITIAL_STATE__) {
      store.replaceState(window.__INITIAL_STATE__);
    }
    
    router.onReady(() => {
      #         ,     asyncData.
      #       resolve    ,          (double-fetch)     。
      #    `router.beforeResolve()`,            resolve。
      router.beforeResolve((to, from, next) => {
        ...
      });
    
      app.$mount('#app');
    });
    
    

    서버 entry는 default export 함수를 내보내고, 렌더링할 때마다 이 함수를 반복합니다.여기서 서버 사이드 루트 일치(server-side route matching)와 데이터 추출 논리(data-pre-fetching logic)를 실행할 수 있습니다.
    # 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(() => {
          #         
          ...
        }, reject);
      });
    };
    
    

    2. 웹팩 구축 구성
    구성 파일 구조는 다음과 같습니다.
    build
    ├── dev-server.js
    ├── setup-dev-server.js
    ├── webpack.base.conf.js 
    ├── webpack.client.conf.js 
    ├── webpack.dev.conf.js 
    ├── webpack.prod.conf.js 
    └── webpack.server.conf.js
    
    package.json 패키지 명령:
    "scripts": {
        "dev": "NODE_ENV=dev node server/index.js",
        "build:client": "webpack --config build/webpack.client.conf.js --progress --hide-modules --progress",
        "build:server": "webpack --config build/webpack.server.conf.js --progress --hide-modules --progress",
        "build:prod": "NODE_ENV=prod npm run build:client && NODE_ENV=prod npm run build:server",
      },
    

    3. 서버 통합 개발
    개발 서비스는 Koa를 사용하고 구성 참조:
    import Koa from 'koa';
    import koaRouter from 'koa-router';
    import { createBundleRenderer } from 'vue-server-renderer';
    
    const app = new Koa();
    const router = koaRouter();
    const createRenderer = (bundle, options) => {
      return createBundleRenderer(
        bundle,
        {...options, { runInNewContext: false }
      );
    };
    
    const renderData = (ctx, renderer) => {
      const context = {
        url: ctx.url
      };
      return new Promise((resolve, reject) => {
        renderer.renderToString(context, (err, html) => {
          if (err) {
            reject(err);
          }
          resolve(html);
        });
      });
    };
    
    let renderer;
    require('../build/setup-dev-server.js')(app, (bundle, options) => {
      renderer = createRenderer(bundle, options);
    });
    
    # proxy api request
    const proxy = require('koa-server-http-proxy');
    #     ...
    
    router.get('*', async (ctx, next) => {
        if (!renderer) {
          ctx.type = 'html';
          return (ctx.body = 'waiting for compilation...');
        }
        
        let html;
        try {
          html = await renderData(ctx, renderer);
        } catch (e) {
          #       
          ...
        }
        ctx.body = html;
    });
    
    app.use(router.routes()).use(router.allowedMethods());
    
    app.listen(80, '0.0.0.0', () => {
      console.log(`server is running...`);
    });
    
    

    4. 온라인 서버 통합
    온라인 서비스는 Egg를 사용합니다.js, 참조 구성은 다음과 같습니다.
    # app/controller/home.js
    const Controller = require('egg').Controller;
    const path = require('path');
    const { createBundleRenderer } = require('vue-server-renderer');
    
    const serverBundle = require('../public/vue-ssr-server-bundle.json');
    const clientManifest = require('../public/vue-ssr-client-manifest.json');
    const template = require('fs').readFileSync(
      path.resolve(__dirname, '../public/index.html'),
      'utf-8'
    );
    const renderer = createBundleRenderer(serverBundle, {
      runInNewContext: false,
      template,
      clientManifest
    });
    
    class HomeController extends Controller {
      async index() {
        const ctx = this.ctx;
        const context = { url: ctx.url };
    
        try {
          #   context       
          renderer.renderToString(context, (err, html) => {
            if (err) {
              throw err;
            }
            ctx.status = 200;
            #    template, html      
            ctx.body = html;
          });
        } catch (error) {
          ctx.status = 500;
          ctx.body = 'Internal Server Error';
        }
      }
    }
    
    module.exports = HomeController;
    
    

    라우팅 일치:
    router.get(/^(?!\/api\/)/, controller.home.index);
    

    이렇게 하면 개발과 생산 환경 설정에 따라 기본적인 서버 렌더링을 실현할 수 있다.편폭이 제한되어 큰 코드가 잠시 붙지 않았으며, 후속으로 원본 코드 예시를 개방할 것입니다.

    좋은 웹페이지 즐겨찾기