2.IHRM人力资源 - 登录

一、登录页结构与表单开发

我们要实现的登录界面

image-20240109112142005

目前的登录界面

image-20240109112337473

1.1 登录页结构

复制下面的代码到views/login/index.vue页面下

image-20240109113326105

<template>
  <div class="login-container">
    <div class="logo"/>
    <div class="form">
      <h1>登录</h1>
      <el-card shadow="never" class="login-card">
        <!--登录表单-->
      </el-card>
    </div>
  </div>
</template>
<script>
export default {
  name: 'Login'
}
</script>
<style lang="scss">
.login-container {
  display: flex;
  align-items: stretch;
  height: 100vh;

  .logo {
    flex: 3;
    background: rgba(38, 72, 176) url(../../assets/common/login_back.png) no-repeat center / cover;
    border-top-right-radius: 60px;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    padding: 0 100px;

    .icon {
      background: url(../../assets/common/logo.png) no-repeat 70px center /
        contain;
      width: 300px;
      height: 50px;
      margin-bottom: 50px;
    }

    p {
      color: #fff;
      font-size: 18px;
      margin-top: 20px;
      width: 300px;
      text-align: center;
    }
  }

  .form {
    flex: 2;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-left: 176px;

    .el-card {
      border: none;
      padding: 0;
    }

    h1 {
      padding-left: 20px;
      font-size: 24px;
    }

    .el-input {
      width: 350px;
      height: 44px;

      .el-input__inner {
        background: #f4f5fb;
      }
    }

    .el-checkbox {
      color: #606266;
    }
  }
}
</style>

保存并看一下页面结构,并没有登录表单,然后下面写一下登录表单

image-20240109113238985

1.2 登录表单结构

<template>
  <div class="login-container">
    <div class="logo"/>
    <div class="form">
      <h1>登录</h1>
      <el-card shadow="never" class="login-card">
        <!--登录表单-->
        <!--el-form element-ui的表单-->
        <!--el-form-item 表单中的一项-->
        <!--el-input 输入框-->
        <el-form>
          <el-form-item>
            <el-input placeholder="请输入手机号"></el-input>
          </el-form-item>

          <el-form-item>
            <el-input placeholder="请输入密码"></el-input>
          </el-form-item>
          <!--勾选框-->
          <el-form-item>
            <el-checkbox>用户平台使用协议</el-checkbox>
          </el-form-item>
          <!--登录按钮
          设置一下宽度 style="width: 350px"
          -->
          <el-form-item>
            <el-button type="primary" style="width: 350px">登录</el-button>
          </el-form-item>
        </el-form>
      </el-card>
    </div>
  </div>
</template>
<script>
export default {
  name: 'Login'
}
</script>
<style lang="scss">
.login-container {
  display: flex;
  align-items: stretch;
  height: 100vh;

  .logo {
    flex: 3;
    background: rgba(38, 72, 176) url(../../assets/common/login_back.png) no-repeat center / cover;
    border-top-right-radius: 60px;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    padding: 0 100px;

    .icon {
      background: url(../../assets/common/logo.png) no-repeat 70px center /
        contain;
      width: 300px;
      height: 50px;
      margin-bottom: 50px;
    }

    p {
      color: #fff;
      font-size: 18px;
      margin-top: 20px;
      width: 300px;
      text-align: center;
    }
  }

  .form {
    flex: 2;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-left: 176px;

    .el-card {
      border: none;
      padding: 0;
    }

    h1 {
      padding-left: 20px;
      font-size: 24px;
    }

    .el-input {
      width: 350px;
      height: 44px;

      .el-input__inner {
        background: #f4f5fb;
      }
    }

    .el-checkbox {
      color: #606266;
    }
  }
}
</style>

二、登录

我们使用了很多Element-ui的组件

官网:Element-ui组件官网

接下来我们的登录流程将会按照下图进行设计

2.1 登录表单校验

比如校验手机号的格式是否正确,密码是否符合位数

13.Vue - ref属性、props配置、mixin混入、插件、scoped样式

在element-ui中其实就是如下所示的组件,进行表单验证

我们可以通过el-form标签的ref属性获取form表单的整体实例,进而进行整体的校验

el-form标签中的:rules="rulesFrom"属性其实就是我们的校验规则,并且校验规则都会写在rulesFrom里

el-form标签中的:model="ruleForm"属性其实就是我们所绑定的数据从ruleForm数据结构中来

el-form-item标签汇总的prop="test1"属性,并且test1字段是ruleForm数据结构中的一个属性

el-input标签做一个v-model双向绑定,这样的话就能做回显了

image-20240112112319251

<template>
  <div class="login-container">
    <div class="logo"/>
    <div class="form">
      <h1>登录</h1>
      <el-card shadow="never" class="login-card">
        <!--登录表单-->
        <!--el-form element-ui的表单-->
        <!--el-form-item 表单中的一项-->
        <!--el-input 输入框-->
        <el-form ref="loginFormRef" :model="loginFromModel" :rules="loginFromRules">
          <el-form-item prop="mobile">
            <el-input placeholder="请输入手机号" v-model="loginFromModel.mobile"></el-input>
          </el-form-item>

          <el-form-item prop="password">
            <!-- show-password是el-input属性,表示不展示input框中信息-->
            <el-input show-password placeholder="请输入密码" v-model="loginFromModel.password"></el-input>
          </el-form-item>
          <!--勾选框-->
          <el-form-item prop="isAgree">
            <el-checkbox v-model="loginFromModel.isAgree">用户平台使用协议</el-checkbox>
          </el-form-item>
          <!--登录按钮
          设置一下宽度 style="width: 350px"
          -->
          <el-form-item prop="isAgree">
            <el-button @click="login" type="primary" style="width: 350px">登录</el-button>
          </el-form-item>
        </el-form>
      </el-card>
    </div>
  </div>
</template>
export default {
  name: 'Login',
  data() {
    return {
      // 登录表单数据结构
      loginFromModel: {
        mobile: '',
        password: '',
        isAgree: false
      },
      // 登录表单规则,数据字段和loginFromModel一样,只不过值是数组的模式
      // 数组中的每一个对象值就是一个校验规则
      loginFromRules: {
        // 手机号校验规则
        mobile: [
          /**
           *  required: true 必填
           *  trigger触发模式
           *    change表示只要我们不断的写就会触发校验
           *    blur 失去焦点才会触发校验
           */
          { required: true, message: '请输入手机号信息', trigger: 'blur' },
          // 正则表达式检验规则
          { pattern: /^1[3-9]\d{9}$/, message: '手机号输入格式不合规范', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          // 密码长度校验规则
          { min: 6, max: 16, message: '密码长度在 6 到 16 个字符', trigger: 'blur' }
        ],
        /**
         * 说明:required命令检测不了false,只能检测null/undefine/空字符串
         *  所以我们要自定义校验方式,其实是自定义校验方式是一个函数
         *    需要三个参数rule,value,callback
         *    rule:校验规则
         *    value:要校验的值
         *    callback: 一个必须要执行的函数(无论校验成功还是失败都要执行)
         *              如果校验成功的话直接执行callback()函数,如果失败的话执行callback(new Error(错误信息))
         */
        isAgree: [
          {
            validator: (rule, value, callback) => {
              value ? callback() : callback(new Error('您必须勾选用户平台使用协议'))
            }
          }
        ]
      }
    }
  },
  methods: {
    login() {
      /**
       * 通过表单的ref属性获取表单实例对象,并且执行此表单实例对象的validate方法校验一下
       * validate方法可以传入一个回调函数,并且会传入一个参数,我们可以命名为isOK
       */
      this.$refs.loginFormRef.validate((isOK) => {
        if (!isOK) {
          // 此时isOK为false,表示校验没有通过
          alert('校验没有通过,请检查您的登录信息')
          return
        }
        // 此时isOK为true,表示校验通过了
      })
    }
  }
}
</script>

效果展示

image-20240113133756705

2.2 Vuex 用户模块

我们在登录表单校验之后并不是直接调用请求到后台验证用户名和密码是否正确

而是前端又调用了VuexAction,之后会再去调用登录接口并返回一个token,我们要把此token进行缓存并使用vuex进行共享,共享之后相当于我们的登录成功了,然后跳转到主页

image-20240113134425480

使用了vuex表示一定有数据要去做共享,在这里我们要共享用户的token,之后在每个页面我们都能拿到用户的token了

Vuex基础知识

vuex中的用户模块如下图所示:

image-20240113134628710

// State理解为数据源,像极了我们之前学的data,用来存放数据
const state = {
  token: null
}

// Mutations类似java中的数据层,只对数据进行操作,不对业务操作(比如数据加减乘除)
const mutations = {
  // 修改token
  setToken(state, token) {
    state.token = token
    // console.log(state.token)
  }
}

/**
 * actions似java中的业务逻辑层,对逻辑操作,然后向mutations发送数据,在这个业务逻辑中也可以互相调用
 * actions可以做异步操作
 */
const actions = {
  /**
   * 参数1: context上下文对象,有commit和dispatch方法
   * 参数2:我们要传入的参数
   */
  login(context, data) {
    console.log(context)
    console.log(data)
    // TODO 调用登录接口(代办)
    // 假设登录成功之后就会返回一个token,我们要将此token实现共享就要存储在state中
    // vuex中想修改state中数据必须通过mutations,不能直接修改state中属性
    context.commit('setToken', '123456')
  }
}

// 对三个变量进行一个默认的导出
export default {
  // 开启命名空间。表示之后调用state/mutations/actions时必须带上模块名称
  namespaced: true,
  state,
  mutations,
  actions
}

但是现在有一个问题,当我们刷新页面之后,state.token的值会消失,所以我们要实现vuex持久化

2.3 vuex 数据持久化

这样就算我们给state.token赋值之后,但刷新页面后仍然是null,所以此时永远不能取到我们缓存的token值

// State理解为数据源,像极了我们之前学的data,用来存放数据
const state = {
  token: null
}

所以我们要实现vuex的数据持久化,我们要从缓存中读取state.token的初始值

编写一个工具类

image-20240113145501980

// 我们使用了一个cookie的包,其实和localStorage都可以实现前端缓存数据
import Cookies from 'js-cookie'

const TokenKey = 'vue_admin_template_token'

// 按需导出了三个函数    
    
// 获取token
export function getToken() {
  return Cookies.get(TokenKey)
}

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

// 移除token
export function removeToken() {
  return Cookies.remove(TokenKey)
}

在vuex用户模块user.js中进行引用

// @符号表示根路径
import { getToken, setToken, removeToken } from '@/utils/auth'

const state = {
  // 这样就算我们给state.token赋值之后,但刷新页面后仍然是null,所以此时永远不能取到我们缓存的token值
  // token: null
  // 从缓存中读取初始值
  token: getToken()
}

const mutations = {
  // 修改token
  setToken(state, token) {
    state.token = token
    // console.log(state.token)
    // 将token值同步到缓存
    setToken(token)
  },
  removeToken() {
    // 删除vuex的token
    state.token = null
    // 删除缓存中的token
    removeToken()
  }
}

我们要在登录组件中进行调用vuex

methods: {
  login() {
    /**
     * 通过表单的ref属性获取表单实例对象,并且执行此表单实例对象的validate方法校验一下
     * validate方法可以传入一个回调函数,并且会传入一个参数,我们可以命名为isOK
     */
    this.$refs.loginFormRef.validate((isOK) => {
      if (!isOK) {
        // 此时isOK为false,表示校验没有通过
        alert('校验没有通过,请检查您的登录信息')
        return
      }
      /**
       * 此时isOK为true,表示校验通过了,调用vuex中的用户模块
       * 此时因为我们开启了命名空间,传参时要带上模块名,即模块名/actions中方法名
       * dispatch方法是调用vuex中的actions
       */
      this.$store.dispatch('user/login', this.loginFromModel)
    })
  }
}

效果如下图所示

image-20240113150404419

2.4 登录联调

image-20240113200709958

2.4.1 请求模块封装

编写登录接口

image-20240113200838635

// 引入封装的axios工具
import request from '@/utils/request'

// 按需暴露一个登录方法
export function login(Data) {
  // request发送登录请求,会得到一个promise结果并将其返回
  return request({
    // 请求地址
    url: '/sys/login',
    // 请求方式
    method: 'POST',
    // 请求参数
    data: Data
    // 在ES6中上面data: Data可以简写为 data
  })
}

2.4.2 登录表单 调用vuex

image-20240113201636001

methods: {
  login() {
    /**
     * 通过表单的ref属性获取表单实例对象,并且执行此表单实例对象的validate方法校验一下
     * validate方法可以传入一个回调函数,并且会传入一个参数,我们可以命名为isOK
     */
    this.$refs.loginFormRef.validate((isOK) => {
      if (!isOK) {
        // 此时isOK为false,表示校验没有通过
        alert('校验没有通过,请检查您的登录信息')
        return
      }
      /**
       * 此时isOK为true,表示校验通过了,调用vuex中的用户模块
       * 此时因为我们开启了命名空间,传参时要带上模块名,即模块名/actions中方法名
       * dispatch方法是调用vuex中的actions
       */
      this.$store.dispatch('user/login', this.loginFromModel)
    })
  }
}

2.4.3 vux模块 调用登录接口

image-20240113201858115

2.4.4 登录测试

超级管理员电话号:138 0000 0002

超级管理员密码:hm#qd@23!

登录密码:网络安全需要,密码以由原来的123456 变更为hm#qd@23! , 管理员不可修改密码,新建用户的密码仍为123456

image-20240113202917035

点击登录接口

image-20240113205216951

请求信息

image-20240113205231956

响应信息

image-20240113205552222

成功存储了浏览器缓存:cookie

image-20240113205300039

image-20240113205459505

刷新之后进入了主页面

image-20240113205515377

补充:为了方便,登录表单的数据我们可以做出如下所示的修改

data() {
  return {
    // 登录表单数据结构
    loginFromModel: {
      mobile: process.env.NODE_ENV === 'development' ? '13800000002' : '',
      password: process.env.NODE_ENV === 'development' ? 'hm#qd@23!' : '',
      isAgree: isAgree: process.env.NODE_ENV === 'development'
    },
 }   

2.5 跳转主页面

当用户登录成功后直接跳转到主页面,而不是等到用户手动刷新之后才跳转到主页面

此功能是登录的最后一个功能

修改登录页面的代码

image-20240113211212413

methods: {
  login() {
    /**
     * 通过表单的ref属性获取表单实例对象,并且执行此表单实例对象的validate方法校验一下
     * validate方法可以传入一个回调函数,并且会传入一个参数,我们可以命名为isOK
     */
    this.$refs.loginFormRef.validate(async(isOK) => {
      if (!isOK) {
        // 此时isOK为false,表示校验没有通过
        alert('校验没有通过,请检查您的登录信息')
        return
      }
      /**
       * 此时isOK为true,表示校验通过了,调用vuex中的用户模块
       * 此时因为我们开启了命名空间,传参时要带上模块名,即模块名/actions中方法名
       * dispatch方法是调用vuex中的actions
       * Vuex中的action返回的是一个promise,说明是一个异步,我们必须等到promise执行成功才能继续跳转到主页
       * 只要是用了await表示下方的代码一定是promise执行成功了
       * await 一定要写,因为我们要等待登录成功,如果失败的话就走axios中的reject
       */
      await this.$store.dispatch('user/login', this.loginFromModel)
      // 登录成功后跳转到主页面
      //  this.$router获取到路由的实例对象,push表示我们要跳转到哪里,'/'表示我们的主页
      this.$router.push('/')
    })
  }
}

我们可以看一下路由的配置

image-20240113212644831

image-20240113212723553

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

Vue.use(Router)

/**
 *  Layout @/在vue中代表路径别名
 *  @ 符号表示当前目录的src
 *  @/ 表示src下的layout,而layout又是一个目录,所以会拉取index.vue文件
 *  即index.vue组件就是我们的路由组件,会实现二级路由
* */
import Layout from '@/layout'

export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },

  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },

  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: 'dashboard',
      name: 'Dashboard',
      component: () => import('@/views/dashboard/index'),
      meta: { title: 'Dashboard', icon: 'dashboard' }
    }]
  },

  {
    path: '/example',
    component: Layout,
    redirect: '/example/table',
    name: 'Example',
    meta: { title: 'Example', icon: 'el-icon-s-help' },
    children: [
      {
        path: 'table',
        name: 'Table',
        component: () => import('@/views/table/index'),
        meta: { title: 'Table', icon: 'table' }
      },
      {
        path: 'tree',
        name: 'Tree',
        component: () => import('@/views/tree/index'),
        meta: { title: 'Tree', icon: 'tree' }
      }
    ]
  },

  {
    path: '/form',
    component: Layout,
    children: [
      {
        path: 'index',
        name: 'Form',
        component: () => import('@/views/form/index'),
        meta: { title: 'Form', icon: 'form' }
      }
    ]
  },

  {
    path: '/nested',
    component: Layout,
    redirect: '/nested/menu1',
    name: 'Nested',
    meta: {
      title: 'Nested',
      icon: 'nested'
    },
    children: [
      {
        path: 'menu1',
        component: () => import('@/views/nested/menu1/index'), // Parent router-view
        name: 'Menu1',
        meta: { title: 'Menu1' },
        children: [
          {
            path: 'menu1-1',
            component: () => import('@/views/nested/menu1/menu1-1'),
            name: 'Menu1-1',
            meta: { title: 'Menu1-1' }
          },
          {
            path: 'menu1-2',
            component: () => import('@/views/nested/menu1/menu1-2'),
            name: 'Menu1-2',
            meta: { title: 'Menu1-2' },
            children: [
              {
                path: 'menu1-2-1',
                component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
                name: 'Menu1-2-1',
                meta: { title: 'Menu1-2-1' }
              },
              {
                path: 'menu1-2-2',
                component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
                name: 'Menu1-2-2',
                meta: { title: 'Menu1-2-2' }
              }
            ]
          },
          {
            path: 'menu1-3',
            component: () => import('@/views/nested/menu1/menu1-3'),
            name: 'Menu1-3',
            meta: { title: 'Menu1-3' }
          }
        ]
      },
      {
        path: 'menu2',
        component: () => import('@/views/nested/menu2/index'),
        name: 'Menu2',
        meta: { title: 'menu2' }
      }
    ]
  },

  {
    path: 'external-link',
    component: Layout,
    children: [
      {
        path: 'https://panjiachen.github.io/vue-element-admin-site/#/',
        meta: { title: 'External Link', icon: 'link' }
      }
    ]
  },

  // 404 page must be placed at the end !!!
  { path: '*', redirect: '/404', hidden: true }
]

const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

const router = createRouter()

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router

跳转后如下如图所示

image-20240113213017032

三、vue-cli 代理解决跨域

下面仅仅是解决开发环境中的跨域问题,生产环境的跨域问题后面会详细解析

image-20240113151249028

如果跨域问题不解决的话,我们的请求是发送不过去的

Vue - 前端代理服务器解决跨域问题

image-20240113152849778

image-20240113152925711

我们前端页面的地址是http://localhost:9528,后端项目的地址是https://heimahr.itheima.net/,一眼可以看出前后端地址不同源

浏览器同源策略:协议、主机、端口三者一致。目前来说都不一致,如果后端不开启cors的话,前端是请求不到后端去的

解决方案:配置vue-cli代理服务器

如果是node向后端接口服务发送请求的话不会发生跨域问题,因为同源策略是浏览器的策略,并不是node的策略

相当于前端向后端请求的中间新加了一个vue-cli(node)

image-20240113153125683

修改vue.config.js文件,并且修改后记得重启项目

module.exports = {
    .....
  // 此属性中存放项目的一些配置,可以在这个位置写代理的一些属性
  devServer: {
    port: port,
    open: true,
    overlay: {
      warnings: false,
      errors: true
    },
    // before: require('./mock/mock-server.js'), 基础模板做的模拟数据
    // 代理属性
    proxy: {
      // path:目标服务器,想要代理到哪里
      '/api': {
        // 要代理到的目标服务器
        target: 'https://heimahr.itheima.net/'
      }
    }
  },     
    ....    
    
}

四、axios 封装

image-20240113194132188

image-20240113154412346

基地址:axios中有一个baseurl的属性,表示我痛序偶axios设置了一个基础的地址,剩下的所有请求都不要再拼接这个地址了

超时时间:请求超时时间,比如设置为10s,但是我们前端发起请求而服务器10s内没有做出响应,则表示请求失败

axios中还有两个比较重要的拦截器:请求拦截器与响应拦截器

一般来说我们的请求拦截器是往请求中注入一个token,后面的请求就不用一个一个的往请求数据中添加token数据了

响应拦截器一般是负责解析一些数据结构及处理异常

拦截器的基础知识

axios——难点语法使用、拦截器的使用、取消请求功能演示

image-20240113154949071

4.1 配置axios基础信息

配置一下axios的基地址和超时时间

image-20240113170125447

我们补充一下其他文件的内容

vuex中的index.js文件

image-20240113171642993

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import app from './modules/app'
import settings from './modules/settings'
import user from './modules/user'

Vue.use(Vuex)

const store = new Vuex.Store({
  // 模块
  modules: {
    app,
    settings,
    user
  },
  // Vuex中的计算属性
  getters
})

export default store

vuex中的getters.js文件

image-20240113171719736

const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token,
  avatar: state => state.user.avatar,
  name: state => state.user.name
}
export default getters

4.2 请求拦截器

如下所示的代码我们配置了基地址、超时时间、请求拦截器

// 引入axios
import axios from 'axios'
// 引入vuex可以获取vuex实例对象,
// 我们在vuex实例对象找那个又添加了一个getters.js,其实是为了获取token数据
import store from '@/store/index.js'

// 创建一个axios实例
// 根据指定配置创建一个新的axios,也就是每个新的axios都有自己的配置
var service = axios.create({
  // 基地址。因为后端的所有地址都有一个共同路径/api
  baseURL: '/api',
  // 超时时间,单位为毫秒,10000毫秒为10秒
  timeout: 10000
})

// 创建请求拦截器,并且两个参数都是回调函数
// 请求成功的时候执行第一个成功回调函数
// 请求失败的时候执行第二个失败回调函数,error就是错误对象
service.interceptors.request.use(
  // 成功时执行
  (config) => {
    // 将vuex中的token值放入到请求头里面
    if (store.getters.token) {
      // 存在token,才放入请求头中
      config.headers.Authorization = `Bearer ${store.getters.token}`
    }
    return config
  },
  // 失败时执行
  (error) => {
    // 默认支持promise的,下面语句相当于终止了当前promise的执行
    return Promise.reject(error)
  })

// 默认导出axios实例对象
export default service

4.3 响应拦截器

仍然是修改如下所示文件

image-20240113193909930

image-20240113192714709

// 创建响应拦截器,并且两个参数都是回调函数
service.interceptors.response.use(
  // 请求成功时响应,此时的响应默认包裹了一层data,即response.data才是后台服务返回的内容
  (response) => {
    // 一次性解析出response.data中的三个属性
    const { data, message, success } = response.data
    if (success) {
      // 此时响应正常
      return data
    } else {
      Message({ type: 'error', message: message })
      return Promise.reject(new Error(message))
    }
  },
  // 请求失败时响应
  (error) => {
    // this.$message.warning 不能这么使用,因为此时的this不是组件实例对象
    Message({ type: 'error', message: error.message })
    // 默认支持promise的,下面语句相当于终止了当前promise的执行
    return Promise.reject(error)
  }
)

4.4 axios 区分不同环境

4.4.1 介绍

开发环境:开发人员开发代码、测试的代码环境。此环境对代码的要求不是很高,可以随意更改,因为还不是面向真正的用户

生产环境:也叫做正式环境,面向真实的用户

image-20240113194614759

下面我们就要实现下图流程中的“区分环境”模块

image-20240113194259285

怎么区分不同的环境

使用环境变量,有如下两个位置

  • .env.development中设置开发环境变量 默认 NODE_ENV 值为 development

  • .env.production中设置生产环境变量默认 NODE_ENV 值为 production

image-20240113200035069

执行不同的命令,NODE_ENV的值是不同的,我们需要node命令来取出NODE_ENV的值

process.env.属性的方式获取

process.env是我们node.js的全局变量,可以通过它来取到环境变量的名称

测试一下

开发环境下:在登录界面写一个created钩子函数

created(){
    alert(process.env.NODE_ENV)
}

image-20240113195347418

生产环境下:

image-20240113195433408

4.4.2 区分不同环境

所以为什么要区分环境

开发环境下我们的数据随意更改,但是生产环境下数据是不能随意更改的,并且开发环境下和生产环境下的请求路径也是有所差别的

比如开发环境下请求/api路径,生产环境下走/prod-api路径

image-20240113195841574

vue-cli给我们做了一个变量规范

Vue代码中NODE_ENV之外,所有的变量必须以VUE_APP_开头

image-20240113200155923

所以接下来我们要在axios中区分不同的环境,将VUE_APP_BASE_API的值赋值给axios的基地址

image-20240113200427220

// 创建一个axios实例
// 根据指定配置创建一个新的axios,也就是每个新的axios都有自己的配置
var service = axios.create({
  // 基地址。因为后端的所有地址都有一个共同路径/api
  // 并且这个地方不能写死,否则生产环境和开发环境都走的一个地址
  baseURL: process.env.VUE_APP_BASE_API,
  // 超时时间,单位为毫秒,10000毫秒为10秒
  timeout: 10000
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/322169.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用 Jamf Pro 和 Okta 工作流程实现自动化苹果设备管理

Jamf的销售工程师Vincent Bonnin与Okta的产品经理Emily Wendell一起介绍了JNUC 2021的操作方法会议。它们涵盖了Okta工作流程&#xff08;Okta Workflow&#xff09;&#xff0c;并在其中集成了Jamf Pro&#xff0c;构建了一些工作流程&#xff0c;并提供了几个用例。 Okta 工作…

MySQL加锁规则

项目编写涉及到数据持久化一般选择使用MySQL。由于时间原因&#xff0c;数据库使用我选择了无脑三板斧&#xff1a;1. 建立了索引加速查询、2. 关闭自动提交事务、3. 在需要确保原子性的数据库操作之间手动创建和提交事务。 这么一看&#xff0c;仿佛即使是实际开发也与你此前…

什么是充放电振子理论?

CHAT回复&#xff1a;充放电振子模型&#xff08;Charging-Reversal Oscillator Model&#xff09;是一种解释ENSO现象的理论模型&#xff0c;这个模型把ENSO现象比喻成一个“热力学振荡系统”。 在这个模型中&#xff0c;ENSO现象由三个组成部分&#xff1a;充电&#xff08;C…

利用CHAT写实验结论

问CHAT&#xff1a;通过观察放置在玻璃表面上的单个水滴&#xff0c;人们可以观察到水滴充当成像系统。探究这样一个透镜的放大倍数和分辨率。 CHAT回复&#xff1a;实验报告标题&#xff1a;利用玻璃表面的单一水滴观察成像系统的放大倍数和分辨率&#xff1a; 一、 实验目的…

你不得不知道的常用 Git 命令

最近在学习的时候发现 git 命令没有自己想象中那么简单&#xff0c;特此做一期 《 常用 Git 命令 》&#xff0c;不仅是给掘友分享&#xff0c;也能巩固自己学到的知识。 在此向大家推荐一个学习 git 指令的小游戏 Learn Git Branching&#xff0c;以通关的方式进行学习&#…

鸿蒙Harmony--状态管理器-@Observed装饰器和@ObjectLink装饰器详解

经历的越多&#xff0c;越喜欢简单的生活&#xff0c;干净的东西&#xff0c;清楚的感觉&#xff0c;有结果的事&#xff0c;和说到做到的人。把圈子变小&#xff0c;把语放缓&#xff0c;把心放宽&#xff0c;用心做好手边的事儿&#xff0c;该有的总会有的! 目录 一&#xff…

【壹基金儿童服务站瑞金站】寿司DIY亲子活动火热进行

1月13日下午&#xff0c;“大灰狼来啦....”在小朋友们一声声欢快律动的游戏里在象湖新城小区拉开了一场“寿司DIY”亲子主题活动序幕&#xff0c;活动由壹基金儿童服务站瑞金站&#xff08;瑞金赋能公益&#xff09;和爱心商家安心妈妈飞鹤奶粉联合主办。 热身游戏结束后&…

链接全域直播产业经济,天府锋巢直播产业基地10层正式起航

100㎡-400㎡多种类型的办公户型可选 精装全包 拎包入驻 【天府锋巢直播基地】 由德商产投与无锋科技联袂打造 坐落于天府新区核心区域科学城板块 包含电商直播、娱乐直播、跨境直播 多种直播业态的全域直播基地 基地【10层】于12月初全面竣工 招&#xff5c;商 &#xff5c;火&…

Jan, 一个开源 ChatGPT 替代品

Jan 是一个开源 ChatGPT 替代品&#xff0c;可以在您的计算机上 100% 离线运行。 Jan 可以在任何硬件上运行。从 PC 到多 GPU 集群&#xff0c;Jan 支持通用架构&#xff1a; Nvidia GPU&#xff08;快速&#xff09;Apple M 系列&#xff08;快速&#xff09;苹果英特尔Linu…

LLM之RAG实战(十五)| RAG的自动源引文验证技术

​ 在过去的一年里&#xff0c;检索增强生成&#xff08;RAG&#xff09;已经成为一种基于LLM的流行架构&#xff0c;旨在解决在基于知识的LLM最常见的挑战之一&#xff0c;可怕的幻觉。 一、RAG如何解决幻觉&#xff1f; RAG Pipeline包括两个关键组件&#xff1a;&…

基于Java SSM框架实现摄影器材租赁系统项目【项目源码+论文说明】

基于java的SSM框架实现摄影器材租赁系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&a…

Linux(Centos7)安装 jenkins(jdk11+jenkins2.375),并配置JDK,Maven,Git,GitLab

安装步骤 1. JDK11安装2. Maven安装3. git安装4. Jenkins2.375安装4.1 设置中文显示4.2 端口,用户权限修改4.3 插件下载4.4 全局工具配置4.4.1 Maven配置4.4.2 JDK配置4.4.3 Git配置 4.5 系统配置4.5.1 Gitee配置 4.6 构建测试 1. JDK11安装 #下载 yum -y install fontconfig …

怎样获取power shell 的全部可用命令?2/5(篇幅有点长,分成5份)

在power shell 窗口中&#xff0c;有一个获取全部可用命令的命令&#xff1a;get-command&#xff0c;获取到的命令有1640多个&#xff0c;够学习了吧&#xff1f;那么&#xff0c;power shell 命令有哪些类别呢&#xff1f; PowerShell命令可以分为以下几类&#xff1a; Cmdl…

高可用架构去中心化重要?

1 背景 在互联网高可用架构设计中&#xff0c;应该避免将所有的控制权都集中到一个中心服务&#xff0c;即便这个中心服务是多副本模式。 对某个中心服务&#xff08;组件&#xff09;的过渡强依赖&#xff0c;那等同于把命脉掌握在依赖方手里&#xff0c;依赖方的任何问题都可…

开启C++之旅(上):探索命名空间与函数特性(缺省参数和函数重载)

之前浅显的讲解了数据结构的部分内容&#xff1a;数据结构专栏 那么今天我们迎来了新的起点&#xff1a;C的探索之旅 文章目录 1.命名空间1.1引入命名冲突1.2命名空间1.2.1命名空间的定义1.2.2命名空间的使用 2.c的输入与输出3.缺省参数3.1概念3.2缺省参数分类 4.函数重载4.1概…

【深基9.例4】求第 k 小的数#洛谷(MLE)

题目描述 输入 n n n&#xff08; 1 ≤ n < 5000000 1 \le n < 5000000 1≤n<5000000 且 n n n 为奇数&#xff09;个数字 a i a_i ai​&#xff08; 1 ≤ a i < 10 9 1 \le a_i < {10}^9 1≤ai​<109&#xff09;&#xff0c;输出这些数字的第 k k k 小…

吉祥物如何解锁虚拟主持人身份,赋能品牌营销?

在互联网突破时空的整体语境下&#xff0c;一个吉祥物可以解锁虚拟主持人身份&#xff0c;结合动作捕捉技术&#xff0c;活跃于品牌线上线下营销活动场景&#xff0c;让吉祥物虚拟主持人凭借其“萌”、的特征&#xff0c;带给用户亲近感&#xff0c;快速拉近品牌与用户的距离&a…

基于Web的航空航天数字博物馆推荐系统

介绍 项目背景&#xff1a; 航空航天数字博物馆推荐系统是一个基于Web开发的应用&#xff0c;旨在为用户提供一个全面的航空航天领域的数字博物馆体验。通过展品展示、分类筛选和个性化推荐等功能&#xff0c;用户可以更好地了解航空航天知识和文化&#xff0c;并丰富参观体验…

关于git删除仓库中原本应该忽略的文件的研究

开门见山&#xff0c;先抛出一个结论&#xff1a; 任何被提交到远程仓库中的数据&#xff0c;都不能被彻底删除&#xff0c;只要提交上去了&#xff0c;就会永远留存。 任何被提交到远程仓库中的数据&#xff0c;都不能被彻底删除&#xff0c;只要提交上去了&#xff0c;就会…

centos7 arm服务器编译安装gcc 8.2

前言 当前电脑的gcc版本为4.8.5&#xff0c;但是在编译其他依赖包的时候&#xff0c;出现各种奇怪的问题&#xff0c;会莫名其妙的中断编译。本地文章讲解如何自编译安装gcc&#xff0c;替换系统自带的gcc。 环境准备 gcc 需要 8.2&#xff1a;下载地址 开始编译 1、解压gcc…