• vue ssr(server side rendering)


    1 解决问题:

    a.)解决单页面应用的SEO问题

    b.) 客户端的网络比较慢,初次加载耗时较多

    c.) 客户端运行在老的或者直接没有javascript引擎上

    2. 实现原理

          客户端请求服务器,服务器根据请求地址获得匹配的组件,再调用匹配到的组件返回Promise来将需要的数据拿到。最后再通过 <script>window.__initial_state=data</script> 将其写入网页,最后将服务端渲染好的网页返回客户端。

      客户端利用vuex将写入的__initial_state__替换为当前的全局状态树,再用这个状态树去检查服务端渲染好的数据有没有问题。遇到没被服务端渲染的组件,再去发一步请求拿数据。VUE2使用单向数据流,用了它,就可以通过SSR返回唯一一个全局状态,并确认某个组件是否已经SSR了。

    由于vue2使用了虚拟DOM,因此对浏览器环境和服务端环境要分开渲染,要创建两个对应的入口文件。

    1)浏览器入口文件 client-entry.js, 使用$mount直接挂载

    1 import 'es6-promise/auto';
    2 
    3 import { app, store } from './app';
    4 
    5 store.replaceState(window.__INITIAL_STATE__);
    6 
    7 app.$mount('#app');
    /*
    * 在 client-entry.js 文件中引入了app.js, 判断如果在服务端渲染时已经写入状态,则将vuex的状态进行替换,使得服务端渲染的html和vuex管理的数据是同步的。
    * 然后将vue实例挂载到html指定的节点中。*/

    2) server-entry文件

    import { app, router, store } from './app';
    
    const isDev = process.env.NODE_ENV !== 'production';
        
    export default context => {
      const s = isDev && Date.now();
    
      router.push(context.url);
      const matchedComponents = router.getMatchedComponents();
    
      if (!matchedComponents.length) {
        return Promise.reject({ code: '404' });
      }
        
      return Promise.all(matchedComponents.map(component => {
        if (component.preFetch) {
          return component.preFetch(store);
        }
      })).then(() => {
        return app;
      });
    };

    // 处理所有的get请求
    app.get('*', (req, res) => {
      // 等待编译
      if (!renderer) {
        return res.end('waiting for compilation... refresh in a moment.');
      }
    
      var s = Date.now();
      const context = { url: req.url };
      // 渲染我们的Vue实例作为流
      const renderStream = renderer.renderToStream(context);
        
      // 当块第一次被渲染时
      renderStream.once('data', () => {
           // 将预先的HTML写入响应
        res.write(indexHTML.head);
      });
        
      // 每当新的块被渲染
      renderStream.on('data', chunk => {
           // 将块写入响应
        res.write(chunk);
      });
        
      // 当所有的块被渲染完成
      renderStream.on('end', () => {
        // 当vuex初始状态存在
        if (context.initialState) {
            // 将vuex初始状态以script的方式写入到页面中
          res.write(
            `<script>window.__INITIAL_STATE__=${
              serialize(context.initialState, { isJSON: true })
            }</script>`
          );
        }
        
        // 将结尾的HTML写入响应
        res.end(indexHTML.tail);
      });
        
      // 当渲染时发生错误
      renderStream.on('error', err => {
        if (err && err.code === '404') {
          res.status(404).end('404 | Page Not Found');
          return;
        }
        res.status(500).end('Internal Error 500');
      });
    })

    在 server-entry 文件中服务端会传递一个context对象,里面包含当前用户请求的url,vue-router 会跳转到当前请求的url中,通过 router.getMatchedComponents( ) 来获得当前匹配组件,则去调用当前匹配到的组件里的 preFetch 钩子,并传递store(Vuex下的状态),会返回一个 Promise 对象,并在then方法中将现有的vuex state 赋值给context,给服务端渲染使用,最后返回vue实例,将虚拟DOM渲染成网页。服务端会将vuex初始状态也生成到页面中。 如果 vue-router 没有匹配到请求的url,直接返回 Promise中的reject方法,传入404,这时候会走到下方renderStream的error事件,让页面显示错误信息。

  • 相关阅读:
    每位设计师都应该拥有的50个CSS代码片段
    JAVASCRIPT和JQUERY判断浏览器信息总汇
    jQuery Flux Slider 2D/3D 图片切换效果展示
    JavaScript判断是否IE和是否是IE6的方法
    分享一个不错的软件Clover让资源管理器变身浏览器)
    jQuery bgStretcher 背景图片切换效果插件
    Python安装wxPython和ubuntu使用apt提示不能更新
    IBOutlet & retain
    vue同时监听多个参数变化
    ubuntu 命令行chmod修改文件夹权限
  • 原文地址:https://www.cnblogs.com/drop-in-ocean/p/8109590.html
Copyright © 2020-2023  润新知