越写悦快乐之Vue项目如何使用JSON Server快速生成Mock数据

JSON Server - 图片来自简书App

今天的越写悦快乐系列文章为大家带来Vue项目如何使用JSON Server快速生成Mock数据,做过前端开发的小伙伴都知道,Vue在前端圈的流行程度,它可以快速构建响应式的Web页面,配置数据驱动的方式来操作虚拟DOM,构建易用、灵活、高效的Web页面。今天的文章我为大家介绍一下如何使用JSON Server快速构建Mock数据,利用Express路由转发客户端请求,然后返回响应数据给Vue组件,Vue组件通过比对虚拟DOM并计算变更来更新页面,最后把更新结果渲染在页面上。

开发环境

  • Window 10.0.17763
  • Node 10.15.3
  • Yarn 1.16.0
  • Vue CLI 3.7.0
  • Git 2.21.0

基本原理

基本原理 - 图片来自简书App
  • 启动开发服务器
  • 通过Mock服务器代理页面发送的请求
  • Mock服务器根据路由解析请求并返回数据
  • 数据返回后交由Vue组件(Single File Components)来处理并渲染
  • 渲染完成后显示在页面

Vue Page是一个SFC,也可以包含多个Component。

挂载App的含义是将创建Vue对象与某个DOM节点(也就是HTML中的标签)进行绑定。

安装

  • npm方式

npm install -g json-server

  • yarn方式

yarn global add json-server

  • pnpm方式

pnpm install -g json-server

配置代理

我们在vue.config.js文件中添加devServer

module.exports = {
  lintOnSave: false,
  devServer: {
    proxy: 'http://localhost:3000/'
  },
  css: {
    sourceMap: true
  }
};

生成Mock数据

我们在项目目录下创建__mock__目录并创建faker.js文件,其文件内容如下。

module.exports = function () {
    var faker = require("faker");
    faker.locale = "zh_CN";
    var _ = require("lodash");
    return {
        people: _.times(100, function (n) {
            return {
                id: n,
                name: faker.name.findName(),
                avatar: faker.internet.avatar()
            }
        }),
        address: _.times(100, function (n) {
            return {
                id: faker.random.uuid(),
                city: faker.address.city(),
                county: faker.address.county()
            }
        })
    }
}

我们可以通过 json-server __mock__/faker.js启动服务,此时默认监听的端口是3000,接下来在浏览器地址栏输入http://localhost:3000/address查看返回的数据

配置运行脚本

要通过yarn运行脚本,需要在package.json文件中的scripts中添加

{
  "name": "vant-tour",
  "version": "1.0.0",
  "private": true,
  "description": "An Vant Tour",
  "license": "MIT",
  "author": "Watony Weng <softweitao@126.com>",
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
+   "mock": "json-server --watch __mock__/faker.js"
  },
  "dependencies": {
    "axios": "^0.18.0",
    "vant": "^1.6.14",
    "vue": "^2.6.10",
    "vue-router": "^3.0.5"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.4.0",
    "@vue/cli-plugin-eslint": "^3.4.0",
    "@vue/cli-service": "^3.4.0",
    "babel-plugin-import": "^1.8.0",
    "faker": "^4.1.0",
    "less": "^3.8.1",
    "less-loader": "^4.1.0",
    "postcss-pxtorem": "^4.0.1",
    "vue-cli-plugin-axios": "0.0.4",
    "vue-template-compiler": "^2.6.10"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/recommended"
    ],
    "rules": {},
    "parserOptions": {
      "parser": "babel-eslint"
    }
  },
  "browserslist": [
    "Android >= 4.0",
    "iOS >= 7"
  ]
}

验证

有这样一个场景,需要一个地址列表并且可以模糊搜索,那么我们可以这样操作,先给出示例例页面的截图。

页面截图 - 图片来自简书App

创建单文件组件(SFC)

创建src/views/home/index.vue文件,下面给出这个文件的示例。

<template>
  <div>
    <van-nav-bar :title="pageTitle"/>
    <van-search 
      v-model="value" 
      placeholder="请输入搜索关键词" 
      show-action 
      @search="onSearch">
      <div 
        slot="action" 
        @click="onSearch">搜索</div>
    </van-search>
    <van-list 
      v-model="loading" 
      :finished="finished" 
      finished-text="我是有底线的" 
      @load="onLoad">
      <van-cell
        v-for="(item, index) in list"
        :key="item"
        :title="item"
        @click.native="onClickItem(index)"
      />
    </van-list>
  </div>
</template>

<script>
import { Cell, List, NavBar, Toast, Search } from 'vant'

export default {
  name: 'Home',
  components: {
    [Cell.name]: Cell,
    [List.name]: List,
    [NavBar.name]: NavBar,
    [Toast.name]: Toast,
    [Search.name]: Search
  },
  data() {
    return {
      pageTitle: '地址列表',
      value: '',
      url: '',
      loading: false,
      finished: false,
      list: [],
      items: null
    }
  },
  watch: {
    value(newVal, oldVal) {
      if (newVal.length == 0) {
        this.items = null
        this.list = []
        this.onLoad()
      }
    }
  },
  methods: {
    onLoad() {
      this.loading = false
      this.url = 'address'
      this.axios
        .get(
          this.url,
          {},
          {
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
            }
          }
        )
        .then(result => {
          const data = result.data
          if (data.length <= 0) {
            Toast.fail('暂无数据')
          } else {
            this.items = data
            for (var i = 0; i < data.length; i++) {
              this.list.push(data[i].city)
            }
            this.finished = true
          }
        })
    },
    onClickItem(index) {
      console.log('onClickItem ' + JSON.stringify(this.items[index]))
    },
    onSearch() {
      if (this.value.length == 0) {
        Toast.fail('关键词去哪了')
        return
      }
      this.url = 'address?q=' + this.value
      this.axios
        .get(
          this.url,
          {},
          {
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
            }
          }
        )
        .then(result => {
          const data = result.data
          this.items = null
          this.list = []

          if (data.length <= 0) {
            Toast.fail('暂无数据')
          } else {
            this.items = data
            for (var i = 0; i < data.length; i++) {
              this.list.push(data[i].city)
            }
            this.finished = true
          }
        })
    }
  }
}
</script>
<style lang="less">
</style>

配置路由

我们在路由文件src/router.js中添加Home组件的声明。

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

const routes = [
  {
    path: '*',
    redirect: '/home'
  },
  {
    name: 'home',
    component: () => import('./views/home'),
    meta: {
      title: '手机端'
    }
  }
]

routes.forEach(route => {
  route.path = route.path || '/' + (route.name || '')
})

const router = new Router({ routes })

router.beforeEach((to, from, next) => {
  const title = to.meta && to.meta.title
  if (title) {
    document.title = title
  }
  next()
})

export { router }

配置插件

我们使用Axios来发送请求,此时我们需添加插件vue-cli-plugin-axios,通过这个插件会自动添加axios依赖并且在src/plugins下生成axios.js文件,其文件内容如下。

'use strict'

import Vue from 'vue'
import axios from 'axios'

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

let config = {
  timeout: 60 * 1000,
  responseType: 'json'
}

axios.defaults.transformRequest = [
  function(data) {
    let ret = ''
    for (let it in data) {
      ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
    }
    return ret
  }
]

const _axios = axios.create(config)

_axios.interceptors.request.use(
  function(config) {
    return config
  },
  function(error) {
    return Promise.reject(error)
  }
)

_axios.interceptors.response.use(
  function(response) {
    return response
  },
  function(error) {
    return Promise.reject(error)
  }
)

Plugin.install = function(Vue, options) {
  Vue.axios = _axios
  window.axios = _axios
  Object.defineProperties(Vue.prototype, {
    axios: {
      get() {
        return _axios
      }
    },
    $axios: {
      get() {
        return _axios
      }
    }
  })
}

Vue.use(Plugin)

export default Plugin

启动

  • 启动代理服务

yarn mock

启动代理服务 - 图片来自简书App
  • 启动应用

yarn serve

启动服务 - 图片来自简书App
  • 打开浏览器输入http://localhost:8080/即可看到列表页面
欢迎来到Vue的世界 - 图片来自简书App

参考

示例

个人总结

总结一下今天分享的内容,我们从Vue CLI出发,使用有赞的Vant搭建了一个手机端的页面,该页面使用List组件进行渲染,数据来源自JSON Server生成的模拟数据,随后我们添加了搜索组件并监听输入框的值将搜索结果实时显示在列表项中,那么对于项目中使用的工具、库和框架,我们需要有一个更加感性的认识,Vue框架是如何渲染页面的,页面的数据从何而来,如何发起请求,组件中如何调用axios这些知识点在实践的过程中会有一个不一样的认识,当然也需要我们了解前端发展的新趋势,在移动互联网的后时代如何把握和利用小程序的红利构建不一样的产品形态,创建不一样的用户价值,构建属于我们的新时代,我相信这仅仅是开始,我们一直在路上,期待遇见更好的自己,期待和大家一起成长,也希望大家保持学习的热情,继续努力,成就非凡梦想。若是我的文章对你有所启发,那将是我莫大的荣幸。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容