0603-Vue

第三章 组件化开发

3.1 组件化入门

1. 组件概述

        前端代码开发中,程序实现是希望尽可能多的做到代码重用,然而前端在代码重用中可能会产生CSS样式和JS业务逻辑的冲突;由此产生的Web Components开发标准:其核心思想是通过创建封装特定功能的定制元素(Vue的实现方式是用一个自定义标签封装独立的前端代码并且具有特定的功能),并且能够解决冲突问题;

        但是这个Web Components标准没有被浏览器广泛支持,但是Vue部分实现了Web Components开发标准。把不同的功能在不同的组件中开发,通过组件组合的方式实现功能的同一实现。

        组件设计是将不同的功能封装在不同的组件中,通过组件的整合形成完整意义上的一个应用;

        Vue中组件分为全局组件和局部组件,在Vue组件本质也是一个Vue实例,拥有Vue对象的全部属性:如data、methods等等;组件注册实际上是将定义好的组件绑定到Vue实例中,可以将组件定义在Vue对象(java中称类)上,称为全局组件,这样所有的vue实例都可以使用该组件;如果将组件定义在Vue实例上,称为局部组件,可以在当前Vue实例范围内使用;

2. 组件分类

  • 根据组件的使用范围:①全局组件②局部组件
  • 根据组件的引用关系:①父组件②子组件

3. 组件特点

  • 组件使用的三个基础步骤:①创建组件、②注册组件、③使用组件;
  • 组件是可复用的 Vue 实例:组件与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项;
  • 组件的 data 选项:必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝;
  • 组件树:通常一个应用会以一棵嵌套的组件树的形式来组织,根组件是Vue实例,其他组件必须先注册便于Vue识别:有两种注册类型全局注册和局部注册;
  • 每个组件必须只有一个根元素:组件中可以将模板的内容包裹在一个父元素内。

3.2 组件创建

1. extend

  • Vue组件创建的底层函数:使用textend()方法根据传递的参数选项创建组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <div id='app'>
    <h3>
    3. 使用组件
    </h3>
    <cpn></cpn>
    </div>
    <script>
    // 1. 创建组件对象
    const cpn = Vue.extend({
    // 组件选项
    // ... ...
    })
    // 2 注册全局组件
    Vue.component('组件名称', cpn)
    // 2. 注册局部组件
    const vm = new Vue({
    components: {
    '组件名称': cpn
    }
    })
    </script>

2. extend语法糖

  • 为简化使用extend()创建组件步骤,Vue提供组件创建语法糖:将组件的创建和注册在语法上整合在一起

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <div id='app'>
    <h3>
    3. 使用组件
    </h3>
    <cpn></cpn>
    </div>
    <script>
    // 2 注册全局组件
    Vue.component('组件名称', {
    // 组件选项
    // ... ...
    })
    // 2. 注册局部组件
    const vm = new Vue({
    components: {
    '组件名称': {
    // 组件选项
    // ... ...
    }
    }
    })
    </script>

3. template抽取<script>

  • 在上述方法中,组件模板内容是定义在template的字符串中,这并适合前端代码开发,所以需要将组件模块内容定义在html文本区域;

  • 方案一:定义在<script>标签中,使用script作为模板的根标签,script标签的type必须是text/x-template

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!-- 1.定义组件模板内容 -->
    <script type="text/x-template" id="自定义组件模板名称">
    <div>
    组件内容
    </div>
    </script>
    <!-- 2. 创建组件时候使用 template 引用组件 -->
    <script>
    let component = Vue.extend({
    template: '#id值'
    })
    </script>

4. template抽取<template>

  • 方案二:使用<template>标签作为组件根标签

    1
    2
    3
    4
    5
    6
    7
    8
    <template id="自定义组件模板名称">
    <div>组件内容</div>
    </template>
    <script>
    let component = Vue.extend({
    template: '#id值'
    })
    </script>

5. template模块化

  • 方案三:将组件选项单独定义为ES6的一个模块,在这个模块中export组件对象,在父组件中import这个模块中的组件对象并注册到父组件中;

5. vue-cli组件

  • 使用vue-cli创建

3.3 组件数据

1. 父传子

  • props数组:Prop 是可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。可以用一个 props 选项将其包含在该组件可接受的 prop 列表中:列表中可以拥有任意数量的 prop,任何值都可以传递给任何 prop。子组件实例中访问这个值,就像访问 data 中的值一样。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <blog-post title="My journey with Vue"></blog-post>
    <blog-post title="Blogging with Vue"></blog-post>
    <blog-post title="Why Vue is so fun"></blog-post>

    <script>
    Vue.component('blog-post', {
    props: ['title'],
    template: '<h3>{{ title }}</h3>'
    })
    </script>
  • props对象:props中的参数可以以对象形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型;并且可以为 props 中的值提供一个带有验证需求的对象,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    Vue.component('my-component', {
    props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
    type: String,
    required: true
    },
    // 带有默认值的数字
    propD: {
    type: Number,
    default: 100
    },
    // 带有默认值的对象
    propE: {
    type: Object,
    // 对象或数组默认值必须从一个工厂函数获取
    default: function () {
    return { message: 'hello' }
    }
    },
    // 自定义验证函数
    propF: {
    validator: function (value) {
    // 这个值必须匹配下列字符串中的一个
    return ['success', 'warning', 'danger'].indexOf(value) !== -1
    }
    }
    }
    })

2. 子传父

  • 自定义事件注意点:不同于组件和 prop,事件名不会被用作一个 JavaScript 变量名或 property 名,所以不建议使用驼峰命名;由于html大小写不敏感,事件名称会转为全小写,在事件命名推荐使用中横线连接符;

  • 数据传输原理是通过子组件的自定义事件将数据以参数的方式传递出去,在使用组件的地方绑定事件并接收参数完成数据传递

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    1. 子组件中定义事件并绑定参数
    <template id="cpn" >
    <div>
    子组件输入框: <input type="text" @input="inputChange">
    </div>
    </template>
    <script>
    const cpn = Vue.extend({
    template: `#cpn`,
    methods: {
    inputChange (event){
    this.$emit('sub-change',event.data)
    }
    }
    })
    </script>

    2. 在使用组件的部分绑定事件并接收参数
    <div id="app">
    子组件-- <cpn @sub-change="subChange"></cpn>
    </div>
    <script>
    const vm = new Vue({
    el: '#app',
    components: {
    cpn: cpn
    },
    methods: {
    subChange (data) {
    console.log(data)
    }
    }
    })
    </script>
  • .sync 修饰符

  • .native 修饰符

3. 同级传值

  • 一般大型的项目,推荐使用Vuex来管理组件之间的通信

  • 同级组件不能直接传值,需要一个中间桥梁,可以先将数据传递给公共的父组件,然后父组件再将数据传递给需要的子组件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <script>
    // 1. 创建一个公共桥梁作为
    const bus = new Vue()
    // 2. 在数据发送方自定义事件将数据发送桥梁
    methods: {
    cpn1Change(event){
    bus.$emit('cnp-one-change',event.data)
    }
    }
    // 3. 在数据接收方的mounted时间钩子函数中始化完成,就开始执行定义的方法
    mounted () {
    bus.$on('cnp-one-change',(val) => {
    })
    }
    </script>

4. $refs

  • ref:被用来给元素或子组件注册引用信息,如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例:

    1
    2
    3
    4
    5
    <!-- `vm.$refs.p` DOM 元素 -->
    <p ref="p">hello</p>

    <!-- `vm.$refs.child` 组件实例 -->
    <child-component ref="child"></child-component>
  • $refs:ref引用信息将会注册在父组件的 $refs 对象上

    • v-for 用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。
    • 当引入DOM元素时候:ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们
    • 当引入组件实例时候:$refs 也不是响应式的,不应该在模板中做数据绑定。

5. $root

  • 当前组件树的根 Vue 实例。

6. $parent

  • 父实例,如果当前实例有的话。

7. $children

  • 当前实例的直接子组件。需要注意 $children 并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 $children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。

3.3 组件插槽

1. 插槽概述

2. 插槽入门

3. 插槽:name

4. 插槽:slot-scope

第五章 vue-router

5.1 路由概述

1. VueRouter是什么

        路由是网络工程中的术语,维基百科对其的解释是通过互联网把信息从源地址传输到目的地址的活动;随着IT技术的发展由后端路由发展到前端路由;

  • 后端路由:在互联网发展早期,浏览器发送请求到服务器,服务器将数据渲染为html并返回,所以前端页面的跳转是由服务器完成,即后端控制请求并路由;
  • 前端路由:指浏览器改变URL不会向服务器发送请求,并且可以进行页面跳转的页面渲染,即前端控制请求并路由到指定页面;

2. VueRouter原理

        SPA(single page application):单一页面应用程序,只有一个完整的页面;它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。单页面应用(SPA)的核心之一是:更新视图而不重新请求页面;vue-router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式;根据配置mode参数来决定采用哪一种方式。

  • hash :vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。 hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页,也就是说hash 出现在 URL 中,但不会被包含在 http 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。hash 模式的原理是 onhashchange 事件(监测hash值变化),可以在 window 对象上监听这个事件

    1
    location.hash='url hash'
  • history **:由于hash模式会在url中自带#,如果不想要很丑的 hash,我们可以用路由的 history 模式,只需要在配置路由规则时,加入”mode: ‘history’”,这种模式充分利用了html5 history interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器记录栈,在当前已有的 back、forward、go 基础之上,它们提供了对历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求**。当你使用 history 模式时,URL 就像正常的 url;

    1
    2
    3
    4
    5
    history.pushStatus({},'','url');
    history.back();
    history.forward();
    history.replaceStatus({},'','url');
    history.go(索引); // 索引为正数向前跳, 索引为负数向后跳

3. VueRouter使用方式

  • 方式1:直接修改地址栏
  • 方式2:this.$router.push(‘路由地址’)
  • 方式3:<router-link to="路由地址"></router-link>

5.2 VueRouter安装

1. 方式一:基础

  • 下载路由组件:npm install vue-router --save

  • 新增router包(路由组件会单独定义一个文件夹,在vue项目中成为包),自定义路由入口文件:router/index.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 第一步 导入路由对象,并且挂载到Vue对象中
    import Vue from 'vue'
    import VueRouter from "vue-router";
    Vue.use(VueRouter);
    // 第二步 创建路由实例,并且设置路由映射配置
    const routes = [{
    path: "/",
    name: "Home",
    component: Home,
    }
    ];

    const router = new VueRouter({
    mode: "history",
    routes,
    });
    export default router;
  • 配置router入口组件到main.js,将路由组件挂载到Vue实例上,为全局添加了两个属性:$router和$route;全局添加了两个组件:<router-link><router-view>

    1
    2
    3
    4
    5
    6
    7
    8
    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'

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

2. 方式二:分模块

  • 下载路由组件:npm install vue-router --save

  • 新增router包,自定义路由入口文件:router/index.js,并在Vue实例上安装router组件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import Vue from 'vue'
    import VueRouter from 'vue-router'

    Vue.use(VueRouter)

    const routes = []

    const router = new VueRouter({
    mode: 'history',
    routes
    })
    export default router
  • 在router包中新增model包,将需要拆分的路由模块单独定义,新增路由的方式采用函数的方式追加到路由映射表中

    1
    2
    3
    4
    5
    6
    export default function (router){
    router.push({
    path:'/router',
    component:()=>import('../../views/Demo01Router')
    })
    }
  • 最后将自定义模块添加到router入口组件中,并调入函数,传入路由映射表作为参数

    1
    2
    3
    4
    5
    // 引入路由模块
    import basic from './models/basic'

    const routes = []
    basic(routes)
  • 配置router入口组件到main.js

    1
    2
    3
    4
    5
    6
    7
    8
    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'

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

5.3 VueRouter基础

1. 路由配置

1
2
3
4
5
const router = new VueRouter({
mode: 'history', // 路由模式:hash | history
routes, // 路由映射表,是个数组
linkActiveClass:'' // 全局配置link激活
})
  • 是vue-router已经内置的一个组件,默认会被渲染为<a>标签;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <!-- 字符串 -->
    <router-link to="home">Home</router-link>

    <!-- 使用 v-bind 的 JS 表达式 -->
    <router-link v-bind:to="'home'">Home</router-link>

    <!-- 不写 v-bind 也可以,就像绑定别的属性一样 -->
    <router-link :to="'home'">Home</router-link>

    <!-- 属性绑定,修改属性改变路由 -->
    <router-link :to="home">Home</router-link>

    <!-- 同上 -->
    <router-link :to="{ path: 'home' }">Home</router-link>

    <!-- 命名的路由 -->
    <router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

    <!-- 带查询参数,下面的结果为 /register?plan=private -->
    <router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>
    • to(必选参数):表示目标路由的链接,可以是一个字符串,或是动态绑定的描述目标位置的对象
    • tag:默认值a,<router-link> 渲染成tag属性所指定的标签
    • replace:默认值
    • active-class:默认值router-link-active,设置 链接激活时使用的 CSS 类名,默认值可以通过路由的构造选项 linkActiveClass 来全局配置
    • exact-active-class:默认值router-link-exact-active,当链接被精确匹配的时候应该激活的 class。默认值也是可以通过路由构造函数选项 linkExactActiveClass 进行全局配置的。
    • exact:类型Boolean,默认值false,表示是否激活
    • event:默认值click,声明可以用来触发导航的事件。可以是一个字符串。
    • append:设置 append 属性后,则在当前 (相对) 路径前添加基路径,例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b

3. router-view

  • 作用:改标签会根据当前的路径,动态的替换路径所映射的组件,路由切换时,切换的是<router-view>挂载的组件;

  • 视图命名:如果希望同时展示多个视图,而不是嵌套视图,可以在界面中定义多个单独命名的视图;如果 router-view 没有设置名字,那么默认为 default

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <router-view></router-view>
    <router-view name="a"></router-view>
    <router-view name="b"></router-view>

    <script>
    // 一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件
    const router = new VueRouter({
    routes: [
    {
    path: '/',
    components: {
    default: Foo,
    a: Bar,
    b: Baz
    }
    }
    ]
    })
    </script>
  • 嵌套命名视图:用命名视图创建嵌套视图的复杂布局:例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <div>
    <h1>User Settings</h1>
    <NavBar/>
    <router-view/>
    <router-view name="helper"/>
    </div>
    <script>
    const routers = [
    {
    path: '/settings',
    // 你也可以在顶级路由就配置命名视图
    component: UserSettings,
    children: [{
    path: 'emails',
    component: UserEmailsSubscriptions
    }, {
    path: 'profile',
    components: {
    default: UserProfile,
    helper: UserProfilePreview
    }
    }]
    }
    ]
    </script>

4. $router

  • 说明:指的是router实例

  • $router的属性

    属性 说明
    $router.app 配置了router的Vue根实例
    $router.mode 路由模式, hash | history
    $router.currentRoute 当前路由的路由信息对象,包含了当前匹配路由的信息
  • $router的方法

    方法 说明
    $router.addRoutes(routes) 动态添加路由规则,参数为符合routes选项要求的数组
    $router.beforeEach(to,from,next) 全局前置守卫
    $router.beforeResolve() 全局解析守卫 , 在导航被确认之前,且在锁头组件内守卫和异步路由组件被解析之后调用,参数和全局前置守卫相同;
    $router.afterEach() 全局后置守卫
    $router.go(n) 接受一个整数作为参数,类似window.history.go(n),在浏览器历史记录中前进或后退几步
    $router.push( location ) 跳转导航的方法,这种方法会向history栈添加一个新的记录
    $router.replace( location ) 替换掉当前的history记录,不会添加新的记录
    $router.back() 相当于router.go(-1)
    $router.forward() 相当于router.go(1)
    $router.resolve(location) 解析目标路由,接受一个地址参数,返回location,route,href等属性信息,还可以接受当前默认路由current和当前路由上附加路径append两个参数
    $router.onReady(callback) 把一个回调排队,在路由完成初始导航时调用。
    $router.onError(callback) 注册一个回调,该回调会在路由导航过程中出错的时候被调用
    - 在一个路由守卫函数中被同步抛出
    - 在一个路由守卫函数中通过调用next(error)的方式异步捕获并处理
    - 渲染一个路由的过程中,需要尝试解析一个异步组件时发生错误

5. $route

  • 说明:每次路由导航成功后都会产生一个新的对象。是当前激活的路由信息对象,是只读属性,不可更改,但是可以watch(监听)

  • $route属性

    属性 说明
    $route.fullPath 完成解析后的url,包含查询参数和hash的完整路径
    $route.path 路径,字符串类型,解析为绝对路径
    $route.hash 当前路由的hash值(带#号的),如果没有hash值则为空字符串
    $route.name 当前路由的名称,如果有的话(用于命名路由)
    $route.params 一个键值对对象,路由参数
    $route.query 一个键值对对象,表示url查询参数
    $route.matched 一个包含了当前路由的所有嵌套路径片段的路由记录
    $route.redirectedFrom 重定向来源的路由的名字,如果存在重定向的话

6. 入门案例

  • 安装路由并初始化路由模块:src/router/index.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import Vue from 'vue'
    import VueRouter from "vue-router";
    Vue.use(VueRouter)

    const routes = [

    ]
    const router = new VueRouter({
    mode:"history",
    routes
    })
    export default router
  • 首先在项目目录中定义好需要映射的组件,Vue脚手架的项目结构中:①components用于存放页面组件②views中存放页面(本质也是组件),例:在components中定义VueComponent.vue文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <template>
    <div>
    VueComponent.vue
    </div>
    </template>

    <script>
    export default {
    name: "VueComponent"
    }
    </script>
  • 在router模块中导入需要路由的组件并在路由表中配置路由

    1
    2
    3
    4
    5
    6
    7
    8
    import Router01Map from "../components/router/Router01Map";

    const routes = [
    {
    path: '/map',
    component: Router01Map
    }
    ]
  • 在Vue根组件App.vue中新增路由标签,启动项目测试路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <template>
    <div id="app">
    <router-link to="/map">XX</router-link>
    <router-view></router-view>
    </div>
    </template>
    <script>

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

5.4 VueRouter核心

使用模块化编程首先导入Vue和VueRouter,要调用 Vue.use(VueRouter)

1. 路由导航

① 路由组件映射:导入

  • 定义 (路由) 组件,可以从其他文件 import 进来

    1
    2
    3
    4
    5
    import 组件对象 from "组件路径";

    const routes = [
    {path: '路由',component: 组件对象}
    ]
    • path:URI
    • component:可以是①通过 Vue.extend() 创建的组件构造器②一个组件对象③一个组件配置对象(在嵌套路由中使用)

② 路由组件映射:懒加载

  • 如果使用import语法在组件映射之前就将组件导入,当打包构建应用时,就会将所有组件打包为一个非常大的文件,影响页面加载;使用懒加载方式可以在打包的时候把不同的路由对应的组件分隔成独立的文件,然后当路由被访问时候才加载对应的组件,可以提高效率;

    1
    2
    3
    const routes = [
    {path: '路由',component: () => import("组件路径")
    ]
  • 如果每一个异步块打包为一个单独的文件,打包后的文件就会很多;有时候可以把多个路由下的组件打包为同异步块中(chunk),只需要使用命名chunk语法:/* webpackChunkName: “chunk块名称” */,例如下:两个路径下的组件都会打包到名称为A的js文件中了;

    1
    2
    3
    4
    const routes = [
    {path: '路由',component: () => import(/* webpackChunkName: "A" */"组件路径")},
    {path: "路由",component: () => import(/* webpackChunkName: "A" */"组件路径")}
    ]

③ 路由命名组件映射

  • 命名路由映射:改名称就会作为路由的标识

    1
    2
    3
    4
    5
    6
    7
    const routes = [
    {
    name: "路由名称",
    path: "路由",
    component: () => import("组件路径")
    }
    ]
  • 命名路由的使用

    1. 链接到一个命名路由:绑定一个对象类型,name属性指向路由的name标识

      1
      <router-link :to="{ name: '路由名称'">XX</router-link>
    2. 使用导航式路由指定一个名称的路由

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      <button @click="routerName">routerName</button>
      <script>
      export default {
      methods:{
      routerName(){
      this.$router.push({name:'Name'})
      }
      }
      }
      </script>

④ 路由通配符映射组件

  • 路由通配符*:含有通配符的路由应该放在最后,当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 参数。它包含了 URL 通过通配符被匹配的部分;

    一般把通配符配置在最后一个,用于客户端的404错误;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const routes = [
    {
    path: '*' // 会匹配所有路径
    }
    ]
    this.$router.push('/non-existing')
    this.$route.params.pathMatch // '/non-existing'


    const routes = [
    {
    path: '/user-*' // 会匹配以 `/user-` 开头的任意路径
    }
    ]
    this.$router.push('/user-admin')
    this.$route.params.pathMatch // 'admin'

⑤ 路由优先级

  • 同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高

⑥ 编程式导航

  • 借助 router 的实例方法,通过编写代码来实现。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 字符串
    router.push('home')

    // 对象
    router.push({ path: 'home' })

    // 命名的路由
    router.push({ name: 'user', params: { userId: '123' }})

    // 带查询参数,变成 /register?plan=private
    router.push({ path: 'register', query: { plan: 'private' }})

    // 如果提供了 path,params 会被忽略
    const userId = '123'
    router.push({ name: 'user', params: { userId }}) // -> /user/123
    router.push({ path: `/user/${userId}` }) // -> /user/123
    // 这里的 params 不生效
    router.push({ path: '/user', params: { userId }}) // -> /user

2. 路由重定向

① 重定向到其他路由

  • redirect值是字符串默认会匹配路由映射表中的path属性实现重定向;

    1
    2
    3
    4
    5
    const router = new VueRouter({
    routes: [
    { path: '/a', redirect: '/b' }
    ]
    })

② 重定向到命名路由

  • redirect的值是对象,如果指定name属性会根据路由名称进行匹配实现重定向

    1
    2
    3
    4
    5
    6
    7
    const router = new VueRouter({
    routes: [
    { path: '/a', redirect: { name: 'foo' }}
    ]
    })
    // 编程式导航
    this.$router.push({name:"nameRouter"})

③ 重定向到函数

  • redirect的值也可以是一个函数:参数是目标路由对象,返回值可以是字符串或对象;

    1
    2
    3
    4
    5
    6
    7
    8
    const router = new VueRouter({
    routes: [
    { path: '/a', redirect: to => {
    // 方法接收 目标路由 作为参数
    // return 重定向的 字符串路径/路径对象
    }}
    ]
    })

④ 路由别名重定向

  • 在嵌套路由内的组件的路由是多级结构的;“别名”的功能可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。

    1
    2
    3
    4
    5
    const router = new VueRouter({
    routes: [
    { path: '/a', component: A, alias: '/b' }
    ]
    })

3. 路由传参

① name传参

  • name作为路由标识:可以当做一个参数传递,但是只能是一个参数,使用$route.name接收参数

    1
    2
    3
    4
    5
    6
    7
    routes: [
    {
    path: '/',
    name: 'Hello',
    component: Hello
    }
    ]

② 动态路由参数:params

使用params传参路由导航只能使用name属性,参数可以定义在url中

  • 路由参数params:使用路由导航时候可以为路由添加params的对象类型参数,在目标组件中使用$route.params的方式接受params对象;<router-link>的to属性绑定的对象中必须使用命名组件进行路由,并指定params参数对象;

    1
    <router-link :to="{name: 'param',params:{name:'JJ',age:1111}}">参数传递</router-link>
  • 动态路由导航:有时候需要在路由中显示这些参数,则使用动态路由参数将参数绑定到路由参数中,在路由中定义参数的格式:/:key,路由中参数的key要和params中的key相匹配;参数以路径形式显示在路由中

    1
    2
    3
    4
    5
    6
    routes: [
    {
    path: "/param/:name/:age",
    component: () => import("组件路径")
    }
    ]
    1. 使用标签导航时候指定路由参数

      1
      <router-link :to="{name:'dyParam',params:{name:'Tom',age:23}}">标签导航</router-link>
    2. 使用编程式导航并制定路由参数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      <button @click="param">编程式导航</button>
      <script>
      export default {
      methods:{
      param(){
      this.$router.push({name:'dyParam',params:{name:"Admin",age:'12'}})
      }
      }
      }
      </script>

③ 路由参数:query

使用query传参数,路由导航可以使用path或name属性,并且参数会以?方式显示在url中

  • 使用query传参:定义组件并配置路由

    1
    2
    3
    4
    5
    6
    routes: [
    {
    path: "路由",
    component: () => import("组件路径")
    }
    ]
    1. 使用<router-link>进行路由导航并定义参数

      1
      <router-link :to="{path:'/query',query:{age:12}}">Query</router-link>
    2. 使用编程式路由导航

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      <button @click="query">编程式路由导航 query</button>
      <script>
      export default {
      methods:{
      query(){
      this.$router.push({path:'/query',query:{name:"Admin",age:'12'}})
      }
      }
      }
      </script>

④ 路由组件传参:props

  • 组件传值优势:在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。

4. 嵌套路由

  • 在实际页面中,通常由多层嵌套的组件组合而成,同样的url中隔断路由也需要按对应的嵌套各层组件,借助 vue-router,使用嵌套路由配置,就可以很简单地表达这种嵌套关系。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    routes: [
    {
    path: "路由",
    component: () => import("组件路径"),
    children: [
    {
    path: '子路由',
    component: () => import("组件路径"),
    children:[
    {
    path: '子路由的子路由',
    component: () => import("组件路径")
    }
    ]
    }
    ]
    }
    ]
    • 如果子路由非/开头:①嵌套的路径会层级结构②子路由组件会映射在子组件的<router-view>中;
    • 如果子路是/开头:①嵌套路径会被当作根路径②组件会映射在根<router-view>中;

5. 导航守卫

〇 完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

① 全局前置守卫

  • ② 全局解析守卫

  • 是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

③ 全局后置钩子

  • 后置钩子不会接受 next 函数也不会改变导航本身:

④ 路由独享的守卫

  • 在路由配置上直接定义 beforeEnter 守卫:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    routes: [
    {
    path: '/foo',
    component: Foo,
    beforeEnter: (to, from, next) => {
    // ...
    }
    }
    ]

⑤ 组件内的守卫

  • 6. 路由行为

  • 过渡效果

  • 滚动功能

7. 接口数据获取

① 导航完成之后获取

② 导航完成后获取数据

第六章 Vuex

6.1 Vuex基础

1. 简介

  • vuex是专门为Vue开发的状态管理工具,vuex采用集中式存储管理应用中的所有状态,并提供状态维护的方式,使应用对状态的管理可以做到响应式

    • 状态:在单组件中成为组件的数据属性,封装在data函数的中;在多组件中共享的数据则成为状态,是统一维护在Vuex;

    • 状态管理:指的是在多组件中维护共享的数据状态;

2. 安装与配置

  • 使用npm安装vuex4

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    ### 3. 项目结构

    - Vuex在实际开发中管理的状态会很多,需要将相关代码分隔到模块中,一般公司约定的统一的项目结构,方便管理与维护

    ```tex
    ├── index.html
    ├── main.js
    ├── api
    │ └── ... # 抽取出API请求
    ├── components
    │ ├── App.vue
    │ └── ...
    └── store
    ├── index.js # 我们组装模块并导出 store 的地方
    ├── actions.js # 根级别的 action
    ├── mutations.js # 根级别的 mutation
    └── modules
    ├── cart.js # 购物车模块
    └── products.js # 产品模块

6.2 核心概念

6.3 Vuex实践

第七章 axios

7.1 前端网络请求

  1. XMLHTTPRequest:原生JavaScript请求
  2. jQuery:ajax网络qingq
  3. JSONP:跨域伪造访问
  4. Axios:基于promise的HTTP 库,可以用在浏览器和node.js中

7.2 Vue中安装Axios

1. 加载axios安装包

1
npm install axios --save

2. 配置axios到Vue实例

  • 方案一:将axios挂载到Vue实例,可以全局访问axios的API

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // main.js:基础配置方法
    import axios from 'axios'
    Vue.prototype.$axios = axios

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

    // 在其他模块的方法中可以使用axios api
    methods: {
    funcName() {
    this.$axios.get({
    url: 'xxx'
    }).then(res => {

    })
    }
    }
  • 方案二:将axios单独封装为模块,创建并配置axios实例对象,然后将实例对象导出到main.js入口文件中,那么和接口请求的相关配置都可以定义在这个单独的axios模块中了;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 自定义api目录:/api/index.js:避免每个vue模块都使用this.$axios的api,避免业务与axios高耦合
    import axios from 'axios'
    const requests = axios.create()

    export default {
    requests
    }

    // main.js
    import '@/api'

    // 其他模块可以引用api模块进行接口调用
    import request from '@/api'
    methods: {
    funcName() {
    request.get({
    url: 'xxx'
    }).then(res => {

    })
    }
    }
  • 方案三:一般项目开发中会将接口的请求与业务方法分离,第一步会定义独立模块封装接口调用的方法,第二步在业务模块中调用接口模块传入接口参数获取接口响应;在方案二的基础上需要额外定义一个封装接口的模块:如order模块的相关接口都定义在一个js文件中;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // @/api/order/index.js:将后台Order服务的相关接口定义在当前模块中
    import request from '@/api'
    export function getList (param) {
    return order({
    url: '/order/list',
    method: 'get',
    data: param
    })
    }

    // 然后在order组件的method中直接调用
    import {getList} from '@/api/order/index'
    methods: {
    funcName() {
    const param = {}
    getList(param).then(res => {

    })
    }
    }
  • 方案四:如果前端项目需要调用多个服务的接口,而且接口的规则不统一,则需要将axios模块化,不同的服务对应各自的axios实例对象,并分别配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    // @/api/index.js
    import axios from 'axios'

    const order = axios.create({
    baseURL: 'http://Order',
    timeout: 5000
    })

    const user = axios.create({
    baseURL: 'http://Usre',
    timeout: 5000
    })

    const axiosReq = axios.create({
    baseURL: 'http://Other',
    timeout: 5000
    })

    export default {
    order, user, axiosReq
    }
    // @/api/order/index.js
    import Api from '../'
    export function orderList (param) {
    return Api.order({
    url: '/order/list',
    method: 'get',
    data: param
    })
    }

3. axios响应结构

  • 基本结构

    1
    2
    3
    4
    5
    6
    7
    8
    {
    data: {},
    status: 200,
    statusText: 'OK',
    headers: {},
    config: {},
    request: {}
    }
  • 结构说明

    属性 说明
    data 由服务器提供的响应
    status 来自服务器响应的 HTTP 状态码
    headers 服务器响应的头
    config 是为请求提供的配置信息
    request 生成此响应的请求

7.3 axiosAPI

1. 配置axios请求

  • axios常用配置案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 发送 POST 请求
    axios({
    method: 'post',
    url: '/user/12345',
    data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
    }
    });
    // 获取远端图片
    axios({
    method:'get',
    url:'http://bit.ly/2mTM3nY',
    responseType:'stream'
    })
    .then(function(response) {
    response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
    });
  • axios请求方法别名:使用别名方法时, urlmethoddata 这些属性的使用有区别

    别名 说明
    axios.request(config)
    axios.get(url[, config])
    axios.delete(url[, config])
    axios.head(url[, config])
    axios.options(url[, config])
    axios.post(url[, data[, config]])
    axios.put(url[, data[, config]])
    axios.patch(url[, data[, config]])
    并发
    axios.all(iterable)
    axios.spread(callback)

2. axios实例

  • 创建axios实例

    1
    2
    3
    4
    5
    const instance = axios.create({
    baseURL: 'https://some-domain.com/api/',
    timeout: 1000,
    headers: {'X-Custom-Header': 'foobar'}
    });
  • 实例方法

    别名 说明
    axios.request(config)
    axios.get(url[, config])
    axios.delete(url[, config])
    axios.head(url[, config])
    axios.options(url[, config])
    axios.post(url[, data[, config]])
    axios.put(url[, data[, config]])
    axios.patch(url[, data[, config]])
    并发
    axios.all(iterable)
    axios.spread(callback)

3. axios配置

  • axios的config配置说明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    {
    // `url` 是用于请求的服务器 URL
    url: '/user',

    // `method` 是创建请求时使用的方法
    method: 'get', // default

    // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
    // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
    baseURL: 'https://some-domain.com/api/',

    // `transformRequest` 允许在向服务器发送前,修改请求数据
    // 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
    // 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
    transformRequest: [function (data, headers) {
    // 对 data 进行任意转换处理
    return data;
    }],

    // `transformResponse` 在传递给 then/catch 前,允许修改响应数据
    transformResponse: [function (data) {
    // 对 data 进行任意转换处理
    return data;
    }],

    // `headers` 是即将被发送的自定义请求头
    headers: {'X-Requested-With': 'XMLHttpRequest'},

    // `params` 是即将与请求一起发送的 URL 参数
    // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
    params: {
    ID: 12345
    },

    // `paramsSerializer` 是一个负责 `params` 序列化的函数
    // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
    paramsSerializer: function(params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
    },

    // `data` 是作为请求主体被发送的数据
    // 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
    // 在没有设置 `transformRequest` 时,必须是以下类型之一:
    // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
    // - 浏览器专属:FormData, File, Blob
    // - Node 专属: Stream
    data: {
    firstName: 'Fred'
    },

    // `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
    // 如果请求话费了超过 `timeout` 的时间,请求将被中断
    timeout: 1000,

    // `withCredentials` 表示跨域请求时是否需要使用凭证
    withCredentials: false, // default

    // `adapter` 允许自定义处理请求,以使测试更轻松
    // 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
    adapter: function (config) {
    /* ... */
    },

    // `auth` 表示应该使用 HTTP 基础验证,并提供凭据
    // 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
    auth: {
    username: 'janedoe',
    password: 's00pers3cret'
    },

    // `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
    responseType: 'json', // default

    // `responseEncoding` indicates encoding to use for decoding responses
    // Note: Ignored for `responseType` of 'stream' or client-side requests
    responseEncoding: 'utf8', // default

    // `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
    xsrfCookieName: 'XSRF-TOKEN', // default

    // `xsrfHeaderName` is the name of the http header that carries the xsrf token value
    xsrfHeaderName: 'X-XSRF-TOKEN', // default

    // `onUploadProgress` 允许为上传处理进度事件
    onUploadProgress: function (progressEvent) {
    // Do whatever you want with the native progress event
    },

    // `onDownloadProgress` 允许为下载处理进度事件
    onDownloadProgress: function (progressEvent) {
    // 对原生进度事件的处理
    },

    // `maxContentLength` 定义允许的响应内容的最大尺寸
    maxContentLength: 2000,

    // `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
    validateStatus: function (status) {
    return status >= 200 && status < 300; // default
    },

    // `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
    // 如果设置为0,将不会 follow 任何重定向
    maxRedirects: 5, // default

    // `socketPath` defines a UNIX Socket to be used in node.js.
    // e.g. '/var/run/docker.sock' to send requests to the docker daemon.
    // Only either `socketPath` or `proxy` can be specified.
    // If both are specified, `socketPath` is used.
    socketPath: null, // default

    // `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
    // `keepAlive` 默认没有启用
    httpAgent: new http.Agent({ keepAlive: true }),
    httpsAgent: new https.Agent({ keepAlive: true }),

    // 'proxy' 定义代理服务器的主机名称和端口
    // `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
    // 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
    proxy: {
    host: '127.0.0.1',
    port: 9000,
    auth: {
    username: 'mikeymike',
    password: 'rapunz3l'
    }
    },

    // `cancelToken` 指定用于取消请求的 cancel token
    // (查看后面的 Cancellation 这节了解更多)
    cancelToken: new CancelToken(function (cancel) {
    })
    }
  • 全局的axios配置axios.defaults

    1
    axios.defaults.baseURL = 'https://api.example.com';
  • axios实例的默认配置axios.create().defaults

    1
    2
    3
    4
    const instance = axios.create({
    baseURL: 'https://api.example.com'
    });
    instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
  • axios配置的优先级

    1. 请求的 config 参数
    2. 实例的 defaults 属性
    3. lib/defaults.js 找到的库的默认值

7.4 axios拦截器

1. 请求拦截器

1
2
3
4
5
6
7
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});

2. 响应拦截器

1
2
3
4
5
6
7
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});

3. 移除拦截器

1
2
const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

7.5 axios二次封装

  1. 目录结构说明

    1
    2
    3
    4
    5
    6
    7
    src: 
    api:
    index.js: 作为API总入口
    common:
    http.js: 在该模块中封装多域名的axios服务
    servers:
    xxx.js: 表示具体的http请求
  2. 首先对axios进行封装:/src/api/common/http.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    import axios from "axios";
    import qs from 'qs'

    // 举例:Order域名服务
    const httpOrder = axios.create({
    baseURL: orderBaseUrl(),
    timeout: 10 * 1000
    })
    // 举例:这些配置可以提取到对应环境的配置文件中
    function orderBaseUrl(){
    switch (process.env.NODE_ENV) {
    case 'production':
    return 'http://localhost:8080/order';
    case 'development':
    return 'http://localhost:8081/order';
    case 'test':
    return 'http://localhost:8082/order';
    default:
    console.log("环境变量配置错误")
    throw Error('orderBaseUrl 环境变量配置错误')
    }
    }

    // 举例:User域名服务
    const httpUser = axios.create({
    baseURL: userBaseUrl(),
    timeout: 10 * 1000
    })
    // 举例:这些配置可以提取到对应环境的配置文件中
    function userBaseUrl(){
    switch (process.env.NODE_ENV) {
    case 'production':
    return 'http://localhost:9080/user';
    case 'development':
    return 'http://localhost:9081/user';
    case 'test':
    return 'http://localhost:9082/user';
    default:
    console.log("环境变量配置错误")
    throw Error('userBaseUrl 环境变量配置错误')
    }
    }

    export default {
    httpOrder,
    httpUser
    }
  3. 举例:order服务的order模块添加http请求->getOrder

    1
    2
    3
    4
    5
    6
    import http from '../common/http'
    const getOrder = data => http.httpOrder.get('/order',{data})

    export default {
    getOrder
    }
  4. 举例:order服务的order模块添加http请求->addUser

    1
    2
    3
    4
    5
    6
    import http from '../common/http2'
    const addUser = data => http.httpUser.post('/user',{data})

    export default {
    addUser
    }
  5. 通过api.js将多个http模块进行统一导出

    1
    2
    3
    4
    5
    6
    import order from './servers/order'
    import user from './servers/user'

    export default {
    order,user
    }
  6. 将api挂载到Vue实例之上:main.js

    1
    2
    import api from './api'
    Vue.prototype.$api=api
  7. 举例:通过$api发送对应模块的请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <template>
    <div id="app">
    <button @click="getOrder">getOrder</button>
    <button @click="addUser">user</button>
    </div>
    </template>

    <script>
    export default {
    name: 'App',
    methods:{
    getOrder(){
    this.$api.order.getOrder({id:1})
    },
    addUser(){
    this.$api.user.addUser({name:'zone'})
    }
    }
    }
    </script>

7.6 axios封装案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import axios from 'axios'
import { Notify } from 'vant'
// import Vue from 'vue'
// import store from '@/store' // 我此项目没有用到vuex,所以vuex代码的都注释了,需要的自己打开使用

// import {ACCESS_TOKEN} from '@/store/mutation-types'

// 创建 axios 实例
const requests = axios.create({
baseURL: process.env.VUE_APP_API, // 基础url,如果是多环境配置这样写,也可以像下面一行的写死。
// baseURL: 'http://168.192.0.123',
timeout: 6000 // 请求超时时间
})

// 错误处理函数
const err = (error) => {
if (error.response) {
const data = error.response.data
// const token = Vue.ls.get(ACCESS_TOKEN)
if (error.response.status === 403) {
Notify({
type: 'danger',
message: data.message || data.msg
})
}
if (error.response.status === 401) {
Notify({
type: 'danger',
message: '你没有权限。'
})
// if (token) {
// store.dispatch('Logout').then(() => {
// setTimeout(() => {
// window.location.reload()
// }, 1500)
// })
// }
}
}
return Promise.reject(error)
}

// request interceptor(请求拦截器)
requests.interceptors.request.use(config => {
// const token = Vue.ls.get(ACCESS_TOKEN)
const token = localStorage.getItem('token')
if (token) {
config.headers['token'] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
}
return config
}, err)

// response interceptor(接收拦截器)
requests.interceptors.response.use((response) => {
const res = response.data
if (res.code !== 0 && res.code !== 200) {
Notify({
type: 'danger',
message: res.message || res.msg
})
// 401:未登录;
if (res.code === 401 || res.code === 403 || res.code === 999) {
Notify({
type: 'danger',
message: '请登录'
})
}
return Promise.reject('error')
} else {
return res
}
}, err)

export default {
requests
}

第八章 Vite+Vue3+TS

  1. 下载安装Vite

    1
    2
    npm i -g create-vite-app@1.18.0
    yarn global add create-vite-app@1.18.0
  2. 整合element-plus

    • 安装element-plus

      1
      2
      3
      npm install element-plus --save
      yarn add element-plus
      pnpm install element-plus
    • 完整引入:main.ts

      1
      2
      3
      4
      5
      6
      7
      8
      9
      import { createApp } from 'vue'
      import ElementPlus from 'element-plus'
      import 'element-plus/dist/index.css'
      import App from './App.vue'

      const app = createApp(App)

      app.use(ElementPlus)
      app.mount('#app')
    • 按需导入:【推荐】

      1. 首先需要下载两个插件

        1
        npm install -D unplugin-vue-components unplugin-auto-import
      2. 修改Vite配置文件

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        import AutoImport from 'unplugin-auto-import/vite'
        import Components from 'unplugin-vue-components/vite'
        import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

        export default {
        plugins: [
        // ...
        AutoImport({
        resolvers: [ElementPlusResolver()],
        }),
        Components({
        resolvers: [ElementPlusResolver()],
        }),
        ],
        }
  3. 配置Network:修改vite配置文件server配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'

    // https://vitejs.dev/config/
    export default defineConfig({
    plugins: [vue()],
    server:{
    host: '0.0.0.0',
    port: 18080,
    open: true
    }
    })
  4. 配置vue-router

    • 安装vue-router@4

      1
      npm install vue-router@4
打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2020-2022 xiaoliuxuesheng
  • PV: UV:

老板,来一杯Java

支付宝
微信