点亮你的 Vue 技术栈(四):深入学习贯彻「组件化开发」思想

705 阅读11分钟

「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战」。

SPA 单页面应用程序

我们熟知的 JavaScript 框架如 reactvueangular 都可用于构建 SPA

单页面应用程序(Single Page Application)简称 SPA,顾名思义,指的是只有一个 Web 页面的应用(只加载单个 HTML 页面),在用户与应用程序交互时动态更新该页面。

相较于传统多页面应用程序【每次请求服务器返回的都是一个完整的 HTML 页面】,SPA 只有第一次会加载页面。

🌤显著优势

  • 减轻服务器的压力
    • 服务器只提供数据,不负责页面的合成与逻辑的处理,吞吐能力会提高几倍
  • 良好的前后端工作分离模式
    • 后端专注于提供 API 接口
    • 前端专注于页面渲染
  • 良好的交互体验
    • SPA 的内容改变不需要重新加载整个页面
    • 获取数据也是通过 Ajax 异步获取
    • 没有页面间的跳转,不会出现 “白屏现象”

💦但也其局限性

  • 不利于 SEO 搜索引擎优化
    • SSR 服务器端渲染
  • 首次渲染速度较慢
    • 路由懒加载
    • 代码压缩
    • CDN 加速
    • 网络传输压缩

🥂推荐博客:SPA 首屏加载速度慢怎么解决?

Vue-Cli(脚手架)

vue-cli 是 Vue.js 开发的标准工具,它极大地简化了程序员基于 webpack 创建工程化的 Vue 项目的过程。

🧬引用自 vue-cli 官网上的一句话:程序员可以专注在编写应用上,而不必花好几天纠结 webpack 配置的问题

官网:cli.vuejs.org/zh/

安装

> npm install -g @vue/cli

使用

> vue create 项目名称
# OR
> vue ui

vue create xxx 创建过程:

创建成功✔

或者也可以使用 UI 界面创建脚手架:

运行

> npm run serve

访问 localhost:8080

目录结构

在工程化的项目中,vue 要做的事情很单纯:通过 main.jsApp.vue 渲染到 index.html 指定区域中。

其中:

  • App.vue 用来编写带渲染的模板结构
  • index.html 中需要预留一个 el 区域
  • main.jsApp.vue 渲染到 index.html 所预留的区域中

下面来看看工程化项目的目录结构

组件化开发

定义

组件化开发指的是:根据封装的思想,把页面上可重用的部分封装为组件,从而方便项目的开发和维护。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树

👑如下图所展示的效果,契合了组件化开发的思想,用户可以通过拖拽组件的方式,快速生成一个页面的布局结构。

感兴趣的可以去体验下拖拽组件的快感:www.ibootstrap.cn/

好处

前端组件化开发的好处主要体现在以下两方面:

  • 提高了前端代码的复用性灵活性
  • 提高了开发效率和后期的可维护性

Vue 中的组件化

vue 是一个完全支持组件化开发的框架,规定组件的后缀名是 .vue。之前接触到的 App.vue 本质就是一个 vue 组件。

.vue 组件构成

每个 .vue 都由 3 部分构成:

  • <template>:模板结构,是 vue 提供的容器标签,其内 DOM 结构仅支持单个根节点(Vue 3.x 支持多个根节点)
  • <script>:封装组件的 JavaScript 业务逻辑
  • <style>:组件的 UI 样式

其中,每个组件必须包含 template 模板结构,script 行为style 样式 是可选部分。

基本的 .vue 组件案例

<template>
  <div id="app">
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
    }
  }
}
</script>

<style lang="less" scoped>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
  text-align: center;
}
</style>

注册组件

🌅VScode 配置 @

路径提示符@ 配置(@ 导向 /src 目录),可以提示文件/文件夹路径防止写错。

🎯先安装 Path Autocomplete 插件:

🎯修改 setting.json 配置文件,添加如下代码:

// 导入文件时是否携带文件的拓展名
"path-autocomplete.extensionOnImport": true,
// 配置 @ 的路径提示符
"path-autocomplete.pathMappings": {
    "@": "${folder}/src"
},

🌈@ 路径提示效果演示:

前提:VScode 打开的文件夹是单个项目的根目录,而不能是多个项目的根目录,否则 @ 失效。

全局组件

🛫以下使用 2 种方式注册并使用全局组件

方式一: Vue.component()

main.js

import Vue from 'vue'
import App from './App.vue'
import Header from './components/Header.vue'

Vue.config.productionTip = false
// 注册全局组件
Vue.component('Header', Header)
// 如果 Header 的 name 属性存在,可作为注册后的组件名称
// Vue.component(Header.name, Header)

new Vue({
  render: h => h(App),
}).$mount('#app')

App.vue

<template>
  <div id="app">
    <!-- 使用全局组件 -->
    <Header></Header>
  </div>
</template>

<script>
export default {
  name: 'App',
}
</script>

<style lang="less" scoped>
</style>

渲染效果:

方式二: Vue.use()

方式二参考并引用:在 Vue 项目中注册全局组件

Vue 官方提供的插件有 Vue Router、Vuex 和 Vue 服务端渲染三个 Vue.use 可以接受一个对象,Vue.use(obj) 对象 obj 中需要一个 install 函数在 Vue.use(obj) 时,会自动调用该 install 函数,并传入到 Vue 构造器。

👑首先在 @/components 文件夹下新建一个 index.js

// 引入待全局注册的组件
import EChart from "@/components/Echart.vue";
import Header from '@/components/Header.vue'
// npm install echarts --save
import * as echarts from 'echarts'

export default {
  install: function(Vue) {
    console.log('自定义插件');
	// 注册全局组件!
    Vue.component('EChart', EChart)
    Vue.component('Header', Header)
    // 可以往Vue的原型对象上添加自定义属性或者方法, 在其他.vue文件中,可以通过this访问自定义的属性和方法
    Vue.prototype.$echarts = echarts
  }
}

🛫main.js

import Vue from 'vue'
import App from './App.vue'
import Plugin from '@/components'

Vue.config.productionTip = false
// Vue.prototype.$echarts = echarts
// Vue.component('Header', Header)
// Vue.component('EChart', EChart)

// 使用自定义插件
Vue.use(Plugin)

new Vue({
  render: h => h(App),
}).$mount('#app')

App.vue

<template>
  <div id="app">
    <!-- 务必设置宽高! -->
    <div id="echart" ref="echart"></div>
    <!-- 使用全局注册组件 -->
    <EChart></EChart>
    <Header></Header>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    initEchart() {
      var myChart = this.$echarts.init(this.$refs.echart)
      // 绘制图表
      myChart.setOption({
        title: {
          text: 'ECharts 入门示例'
        },
        tooltip: {},
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      });
    }
  },
  mounted() {
    this.initEchart()
  },
}
</script>

<style lang="less" scoped>
#echart {
  width: 450px;
  height: 300px;
}
</style>

效果演示:

局部组件

局部组件是在某一个具体的 vue 实例中定义的,只能在该 vue 实例中使用。

引入 => 注册 => 使用

<template>
  <div id="app">
    <!-- 3.使用组件 -->
    <Header></Header>
  </div>
</template>

<script>
// 1.引入组件
import Header from '@/components/Header.vue'

export default {
  name: 'App',
  // 2.注册局部组件
  components: {
    Header,
  },
}
</script>

访问组件数据

父组件访问子组件数据

🏍使用 ref 给子组件定义一个别名,然后使用 vm.$refs.子组件名.xxx 获取子组件中的数据 data 或方法 methods

<template>
  <div>
    <div style="background-color: pink;">
      <h2>父组件</h2>
      <Child ref="children"></Child>
      <button @click="getMsg()">获取</button>
      获取到的子组件数据: {{ message }}
    </div>
  </div>
</template>

<script>
import Child from './Child.vue'

export default {
  name: 'parent',
  components: {
    Child,
  },
  data() {
    return {
      message: '',
    }
  },
  methods: {
    getMsg() {
      // 访问子组件的数据
      this.message = this.$refs.children.msg
    },
  },
}
</script>

演示效果:

子组件访问父组件数据

🚎使用 vm.$parent 访问父组件。

// 场景类似父组件访问子组件数据, 只不过此处无需 ref 引用
console.log(this.$parent.message)

组件间的数据共享

不同于组件之间访问数据,数据共享指的是在组件间传递值。

组件树

组件之间的关系分为以下 3 种:

  • 父子关系
  • 兄弟关系
  • 后代关系

父子组件

父子组件之间的数据共享又分为父->子子->父

🚀父子组件间的数据流动图

父组件向子组件共享数据

父组件通过 v-bind 属性绑定向子组件共享数据,同时,子组件需要使用 props 接收数据

props 是组件的自定义属性,可以通过 props 把数据传递到子组件内部,供子组件内部进行使用。

父组件 Parent.vue

<template>
  <div style="width: 350px; background-color: lightblue;">
    <h2>Parent 组件</h2>
    <Child :msg="message"></Child>
  </div>
</template>

<script>
import Child from './Child.vue'

export default {
  name: 'Parent',
  components: {
    Child,
  },
  data() {
    return {
      message: 'Hello Vue.js!'
    }
  },
}
</script>

子组件 Child.vue

<template>
  <div>
    <div style="width: 300px; height: 100px; background-color: tomato;">
      <h3>Child 组件 --- {{ msg }}</h3>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Child',
  props: {
    msg: String
  },
}
</script>

执行结果:

💝注:除了指定 props 对象外,还可以指定数组类型的 props,如 props: ['value', 'name', 'age']

props 验证

对象类型的 props 节点提供了多种数据验证方案,例如:

  1. 🌈基础的类型检查
  2. 🪁多个可能的类型
  3. 🎯必填项校验
  4. 🔍属性默认值
  5. 🚀自定义验证函数
基础的类型检查

可以直接为组件的 props 属性指定校验类型,防止绑定错误类型的数据。

export default {
  props: {
    prop1: String,  // 字符串类型
    prop2: Number,  // 数字类型
    prop3: Boolean, // 布尔值类型
    prop4: Array,   // 数组类型
    prop5: Object,  // 对象类型
    prop6: Date,    // 日期类型
    prop7: Function,// 函数类型
    prop8: Symbol,  // 符号类型
  },
}
多个可能的类型

props 种的某个属性类型不唯一,可将其定义为数组。

export default {
  props: {
    username: [String, Object]
  },
}
必填项 & 默认值
export default {
  props: {
    age: {
      type: Number,
      // 必填项
      required: true,
      // 默认值
      default: 19
    }
  },
}
自定义验证函数

可为 props 属性指定自定义验证函数,实现更加精确的校验。

export default {
  name: 'Child',
  props: {
    msg: {
      validator(value) {
        // validator 函数的返回值为 true 表示验证通过, false 表示验证失败!
        // 例如传入的msg必须匹配某些字符串才可通过校验:
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      },
    },
  },
}

不通过校验的错误信息:

子组件向父组件共享数据

子组件通过自定义事件的方式向父组件共享数据。

父组件 Parent.vue

<template>
  <div>
    <h2>Parent 组件 --- {{ message }}</h2>
    <Child :msg="message" @changeMessage="getMsg"></Child>
  </div>
</template>

<script>
import Child from './Child.vue'

export default {
  name: 'Parent',
  components: {
    Child,
  },
  data() {
    return {
      message: 'Hello Vue.js!',
    }
  },
  methods: {
    // 通过形参接收子数组传递来的数据
    getMsg(msg) {
      this.message = msg
    },
  },
}
</script>

子组件 Child.vue

<template>
  <div>
    <div>
      <h3>Child 组件 --- {{ msg }}</h3>
      <button @click="changeMsg">点我:触发子传父</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Child',
  props: ['msg'],
  methods: {
    changeMsg() {
      // 点击按钮时, 改变数据
      this.msg = 'Hello React!'
      // 同时触发自定义事件 changeMessage
      this.$emit('changeMessage', this.msg)
    },
  },
}
</script>

演示一下:

兄弟组件

兄弟组件之间实现数据共享的方案是 EventBus。可以借助于第三方的包 mitt 来创建 eventBus 对象

兄弟组件间的数据共享

安装 mitt 依赖包:

> npm install mitt

在项目中创建公共的 eventBus 模块:

// eventBus.js

// 导入 mitt 包
import mitt from 'mitt'
// 创建 EventBus 的实例对象
const bus = mitt()
// 将 EventBus 的示例对象共享出去
export default bus

数据发送方:

<template>
  <div>
    <span>Right(数据发送方)---{{ count }}</span>
    <button @click="changeCount">Click</button>
  </div>
</template>

<script>
// 导入 eventBus.js 模块,得到共享的 bus 对象
import bus from '@/eventBus.js'

export default {
  name: 'Right',
  data() {
    return {
      count: 0,
    }
  },
  methods: {
    changeCount() {
      this.count++
      // 调用 bus.emit() 方法触发自定义事件并发送数据
      bus.emit('countChange', this.count)
    },
  },
}
</script>

数据接收方:

<template>
  <div>
    <span>Left(数据接收方): {{ lCount }}</span>
  </div>
</template>

<script>
// 导入 eventBus.js 模块,得到共享的 bus 对象
import bus from '@/eventBus.js'

export default {
  name: 'Left',
  data() {
    return {
      lCount: '',
    }
  },
  created() {
    // 调用 bus.on() 方法注册一个自定义事件,通过事件处理函数的形参接收数据
    bus.on('countChange', (count) => {
      this.lCount = count
    })
  },
}
</script>

演示一下:

全局数据共享 vuex

🚀Vuex 是终极的组件之间的数据共享方案。在企业级的 vue 项目开发中 vuex 可以让组件之间的数据共享变得高效、清晰、且易于维护。

🔎持续关注本专栏,后续文章会单独聊聊 vuex 的内容。

动态组件

动态组件指的是动态切换组件的显示与隐藏。vue 提供了一个内置的 <component> 组件,专门用于实现组件的动态渲染。

基本语法:

<component> 是组件的占位符
② 通过 is 属性 动态指定要渲染的组件名称
<component is="要渲染的组件名称"></component>

示例代码:

<template>
  <div>
    <button @click="changeLeft">Left</button>
    <button @click="changeRight">Right</button>
    <div>
      <!-- 动态渲染组件:动态组件的使用<components> -->
      <component :is="which"></component>
    </div>
  </div>
</template>

<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'

export default {
  name: 'App',
  data() {
    return {
      which: 'Left'
    }
  },
  components: {
    Left,
    Right
  },
  methods: {
    changeLeft() {
      this.which = 'Left'
    },
    changeRight() {
      this.which = 'Right'
    }
  }
}
</script>

稍微演示下:

是不是发现上图不对劲的地方了!没错,默认情况下,切换动态组件时无法保持组件的状态。此时可以使用 vue 内置的 <keep-alive> 组件保持(缓存)动态组件的状态。

注:include / exclude 中包含的名字是组件的 name 属性!

<!-- keep-alive 保持状态: 将内部组件缓存,而不是销毁,Vue 还提供了对应的生命周期函数! -->
<!-- include 属性可指定需缓存的组件、相反,exclude 指定不被缓存的组件名(二选一、不能同时使用) -->
<keep-alive include="Left,Right">
  <component :is="which"></component>
</keep-alive>

实现效果:

组件插槽

插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望用户指定的部分定义为插槽

可以把插槽理解为是组件封装期间,为用户预留的内容占位符

匿名插槽

🔎子组件(带匿名插槽):

<template>
  <div>
    <!-- 插槽: 默认值为Default Message -->
    <slot>Default Message</slot>
  </div>
</template>

🚀父组件:

<template>
  <div>
    <MyComponent>
      <!-- 替换slot插槽 -->
      <template>
        <h3>用户自定义的内容1</h3>
        <h3>用户自定义的内容2</h3>
        <h3>用户自定义的内容3</h3>
      </template>
    </MyComponent>
  </div>
</template>

效果演示:

具名插槽

如果在封装组件时需要预留多个插槽节点,则需要为每个 <slot> 插槽指定具体的 name 名称。这种带有具体名称的插槽叫做 “具名插槽”。

🌈在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称。

🏹Tip:

  • slot-scope 是旧 api (不推荐使用),v-slot 是最新的。
  • 没有指定 name 名称的插槽,默认名称叫做 default

🔎子组件:

<template>
  <div>
    <!-- 具名插槽 -->
    <slot name="header">Header Message</slot>
    <slot name="default">Main Message</slot>
    <footer>
      <slot name="footer">Footer Message</slot>
    </footer>
  </div>
</template>

🚀父组件:

<template>
  <div>
    <MyComponent>
      <!-- 具名(header)插槽 -->
      <template v-slot:header>
        <h2>滕王阁序</h2>
      </template>

      <!-- 具名(default)插槽 -->
      <template v-slot:default>
        <div>豫章故郡, 洪都新府.</div>
        <div>星分翼轸, 地接衡庐.</div>
      </template>

      <!-- v-slot:footer简写形式: #footer -->
      <template #footer>
        <p>Author: 王勃</p>
      </template>
    </MyComponent>
  </div>
</template>

演示结果:

🚀注:插槽节点不仅仅可以传入标签元素,还可以传入组件 (无需 <template> 占位符,直接写入组件内即可),例如:

<MyComponent>
  <HelloWorld :msg="'组件插槽'"></HelloWorld>
</MyComponent>

🏆 v-slot 简写形式

🎨即把 v-slot: 替换为字符 #。例如 v-slot:header 可以被重写为 #header.

作用域插槽

子组件中的作用域插槽可以为父组件中的插槽的显示提供数据。好比复古唱片机,根据提供的不同唱片向外输出不同的音乐。

基本用法

父组件:

<template>
  <div>
    <!-- 定义作用域插槽 -->
    <slot name="func" :infomation="info"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      info: {
        username: 'w',
        age: 21,
        sex: 'M',
      },
    }
  },
}
</script>

子组件:

<MyComponent>
  <!-- 使用作用域插槽 -->
  <template #func="scope">
    <div>
      {{ scope.infomation }}
    </div>
  </template>
</MyComponent>

解构赋值

起步推荐文章:[译] 我最终是怎么玩转了 Vue 的作用域插槽

<MyComponent>
  <!-- 解构赋值 -->
  <!-- <template #func="{ infomation }"> -->

  <!-- 解构赋值之命别名 -->
  <template #func="{ infomation:msg }">
    <div>
      <h5>{{ msg.username }}</h5>
      <h5>{{ msg.age }}</h5>
      <h5>{{ msg.sex }}</h5>
    </div>
  </template>
</MyComponent>

简单常见的应用场景

🌅数据在子组件自身,但根据数据生成的结构需要组件的使用者来决定。

父组件:

<template>
  <div>
    <tr v-for="item in userList" :key="item.id">
      <slot :user="item"></slot>
    </tr>
  </div>
</template>

子组件:

<MyComponent>
  <template #default="scope">
    <td>{{ scope.user.id }}</td>
    <td>{{ scope.user.username }}</td>
    <td>{{ scope.user.age }}</td>
    <td>{{ scope.user.sex }}</td>
  </template>
</MyComponent>

渲染后的效果等价于:

<tr v-for="item in userList" :key="item.id">
  <td>{{ scope.user.id }}</td>
  <td>{{ scope.user.username }}</td>
  <td>{{ scope.user.age }}</td>
  <td>{{ scope.user.sex }}</td>
</tr>

🔎关于作用域插槽的实际应用场景:vue 作用域插槽,你真的懂了吗?

作用域插槽适合的场景是至少包含三级以上的组件层级(文中例子),是一种优秀的组件化方案!

axios

Axios 是一个基于 promise 网络请求库。

安装

npm install axios --save

使用

GET

原始请求:

// 发起一个get请求
axios({
  method: 'get',
  url: 'http://localhost:8090/user/get',
  params: {
    name: 'w'
  }
})
  .then(function (response) {
    console.log(response)
  });

axios GET API:

// 公共路径抽取到 main.js 文件中, 见下文 '全局配置' 部分

// GET请求
axios.get('/user?params=12345' )
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

// params
axios.get( '/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  })
  .then(function () {
    // 总是会执行
  });

// 支持 async/await 用法
async function getUser() {
  try {
    const response = await axios.get('/user?ID=12345');
    console.log(response);
  } catch (error) {
    console.error(error);
  }
}

// 解构赋值
async function getUser() {
  try {
    // 解构 data 数据部分
    const { data } = await axios.get('/user?ID=12345');
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

POST

原始请求:

// 发起一个post请求
axios({
  method: 'post',
  url: '/user/login',
  data: {
    name: 'author',
    password: '123456'
  }
});

axios POST API:

// POST请求
axios.post('/user/login', {
    name: 'author',
    password: '123456'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

// await/async + 解构赋值
async function login() {
  try {
    // 解构赋值后重命名
    const { data: msg } = await axios.post('/user/login', this.user);
    console.log(msg);
  } catch (error) {
    console.error(error);
  }
}

全局配置

// 设置全局公共请求路径
axios.defaults.baseURL = 'https://www.escook.cn'
// 配置后使用 this.$axios.get()/post() 发请求
Vue.prototype.$axios = axios

如果遇到以下问题均为跨域问题,后端跨域有问题建议看这篇

如果后端接口不归自己提供,那么就需要在前端配置跨域。简单提一下:

vue 项目运行地址:http://localhost:8080

API 接口运行地址:www.escook.cn

main.js 入口文件中配置:

// 向当前 vue 项目运行端口发送请求, 这样就不存在跨域问题, 然后发现请求的接口不存在, 直接将请求交由 proxy 代理!
axios.defaults.baseURL = 'http://localhost:8080'
Vue.prototype.$axios = axios

然后在项目根目录创建 vue.config.js 配置文件,并添加以下代码:

必须重启项目才能让 vue.config.js 改动的配置生效。

module.exports = {
  devServer: {
    port: 8080,
    // 代理转发地址
    proxy: 'https://www.escook.cn'
  }
}

发送请求:

export default {
  methods: {
    async getData() {
      const data = await this.$axios.get('/api/cart')
      console.log(data);
    },
  },
  created() {
    this.getData()
  },
}

响应结果:

由此可见,一个请求的响应包含以下信息。

{
  // config 是 axios 请求的配置信息
  config: {},

  // data 由服务器提供的响应
  data: {},

  // headers 是服务器响应头
  // 所有的 header 名称都是小写,而且可以使用方括号语法访问
  // 例如: response.headers['content-type']
  headers: {},

  // request 是生成此响应的请求
  // 在 node.js 中它是最后一个 ClientRequest 实例 (in redirects),
  // 在浏览器中则是 XMLHttpRequest 实例
  request: {},

  // status 来自服务器响应的 HTTP 状态码
  status: 200,

  // statusText 来自服务器响应的 HTTP 状态信息
  statusText: 'OK'
}

当使用 then 时,可以接收如下响应:

axios.get('/api/cart')
  .then(function (response) {
    console.log(response.config);
    console.log(response.data);
    console.log(response.headers);
    console.log(response.status);
    console.log(response.statusText);
  });

拦截器

基本用法

在发送请求前/接收请求数据后若要进行一些处理,则可以使用 axios 提供的拦截器实现,分别对应请求拦截器 (发送请求前执行) 与响应拦截器 (响应数据前执行)。

// 添加请求拦截器(发送请求前执行)
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器(响应数据前执行)
axios.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  });

如果你稍后需要移除拦截器,可以这样:

const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
axios 拦截器 + Element UI 实现 Loading 加载效果
// 通过axios拦截器增添Loading加载效果!
let loadingInstance = null

axios.interceptors.request.use((config) => {
  // 发送请求后且返回数据前, 页面会一直处于加载状态
  loadingInstance = Loading.service({ fullscreen: true, text: '加载数据中, 请稍等..' })
  return config
}, (error) => {
  return Promise.reject(error)
})

axios.interceptors.response.use((response) => {
  // 关闭加载页面的效果
  loadingInstance.close()
  return response
}, (error) => {
  return Promise.reject(error)
})

演示效果:

关于 axios 的内容,这里仅推荐一份文档:

Element UI

Element UI:一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的 PC 端组件库。

安装

> npm i element-ui -S

引入

在 main.js 中写入以下内容:

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

Vue.use(ElementUI);

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

使用

<el-tag>标签一</el-tag>
<el-tag type="success">标签二</el-tag>
<el-tag type="info">标签三</el-tag>
<el-tag type="warning">标签四</el-tag>
<el-tag type="danger">标签五</el-tag>

🚀类似的组件库还有:

Echarts

Apache ECharts:一个基于 JavaScript 的开源可视化图表库。

安装

> npm install echarts --save

引入

import Vue from 'vue'
import App from './App.vue'
import * as echarts from 'echarts'

Vue.prototype.$echarts = echarts

new Vue({
  render: h => h(App),
}).$mount('#app')

使用

🪁根据文档修改 setOption 以修改图形

<template>
  <div>
    <!-- 一定要设置宽高才能显示图形 -->
    <div id="echart" ref="echart"></div>
  </div>
</template>

<script>
export default {
  methods: {
    initEchart() {
      // 实例化echarts对象
      var myChart = this.$echarts.init(this.$refs.echart)
      // 绘制图表
      myChart.setOption({
        title: {
          text: 'ECharts 入门示例'
        },
        tooltip: {},
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      });
    }
  },
  mounted() {
    this.initEchart()
  },
}
</script>

<style lang="less" scoped>
#echart {
  width: 450px;
  height: 300px;
}
</style>

❤️/ END / 如果本文对你有帮助,点个「赞」支持下吧。