2021-06-10

2021/6/15 18:28:31

本文主要是介绍2021-06-10,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

使用webpack创建项目,mock.js模拟请求数据,vux模块化方式发起请求处理请求数据

    • 技术栈
        • vue+webpack+mock.js+vuex
    • 准备工作

技术栈

vue+webpack+mock.js+vuex

废话不多说开始迈出第一步

准备工作

详细可查看https://blog.csdn.net/antma/article/details/86104068.
:下载node.js
window10进去点击第一个下载NODE.JS官网.
注意:如果是window7的可以去这里NODE.JS官网.
window7可以下载12.18.4版本当然其他低版本的也可以,进去后可以下载.msi后缀的安装包,其他的也可以
window7只支持旧版本,新版本不支持
然后下一步下一步一路傻瓜式下载,下载完成后使用node -v查看是否下载成功(v是小v)

:配置node.js
1:在nodejs安装路径下(就是你下载时的那个路径)新建文件夹node_cache和node_global用来存放下载包的缓存和配置全局模块的路径
2:在命令行执行以下命令
注意:后面的路径是你自己刚才新建的文件夹路径
1): npm config set prefix “D:\Program Files\nodejs\node_global”
2): npm config set cache “D:\Program Files\nodejs\node_cache”
3): 查看配置信息npm config list
3:开始环境变量的配置
1):单击我的电脑——右键属性——选择高级系统设置——单击环境变量
2):配置系统变量,单击系统变量下的新建在弹出的框中输入——变量名: NODE_PATH以及,变量值:D:\node\node_modules(注意:这里的地址是你node_modules文件夹的路径地址)配置完后点击确定
3):配置用户变量——选中path单击编辑,在弹出的对话框中单击新建按钮,复制你刚才新建的node_global文件夹的路径,粘贴进去比如:D:\node\node_global然后单击确定(如果是window7用户就直接点击编辑或者双击path在变量值的内容里把路径拼接上去,路径前面用分号(;)分割就行)到此配置完毕
4):配置淘宝镜像npm install -g cnpm --registry=https://registry.npm.taobao.org
5):查看淘宝镜像配置: npm config get registry 或 cnpm config get registry
:全局安装vue以及脚手架工具vue-cli Vue Cli.
1)npm install -g vue
1):npm install -g @vue/cli
2):vue/cli查看命令: vue -v。vue查看命令: npm list vue
:全局安装webpack和webpack-cli脚手架 webpack.
1)npm install -g webpack webpack-cli
2):查看版本号 webpack -v
:创建项目
1):vue init webpack admin-project
2):可能会提示让你安装npm i -g @vue/cli-init,安装一下。以及可能会报错 vue-cli · Failed to download repo vuejs-templates/webpack: read ECONNRESET/网络问题,多安装几下
3):安装完成后接着第一个操作
4):然后根据提示安装
提示如下
①Project name admin-project(设置项目名称)
②Project description A Vue.js project(项目描述vue.js项目)
③Author 老白(作者,输入你的名称什么都行)
④Vue build两个选项Runtime + Compiler以及Runtime-only(推荐使用第二个因为他更轻更快)具体可查看https://cn.vuejs.org/v2/guide/installation.html
⑤Install vue-router? Yes(是否安装vue-router输入y)
⑥Use ESLint to lint your code?(是否使用ESLint,这里看你的习惯可以不装)
⑦Yes, use NPM(看使用习惯选择)
然后等待安装完成…完成后的目录结构如下目录

:启动项目
1)npm run dev
在这里插入图片描述
:修改一下目录添加登录页面
在这里插入图片描述
:使用mock.js模拟数据和请求Mock.js原文vue-element-admin
这里不是在min.js中引入的mock.js而是在webpack.dev.conf.js中的devServer中引入的,为什么要在这里引入,第一可以使用devServer服务器内部的中间件进行接口的注册在配合chokidar来监听文件及文件内容的变化来清除之前注册的接口从新注册新街口实现热更新,第二因为我们只在本地开发,生产环境就不需要了。devServer是一个为我们提供了开发过程中的服务器他相当于一个node.js express服务器只是他封装度比较高而devServer中的before可以为我们提供在服务器内部的所有其他中间件之前执行自定义中间件的能力

第一步:下载

	1):下载mock.js作用:拦截所有的请求并代理到本地,然后进行数据模拟npm install mockjs -D
	2):下载chalk——这个是设置终端打印字体的颜色npm install chalk -D
	3):下载chokidar——这个是用来监听文件和文件内容的变化npm install chokidar -D

第二部: 新建mock文件夹与src文件夹同级
在这里插入图片描述
在webpack.dev.conf.js中的devServer引入mock_server.js文件,引入之后mock_server.js里面内容被改变只有重启项目才生效
在这里插入图片描述

index.js文件

// 引入接口文件

const user = require('./user')


const mocks = [
	...user
]

module.exports = {
  mocks
}

mock_server.js文件

// 请求、响应头解析,注册监听逻辑、清除缓存、响应服务
const Mock = require('mockjs')
const chalk = require('chalk')//给终端的打印值添加颜色
const path = require('path')//路径拼接
const chokidar = require('chokidar')//安装插件用来监听当前文件夹和文件内容的变化
const bodyParser = require('body-parser')//请求体的解析

//获取当前文件夹路径D:\project\practice\vue\admin-project\mock
// process.cwd(): \project\practice\vue\admin-project\
const mockDir = path.join(process.cwd(), 'mock')

// 注册接口
function registerRouter(app) {
  let mockLastIndex
  // 引入接口
  const { mocks } = require('./index.js')
  // 遍历接口
  const mocksForServer = mocks.map(router => {
    //router的格式{ url: '/user/login', type: 'post', response: [Function: response] }
    return responseFake(router.url, router.type, router.response)//传递请求的地址,类型,内容进行响应操作
  })
  // 遍历mocksForServer数组项
  // mock数据格式{url: /dev-api\/user\/login/,type: 'post',response: [Function: response]}
  for (const mock of mocksForServer) {

    app[mock.type](mock.url, mock.response)

    mockLastIndex = app._router.stack.length
  }
  // 获取mocksForServer的长度
  const mockRoutesLength = Object.keys(mocksForServer).length
  return {
    mockRoutesLength: mockRoutesLength,
    mockStartIndex: mockLastIndex - mockRoutesLength
  }
}

function unregisterRoutes() {
  // 通过require引入的js,会被缓存,可能会导致切换页面后不再加载通过 require.cache即可删除该js缓存。
  Object.keys(require.cache).forEach(i => {
    if (i.includes(mockDir)) {
      delete require.cache[require.resolve(i)]
    }
  })
}

// 模拟服务器响应
function responseFake(url, type, respond) {
  return {
    url: new RegExp(`${'dev-api'}${url}`),
    type: type || 'get',
    response(req, res) {
      // 生成模板数据
      res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
    }
  }
}

// 第一步
module.exports = app => {// app: before中间件功能

  // 请求体的解析,下面两个方法支持两种请求体解析虽然格式不同但是解析后得到的都是就json格式的对象
  app.use(bodyParser.json())//支持application/json
  app.use(bodyParser.urlencoded({
    extended: true//支持application/x-www-form-urlencoded
  }))

  // 注册接口
  const mockRouter = registerRouter(app)
  var mockRoutesLength = mockRouter.mockRoutesLength
  var mockStartIndex = mockRouter.mockStartIndex

  // 监听mockDir文件夹下文件和内容的变化
  chokidar.watch(mockDir, {
    ignored: /mock-server/, //不需要监听当前的文件
    ignoreInitial: true //是否对新增文件或新增文件夹时发送事件
  }).on('all', (event, path) => {
    // 判断如果有文件内容发生改变或者添加了新的文件执行以下操作
    if (event === 'change' || event === 'add') {
      try {
        // 就是说从哪里添加就从哪里删除
        app._router.stack.splice(mockStartIndex, mockRoutesLength)
        // 明确路线缓存
        unregisterRoutes()

        // 重新注册
        const mockRoutes = registerRouter(app)
        mockRoutesLength = mockRoutes.mockRoutesLength
        mockStartIndex = mockRoutes.mockStartIndex
        console.log(chalk.magentaBright(`模拟服务器加载成功! ${path}`))
      } catch (error) {
        console.log(chalk.redBright(`加载失败! ${error}`))
      }
    }
  })

}

user.js文件

// user.js针对用户的请求,登录,用户信息等

// 定义token信息用来判断该角色身份
const tokens = {
  admin: {
    token: 'admin-token'
  }
}
// 接收请求并响应对应的数据
module.exports = [
  {
    // 地址
    url: '/user/login',
    // 类型
    type: 'post',
    // 响应
    response: config => {
      // config.body就是你发送请求是携带的请求内容body: { username: 'admin', password: '111111' },
      const { username} = config.body
      // 根据用户名获取对应的token信息
      const token = tokens[username]
      // 判断并响应
      if (!token) {//没有接收到用户名
        return {
          code: 6001,
          message: '账号和密码错误'
        }
      }
      return {
        code: 200,
        message: '登录成功',
        data: token
      }
    }
  }
]

:发送请求获取数据使用vuex模块化管理各个模块的请求以及数据处理
1): 安装vuex以及axios: npm install vuex axios --save
2):安装js-cookie以及element-ui: npm install js-cookie element-ui --save
第一步:创建文件夹并在min.js中引入挂载
min.js

import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(Element)

//这个相当于this.$router.push({path:'地址', query: {}})
Vue.prototype.changeRoute = changeRoute;
function changeRoute(newRoute, routeQuery = {}) {
  router.push({
    path: newRoute,
    query: routeQuery
  }).catch(() => {})//这个.catch是因为之前一直报错Redirected when going from "/login?redirect=%2Fdashboard" to "/dashboard" via a navigation guard.所以在这里加了一个catch方法用来捕获错误,当然还有其他方法可以自行百度
}

Vue.config.productionTip = false


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

第二步: 新建文件及文件夹
在这里插入图片描述

1)发起登录请求
login/index.vue
<template>
  <div class="login-container">
    <div @click="handleLogin">登录</div>
  </div>
</template>

<script>
export default {
  name: 'Login',
  data () {
    return {
      loginForm: {
        username: 'admin',
        password: '123456'
      }
    }
  },
  methods: {
    handleLogin() {
    //user/login意识是user文件中的login方法
      this.$store.dispatch('user/login', this.loginForm)
    }
  }
}
</script>

2)store/modules/user.js中接收传递过来的参数并发起请求
store/modules/user.js

import { login } from '@/api/user'
import { getToken, setToken } from '@/utils/auth'

const state = {
  token: getToken()
}


const mutations = {
  // token
  SET_TOKEN: (state, token) => {
    state.token = token
  }
}

const actions = {
  // user login
  login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      login({ username: username, password: password })
      .then(response => {
        const { data } = response
        commit('SET_TOKEN', data.token)
        setToken(data.token)
        console.log(response, 'store/modules/user.js')
        resolve()
      })
      .catch(err => {
        reject(err)
      })
    })
  },
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

3)引入api/user.js,引入utils/auth.js
api/user.js

import request from '@/utils/request'
// 登录
export function login(data) {
  return request({
    url: '/user/login',
    method: 'post',
    data
  })
}

utils/auth.js

import Cookies from 'js-cookie'

const TokenKey = 'Admin-Token'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

4)封装请求拦截和响应拦截以及报错的处理并在api/user.js中引入
请求地址在config/的dev.env.js文件,记得要重启
dev.env.js

'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  DEV_API_URL: '"/dev-api"',//开发域名
})

utils/request.js

import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// 创建axios实例
const service = axios.create({
  baseURL: process.env.DEV_API_URL,
  timeout: 5000,
})
axios.defaults.headers.post["Content-Type"] =
    "application/x-www-form-urlencoded;charset=UTF-8"; //请求头
// 请求拦截
service.interceptors.request.use(
  config => {
    // 请求头添加token
    if (store.getters.token) {
      config.headers['X-Token'] = getToken()
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// 响应拦截
service.interceptors.response.use(
  response => {
    const res = response.data
    if (res.code !== 200) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })

      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        MessageBox.confirm('您已注销,您可以取消以留在此页上,或再次登录,确认注销', {
          confirmButtonText: 'Re-Login',
          cancelButtonText: 'Cancel',
          type: 'warning'
        }).then(() => {
          store.dispatch('user/resetToken').then(() => {
            location.reload()
          })
        })
      }
      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return Promise.resolve(res, '状态等于2000');
    }
  },
  error => {
    if (error.code) {
      switch (error.response.status) {
        // 404请求不存在
        case 404:
            Message({
              message: '请求不纯在',
              type: 'error',
              duration: 5 * 1000
            })
            break;

        case 500:
            Message({
              message: '服务器内部错误',
              type: 'error',
              duration: 5 * 1000
            })
            break;

            // 其他错误,直接抛出错误提示
        default:
            Message({
              message: error.response.data.msg,
              type: 'error',
              duration: 5 * 1000
            })
    }
    return Promise.reject(error.response);
    }
  }
)

export default service

5):在store/getters.js中获取token,写到这里的原因大家看看vue对计算属性的说明就明白了
getters.js

const getters = {
  token: state => state.user.token
}
export default getters

6):在store/index.js索文件中获取modules文件夹中的所有模块。引入getters.js把它们挂载到vue实例上
store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
Vue.use(Vuex)

// 返回module文件夹下以.js结尾的文件
// https://webpack.js.org/guides/dependency-management/#requirecontext
//(要搜索的目录, 是否也应搜索子目录, 匹配文件的正则表达式)执行后返回的是一个函数,并且这个函数有3个属性
// resolve: 返回已解析请求的模块ID---------function
// keys:返回匹配正则成功模块的名字组成的数组------function
// ID: 上下文模块的模块ID。这可能对有用module.hot.accept----string
const modulesFiles = require.context('./modules', true, /\.js$/)

// 拿到modules文件夹中所有js的信息
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  // set './app.js' => 'app'
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')//获取文件名
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {})

const store = new Vuex.Store({
  modules,
  getters
})

export default store

最后结果------------------

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里说简单说一下vuex中state和mutation
1: state以及mutation和this. s t o r e . c o m m i t ( 方 法 名 , 值 ) 存 钱 ( 存 数 据 ) 把 s t a t e 比 作 银 行 的 金 库 这 个 金 库 他 是 开 放 性 的 你 可 以 通 过 银 行 定 义 的 规 则 在 这 个 金 库 创 建 属 于 你 自 己 的 空 间 和 状 态 , 当 某 一 天 你 家 拆 迁 了 , 给 了 你 100 亿 , 你 很 高 兴 但 是 钱 太 多 放 不 下 , 你 要 把 钱 存 银 行 , 银 行 不 会 直 接 把 金 库 的 存 放 权 限 给 你 ( 你 不 能 直 接 改 变 s t a t e 中 的 状 态 。 改 变 s t a t e 中 的 状 态 的 唯 一 途 径 就 是 显 式 地 提 交 ( c o m m i t ) m u t a t i o n 。 ) , 所 以 你 只 能 到 银 行 通 过 银 行 的 存 钱 通 道 ( c o m m i t ) 找 到 那 个 可 以 给 你 权 限 的 人 ( m u t a t i o n ) 找 到 他 后 你 要 把 你 存 钱 的 地 址 告 诉 他 , 他 会 帮 你 开 一 个 单 独 的 房 间 , 这 个 房 间 的 名 称 就 是 你 给 他 的 地 址 ( 方 法 名 ) 这 个 地 址 ( 方 法 名 ) 可 以 随 便 填 , 但 是 他 必 须 和 你 放 钱 的 地 址 ( 提 交 时 用 到 的 方 法 名 ) 一 样 这 样 他 就 可 以 建 立 连 接 , 并 抬 手 在 房 间 里 放 一 个 传 送 门 , 这 样 在 这 个 房 间 里 面 你 就 可 以 通 过 特 有 的 方 式 进 行 存 钱 取 钱 当 然 你 存 的 每 一 笔 钱 都 要 给 他 一 个 唯 一 标 识 名 这 样 就 不 会 和 别 人 的 弄 混 , 并 进 行 一 些 其 他 操 作 。 2 : t h i s . store.commit(方法名,值) 存钱(存数据) 把state比作银行的金库这个金库他是开放性的你可以通过银行定义的规则在这个金库创建属于你自己的空间和状态,当某一天你家拆迁了,给了你100亿,你很高兴但是钱太多放不下,你要把钱存银行,银行不会直接把金库的存放权限给你(你不能直接改变 state中的状态。改变 state中的状态的唯一途径就是显式地提交 (commit) mutation。),所以你只能到银行通过银行的存钱通道(commit)找到那个可以给你权限的人(mutation)找到他后你要把你存钱的地址告诉他,他会帮你开一个单独的房间,这个房间的名称就是你给他的地址(方法名)这个地址(方法名)可以随便填,但是他必须和你放钱的地址(提交时用到的方法名)一样这样他就可以建立连接,并抬手在房间里放一个传送门,这样在这个房间里面你就可以通过特有的方式进行存钱取钱当然你存的每一笔钱都要给他一个唯一标识名这样就不会和别人的弄混,并进行一些其他操作。 2: this. store.commit(方法名,值)存钱(存数据)把state比作银行的金库这个金库他是开放性的你可以通过银行定义的规则在这个金库创建属于你自己的空间和状态,当某一天你家拆迁了,给了你100亿,你很高兴但是钱太多放不下,你要把钱存银行,银行不会直接把金库的存放权限给你(你不能直接改变state中的状态。改变state中的状态的唯一途径就是显式地提交(commit)mutation。),所以你只能到银行通过银行的存钱通道(commit)找到那个可以给你权限的人(mutation)找到他后你要把你存钱的地址告诉他,他会帮你开一个单独的房间,这个房间的名称就是你给他的地址(方法名)这个地址(方法名)可以随便填,但是他必须和你放钱的地址(提交时用到的方法名)一样这样他就可以建立连接,并抬手在房间里放一个传送门,这样在这个房间里面你就可以通过特有的方式进行存钱取钱当然你存的每一笔钱都要给他一个唯一标识名这样就不会和别人的弄混,并进行一些其他操作。2:this.store.state.标识名
取钱(取数据)
钱存起来取就没有那么麻烦了,你身上只要是这个银行的权限卡,你对这个卡大喊一声this.$store.state.标识名钱就会出现在你口袋里
另外3个属性getters,actions,modules
getters类似于计算属性computed作用就是可以缓存数据
例:拿computed举例

data() {
	return {
		a: 1,
		b: 2,
		c: 0
	}
},
computed: {
	add() {
		console.log('执行了computed中的add')
		return this.c = this.a + this.b
	}
},
methods: {
	subtraction() {
		console.log('执行了methods中的subtraction')
		this.c = this.b - this.a
	},
	btn() {
		this.subtraction()
		this.add
		//当你执行了3次这个事件后打印结果如下
		//第一遍
		//执行了computed中的add
		//执行了methods中的subtraction
		//******************************
		//第二遍
		//执行了methods中的subtraction
		//******************************
		//第三遍
		//执行了methods中的subtraction
		//前提是a或者b的值不变,如果其中有一个值变了就会从新缓存也就是从新执行add
	}
}

actions因为他不能直接操作state中的数据,所以他需要提交一个mutation来操作state中的数据,他和mutation的区别就是actions是异步的而mutation是同步的为什么是同步呢,官方说明:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的,重点其实就是不可追踪性,我理解的就是,比如你给你喜欢的女孩子发了一条消息,同时看了一下她的朋友圈,过了一会他回复了你朋友的评论但没回你消息,又过了一会他又回复了朋友的评论,还是没回你消息,第二天她回你消息了所以这件事告诉我们发消息不如去朋友圈评论,这就是不可追踪性,也就是说最后的结果你肯定会得到的,但是这个结果会和你预期的不一样,所以说mutation只支持同步
modules模块化:让每一个模块都有自己的state,getters,mutation,actions

目前就写到这里,一部分都是自己的理解如有不对请指正谢谢。



这篇关于2021-06-10的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程