登录  /   注册
  • 首页

  • PHP培训

  • 视频教程

    视频课程 直播课程 精品课
  • 学习路径

    入门教程 独孤九贱 玉女心经 天龙八部 趣味闯关
  • 资源下载

    源码市场 工具下载 在线工具 手册下载 电子课件 js特效 网站源码 网站素材 类库下载
  • 技术文章

    前端开发 后端开发 数据库 php框架 每日编程
  • 社区

    问答 博客 文章 专题
  • 微信公众号

    扫码关注官方订阅号

  • 编程词典 APP下载 源码市场
首页 > web前端 > js教程 > 正文

使用vue-ssr如何实现服务端渲染

亚连
发布: 2018-06-12 18:21:50
原创
4483人浏览过

这篇文章主要介绍了基于 vue-ssr服务端渲染入门详解,现在分享给大家,也给大家做个参考。

第一部分 基本介绍

1、前言

服务端渲染实现原理机制:在服务端拿数据进行解析渲染,直接生成html片段返回给前端。然后前端可以通过解析后端返回的html片段到前端页面,大致有以下两种形式:

1、服务器通过模版引擎直接渲染整个页面,例如java后端的vm模版引擎,php后端的smarty模版引擎。
2、服务渲染生成html代码块, 前端通过AJAX获取然后使用js动态添加。

2、服务端渲染的优劣

服务端渲染能够解决两大问题:

1、seo问题,有利于搜索引擎蜘蛛抓取网站内容,利于网站的收录和排名。
2、首屏加载过慢问题,例如现在成熟的SPA项目中,打开首页需要加载很多资源,通过服务端渲染可以加速首屏渲染。
同样服务端渲染也会有弊端,主要是根据自己的业务场景来选择适合方式,由于服务端渲染前端页面,必将会给服务器增加压力。

3、SSR的实现原理

客户端请求服务器,服务器根据请求地址获得匹配的组件,在调用匹配到的组件返回 Promise (官方是preFetch方法)来将需要的数据拿到。最后再通过

<script>window.__initial_state=data</script>
登录后复制

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

接下来客户端会将vuex将写入的 initial_state 替换为当前的全局状态树,再用这个状态树去检查服务端渲染好的数据有没有问题。遇到没被服务端渲染的组件,再去发异步请求拿数据。说白了就是一个类似React的 shouldComponentUpdate 的Diff操作。

Vue2使用的是单向数据流,用了它,就可以通过 SSR 返回唯一一个全局状态, 并确认某个组件是否已经SSR过了。

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

由于virtual dom的引入,使得vue的服务端渲染成为了可能,下面是官方 vue-server-renderer提供的渲染流程图:

可以看出vue的后端渲染分三个部分组成:页面的源码(source),node层的渲染部分和浏览器端的渲染部分。

source分为两种entry point,一个是前端页面的入口client entry,主要是实例化Vue对象,将其挂载到页面中;另外一个是后端渲染服务入口server entry,主要是控服务端渲染模块回调,返回一个Promise对象,最终返回一个Vue对象(经过测试,直接返回Vue对象也是可以的);

前面的source部分就是业务开发的代码,开发完成之后通过 webpack 进行构建,生成对应的bundle,这里不再赘述client bundle,就是一个可在浏览器端执行的打包文件;这里说下server bundle, vue2提供 vue-server-renderer模块,模块可以提供两种render: rendererer/bundleRenderer ,下面分别介绍下这两种render。

renderer接收一个vue对象 ,然后进行渲染,这种对于简单的vue对象,可以这么去做,但是对于复杂的项目,如果使用这种直接require一个vue对象,这个对于服务端代码的结构和逻辑都不太友好,首先模块的状态会一直延续在每个请求渲染请求,我们需要去管理和避免这次渲染请求的状态影响到后面的请求,因此vue-server-renderer提供了另外一种渲染模式,通过一个 bundleRenderer去做渲染。

bundleRenderer是较为复杂项目进行服务端渲染官方推荐的方式,通过webpack以server entry按照一定的要求打包生成一个 server-bundle,它相当于一个可以给服务端用的app的打包压缩文件,每一次调用都会重新初始化 vue对象,保证了每次请求都是独立的,对于开发者来说,只需要专注于当前业务就可以,不用为服务端渲染开发更多的逻辑代码。 renderer生成完成之后,都存在两个接口,分别是renderToString和renderToStream,一个是一次性将页面渲染成字符串文件,另外一个是流式渲染,适用于支持流的web服务器,可以是请求服务的速度更快。

第二部分 从零开始搭建

1、前言

上一节我们大致讲了为什么需要使用vue后端渲染,以及vue后端渲染的基本原理,这节内容我们将从零开始搭建属于自己的vue后端渲染脚手架,当然不能不参考官方页面响应的实例vue-hackernews-2.0,从零开始搭建项目,源码在将在下节与大家共享。

2、前期准备

基本环境要求:node版本6.10.1以上,npm版本3.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 https://github.com/kriskowal/q.git
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"
 }
登录后复制

3、项目主目录搭建

基本目录结构如下:

├── 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 &#39;vue&#39;;
import App from &#39;./index.vue&#39;;
import router from &#39;./router&#39;
import store from &#39;./store&#39;;

new Vue({
 el: &#39;#app&#39;,
 store,
 router,
 render: (h) => h(App)
});
登录后复制

这种写法是程序共享一个vue实例,但是在后端渲染中很容易导致交叉请求状态污染,导致数据流被污染了。

所以,避免状态单例,我们不应该直接创建一个应用程序实例,而是应该暴露一个可以重复执行的工厂函数,为每个请求创建新的应用程序实例,同样router和store入口文件也需要重新创建一个实例。

为了配合webpack动态加载路由配置,这里会改写常规路由引入写法,这样可以根据路由路径来判断加载相应的组件代码:

import Home from &#39;../views/index/index.vue&#39;
// 改写成
component: () => (&#39;../views/index/index.vue&#39;)
登录后复制

以下是路由的基本写法router,对外会抛出一个createRouter方法来创建一个新的路由实例:

import Vue from &#39;vue&#39;
import Router from &#39;vue-router&#39;;
Vue.use(Router)
export function createRouter() {
 return new Router({
  mode: &#39;history&#39;,
  routes: [{
   name:&#39;Home&#39;,
   path: &#39;/&#39;,
   component: () =>
    import (&#39;../views/index/index.vue&#39;)
  }]
 })
}
登录后复制

以下是store状态管理的基本写法,对外暴露了一个createStore方法,方便每次访问创建一个新的实例:

// store.js
import Vue from &#39;vue&#39;
import Vuex from &#39;vuex&#39;
import * as actions from &#39;./actions&#39;
import getters from &#39;./getters&#39;
import modules from &#39;./modules/index&#39;
Vue.use(Vuex)
export function createStore() {
 return new Vuex.Store({
 actions,
 getters,
 modules,
 strict: false
 })
}
登录后复制

结合写好的router和store入口文件代码来编写整个项目的入口文件app.js代码内容,同样最终也会对外暴露一个createApp方法,在每次创建app的时候保证router,store,app都是新创建的实例,这里还引入了一个vue路由插件vuex-router-sync,主要作用是同步路由状态(route state)到 store,以下是app.js完整代码:

import Vue from &#39;vue&#39;
import App from &#39;./App.vue&#39;
import { createRouter } from &#39;./router&#39;
import { createStore } from &#39;./store&#39;
import { sync } from &#39;vuex-router-sync&#39;
require(&#39;./assets/style/css.less&#39;);
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 &#39;./app&#39;
const { app, router, store } = createApp()
登录后复制

这里我们会使用到onReady方法,此方法通常用于等待异步的导航钩子完成,比如在进行服务端渲染的时候,例子代码如下:

import { createApp } from &#39;./app&#39;
const { app, router, store } = createApp()
router.onReady(() => {
 app.$mount('#app')
})
登录后复制

我们会调用一个新方法beforeResolve,只有在router2.5.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就能编译es6自带的Promise,因为本人使用习惯了,这里可以根据自身的需求是否安装:

import { createApp } from &#39;./app&#39;
import Q from &#39;q&#39;
import Vue from &#39;vue&#39;

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(&#39;#app&#39;)
})
登录后复制

entry-server.js代码编写:

基本编写和客户端的差不多,因为这是服务端渲染,涉及到与后端数据交互定义的问题,我们需要在这里定义好各组件与后端交互使用的方法名称,这样方便在组件内部直接使用,这里根我们常规在组件直接使用ajax获取数据有些不一样,代码片段如下:

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

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

import { createApp } from &#39;./app&#39;
import Q from &#39;q&#39;
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)
 })
}
登录后复制

4、脚手架其他目录介绍:

到这里src下面主要的几个文件代码已经编写完成,接下里介绍下整个项目的目录结构如下:

主要几个文件介绍如下:

  1. build 主要存放webpack打包配置文件

  2. dist webpack打包后生成的目录

  3. log 使用pm2监控进程存放的日志文件目录

  4. server.js node服务器启动文件

  5. pmlog.json pm2配置文件

server.js入口文件编写

我们还需要编写在服务端启动服务的代码server.js,我们会使用到部分node原生提供的api,片段代码如下:

const Vue = require(&#39;vue&#39;)
const express = require(&#39;express&#39;)
const path = require(&#39;path&#39;)
const LRU = require(&#39;lru-cache&#39;)
const { createBundleRenderer } = require(&#39;vue-server-renderer&#39;)
const fs = require(&#39;fs&#39;)
const net = require(&#39;net&#39;)
登录后复制

大致思路是,引入前端模版页面index.template.html,使用express启动服务,引入webpack打包项目代码的dist文件,引入缓存模块(这里不做深入介绍,后期会单独详细介绍),判断端口是否被占用,自动启动其他接口服务。

引入前端模版文件并且设置环境变量为production,片段代码如下:

const template = fs.readFileSync(&#39;./src/index.template.html&#39;, &#39;utf-8&#39;)
const isProd = process.env.NODE_ENV === &#39;production&#39;
登录后复制

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(&#39;./dist&#39;),
 runInNewContext: false
 }))
}
let renderer;
let readyPromise
if (isProd) {
 const bundle = require(&#39;./dist/vue-ssr-server-bundle.json&#39;)
 const clientManifest = require(&#39;./dist/vue-ssr-client-manifest.json&#39;)
 renderer = createRenderer(bundle, {
 clientManifest
 })
} else {
 readyPromise = require(&#39;./build/setup-dev-server&#39;)(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(&#39;/dist&#39;, serve(&#39;./dist&#39;, true))
server.get(&#39;*&#39;, (req, res) => {
 const context = {
 title: &#39;hello&#39;,
 url: req.url
 }
 renderer.renderToString(context, (err, html) => {
 if (err) {
  res.status(500).end(&#39;Internal Server Error&#39;)
  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(&#39;listening&#39;, function() {
  clearTimeout(timeoutRef)

  if (servers)
   servers.close()

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

  let result = true
  if (err.code === &#39;EADDRINUSE&#39;)
   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 + &#39;\n&#39;);
    resolve(_pt);
   } else {
    serverport(_pt + 1)
   }
  })
 })()

})
checkPortPromise.then(data => {
 uri = &#39;http://localhost:&#39; + data;
 console.log(&#39;启动服务路径&#39;+uri)
 server.listen(data);
});
登录后复制

到这里,基本的代码已经编写完成,webpack打包配置文件基本和官方保持不变,接下来可以尝试启动本地的项目服务,这里简要的使用网易严选首页作为demo示例,结果如下:

第三部分 mockjs和axios配合使用

1、前言

上一节大致介绍了服务端和客户端入口文件代码内容,现在已经可以正常运行你的后端渲染脚手架了,这一节,跟大家分享下如何使用axios做ajax请求,如何使用mockjs做本地假数据,跑通本地基本逻辑,为以后前后端连调做准备。

2、前期准备

需要用npm安装axios,mockjs依赖包,由于mockjs只是代码开发的辅助工具,所以安装的时候我会加--save-dev来区分,具体可以根据自己的需求来定,当然,如果有mock服务平台的话,可以直接走mock平台造假数据,本地直接访问mock平台的接口,例如可以使用阿里的Rap平台管理工具生成。

npm install axios --save
npm install mockjs --save-dev
登录后复制

3、简要介绍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 &#39;axios&#39;
import qs from &#39;qs&#39;
import Q from &#39;q&#39;
/**
 * 兼容 不支持promise 的低版本浏览器
 */
require(&#39;es6-promise&#39;).polyfill();
import C from &#39;../conf&#39;

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

function ajax(url, type, options) {

 return Q.Promise((resolve, reject) => {
 axios({
  method: type,
  url: C.HOST + url,
  params: type === &#39;get&#39; ? options : null,
  data: type !== &#39;get&#39; ? qs.stringify(options) : null
  })
  .then((result) => {
  if (result && result.status === 401) {
   // location.href = &#39;/views/401.html&#39;
  }
  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, &#39;get&#39;, options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

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

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

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

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

export default config;
登录后复制

mockjs项目基本配置如下:

1、在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: &#39;首页&#39;,
 MOCK: true,
 API: {
  GET: &#39;/api/home&#39;,
 }
 }
};
登录后复制

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

import Mock from &#39;mockjs&#39;;
const mData = {
 index: {
 API: {
  GET: {
  "code": 200,
  "data": {
   "pin": &#39;wangqi&#39;,
   "name": &#39;王奇&#39;
  }
  }
 }
 }
}
登录后复制

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在微信小程序中如何实现流程进度样式?

在vue中使用vue-cli如何搭建helloWorld

layui中有关取值传值方面的问题

详细解答vue的变化对组件有什么影响?

在vue-scroller中如何标记记录滚动位置

以上就是使用 vue-ssr如何实现服务端渲染的详细内容,更多请关注php中文网其它相关文章!

智能AI问答
PHP中文网智能助手能迅速回答你的编程问题,提供实时的代码和解决方案,帮助你解决各种难题。不仅如此,它还能提供编程资源和学习指导,帮助你快速提升编程技能。无论你是初学者还是专业人士,AI智能助手都能成为你的可靠助手,助力你在编程领域取得更大的成就。
我要提问
相关标签:
vue Java php json ajax html es6 npm webpack express Static 封装 require 全局变量 字符串 接口 栈 public JS 对象 dom promise 异步 github 微信小程序 https 搜索引擎 SEO
来源:php中文网
收藏 点赞
上一篇:在react-router中如何使用history控制路由(详细教程) 下一篇:有关Vue2.0父子组件间实现派发机制(详细教程)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
  • PHP闭包和匿名函数(详细教程)
    2023-03-30 20:04:02
  • 详细讲解CSS基础知识点
    2023-03-26 12:14:02
  • 在PHP命名空间中使用Exception报错not found的问题,结合具体实例详细为你讲解
    2023-03-26 11:44:01
  • 关于PHP的命名空间(结合代码实例,简单粗暴易懂)
    2023-03-26 11:40:01
  • php命名空间(结合代码详细解答)
    2023-03-26 11:34:01
  • 详细介绍php命名空间与自动加载的区别
    2023-03-26 11:28:02
  • 结合代码详细为你讲解,php中的array_map,array_walk以及匿名函数
    2023-03-26 11:06:02
  • 结合代码详细介绍php中的作用域
    2023-03-26 11:00:02
  • 详细讲解php中require,include,use区分
    2023-03-26 10:56:01
  • 在PHP中静态变量和全局变量有哪些不同?
    2023-03-26 10:46:01
最新问题
function_exists()无法判定自定义函数 function test()    {        return true;    } ...
凡人来自于2024-04-29 11:01:01
0 1 208
google 浏览器 手机版显示的怎么实现 老师您好,google 浏览器怎么变成手机版样式的?
艾尼宛尔.亚森来自于2024-04-23 00:22:19
0 6 356
子窗口操作父窗口,输出没反应 前两句可执行,最后一句没法应
P粉722478067来自于2024-04-19 15:37:47
0 1 388
父窗口没有输出 document.onclick = function(){ window.opener.document.write('我是子窗口的输出');  &nb...
P粉722478067来自于2024-04-18 23:52:34
0 0 267
关于CSS思维导图的课件在哪? 课件
凡人来自于2024-04-16 10:10:18
0 0 358
PX自动转换为REM错误  <style>html {   font-size: calc(100vw / 3.75);      }...
凡人来自于2024-04-16 09:34:16
0 0 3389
PHP数组从URL参数中获取的行为不如预期 我有一个包含类别ID的URL参数,我想将其视为一个数组,如下所示:http://example.com?cat[]=3,9,13在PHP中,我使用它从URL参数获取数组:$catI...
P粉785905797来自于2024-04-06 22:09:02
0 1 668
通过添加 Width 属性将内容向左移动 我已经为主体提供了边距。主要{左边缘:200px;右边距:200px;文本对齐:居中}由于我想以两行而不是一行显示文本,因此我在样式中添加了width属性。.p{字体大小:12px...
P粉738046172来自于2024-04-06 22:01:35
0 3 455
我应该在 apache 中哪里放置 CustomLog 指令 我正在使用php:7.2-apachedocker。我需要禁用运行状况检查url登录访问日志。基于此链接,他们提到了有关修改Customlog指令的信息。我不是关于需要更改Cust...
P粉573809727来自于2024-04-06 22:03:59
0 1 575
返回值中变量的格式是什么? 我是php的新学习者。我发现有一段代码:if($x<time()){return[false,'error'];}逻辑或变量并不重要,但我不明白[false,'error']...
P粉757556355来自于2024-04-06 21:55:20
0 1 363
相关专题
更多>
  • java
  • java流程控制语句有哪些
  • java正则表达式语法
  • java自学难吗
  • java配置jdk环境变量
  • java保留两位小数
  • java基本数据类型
  • java有什么用
热门推荐
  • js中的原型链是什么
  • js中百分号是什么意思
  • js中的$符号是什么意思
  • js中prototype的作用
  • js中onclick事件怎么写
  • document在js中有什么用
  • js中===和==的区别
  • js中switch的用法
  • js中如何实现数字相加
  • function在js中的作用
热门教程
更多>
相关推荐
热门推荐
最新课程
  • phpStudy极速入门视频教程
    phpStudy极速入门视频教程
    513929次学习
    收藏
  • 独孤九贱(1)_HTML5视频教程
    独孤九贱(1)_HTML5视频教程
    596063次学习
    收藏
  • 独孤九贱(5)_ThinkPHP5视频教程
    独孤九贱(5)_ThinkPHP5视频教程
    1191257次学习
    收藏
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
  • [表单按钮] jQuery企业留言表单联系代码
  • [播放器特效] HTML5 MP3音乐盒播放特效
  • [菜单导航] HTML5炫酷粒子动画导航菜单特效
  • [表单按钮] jQuery可视化表单拖拽编辑代码
  • [播放器特效] VUE.JS仿酷狗音乐播放器代码
  • [html5特效] 经典html5推箱子小游戏
  • [图片特效] jQuery滚动添加或减少图片特效
  • [相册特效] CSS3个人相册封面悬停放大特效
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
app下载
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2024 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号

  • 精品班

  • 技术支持

  • 技术咨询

  • 学习群

  • 会员优惠

  • 返回顶部

  • 代做工资流水公司洛阳企业对公流水公司长春车贷工资流水 代办淄博制作个人流水兰州购房银行流水样本沈阳背调银行流水代做镇江代开在职证明宁波查询薪资流水单商丘代开自存流水蚌埠工资流水账单费用金华流水单模板石家庄工资流水app截图报价台州代开入职工资流水泰州企业对公流水多少钱无锡代做房贷工资流水宜昌办企业对公流水新乡开贷款银行流水郑州工资流水app截图代开青岛背调流水查询天津对公银行流水代开上饶流水账单模板海口做背调银行流水邢台车贷工资流水 代开西安银行流水PS打印重庆查个人流水合肥房贷收入证明开具兰州入职银行流水代开石家庄对公账户流水多少钱无锡薪资流水代开银川制作银行流水单邯郸工资流水公司香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤

    代做工资流水公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化