ssr服务端渲染入门详解,webpack怎么着绕过QQ音乐接口对host的求证详解

By admin in 4858美高梅 on 2019年5月25日

前言

vue中请求本地数据

率先有的 基本介绍

依靠vue-ssr服务端渲染入门详解,vue-ssr服务端详解

不久前在采取vue2.5+webpack三.陆撸三个移动端音乐系列, 获取全体歌单json数据时相遇了接口对host和referer的限量
,故不可能一直在前端采取jsonp对接口数据的读取。 

一.改变webpack.dev.conf.js配置(用express)(只引进json文件,可能在配置中一向用mock.js)

VUE开辟请求本地数据的配置,旧版本dev-server.js,新本子webpack.dev.conf.js

在此文件中增加:

var appData = require('../data.json')//加载本地数据文件
var seller = appData.seller//获取对应的本地数据
var ratings = appData.ratings

地点的第贰行是引进json数据所在的地方,第三和第壹行是分别猎取相应的数额。

data.json一般位于index同级下,自感到可以放在其余地点,此时也就率先行有照望的路线对应。

//data.json
{
  "seller": {
    "name": "粥品香坊(回龙观)",
    "description": "蜂鸟专送",
    "deliveryTime": 38,
    "score": 4.2,
    "serviceScore": 4.1,
    "supports": [
      {
        "type": 0,
        "description": "在线支付满28减5"
      },
      {
        "type": 1,
        "description": "VC无限橙果汁全场8折"
      }
    ],
    "infos": [
      "该商家支持发票,请下单写好发票抬头",
      "品类:其他菜系,包子粥店",
      "北京市昌平区回龙观西大街龙观置业大厦底商B座102单元1340",
      "营业时间:10:00-20:30"
    ]
  },
  "ratings":{
      "id":1,"name":"wangzi","age":24
  },
  "goods":[
       {"id":1,"name":"wangzi","age":24},
       {"id":2,"name":"lisi","age":44},
       {"id":3,"name":"zhangsan","age":22}
  ]
}

上述json文件中,有三组数据,seller、rating、goods,那都足以在webpack.dev.conf.js中引入使用

再者在webpack.dev.conf.js中,需求做那样的配置才可在任何文件中开始展览呼吁,在devServer最后扩张下边包车型地铁代码:

before(app) {
    app.get('/api/seller', (req, res) => {
        res.json({
          errno: 0,
          data: seller
        })//接口返回json数据,上面配置的数据seller就赋值给data请求后调用
    }),
    app.get('/api/goods', (req, res) => {
        res.json({
          errno: 0,
          data: goods
        })
    }),
    app.get('/api/ratings', (req, res) => {
        res.json({
          errno: 0,
          data: ratings
        })
    })
}

这里就贯彻了get请求,在浏览器框口输入http://localhost:8080/api/seller
就可以看出json数据了。
要实现post请求:

app.post('/api/foods', function (req, res) { // 注意这里改为post就可以了
  res.json({
    errno: 0,
    data: foods
  });
})

专注每一回退换配置后,都不能够不重新起动,npm run dev

1、前言

服务端渲染实现原理机制:在服务端拿多少进行解析渲染,直接生成html片段再次回到给前端。然后前端能够通过剖析后端重临的html片段到前者页面,大约有以下二种样式:

1、服务器通过沙盘引擎直接渲染整个页面,比方java后端的vm模版引擎,php后端的smarty模版引擎。
贰、服务渲染生成html代码块, 前端通过AJAX获取然后利用js动态增进。

先是有的 基本介绍

一、 先完结应用jsonp读取的章程安装jsonp模块并,
封装请求方法

补充:要是大家不从引进外部的json文件,间接用mock.js生成数据。

可以在webpack.dev.conf.js文件中:

var Mock = require('mockjs');

下一场在底下的before这里就成:

before(app){
    app.get('/api/s1', (req, res) => {
        res.json(Mock.mock({
            "status": 200,
            "data|1-9": [{
                "name|5-8": /[a-zA-Z]/,
                "id|+1": 1,
                "value|0-500": 20
            }]
        }))
    })
}

一点差异也未有于能够用上面包车型客车方法请求。

在文书中,则足以开始展览对此接口数据进行呼吁,请求方式此处列举多少个:
均能够在network中张开查看

  • 使用es6增加的fetch请求:

methods: {
    getPrice () {
        fetch('/api/seller')
        .then(response => response.json())
        .then(function(datas){
            console.log(datas);
        })
    },
    postRatings (){
        fetch('/api/goods',{method:'post'})
        .then(response => response.json())
        .then(function(datas){
            console.log(datas);
        })
       }
    },
created () {
  this.getPrice(),
  this.postRatings()
}
  • 由此vue-resource插件中的this.$http.get(‘url’).then()获取

methods: {
    getRatings (){
        // 这个需要vue-resource插件
        this.$http.get('/api/ratings')
        .then((res) => {
            console.log(res)
            console.log(res.body);//获取数据
        })
     },
     postR (){
        this.$http.post('/api/goods')
        .then((res) => {
            console.log(res)
            console.log(res.body);//获取数据
        })
     },
},
created () {
  this.getRatings(),
  this.postR()
}
  • vue-resource在2.0事后不再更新,所以选用axios来实行呼吁:

二、服务端渲染的叁69等

服务端渲染能够缓慢解决两大难题:

一、seo难点,有利于寻觅引擎蜘蛛抓取网址内容,利于网址的录用和排名。
二、首屏加载过慢难点,比如未来成熟的SPA项目中,打起初页供给加载繁多财富,通过服务端渲染能够加快首屏渲染。
平等服务端渲染也是有坏处,重要是依据本身的事务场景来选用切合格局,由于服务端渲染前端页面,必将会给服务器增添压力。

1、前言

服务端渲染完结原理机制:在服务端拿多少开始展览分析渲染,直接生成html片段再次来到给前端。然后前端能够经过分析后端重返的html片段到前端页面,大约有以下三种形式:

一、服务器通过沙盘引擎直接渲染整个页面,举个例子java后端的vm模版引擎,php后端的smarty模版引擎。
2、服务渲染生成html代码块, 前端通过AJAX获取然后选取js动态拉长。

  1. $ npm install -S jsonp

此间扩充vue-resource插件使用:

在Vue一.0的时候有2个官方推荐的 ajax 插件 vue-resource,然则自从 Vue
更新到 二.0 之后,官方就不再更新 vue-resource。

一.装置vue-resource到品种中,找到当前项目

npm install vue-resource --save

贰.设置收尾后,在main.js中程导弹入,如下所示:

import  VueResource  from 'vue-resource'

Vue.use(VueResource) 

谈谈Vue.js——vue-resource全攻略

支持的HTTP方法

vue-resource的央浼API是比照REST风格设计的,它提供了五种请求API:

  • get(url, [options])
  • head(url, [options])
  • delete(url, [options])
  • jsonp(url, [options])
  • post(url, [body], [options])
  • put(url, [body], [options]ssr服务端渲染入门详解,webpack怎么着绕过QQ音乐接口对host的求证详解。)
  • patch(url, [body], [options])

除开jsonp以外,其余⑥种的API名称是明媒正娶的HTTP方法。当服务端使用REST
API时,客户端的编码风格和服务端的编码风格类似壹致,那足以减掉前端和后端开荒人士的联系成本。

客户端请求方法 服务端管理措施

  • this.$http.get(…) Getxxx
  • this.$http.post(…) Postxxx
  • this.$http.put(…) Putxxx
  • this.$http.delete(…) Deletexxx

三、SS奥德赛的兑现原理

客户端请求服务器,服务器依照请求地址获得特别的机件,在调用相配到的零件重回Promise (官方是preFetch方法)来将急需的多寡获得。最终再通过

<script>window.__initial_state=data</script>

将其写入网页,最终将服务端渲染好的网页再次来到回去。

接下去客户端会将vuex将写入的 initial_state
替换为如今的全局状态树,再用这些状态树去检查服务端渲染好的数额有没临时。境遇没棉被和衣服务端渲染的机件,再去发异步请求拿多少。说白了正是三个近似React的
shouldComponentUpdate 的Diff操作。

Vue二使用的是单向数据流,用了它,就可以透过 SSCRUISER 重返唯13个大局状态,
并确认有些组件是不是已经SSRAV4过了。

二、服务端渲染的上下

服务端渲染能够缓慢解决两大主题材料:

壹、seo难点,有利于寻觅引擎蜘蛛抓取网址内容,利于网址的采取和排行。
二、首屏加载过慢难点,比近些日子后成熟的SPA项目中,打初阶页要求加载繁多财富,通过服务端渲染能够加速首屏渲染。
同样服务端渲染也可能有坏处,主借使依赖本人的政工场景来挑选切合形式,由于服务端渲染前端页面,必将会给服务器扩大压力。

2. 封装import originJSONP from 'jsonp'

此间扩张使用axios的利用方法

vue二.0品种实战(三)使用axios发送请求
安装:

npm install axios

axios并不是插件,所以不需求选拔Vue.use(),而是在哪些地点选取时引用就能够;
参考vue二.0类型实战(叁)使用axios发送请求

上面是本人的其实试行结果,代码已经上传,可参照github上代码
(刚开始由于小编在测试学习进程中使用了vue-resource,所以$http已经侵吞,所以在main.js中追加的原型链一直不能够采用,得到了以下的代码可行)

//home.vue

<script>
    import axios from 'axios'
    export default{
        methods:{
            getpostAxios (){
                axios.get('/api/ratings')
                .then(res => {
                    console.log(res.data);
                })
                axios.post('./api/goods',
                {
                    data:1,
                    params:233
                })
                .then(res => {
                    console.log(11111);
                    console.log(res.data);
                })
            },
            axiosAllTest (){
                function getUserAccount() {
                    return axios.get('/api/seller')
                        .then(res => {
                            console.log(2222);
                            console.log(res);
                        })
                }
                function getUserPermissions() {
                  return axios.get('/api/ratings')
                    .then(res => {
                        console.log(333333);
                        console.log(res);
                    })

                }
                axios.all([getUserAccount(), getUserPermissions()])
                .then(axios.spread(function (acct, perms){
                    //两个请求现已完成
                    console.log(acct);//undefined
                    console.log(perms);//undefined
                    console.log('complete');
                }));
            }

        },
        created (){
            this.getpostAxios();
            this.axiosAllTest();
        }
    }

</script>

首先须求引进axios:

import axios from 'axios'

末尾使用的正是

  • axios.request(config)
  • axios.get(url [,config])
  • axios.delete(url [,config])
  • axios.head(url [,config])
  • axios.post(url [,data [,config]])
  • axios.put(url [,data [,config]])
  • axios.patch(url [,data [,config]])

而文章一同先所说的在main.js中加多原型链也可用(幸免重复,作者动用了$http壹):

import axios from 'axios'
Vue.prototype.$http1 = axios

//entity.vue中
methods:{
    getData(){
        console.log(this);
        this.$http1.get('/api/seller')
        .then(function (response) {
            console.log(1123);
            console.log(response.data);
        })
        .catch(function (error) {
            console.log(error);
        });
    }
},
created (){
    this.getData();
}

4858美高梅 1

WX20180226-163852@2x.png

这里想唤起本人,this就对准vue。

能够封装axio方法:

import axios from 'axios'
class MlTools {
    /**
     * 封装全局ajax
     * @param param
     */
    static ajax(param) {
        if (param.type === 'post') {
            axios({
                method: 'post',
                url: param.url,
                data:param.data
            }).then((res) => {
                const result = typeof(res.data) =='object' ? res.data : JSON.parse(res.data);
                param.success(result);
            }, (err) => {
                const error = typeof(err.data) =='object' ? err.data : JSON.parse(err.data);
                param.error(error.message);
            })
        } else if (param.type === 'get') {
            axios({
                method: 'get',
                url:param.url
            }).then((res) => {
                const result = typeof(res.data) =='object' ? res.data : JSON.parse(res.data);
                param.success(result);
            }, (err) => {
                const error = typeof(err.data) =='object' ? err.data : JSON.parse(err.data);
                param.error(error);
            })
        }
    }
}

export default MlTools;

如此这般使用起来就和$.ajax同样了,同时也得以在vue中引进jqu’ery插件那样就足以应用$.ajax了。

四、vue后端渲染主要插件:vue-server-renderer

出于virtual dom的引进,使得vue的服务端渲染成为了说不定,下边是法定
vue-server-renderer提供的渲染流程图:

4858美高梅 2

能够看出vue的后端渲染分两个部分组成:页面包车型大巴源码(source),node层的渲染部分和浏览器端的渲染部分。

source分为二种entry point,3个是前者页面包车型客车入口client
entry,主借使实例化Vue对象,将其挂载到页面中;别的三个是后端渲染服务入口server
entry,首假如控服务端渲染模块回调,再次来到二个Promise对象,最后回到三个Vue对象(经过测试,直接回到Vue对象也是能够的);

前方的source部分正是事情费用的代码,开荒形成之后经过 webpack
实行构建,生成对应的bundle,这里不再赘言client
bundle,便是一个可在浏览器端实践的打包文件;这里说下server bundle,
vue二提供 vue-server-renderer模块,模块能够提供三种render:
rendererer/bundleRenderer ,上边分别介绍下那二种render。

renderer接收一个vue对象
,然后开始展览渲染,这种对于简易的vue对象,能够那样去做,可是对于复杂的档期的顺序,假如利用这种直接require2个vue对象,那些对于服务端代码的结商谈逻辑都不太融洽,首先进范例块的景况会直接承接在各类请求渲染请求,我们供给去管理和幸免此次渲染请求的景况影响到前边的呼吁,由此vue-server-renderer提供了别的一种渲染方式,通过一个bundleRenderer去做渲染。

bundleRenderer是较为复杂项目张开服务端渲染官方推荐的法门,通过webpack以server
entry遵照一定的渴求打包生成2个server-bundle,它也正是三个方可给劳务端用的app的打包压缩文件,每趟调用都会重复初叶化
vue对象,保险了每便请求都以独立的,对于开辟者来讲,只须要注意于最近专门的学业就足以,不用为服务端渲染开采更加多的逻辑代码。
renderer生成达成之后,都存在八个接口,分别是renderToString和renderToStream,3个是一次性将页面渲染成字符串文件,其它三个是流式渲染,适用于支撑流的web服务器,能够是伸手服务的进程越来越快。

3、SS路虎极光的兑现原理

客户端请求服务器,服务器依据请求地址得到非凡的机件,在调用相称到的零件再次来到Promise (官方是preFetch方法)来将急需的数额获得。最终再经过

<script>window.__initial_state=data</script>

将其写入网页,最终将服务端渲染好的网页重返回去。

接下去客户端会将vuex将写入的 initial_state
替换为眼下的大局状态树,再用那一个情景树去检查服务端渲染好的数码有未有标题。蒙受没棉被和衣服务端渲染的机件,再去发异步请求拿多少。说白了正是一个看似React的
shouldComponentUpdate 的Diff操作。

Vue二使用的是单向数据流,用了它,就足以因此 SSPAJERO 重回唯一2个大局状态,
并确认某些组件是不是曾经SS帕杰罗过了。

function jsonp(url, data, options) {
 // 如果存在?则直接加params(data), 否则先加?再加 params(data)
 url += (url.indexOf('?') < 0 ? '?' : '') + params(data)
 // 结果返回一个Promise对象
 return new Promise((resolve, reject) => {
 originJSONP(url, options, (err, data) => {
  if (!err) {
  resolve(data)
  } else {
  reject(err)
  }
 })
 })
}

function params(data) {
 let params = ''
 for (var k in data) {
 let value = data[k] != undefined ? data[k] : ''
 // url += '&' + k + '=' + encodeURIComponent(value) ES5
 params += `&${k}=${encodeURIComponent(value)}` // ES6
 }
 // 去掉首个参数的&, 因为jsonp方法中参数自带&
 return params ? params.substring(1) : ''
}

补偿:express起后台服务,与mock.js生成数据

创建mockexpress.js文件:

var Mock = require('mockjs');
var express = require('express');

let app = express();

app.listen('8090');
app.all('/api1/tests', function(req, res) {
    res.json(Mock.mock({
        "status": 200,
        "data|5": [{
            "name|5-8": /[a-zA-Z]/,
            "id|+1": 1,
            "value|0-500": 20
        }]
    }));
});

此刻下令行中node mockexpress.js,然后便得以在http://localhost:8090/api1/tests便得以赢得扭转的数额

接下来在大家的前端项目中使用:
首先要消除跨域难点,使用代理,vue-cli已经写好了在config/index.js中写代理:

proxyTable: {
        '/api1/':{
            target:'http://localhost:8090',
            changeOrigin:true,
        }
    },

接下来还要起步前端服务和后端数据服务,如此改换package.json中的script。

"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js  & node src/common/Mock/Mockexpress.js",

npm run dev就能够运转,并得以开始展览呼吁数据处理。

第三部分 从零初阶搭建

四、vue后端渲染首要插件:vue-server-renderer

鉴于virtual dom的引进,使得vue的服务端渲染成为了说不定,下边是合法
vue-server-renderer提供的渲染流程图:

4858美高梅 3

能够看出vue的后端渲染分四个部分构成:页面包车型大巴源码(source),node层的渲染部分和浏览器端的渲染部分。

source分为二种entry point,贰个是前者页面包车型客车入口client
entry,首固然实例化Vue对象,将其挂载到页面中;其它贰个是后端渲染服务入口server
entry,首借使控服务端渲染模块回调,再次来到三个Promise对象,最终回到3个Vue对象(经过测试,直接再次来到Vue对象也是足以的);

后边的source部分正是职业支付的代码,开垦成功今后通过 webpack
实行塑造,生成对应的bundle,这里不再赘言client
bundle,正是2个可在浏览器端实行的打包文件;这里说下server bundle,
vue二提供 vue-server-renderer模块,模块能够提供二种render:
rendererer/bundleRenderer ,下边分别介绍下那二种render。

renderer接收二个vue对象
,然后开展渲染,这种对于简易的vue对象,能够如此去做,不过对于复杂的类型,如若应用这种直接require贰个vue对象,那个对于服务端代码的组织和逻辑都不太对劲儿,首先进轨范块的情景会直接继续在每一个请求渲染请求,我们必要去管理和制止此次渲染请求的气象影响到末端的请求,由此vue-server-renderer提供了别的一种渲染形式,通过1个bundleRenderer去做渲染。

bundleRenderer是较为复杂项目开展服务端渲染官方推荐的格局,通过webpack以server
entry依照一定的要求打包生成三个server-bundle,它一定于一个足以给服务端用的app的打包压缩文件,每贰回调用都会重新开首化
vue对象,有限支撑了历次请求都以单独的,对于开采者来说,只必要专注于当下政工就能够,不用为服务端渲染开采越来越多的逻辑代码。
renderer生成达成以后,都设有五个接口,分别是renderToString和renderToStream,多个是一遍性将页面渲染成字符串文件,其余2个是流式渲染,适用于辅助流的web服务器,可以是呼吁服务的快慢更加快。

三. 呼吁数据

2.使用json server 搭建mock服务器

可参考vue-cli
当地开辟mock数据应用方法

先是安装json-server(注意全局安装,mac上要丰盛sudo)

npm install -g json-server

随即项目目录下开创mock文件夹,在文件夹中开创db.json和mock-test.js,还应该有将兼具post请求转为get请求的post-to-get.js

其中db.json中存放json数据:

{
  "posts": [
    { "id": 1, "title": "json-server", "author": "typicode" }
  ],
  "comments": [
    { "id": 1, "body": "some comment", "postId": 1 }
  ],
  "profile": { "name": "typicode" }
}

相应的在package.json中在script中加进mock的运行项:

"mock": "json-server --watch mock/db.json",
"mockdev": "npm run mock & npm run dev"

第二句的意味是监测路线mock/db.json,如此就足以在那边更换文件存放地点以及文件名字了。
其次句是为着mock起的劳动和vue命令服务同一时间起步。

接下来在命令行中:

npm run mock

这时候在浏览器中张开http://localhost:3000/可得到

4858美高梅 4

1651860-349bfc482aba7065.png

那儿得以分级走访:http://localhost:3000/posts,http://localhost:3000/comments,http://localhost:3000/profile
即再次回到数据

文章中运用fake.js制作数据的,而笔者辈早已对mock.js熟谙了,所以采纳mock.js来发生多少个假数据:
前边早已选取npm install mock安装了mock.js,则

//mock-test.js
module.exports = function () {
    let Mock=require('mockjs');

    var bd = Mock.mock({
        'bodyData|15':[
           {   
               'id|+1':1,
               'methods|1':["火车频次战法","飞机频次战法","汽车频次战法"],
               'hasContent|1-2':true
           }]
    });


    return{
        tasklists:bd
    }
}

能够在指令行中使用json-server
mock/mock-test.js来运行那些服务,然后就足以经过http://localhost:3000/tasklists来拜访数据

若是使用的是那些数目,那么在package.json中的script中的mock项可以修改为:

"mock":"json-server --watch mock/mock-test.js"

在支付进度中,有的时候候想直接模拟获取POST请求重返结果,能够增加 express
中间件 将POST请求转为GET请求
在mock文件夹下生成post-to-get.js文件:

module.exports = function (req, res, next) {
  req.method = "GET";
  next();
}

那儿陈设项可改动为:

"mock": "json-server --watch mock/db.json --m mock/post-to-get.js",

1、前言

上1节大家大要讲了怎么供给使用vue后端渲染,以及vue后端渲染的基本原理,那节内容大家将从零起始搭建属于本身的vue后端渲染脚手架,当然不可不仿效官方页面响应的实例vue-hackernews-2.0,从零起头搭建项目,源码在就要下节与大家共享。

其次片段 从零开首搭建

4858美高梅 5

下面说一下代理,接口写好了,那么哪些请求呢?

在 config/index.js 的 proxyTable 将请求映射到
http://localhost:3000

proxyTable: {
    '/api1/':{
        target:'http://localhost:3000',
        changeOrigin:true,
        pathRewrite:{
            '^/api1':''
        }
    }
},

在组件中开始展览呼吁:

methods:{
    mockReq (){
        axios.get('/api1/posts')
        .then(res => {
            console.log('mock server');
            console.log(res.data);
        })
        axios.post('/api1/comments')
        .then(res => {
            console.log('mock server1');
            console.log(res.data);
        })
        axios.get('/api1/tasklists')
        .then(res => {
            console.log('mock server2');
            console.log(res.data);
        })
    }

},
created (){
    this.mockReq();
}

启动:

npm run mockdev

一旦配置到db.json时前多少个请求就打响,要是配置到mock-test.js第八个请求就是成功的。


2、中期计划

骨干条件供给:node版本6.10.一上述,npm版本叁.10.10以上,本机情形是如此的,提议晋级到官方最新版本。

选择的技能栈:

1、vue 2.4.2
2、vuex 2.3.1
3、vue-router 2.7.0
4、vue-server-renderer 2.4.2
5、express 4.15.4
6、axios 0.16.2
7、qs 6.5.0
8、q
9、webpack 3.5.0
10、mockjs 1.0.1-beta3
1一、babel 相关插件

以上是首要是用的技艺栈,在创设进度中会是用相应的插件注重包来协作开始展览削减打包,以下是npm
init后package.json文件所要增多的注重包。

"dependencies": {
 "axios": "^0.16.2",
 "es6-promise": "^4.1.1",
 "express": "^4.15.4",
 "lodash": "^4.17.4",
 "q": "git+https://github.com/kriskowal/q.git",
 "qs": "^6.5.0",
 "vue": "^2.4.2",
 "vue-router": "^2.7.0",
 "vue-server-renderer": "^2.4.2",
 "vuex": "^2.3.1"
 },
 "devDependencies": {
 "autoprefixer": "^7.1.2",
 "babel-core": "^6.25.0",
 "babel-loader": "^7.1.1",
 "babel-plugin-syntax-dynamic-import": "^6.18.0",
 "babel-plugin-transform-runtime": "^6.22.0",
 "babel-preset-env": "^1.6.0",
 "babel-preset-stage-2": "^6.22.0",
 "compression": "^1.7.1",
 "cross-env": "^5.0.5",
 "css-loader": "^0.28.4",
 "extract-text-webpack-plugin": "^3.0.0",
 "file-loader": "^0.11.2",
 "friendly-errors-webpack-plugin": "^1.6.1",
 "glob": "^7.1.2",
 "less": "^2.7.2",
 "less-loader": "^2.2.3",
 "lru-cache": "^4.1.1",
 "mockjs": "^1.0.1-beta3",
 "style-loader": "^0.19.0",
 "sw-precache-webpack-plugin": "^0.11.4",
 "url-loader": "^0.5.9",
 "vue-loader": "^13.0.4",
 "vue-style-loader": "^3.0.3",
 "vue-template-compiler": "^2.4.2",
 "vuex-router-sync": "^4.2.0",
 "webpack": "^3.5.0",
 "webpack-dev-middleware": "^1.12.0",
 "webpack-hot-middleware": "^2.18.2",
 "webpack-merge": "^4.1.0",
 "webpack-node-externals": "^1.6.0"
 }

1、前言

上一节我们大约讲了干吗需求选用vue后端渲染,以及vue后端渲染的基本原理,那节内容大家将从零开始搭建属于本人的vue后端渲染脚手架,当然不可不参照他事他说加以考察官方页面响应的实例vue-hackernews-二.0,从零伊始搭建项目,源码在将要下节与我们共享。

# 代码
 const commonParams = {
 g_tk: 5381,
 inCharset: 'utf-8',
 outCharset: 'utf-8',
 notice: 0,
 format: 'jsonp'
}
const options = {
 param: 'jsonpCallback'
}

getRecommend() {
 const url = 'https://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg'
 const data = Object.assign({}, commonParams, {
 platform: 'h5',
 uin: 0,
 needNewCode: 1
 })
 return jsonp(url, data, options)
}

3. 纯前端请求根目录的本土数据

就算大家只为了简单的勤学苦练前端开拓,数据也许有前端提供,不牵扯后端服务,那么创制文件mockData.js

import Mock from 'mockjs';

Mock.mock('/lists','get',function(){
    var lists = Mock.mock({
        "result|15":[
            {
                "id|+1":1,
                "title|1":['科目二第07考点马路','科目二第01考点马路','科目二第08考点马路','科目二第09考点'],
                "desc|0-1":'@county(true)'
            }
        ]
    })

    return {
        lists
    }
})

在main.js中:

require('./assets/mock.js');
//完整点可以使用:
//开发环境加载mock数据
if(process.env.NODE_ENV == 'development'){
    require('./assets/mock.js');
}

接下来在急需的地点进行呼吁就能够了。

mockData(){
    let self = this;
     axios.get('/lists')
     .then(res => {
        console.log(res.data);
        self.examinationLists = res.data.lists.result;
     })
},

切注意请求后的this的针对,假使想改换vue中的数据,那么鲜明要起来将this先赋给别的然后再用。

三、项目主目录搭建

主标题录结构如下:

├── LICENSE
├── README.md
├── build
│ ├── setup-dev-server.js
│ ├── vue-loader.config.js
│ ├── webpack.base.config.js
│ ├── webpack.client.config.js
│ └── webpack.server.config.js
├── log
│ ├── err.log
│ └── out.log
├── package.json
├── pmlog.json
├── server.js
└── src
 ├── App.vue
 ├── app.js
 ├── assets
 │ ├── images
 │ ├── style
 │ │ └── css.less
 │ └── views
 │  └── index.css
 ├── components
 │ ├── Banner.vue
 │ ├── BottomNav.vue
 │ ├── FloorOne.vue
 │ └── Header.vue
 ├── entry-client.js
 ├── entry-server.js
 ├── index.template.html
 ├── public
 │ ├── conf.js
 │ └── utils
 │  ├── api.js
 │  └── confUtils.js
 ├── router
 │ └── index.js
 ├── static
 │ ├── img
 │ │ └── favicon.ico
 │ └── js
 │  └── flexible.js
 ├── store
 │ ├── actions.js
 │ ├── getters.js
 │ ├── index.js
 │ ├── modules
 │ │ └── Home.js
 │ ├── mutationtypes.js
 │ └── state.js
 └── views
  └── index
   ├── conf.js
   ├── index.vue
   ├── mock.js
   └── service.js

文件目录基本介绍:

  1. views文件夹下分模块文件,模块文件下下又分模块自身的.vue文件(模版文件),index.js文件(后台数据交互文件),mock.js(本模块的mock假数据),conf.js(配置本模块一些参数,请求路线,模块名称等新闻)
  2. components 公共组件文件夹
  3. router
    主要存放在前端路由安插文件,写法标准依照vue-router官方例子就可以。
  4. store
    首要是存放共享状态文件,里面含有action.js,getter.js,mutationtype.js等,先前时期会基于模块再分开那么些。
  5. public
    主要存放在公共组件代码和品种应用的公物文件代码,举个例子前期大家将axios封装成公共的api库文件等等
  6. static文件夹代表静态文件,不会被webpack打包的
  7. app.js 是项目入口文件
  8. App.vue 是种类进口文件
  9. entry-client和entry-server分别是客户端入口文件和服务端的输入文件
  10. index.template.html是全方位项指标沙盘文件

起首编写app.js项目入口代码

利用vue开拓品种入口文件一般都会如下写法:

import Vue from 'vue';
import App from './index.vue';
import router from './router'
import store from './store';

new Vue({
 el: '#app',
 store,
 router,
 render: (h) => h(App)
});

这种写法是先后共享两个vue实例,不过在后端渲染中很轻巧造成交叉请求状态污染,导致数据流被传染了。

之所以,幸免事态单例,大家不应有直接创设1个应用程序实例,而是应当暴露三个足以重复试行的厂子函数,为每种请求创设新的应用程序实例,同样router和store入口文件也亟需重新成立三个实例。

为了合营webpack动态加载路由安插,这里会改写常规路由引进写法,那样能够依赖路由路线来判别加载相应的机件代码:

import Home from '../views/index/index.vue'
// 改写成
component: () => ('../views/index/index.vue')

以下是路由的主干写法router,对外会抛出1个createRouter方法来成立二个新的路由实例:

import Vue from 'vue'
import Router from 'vue-router';
Vue.use(Router)
export function createRouter() {
 return new Router({
  mode: 'history',
  routes: [{
   name:'Home',
   path: '/',
   component: () =>
    import ('../views/index/index.vue')
  }]
 })
}

以下是store状态管理的中央写法,对外揭穿了四个createStore方法,方便每一回访问创造一个新的实例:

// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import getters from './getters'
import modules from './modules/index'
Vue.use(Vuex)
export function createStore() {
 return new Vuex.Store({
 actions,
 getters,
 modules,
 strict: false
 })
}

结合写好的router和store入口文件代码来编排整个项目标进口文件app.js代码内容,同样最后也会对外暴光3个createApp方法,在历次创设app的时候保险router,store,app都以新创立的实例,这里还引进了八个vue路由插件vuex-router-sync,首要效用是同步路由气象(route
state)到 store,以下是app.js完整代码:

import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './router'
import { createStore } from './store'
import { sync } from 'vuex-router-sync'
require('./assets/style/css.less');
export function createApp () {
 // 创建 router 和 store 实例
 const router = createRouter()
 const store = createStore()
 // 同步路由状态(route state)到 store
 sync(store, router)
 // 创建应用程序实例,将 router 和 store 注入
 const app = new Vue({
 router,
 store,
 render: h => h(App)
 })
 // 暴露 app, router 和 store。
 return { app, router, store }
}

entry-client.js代码编写:

首页引进从app文件中爆出出来的createApp方法,在每趟调用客户端的时候,重新成立三个新的app,router,store,部分代码如下:

import { createApp } from './app'
const { app, router, store } = createApp()

那边大家会利用到onReady方法,此办法一般用于等待异步的领航钩子完毕,比方在拓展服务端渲染的时候,例子代码如下:

import { createApp } from './app'
const { app, router, store } = createApp()
router.onReady(() => {
 app.$mount('#app')
})

4858美高梅,咱俩会调用3个新措施beforeResolve,唯有在router二.五.0之上的本子才会有的艺术,注册三个近乎于大局路由保安router.beforeEach(),除了在导航确认之后,在具有其余珍视和异步组件已消除以往调用。基本写法如下:

router.beforeResolve((to, from, next) => {
 // to 和 from 都是 路由信息对象
 // 返回目标位置或是当前路由匹配的组件数组(是数组的定义/构造类,不是实例)。通常在服务端渲染的数据预加载时时候。
 const matched = router.getMatchedComponents(to)
 const prevMatched = router.getMatchedComponents(from)
})

服务端把要给客户端的 state 放在了 window. INITIAL_STATE
那个全局变量上面。前后端的 HTML 结构应该是如出一辙的。然后要把 store
的气象树写入四个全局变量( INITIAL_STATE ),这样客户端先河化 render
的时候能够校验服务器生成的 HTML
结构,并且一路到伊始化状态,然后全部页面被客户端接管。基本代码如下:

// 将服务端渲染时候的状态写入vuex中
if (window.__INITIAL_STATE__) {
 store.replaceState(window.__INITIAL_STATE__)
}

接下去贴出来完整的客户端代码,这里的Q也能够毫不引进,直接利用babel就能够编写翻译es陆自带的Promise,因为作者使用习贯了,这里能够遵照自身的供给是或不是安装:

import { createApp } from './app'
import Q from 'q'
import Vue from 'vue'

Vue.mixin({
 beforeRouteUpdate (to, from, next) {
 const { asyncData } = this.$options
 if (asyncData) {
  asyncData({
  store: this.$store,
  route: to
  }).then(next).catch(next)
 } else {
  next()
 }
 }
})
const { app, router, store } = createApp()

// 将服务端渲染时候的状态写入vuex中
if (window.__INITIAL_STATE__) {
 store.replaceState(window.__INITIAL_STATE__)
}

router.onReady(() => {
 router.beforeResolve((to, from, next) => {
  const matched = router.getMatchedComponents(to)
  const prevMatched = router.getMatchedComponents(from)
  // 我们只关心之前没有渲染的组件
  // 所以我们对比它们,找出两个匹配列表的差异组件
  let diffed = false
  const activated = matched.filter((c, i) => {
  return diffed || (diffed = (prevMatched[i] !== c))
  })
  if (!activated.length) {
  return next()
  }
  // 这里如果有加载指示器(loading indicator),就触发
  Q.all(activated.map(c => {
  if (c.asyncData) {
   return c.asyncData({ store, route: to })
  }
  })).then(() => {
  // 停止加载指示器(loading indicator)
  next()
  }).catch(next)
 })
 app.$mount('#app')
})

entry-server.js代码编写:

大旨编写和客户端的大约,因为那是服务端渲染,涉及到与后端数据交互定义的主题素材,大家必要在这里定义好各组件与后端交互使用的主意名称,那样方便在组件内部平素行使,这里根大家平常在组件直接动用ajax获取数据有个别不相同,代码片段如下:

//直接定义组件内部asyncData方法来触发相应的ajax获取数据
if (Component.asyncData) {
 return Component.asyncData({
 store,
 route: router.currentRoute
 })
}

以下是总体的服务端代码:

import { createApp } from './app'
import Q from 'q'
export default context => {
 return new Q.Promise((resolve, reject) => {
 const { app, router, store } = createApp()
 router.push(context.url)
 router.onReady(() => {
  const matchedComponents = router.getMatchedComponents()
  if (!matchedComponents.length) {
  return reject({ code: 404 })
  }
  // 对所有匹配的路由组件调用 `asyncData()`
  Q.all(matchedComponents.map(Component => {
  if (Component.asyncData) {
   return Component.asyncData({
   store,
   route: router.currentRoute
   })
  }
  })).then(() => {
  // 在所有预取钩子(preFetch hook) resolve 后,
  // 我们的 store 现在已经填充入渲染应用程序所需的状态。
  // 当我们将状态附加到上下文,
  // 并且 `template` 选项用于 renderer 时,
  // 状态将自动序列化为 `window.__INITIAL_STATE__`,并注入 HTML。
  context.state = store.state
  resolve(app)
  }).catch(reject)
 }, reject)
 })
}

2、早先时期企图

骨干条件供给:node版本陆.十.壹上述,npm版本三.10.10以上,本机景况是如此的,建议升高到官方最新版本。

采纳的技能栈:

1、vue 2.4.2
2、vuex 2.3.1
3、vue-router 2.7.0
4、vue-server-renderer 2.4.2
5、express 4.15.4
6、axios 0.16.2
7、qs 6.5.0
8、q
9、webpack 3.5.0
10、mockjs 1.0.1-beta3
11、babel 相关插件

上述是关键是用的本领栈,在创设进程中会是用相应的插件信赖包来协作举行压缩打包,以下是npm
init后package.json文件所要加多的注重包。

"dependencies": {
 "axios": "^0.16.2",
 "es6-promise": "^4.1.1",
 "express": "^4.15.4",
 "lodash": "^4.17.4",
 "q": "git+https://github.com/kriskowal/q.git",
 "qs": "^6.5.0",
 "vue": "^2.4.2",
 "vue-router": "^2.7.0",
 "vue-server-renderer": "^2.4.2",
 "vuex": "^2.3.1"
 },
 "devDependencies": {
 "autoprefixer": "^7.1.2",
 "babel-core": "^6.25.0",
 "babel-loader": "^7.1.1",
 "babel-plugin-syntax-dynamic-import": "^6.18.0",
 "babel-plugin-transform-runtime": "^6.22.0",
 "babel-preset-env": "^1.6.0",
 "babel-preset-stage-2": "^6.22.0",
 "compression": "^1.7.1",
 "cross-env": "^5.0.5",
 "css-loader": "^0.28.4",
 "extract-text-webpack-plugin": "^3.0.0",
 "file-loader": "^0.11.2",
 "friendly-errors-webpack-plugin": "^1.6.1",
 "glob": "^7.1.2",
 "less": "^2.7.2",
 "less-loader": "^2.2.3",
 "lru-cache": "^4.1.1",
 "mockjs": "^1.0.1-beta3",
 "style-loader": "^0.19.0",
 "sw-precache-webpack-plugin": "^0.11.4",
 "url-loader": "^0.5.9",
 "vue-loader": "^13.0.4",
 "vue-style-loader": "^3.0.3",
 "vue-template-compiler": "^2.4.2",
 "vuex-router-sync": "^4.2.0",
 "webpack": "^3.5.0",
 "webpack-dev-middleware": "^1.12.0",
 "webpack-hot-middleware": "^2.18.2",
 "webpack-merge": "^4.1.0",
 "webpack-node-externals": "^1.6.0"
 }

四. 零部件内调用getRecommend()艺术, 获取数据

四、脚手架其余目录介绍:

到此地src下边重要的多少个文本代码已经编制成功,接下里介绍下总体项目的目录结构如下:

4858美高梅 6

最首要几个公文介绍如下:

  1. build 重要存放webpack打包配置文件
  2. dist webpack打包后生成的目录
  3. log 使用pm2监督检查进度存放的日志文件目录
  4. server.js node服务器运转文件
  5. pmlog.json pm二配置文件

server.js入口文件编写制定

咱俩还须求编写制定在服务端运行服务的代码server.js,我们会动用到某个node原生提供的api,片段代码如下:

const Vue = require('vue')
const express = require('express')
const path = require('path')
const LRU = require('lru-cache')
const { createBundleRenderer } = require('vue-server-renderer')
const fs = require('fs')
const net = require('net')

差十分少思路是,引进前端模版页面index.template.html,使用express运维服务,引入webpack打包项目代码的dist文件,引进缓存模块(这里不做深入介绍,早先时期会单独详细介绍),判别端口是或不是被占用,自动运维别的接口服务。

引进前端模版文件同临时候安装遭遇变量为production,片段代码如下:

const template = fs.readFileSync('./src/index.template.html', 'utf-8')
const isProd = process.env.NODE_ENV === 'production'

vue-server-renderer插件的切实可行使用,通过读取dist文件夹下的目录文件,来创设createBundleRenderer函数,并且动用LRU来安装缓存的时间,通过判定是生育情形照旧支付条件,调用分歧的办法,代码片段如下:

const resolve = file => path.resolve(__dirname, file)
function createRenderer (bundle, options) {
 return createBundleRenderer(bundle, Object.assign(options, {
 template,
 cache: LRU({
  max: 1000,
  maxAge: 1000 * 60 * 15
 }),
 basedir: resolve('./dist'),
 runInNewContext: false
 }))
}
let renderer;
let readyPromise
if (isProd) {
 const bundle = require('./dist/vue-ssr-server-bundle.json')
 const clientManifest = require('./dist/vue-ssr-client-manifest.json')
 renderer = createRenderer(bundle, {
 clientManifest
 })
} else {
 readyPromise = require('./build/setup-dev-server')(server, (bundle, options) => {
 renderer = createRenderer(bundle, options)
 })
}

使用express运维服务,代码片段如下:

const server = express();

//定义在启动服务钱先判断中间件中的缓存是否过期,是否直接调用dist文件。
const serve = (path, cache) => express.static(resolve(path), {
 maxAge: cache && isProd ? 1000 * 60 * 60 * 24 * 30 : 0
})
server.use('/dist', serve('./dist', true))
server.get('*', (req, res) => {
 const context = {
 title: 'hello',
 url: req.url
 }
 renderer.renderToString(context, (err, html) => {
 if (err) {
  res.status(500).end('Internal Server Error')
  return
 }
 res.end(html)
 })
})

认清端口是或不是被占用,片段代码如下:

function probe(port, callback) {
 let servers = net.createServer().listen(port)
 let calledOnce = false
 let timeoutRef = setTimeout(function() {
  calledOnce = true
  callback(false, port)
 }, 2000)
 timeoutRef.unref()
 let connected = false
 servers.on('listening', function() {
  clearTimeout(timeoutRef)

  if (servers)
   servers.close()

  if (!calledOnce) {
   calledOnce = true
   callback(true, port)
  }
 })
 servers.on('error', function(err) {
  clearTimeout(timeoutRef)

  let result = true
  if (err.code === 'EADDRINUSE')
   result = false

  if (!calledOnce) {
   calledOnce = true
   callback(result, port)
  }
 })
}
const checkPortPromise = new Promise((resolve) => {
 (function serverport(_port) {
  let pt = _port || 8080;
  probe(pt, function(bl, _pt) {
   // 端口被占用 bl 返回false
   // _pt:传入的端口号
   if (bl === true) {
    // console.log("\n Static file server running at" + "\n\n=> http://localhost:" + _pt + '\n');
    resolve(_pt);
   } else {
    serverport(_pt + 1)
   }
  })
 })()

})
checkPortPromise.then(data => {
 uri = 'http://localhost:' + data;
 console.log('启动服务路径'+uri)
 server.listen(data);
});

到这里,基本的代码已经编写制定成功,webpack打包配置文件焦点和法定保持不改变,接下去能够品尝运行本地的品种劳务,这里大约的利用果壳网严选首页作为demo示例,结果如下:

4858美高梅 7

叁、项目主目录搭建

主干目录结构如下:

├── LICENSE
├── README.md
├── build
│ ├── setup-dev-server.js
│ ├── vue-loader.config.js
│ ├── webpack.base.config.js
│ ├── webpack.client.config.js
│ └── webpack.server.config.js
├── log
│ ├── err.log
│ └── out.log
├── package.json
├── pmlog.json
├── server.js
└── src
 ├── App.vue
 ├── app.js
 ├── assets
 │ ├── images
 │ ├── style
 │ │ └── css.less
 │ └── views
 │  └── index.css
 ├── components
 │ ├── Banner.vue
 │ ├── BottomNav.vue
 │ ├── FloorOne.vue
 │ └── Header.vue
 ├── entry-client.js
 ├── entry-server.js
 ├── index.template.html
 ├── public
 │ ├── conf.js
 │ └── utils
 │  ├── api.js
 │  └── confUtils.js
 ├── router
 │ └── index.js
 ├── static
 │ ├── img
 │ │ └── favicon.ico
 │ └── js
 │  └── flexible.js
 ├── store
 │ ├── actions.js
 │ ├── getters.js
 │ ├── index.js
 │ ├── modules
 │ │ └── Home.js
 │ ├── mutationtypes.js
 │ └── state.js
 └── views
  └── index
   ├── conf.js
   ├── index.vue
   ├── mock.js
   └── service.js

文件目录基本介绍:

  1. views文件夹下分模块文件,模块文件下下又分模块本身的.vue文件(模版文件),index.js文件(后台数据交互文件),mock.js(本模块的mock假数据),conf.js(配置本模块一些参数,请求路径,模块名称等信息)
  2. components 公共组件文件夹
  3. router
    紧要存放前端路由安插文件,写法规范遵照vue-router官方例子就能够。
  4. store
    首假诺存放在共享状态文件,里面富含action.js,getter.js,mutationtype.js等,前期会依照模块再分叉这一个。
  5. public
    重要存放在公共组件代码和等级次序采取的国有文件代码,举例中期我们将axios封装成公共的api库文件等等
  6. static文件夹代表静态文件,不会被webpack打包的
  7. app.js 是连串进口文件
  8. App.vue 是连串进口文件
  9. entry-client和entry-server分别是客户端入口文件和服务端的入口文件
  10. index.template.html是①体项指标模板文件

起头编写app.js项目入口代码

使用vue开辟品种入口文件一般都会如下写法:

import Vue from 'vue';
import App from './index.vue';
import router from './router'
import store from './store';

new Vue({
 el: '#app',
 store,
 router,
 render: (h) => h(App)
});

这种写法是先后共享2个vue实例,不过在后端渲染中很轻易导致交叉请求状态污染,导致数据流被传染了。

于是,幸免事态单例,大家不该平昔开立3个应用程序实例,而是应该暴光一个方可另行推行的工厂函数,为种种请求创造新的应用程序实例,同样router和store入口文件也急需再行创立二个实例。

为了合作webpack动态加载路由安顿,这里会改写常规路由引进写法,那样能够凭仗路由路径来判定加载相应的机件代码:

import Home from '../views/index/index.vue'
// 改写成
component: () => ('../views/index/index.vue')

以下是路由的骨干写法router,对外会抛出三个createRouter方法来成立二个新的路由实例:

import Vue from 'vue'
import Router from 'vue-router';
Vue.use(Router)
export function createRouter() {
 return new Router({
  mode: 'history',
  routes: [{
   name:'Home',
   path: '/',
   component: () =>
    import ('../views/index/index.vue')
  }]
 })
}

以下是store状态管理的大旨写法,对外暴光了2个createStore方法,方便每回访问创设三个新的实例:

// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import getters from './getters'
import modules from './modules/index'
Vue.use(Vuex)
export function createStore() {
 return new Vuex.Store({
 actions,
 getters,
 modules,
 strict: false
 })
}

组合写好的router和store入口文件代码来编排整个项目标进口文件app.js代码内容,一样最终也会对外暴光2个createApp方法,在每一次创设app的时候保障router,store,app都是新制造的实例,这里还引进了一个vue路由插件vuex-router-sync,首要效用是壹块路由气象(route
state)到 store,以下是app.js完整代码:

import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './router'
import { createStore } from './store'
import { sync } from 'vuex-router-sync'
require('./assets/style/css.less');
export function createApp () {
 // 创建 router 和 store 实例
 const router = createRouter()
 const store = createStore()
 // 同步路由状态(route state)到 store
 sync(store, router)
 // 创建应用程序实例,将 router 和 store 注入
 const app = new Vue({
 router,
 store,
 render: h => h(App)
 })
 // 暴露 app, router 和 store。
 return { app, router, store }
}

entry-client.js代码编写:

首页引进从app文件中暴表露来的createApp方法,在历次调用客户端的时候,重新创立四个新的app,router,store,部分代码如下:

import { createApp } from './app'
const { app, router, store } = createApp()

此处大家会使用到onReady方法,此措施一般用于等待异步的领航钩子落成,举例在张开服务端渲染的时候,例子代码如下:

import { createApp } from './app'
const { app, router, store } = createApp()
router.onReady(() => {
 app.$mount('#app')
})

大家会调用二个新点子beforeResolve,唯有在router二.5.0以上的本子才会有的艺术,注册3个好像于大局路由保卫安全router.beforeEach(),除了在导航确认之后,在装有其余保证和异步组件已化解现在调用。基本写法如下:

router.beforeResolve((to, from, next) => {
 // to 和 from 都是 路由信息对象
 // 返回目标位置或是当前路由匹配的组件数组(是数组的定义/构造类,不是实例)。通常在服务端渲染的数据预加载时时候。
 const matched = router.getMatchedComponents(to)
 const prevMatched = router.getMatchedComponents(from)
})

服务端把要给客户端的 state 放在了 window. INITIAL_STATE
那个全局变量上边。前后端的 HTML 结构应该是同样的。然后要把 store
的情景树写入二个全局变量( INITIAL_STATE ),那样客户端开头化 render
的时候可以校验服务器生成的 HTML
结构,并且一路到开首化状态,然后全数页面被客户端接管。基本代码如下:

// 将服务端渲染时候的状态写入vuex中
if (window.__INITIAL_STATE__) {
 store.replaceState(window.__INITIAL_STATE__)
}

接下去贴出来完整的客户端代码,这里的Q也足以不用引进,间接运用babel就能够编写翻译es陆自带的Promise,因为本身使用习贯了,这里能够依靠小编的需借使否安装:

import { createApp } from './app'
import Q from 'q'
import Vue from 'vue'

Vue.mixin({
 beforeRouteUpdate (to, from, next) {
 const { asyncData } = this.$options
 if (asyncData) {
  asyncData({
  store: this.$store,
  route: to
  }).then(next).catch(next)
 } else {
  next()
 }
 }
})
const { app, router, store } = createApp()

// 将服务端渲染时候的状态写入vuex中
if (window.__INITIAL_STATE__) {
 store.replaceState(window.__INITIAL_STATE__)
}

router.onReady(() => {
 router.beforeResolve((to, from, next) => {
  const matched = router.getMatchedComponents(to)
  const prevMatched = router.getMatchedComponents(from)
  // 我们只关心之前没有渲染的组件
  // 所以我们对比它们,找出两个匹配列表的差异组件
  let diffed = false
  const activated = matched.filter((c, i) => {
  return diffed || (diffed = (prevMatched[i] !== c))
  })
  if (!activated.length) {
  return next()
  }
  // 这里如果有加载指示器(loading indicator),就触发
  Q.all(activated.map(c => {
  if (c.asyncData) {
   return c.asyncData({ store, route: to })
  }
  })).then(() => {
  // 停止加载指示器(loading indicator)
  next()
  }).catch(next)
 })
 app.$mount('#app')
})

entry-server.js代码编写:

着力编写和客户端的好些个,因为那是服务端渲染,涉及到与后端数据交互定义的难点,我们需求在此处定义好各组件与后端交互使用的主意名称,那样有利于在组件内部直接使用,这里根我们例行在组件直接利用ajax获取数据某个不一致,代码片段如下:

//直接定义组件内部asyncData方法来触发相应的ajax获取数据
if (Component.asyncData) {
 return Component.asyncData({
 store,
 route: router.currentRoute
 })
}

以下是完整的服务端代码:

import { createApp } from './app'
import Q from 'q'
export default context => {
 return new Q.Promise((resolve, reject) => {
 const { app, router, store } = createApp()
 router.push(context.url)
 router.onReady(() => {
  const matchedComponents = router.getMatchedComponents()
  if (!matchedComponents.length) {
  return reject({ code: 404 })
  }
  // 对所有匹配的路由组件调用 `asyncData()`
  Q.all(matchedComponents.map(Component => {
  if (Component.asyncData) {
   return Component.asyncData({
   store,
   route: router.currentRoute
   })
  }
  })).then(() => {
  // 在所有预取钩子(preFetch hook) resolve 后,
  // 我们的 store 现在已经填充入渲染应用程序所需的状态。
  // 当我们将状态附加到上下文,
  // 并且 `template` 选项用于 renderer 时,
  // 状态将自动序列化为 `window.__INITIAL_STATE__`,并注入 HTML。
  context.state = store.state
  resolve(app)
  }).catch(reject)
 }, reject)
 })
}
methods: {
 _getRecommend() {
 getRecommend().then((res) => {
  // ERR_OK = 0是自定义的语义化参数, 对应返回json对象的code
  if (res.code === ERR_OK) {
  console.log(res.data)
  const data = res.data
  this.slider = data.slider
  }
 })
 }
},
created() {
 this._getRecommend()
}

其3局地 mockjs和axios合营使用

4、脚手架别的目录介绍:

到那边src下边首要的多少个公文代码已经编写制定成功,接下里介绍下任何项指标目录结构如下:

4858美高梅 8

重大多少个文件介绍如下:

  1. build 重要存放在webpack打包配置文件
  2. dist webpack打包后生成的目录
  3. log 使用pm二监督检查进度存放的日志文件目录
  4. server.js node服务器运营文件
  5. pmlog.json pm二配置文件

server.js入口文件编写制定

咱俩还亟需编写制定在服务端运营服务的代码server.js,大家会选用到有个别node原生提供的api,片段代码如下:

const Vue = require('vue')
const express = require('express')
const path = require('path')
const LRU = require('lru-cache')
const { createBundleRenderer } = require('vue-server-renderer')
const fs = require('fs')
const net = require('net')

大略思路是,引进前端模版页面index.template.html,使用express运维服务,引进webpack打包项目代码的dist文件,引进缓存模块(这里不做深入介绍,早先时期会单独详细介绍),判定端口是或不是被占用,自动运维别的接口服务。

引进前端模版文件同时安装情形变量为production,片段代码如下:

const template = fs.readFileSync('./src/index.template.html', 'utf-8')
const isProd = process.env.NODE_ENV === 'production'

vue-server-renderer插件的实际运用,通过读取dist文件夹下的目录文件,来创立createBundleRenderer函数,并且选择LRU来安装缓存的时日,通过判定是生产条件依旧支付情形,调用区别的秘籍,代码片段如下:

const resolve = file => path.resolve(__dirname, file)
function createRenderer (bundle, options) {
 return createBundleRenderer(bundle, Object.assign(options, {
 template,
 cache: LRU({
  max: 1000,
  maxAge: 1000 * 60 * 15
 }),
 basedir: resolve('./dist'),
 runInNewContext: false
 }))
}
let renderer;
let readyPromise
if (isProd) {
 const bundle = require('./dist/vue-ssr-server-bundle.json')
 const clientManifest = require('./dist/vue-ssr-client-manifest.json')
 renderer = createRenderer(bundle, {
 clientManifest
 })
} else {
 readyPromise = require('./build/setup-dev-server')(server, (bundle, options) => {
 renderer = createRenderer(bundle, options)
 })
}

使用express运行服务,代码片段如下:

const server = express();

//定义在启动服务钱先判断中间件中的缓存是否过期,是否直接调用dist文件。
const serve = (path, cache) => express.static(resolve(path), {
 maxAge: cache && isProd ? 1000 * 60 * 60 * 24 * 30 : 0
})
server.use('/dist', serve('./dist', true))
server.get('*', (req, res) => {
 const context = {
 title: 'hello',
 url: req.url
 }
 renderer.renderToString(context, (err, html) => {
 if (err) {
  res.status(500).end('Internal Server Error')
  return
 }
 res.end(html)
 })
})

推断端口是还是不是被占用,片段代码如下:

function probe(port, callback) {
 let servers = net.createServer().listen(port)
 let calledOnce = false
 let timeoutRef = setTimeout(function() {
  calledOnce = true
  callback(false, port)
 }, 2000)
 timeoutRef.unref()
 let connected = false
 servers.on('listening', function() {
  clearTimeout(timeoutRef)

  if (servers)
   servers.close()

  if (!calledOnce) {
   calledOnce = true
   callback(true, port)
  }
 })
 servers.on('error', function(err) {
  clearTimeout(timeoutRef)

  let result = true
  if (err.code === 'EADDRINUSE')
   result = false

  if (!calledOnce) {
   calledOnce = true
   callback(result, port)
  }
 })
}
const checkPortPromise = new Promise((resolve) => {
 (function serverport(_port) {
  let pt = _port || 8080;
  probe(pt, function(bl, _pt) {
   // 端口被占用 bl 返回false
   // _pt:传入的端口号
   if (bl === true) {
    // console.log("\n Static file server running at" + "\n\n=> http://localhost:" + _pt + '\n');
    resolve(_pt);
   } else {
    serverport(_pt + 1)
   }
  })
 })()

})
checkPortPromise.then(data => {
 uri = 'http://localhost:' + data;
 console.log('启动服务路径'+uri)
 server.listen(data);
});

到这里,基本的代码已经编写制定成功,webpack打包配置文件中央和官方保持不改变,接下去能够尝尝运维本地的花色服务,这里差相当的少的应用果壳网严选首页作为demo示例,结果如下:

4858美高梅 9

console.log(res.data)可打字与印刷出json数据

1、前言

上壹节大概介绍了服务端和客户端入口文件代码内容,今后一度足以符合规律运营你的后端渲染脚手架了,那一节,跟大家大饱眼福下哪些运用axios做ajax请求,如何行使mockjs做地点假数据,跑通本地基本逻辑,为事后前后端连调做希图。

其叁某些 mockjs和axios协作使用

4858美高梅 10

二、早先时期策动

急需用npm安装axios,mockjs正视包,由于mockjs只是代码开荒的支持理工科程师具,所以安装的时候笔者会加–save-dev来区别,具体能够依靠自身的急需来定,当然,假若有mock服务平台的话,可以一向走mock平台制造假的数据,本地平昔访问mock平台的接口,比方能够使用Ali的Rap平台管理工科具生成。

npm install axios --save
npm install mockjs --save-dev

1、前言

上1节大约介绍了服务端和客户端入口文件代码内容,未来早就足以健康运作你的后端渲染脚手架了,那一节,跟大家享用下怎么样利用axios做ajax请求,怎么着运用mockjs做地点假数据,跑通本地基本逻辑,为日后前后端连调做计划。

如上是选择jsonp的法子呼吁数据, 但对于被host和referer限制的json, 须求绕过host验证,先让服务端请求接口,前端页面再经过服务端请求数据。而不可能像jsonp那样直接前端请求json对象。报错如下

三、简单介绍axios

别的请求格局,代码示比方下:

axios.request(config);
axios.get(url[,config]);
axios.delete(url[,config]);
axios.head(url[,config]);
axios.post(url[,data[,config]]);
axios.put(url[,data[,config]])
axios.patch(url[,data[,config]])

实际详尽能够点击查看axios基本使用介绍

api.js完整代码如下:

import axios from 'axios'
import qs from 'qs'
import Q from 'q'
/**
 * 兼容 不支持promise 的低版本浏览器
 */
require('es6-promise').polyfill();
import C from '../conf'

axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
axios.defaults.withCredentials = true

function ajax(url, type, options) {

 return Q.Promise((resolve, reject) => {
 axios({
  method: type,
  url: C.HOST + url,
  params: type === 'get' ? options : null,
  data: type !== 'get' ? qs.stringify(options) : null
  })
  .then((result) => {
  if (result && result.status === 401) {
   // location.href = '/views/401.html'
  }
  if (result && result.status === 200) {
   if (result.data.code === 200) {
   resolve(result.data.data);
   } else if (result.data.code === 401) {
   reject({
    nopms: true,
    msg: result.data.msg
   });
   } else {
   reject({
    error: true,
    msg: result.data.msg
   });
   }
  } else {
   reject({
   errno: result.errno,
   msg: result.msg
   });
  }
  })
  .catch(function(error) {
  console.log(error, url);
  });
 })
}

const config = {
 get(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'get', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 post(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'post', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 put(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'put', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 delete(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'delete', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 jsonp(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'jsonp', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 }
};

export default config;

mockjs项目为主配备如下:

一、在public下新建conf.js全局定义请求url地址,代码如下:

module.exports = {
 HOST: "http://www.xxx.com",
 DEBUGMOCK: true
};

2、在views/index根目录下新建conf.js,定义组件mock的央浼路线,并且定义是还是不是上马单个组件使用mock数据或许线上接口数据,代码如下:

const PAGEMOCK = true;
const MODULECONF = {
 index: {
 NAME: '首页',
 MOCK: true,
 API: {
  GET: '/api/home',
 }
 }
};

三、在组件内部定义mockjs来编排mock假数据,代码如下:

import Mock from 'mockjs';
const mData = {
 index: {
 API: {
  GET: {
  "code": 200,
  "data": {
   "pin": 'wangqi',
   "name": '王奇'
  }
  }
 }
 }
}

以上正是主导的流程,假若有越来越好更加灵活的采纳方案,希望可以参预球联合会系并且享受,项目职业流已经在github上分享,并且会三番八次保证更新,
点击查看详细的情况,希望对大家的读书抱有帮助,也冀望大家多多援助脚本之家。

2、早先时代打算

亟待用npm安装axios,mockjs信赖包,由于mockjs只是代码开辟的帮助理工科程师具,所以安装的时候小编会加–save-dev来分歧,具体能够依附自身的必要来定,当然,假如有mock服务平台的话,能够一直走mock平台冒充真的数据,本地一向访问mock平台的接口,比方能够运用Ali的Rap平台管理工具生成。

npm install axios --save
npm install mockjs --save-dev

4858美高梅 11

您只怕感兴趣的稿子:

  • 简易的Vue SS昂科威的演示代码
  • 详解vue服务端渲染(SSOdyssey)初探
  • 加载 vue
    远程代码的组件实例详解
  • vue
    loadmore组件上拉加载越来越多效益示例代码
  • 详解vue项目优化之按需加载组件-使用webpack
    require.ensure
  • vue二组件实现懒加载浅析
  • Vue.js中用webpack合并打包两个零件并落到实处按需加载
  • Vue SS奇骏 组件加载难题

三、简介axios

别的请求格局,代码示举例下:

axios.request(config);
axios.get(url[,config]);
axios.delete(url[,config]);
axios.head(url[,config]);
axios.post(url[,data[,config]]);
axios.put(url[,data[,config]])
axios.patch(url[,data[,config]])

切切实实详尽能够点击查看axios基本使用介绍

api.js完整代码如下:

import axios from 'axios'
import qs from 'qs'
import Q from 'q'
/**
 * 兼容 不支持promise 的低版本浏览器
 */
require('es6-promise').polyfill();
import C from '../conf'

axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
axios.defaults.withCredentials = true

function ajax(url, type, options) {

 return Q.Promise((resolve, reject) => {
 axios({
  method: type,
  url: C.HOST + url,
  params: type === 'get' ? options : null,
  data: type !== 'get' ? qs.stringify(options) : null
  })
  .then((result) => {
  if (result && result.status === 401) {
   // location.href = '/views/401.html'
  }
  if (result && result.status === 200) {
   if (result.data.code === 200) {
   resolve(result.data.data);
   } else if (result.data.code === 401) {
   reject({
    nopms: true,
    msg: result.data.msg
   });
   } else {
   reject({
    error: true,
    msg: result.data.msg
   });
   }
  } else {
   reject({
   errno: result.errno,
   msg: result.msg
   });
  }
  })
  .catch(function(error) {
  console.log(error, url);
  });
 })
}

const config = {
 get(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'get', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 post(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'post', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 put(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'put', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 delete(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'delete', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 jsonp(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'jsonp', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 }
};

export default config;

mockjs项目为主配备如下:

壹、在public下新建conf.js全局定义请求url地址,代码如下:

module.exports = {
 HOST: "http://www.xxx.com",
 DEBUGMOCK: true
};

贰、在views/index根目录下新建conf.js,定义组件mock的伸手路线,并且定义是不是起头单个组件使用mock数据照旧线上接口数据,代码如下:

const PAGEMOCK = true;
const MODULECONF = {
 index: {
 NAME: '首页',
 MOCK: true,
 API: {
  GET: '/api/home',
 }
 }
};

叁、在组件内部定义mockjs来编排mock假数据,代码如下:

import Mock from 'mockjs';
const mData = {
 index: {
 API: {
  GET: {
  "code": 200,
  "data": {
   "pin": 'wangqi',
   "name": '王奇'
  }
  }
 }
 }
}

如上即是基本的流程,假如有越来越好越来越灵活的使用方案,希望能够加入联系并且享受,项目工作流已经在github上享受,并且会继续保险更新,
点击查阅详细情况,希望对大家的上学抱有辅助,也盼望大家多多协助帮客之家。

第3部分 基本介绍 一、前言
服务端渲染完成原理机制:在服务端拿多少进行深入分析渲染,直…

2、后端axios(ajax)请求接口数据

1.  定义后端代理请求 build/webpack.dev.config.js

const axios = require('axios')
devServer: {
 before(app) {
 app.get('/api/getDiscList', function (req, res) {
  var url = 'https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg'
  axios.get(url, {
  headers: {
   referer: 'https://c.y.qq.com/',
   host: 'c.y.qq.com'
  },
  params: req.query
  }).then((response) => {
  res.json(response.data)
  }).catch((e) => {
  console.log(e)
  })
 })
 },
 # ...其他原来的代码
}

二.  前端请求后端已收获的长途数据

import axios from 'axios'function getDiscList() {
 // const url = 'https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg'
 const url = '/api/getDiscList'
 const data = Object.assign({}, commonParams, {
 // 以下参数自行参考源json文件的Query String Parameters
 platform: 'yqq',
 uin: 0,
 needNewCode: 0,
 hostUin: 0,
 sin: 0,
 ein: 29,
 sortId: 5,
 rnd: Math.random(),
 categoryId: 10000000,
 format: 'json'
 })
 return axios.get(url, {
 params: data
 }).then((res) => {
 return Promise.resolve(res.data)
 })
}

4858美高梅 12

三. 零部件内调用getDiscList()方法, 可输出json数据

methods: {
 _getRecommend() {
 getRecommend().then((res) => {
  if (res.code === ERR_OK) {
  // console.log(res.data)
  const data = res.data
  this.slider = data.slider
  }
 })
 },
 _getDiscList() {
 getDiscList().then((res) => {
  console.log(res.data)
 })
 }
},
created() {
 this._getRecommend()
 this._getDiscList()
}

小结, vue+webpack项目中,如需请求获取远程json数据时, 一般由三种情景:

一. 未受host和referer限制的前端组件能够一向利用jsonp插件请求json对象

二. 受host和referer限制供给证实的, 通过后端代理情势,使用axios请求, 前端再请求后端json对象

好了,以上正是那篇小说的全体内容了,希望本文的剧情对大家的学习也许工作具备自然的参阅学习价值,假如十分大家可以留言沟通,谢谢我们对台本之家的支撑。

你或然感兴趣的小说:

  • 详解怎么着行使webpack打包Vue工程
  • webpack+vue.js飞快入门教程
  • webpack+vue.js落成组件化详解
  • vue2.0+webpack情状的组织进程
  • webpack营造vue项指标详尽教程(配置篇)
  • 浅谈vue+webpack项目调节和测试方法步骤
  • vue+node+webpack情况搭建教程
  • vue-cli webpack
    开荒景况跨域详解

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有