简单的服务端渲染

项目地址:https://github.com/barry7/learnSSR/tree/server-side-render-withoutAjax-dev

package.json

查看package.json可以发现, 当我们执行npm run build时,其实是执行了 webpack --config webpack.client.conf.js && webpack --config webpack.server.conf.js 那么我们去查看这几个conf.js

conf.js

webpack.client.conf.js

一开始引入了webpack-merge,和webpack.base.conf.js进行了混入, 并且使用html-webpack-plugin生成html模板。
查看webpack.base.conf.js,其中指定了一些路径和模块。
入口文件为./entry-client.js,其中代码只有三行

const createApp = require('./main.js');
const app = createApp();
app.$mount('#app');
1
2
3

main.js中导入一个工厂函数,然后将生成的VUE实例挂载到div#app上。 查看main.js

const Vue = require('vue');
const App = require('./App.vue').default;
function createApp() {
    const app = new Vue({
        render: h => h(App)
    });
    return app;
};
module.exports = createApp;
1
2
3
4
5
6
7
8
9

简单来说就是引入了一个Vue组件App.vue,也就是Foo.vueBar.vue的根组件。 然后导出一个创建Vue实例的工厂函数。

webpack.server.conf.js

类似client。但其中有些设置需要注意:

  • target: 'node':指定 Node 环境,避免非 Node 环境特定 API 报错,如 document 等;
  • filename: '[name].js':因为服务器是 Node,所以必须按照 commonjs 规范打包才能被服务器调用。

入口文件为./entry-server.js,其中代码只有三行

const createApp = require('./main.js');
module.exports = createApp;
1
2

和上面客户端渲染的文件类似,只是不需要挂载了,这是因为服务端渲染的内容会挂载到HTML文件中 <!--vue-ssr-outlet-->注释处。

至此,打包完成。

server.js

执行npm run,其实是执行了node server.js

查看server.js, 首先引入需要的模块,比如fspath、还有用作服务器的express等。

分别启动两个服务器,serverfeServer

  • server:首先实例化一个renderer,并且使用dist/index.ssr.html作为template, 当请求进入的时候,使用工厂函数实例化一个app, 使用rendererrenderToString方法将app转为HTML字符串传回。 最后监听8002端口
    • 我们可以看下dist/index.ssr.html的结构:
       <!DOCTYPE html>
       <html lang="en">
       
       <head>
           <meta charset="UTF-8">
           <meta name="viewport" content="width=device-width, initial-scale=1.0">
           <meta http-equiv="X-UA-Compatible" content="ie=edge">
           <title>服务端渲染</title>
       </head>
       
       <body>
       <div id="app">
           <!--vue-ssr-outlet-->
       </div>
       <script type="text/javascript" src="client.js"></script>
       </body>
       
       </html>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    其中注释部分就是app转换为字符串插入的地方,并且最后引入了client.js
  • feServerfeServer就相对简单一些了,当请求进入的时候,直接返回相应的HTML即可。
    • 我们也看下dist/index.html的代码:
        <!DOCTYPE html>
        <html lang="en">
        
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta http-equiv="X-UA-Compatible" content="ie=edge">
            <title>浏览器渲染</title>
        </head>
        
        <body>
        <div id="app">
            <app></app>
        </div>
        <script type="text/javascript" src="client.js"></script></body>
        
        </html>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    其中只有一个id为app的div,是一会client.js中VUE实例挂载的位置。

##查看结果 最后使用浏览器打开localhost:8002/index查看SSR的效果,或者打开 localhost:8003/index查看普通客户端渲染的效果。

看到data-server-rendered="true"就知道是服务器渲染的DIV了。

性能对比

客户端渲染

客户端渲染的First Paint时间是靠后的, 他需要等待js下载完成,才可以实例化Vue对象,然后执行完Vue的生命周期才可以渲染。

服务端渲染

可以看到,服务端渲染的First Paint时间是远远快于客户端渲染的, 这是因为他不需要等待js下载就可以渲染。