Подробное введение в рендеринг на стороне сервера на основе vue-ssr

внешний интерфейс JavaScript Vue.js

Часть 1 Основное введение

1. Введение

Принцип и механизм реализации рендеринга на стороне сервера: брать данные на стороне сервера для парсинга и рендеринга, напрямую генерировать html-фрагменты и возвращать их во внешний интерфейс. Затем внешний интерфейс может анализировать фрагменты HTML, возвращаемые серверной частью на страницу внешнего интерфейса, примерно в следующих двух формах:

1. Сервер напрямую отображает всю страницу через механизм шаблонов, такой как механизм шаблонов vm в бэкэнде java и механизм шаблонов smarty в бэкэнде php.

2. Рендеринг сервиса генерирует блоки html-кода, которые клиент получает через AJAX, а затем динамически добавляет с помощью js.

2. Преимущества и недостатки рендеринга на стороне сервера

Рендеринг на стороне сервера может решить две основные проблемы:

1. Проблема поисковой оптимизации помогает поисковым роботам сканировать содержимое веб-сайта, что полезно для индексации и ранжирования веб-сайта.

2. Проблема медленной загрузки первого экрана.Например, в зрелом SPA-проекте для открытия домашней страницы нужно загрузить много ресурсов.Отрисовку первого экрана можно ускорить за счет серверной отрисовки.

Точно так же рендеринг на стороне сервера также будет иметь недостатки.В основном это выбор подходящего метода в соответствии с вашим собственным бизнес-сценарием.Поскольку рендеринг на стороне сервера передней страницы определенно увеличит нагрузку на сервер.

3. Принцип реализации SSR

Клиент запрашивает сервер, сервер получает соответствующий компонент в соответствии с адресом запроса и возвращает Promise (официально метод preFetch) для получения необходимых данных после вызова соответствующего компонента. наконец пройти

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

Запишите его на веб-страницу и, наконец, верните веб-страницу, отображаемую сервером.

Далее клиент будет писать в vuexinitial_stateЗамените его текущим глобальным деревом состояний, а затем используйте это дерево состояний, чтобы проверить, нет ли проблем с данными, отображаемыми сервером. Когда вы сталкиваетесь с компонентом, который не отображается сервером, отправьте асинхронный запрос для получения данных. Проще говоря, это операция Diff, похожая на shouldComponentUpdate в React.

Vue2 использует односторонний поток данных, используя его, вы можете вернуть единственное глобальное состояние через SSR и подтвердить, был ли компонент SSR.

4. Основной плагин для рендеринга vue backend: vue-server-renderer

Из-за введения виртуального дома возможен рендеринг vue на стороне сервера.Ниже приведена блок-схема рендеринга, предоставленная официальным vue-server-renderer:

官方流程图

Можно видеть, что внутренний рендеринг Vue состоит из трех частей: исходный код страницы, часть рендеринга слоя узла и часть рендеринга на стороне браузера.

Источник разделен на две точки входа: одна — клиентская запись внешней страницы, которая в основном предназначена для создания экземпляра объекта Vue и его подключения к странице; другая — серверная запись внутренней службы рендеринга. который в основном управляет модулем рендеринга на стороне сервера.Обратный вызов, возврат объекта Promise и, наконец, возврат объекта Vue (после тестирования также можно напрямую вернуть объект Vue);

Предыдущая исходная часть — это код для бизнес-разработки, после завершения разработки он собирается через webpack для генерации соответствующего бандла, здесь не описывается клиентский бандл, представляющий собой запакованный файл, который может выполняться на стороне браузера; вот пакет сервера, предоставляемый модулем vue2 vue-server-renderer, модуль может предоставлять два типа рендерера: rendererer/bundleRenderer, эти два вида рендерера представлены ниже.

Рендерер получает объект vue и затем рендерит его.Это можно сделать для простых объектов vue, но для сложных проектов, если вы используете это прямое требование объекта vue, это не повлияет на структуру и логику серверного кода. Слишком дружелюбно, во-первых, состояние модуля будет продолжаться в каждом запросе запроса на рендеринг, нам нужно управлять и избегать того, чтобы состояние этого запроса на рендеринг влияло на последующие запросы, поэтому vue-server-renderer предоставляет другой режим рендеринга, через A bundleRenderer делает рендеринг.

bundleRenderer — официально рекомендуемый способ рендеринга на стороне сервера для более сложных проектов.Через webpack серверная запись упаковывается в соответствии с определенными требованиями для создания server-bundle, который эквивалентен упакованному и сжатому файлу, который может использоваться сервер. Повторная инициализация объекта vue гарантирует, что каждый запрос будет независимым. Разработчикам нужно сосредоточиться только на текущем бизнесе, не разрабатывая дополнительный логический код для рендеринга на стороне сервера. После того, как модуль рендеринга сгенерирован, есть два интерфейса: renderToString и renderToStream.Один предназначен для одновременного рендеринга страницы в строковый файл, а другой — потокового рендеринга, который подходит для веб-серверов, поддерживающих потоковую передачу, и может быть скорость запроса услуг. быстрее.

Часть 2 Сборка с нуля

1. Введение

В предыдущем разделе мы кратко говорили о том, почему нам нужно использовать рендеринг бэкэнда vue, и об основных принципах рендеринга бэкэнда vue. ответ на официальной странице Пример vue-hackernews-2.0, создайте проект с нуля, исходный код будет предоставлен вам в следующем разделе.

2. Предварительная подготовка

Основные требования к среде: версия узла 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 相关插件

Выше приведены основные используемые стеки технологий. В процессе создания соответствующие пакеты зависимостей подключаемых модулей будут использоваться для сжатия и упаковки. Ниже приведены пакеты зависимостей, которые необходимо добавить в файл package.json после инициализации npm.

"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  

Основное введение в каталог файлов:

  • Папка представлений разделена на файлы модуля, а файл модуля разделен на файл .vue (файл шаблона) самого модуля, файл index.js (файл взаимодействия с фоновыми данными), mock.js (фиктивные поддельные данные этот модуль), conf.js (настроить некоторые параметры этого модуля, путь запроса, имя модуля и другую информацию)
  • общедоступная папка компонентов компонентов
  • Маршрутизатор в основном хранит файл конфигурации внешнего интерфейса, и спецификация написания может следовать официальному примеру vue-router.
  • store в основном хранит файлы общего состояния, в том числе action.js, getter.js,mutationtype.js и т. д., которые позже будут разделены по модулям.
  • public в основном хранит общедоступный код компонента и общедоступный код файла, используемый проектом.Например, мы будем инкапсулировать axios в файл общедоступной библиотеки API на более позднем этапе и т. д.
  • Статическая папка представляет собой статические файлы и не будет упакована веб-пакетом.
  • app.js — это файл входа в проект.
  • App.vue — файл входа в проект.
  • entry-client и entry-server — это файл записи клиента и файл записи сервера соответственно.
  • 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, но легко вызвать загрязнение статуса перекрестного запроса при внутреннем рендеринге, что приведет к загрязнению потока данных.

Таким образом, чтобы избежать синглтонов с отслеживанием состояния, мы не должны создавать экземпляр приложения напрямую, а должны предоставить фабричную функцию, которая может многократно выполняться для создания нового экземпляра приложения для каждого запроса, а файлы входа маршрутизатора и хранилища также должны быть воссозданы заново. пример.

Чтобы взаимодействовать с 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')
        }]
    })
}

Ниже приведен базовый метод написания управления состоянием хранилища, который предоставляет метод 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
  })
}

Объединение написанного кода файла записи маршрутизатора и хранилища для записи содержимого кода app.js файла записи всего проекта, а также в конечном итоге предоставление метода createApp для обеспечения того, чтобы маршрутизатор, хранилище и приложение создавались заново каждый раз, когда приложение создается пример , здесь также представлен плагин vue routing vuex-router-sync. Основная функция — синхронизировать состояние маршрута с хранилищем. Ниже приведен полный код 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 }
}

Вход - Client.js Код написания:

На домашней странице представлен метод createApp, доступный из файла приложения. Каждый раз, когда вызывается клиент, заново создаются новое приложение, маршрутизатор и хранилище. Часть кода выглядит следующим образом:

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, который доступен только в маршрутизаторе 2.5.0 и выше, зарегистрируем глобальную защиту маршрута, аналогичную router.beforeEach(), за исключением того, что после подтверждения навигации, после того, как все другие средства защиты и асинхронные компоненты были разрешены позвони после. Основное письмо выглядит следующим образом:

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

Сервер помещает состояние, которое должно быть передано клиенту, в окно.INITIAL_STATEвыше этой глобальной переменной. HTML-структура передней и задней частей должна быть одинаковой. Затем запишите дерево состояний хранилища в глобальную переменную (INITIAL_STATE), так что когда клиент инициализирует рендеринг, HTML-структура, сгенерированная сервером, может быть проверена и синхронизирована с состоянием инициализации, а затем вся страница будет передана клиенту. Основной код выглядит следующим образом:

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

Далее я опубликую полный код клиента. Вы также можете использовать babel для компиляции Promise, который поставляется с es6, не вводя здесь Q. Поскольку я привык к этому, я могу установить его в соответствии со своими потребностями:

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)
  })
}

4. Знакомство с другими категориями строительных лесов:

Здесь написаны коды основных файлов в src, далее структура каталогов всего проекта представлена ​​следующим образом:

整个目录结构

Основные документы следующие:

  • сборка в основном хранит файлы конфигурации упаковки webpack
  • Каталог, сгенерированный dist webpack после упаковки
  • log Используйте pm2 для мониторинга каталога файла журнала, хранящегося в процессе.
  • server.js файл запуска сервера узла
  • pmlog.json конфигурационный файл pm2

Написание файла входа server.js

Нам также нужно написать код server.js для запуска службы на стороне сервера. Мы будем использовать некоторые 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, использовать экспресс для запуска службы, ввести веб-пакет для упаковки dist-файла кода проекта и ввести модуль кеша (я не буду вдаваться в подробности). здесь он будет подробно представлен позже), и определить, занят ли порт, чтобы автоматически запускать другие сервисы интерфейса.

Импортируйте файл шаблона внешнего интерфейса и установите для переменной среды значение production.Код фрагмента выглядит следующим образом:

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

Конкретное использование плагина vue-server-renderer заключается в создании функции createBundleRenderer путем чтения файлов каталога в папке dist и использовании 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)
  })
}

Используйте экспресс для запуска службы, фрагмент кода выглядит следующим образом:

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);
});

К этому моменту базовый код был написан, и файл конфигурации упаковки веб-пакета остается в основном таким же, как и официальный. Далее вы можете попробовать запустить локальную службу проекта. Здесь мы кратко используем домашнюю страницу строгого выбора NetEase в качестве демонстрации. Пример. Результаты следующие:

demo

Третья часть использует mockjs и axios вместе.

1. Введение

В общем введении в сервисный вход и содержимое кода клиентского файла теперь можно правильно запустить внутренние леса рендеринга этого раздела, чтобы поделиться с вами тем, как использовать axios в запросе ajax, как локальные mockjs ложные данные, работающие в основном через местную логику, передние и задние концы тона после подготовки.

2. Предварительная подготовка

Вам нужно использовать npm для установки зависимостей axios и mockjs.Поскольку mockjs — это только вспомогательный инструмент для разработки кода, я добавлю --save-dev, чтобы различать его во время установки, что можно определить в соответствии с вашими потребностями.Конечно, если есть фиктивная сервисная платформа Если это так, вы можете перейти непосредственно к фиктивной платформе для подделки данных и напрямую получить локальный доступ к интерфейсу фиктивной платформы.Например, вы можете использовать инструмент управления платформой Ali 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 '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 выглядит следующим образом:

1. Создайте новый conf.js под общедоступным, чтобы определить URL-адрес запроса глобально.Код выглядит следующим образом:

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

2. Создайте новый conf.js в корневом каталоге views/index, определите путь запроса макета компонента и укажите, следует ли запускать один компонент с использованием данных макета или данных онлайн-интерфейса.Код выглядит следующим образом:

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

3. Определите mockjs внутри компонента для записи фиктивных поддельных данных, код выглядит следующим образом:

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

Вышеупомянутый основной процесс.Если будет лучший и более гибкий план использования, я надеюсь принять участие в общении и совместном использовании.Рабочий процесс проекта был опубликован на github и будет продолжать поддерживаться и обновляться.Нажми для деталей