学习Vue.js


0. ES6补充

0.1 var 没有块级作用域

iffor 中会出错


image-20220301165743759







image-20220301182223932

0.2 const 的使用

  • 使用 const 修饰的标识符为常量, 不可以再次赋值.

  • 当我们修饰的标识符不会被再次赋值时, 就可以使用 const 来保证数据的安全性.

  • 建议: 在ES6开发中优先使用 const, 只有需要改变某一个标识符的时候才使用 let.

  • image-20220301192236087

  • image-20220301192325647

  • image-20220301192547339

0.3 ES6 对象字面量增强写法

  1. 属性的增强写法

        // 1. 属性的增强写法
        const name = 'why';
        const age = 18;
        const height = 1.88;
    
        // ES5的写法
        const  obj1 = {
            name: name,
            age: age,
            height: height,
        }
    
        // ES6的写法
        const onj2 = {
            name, age, height
        }
    
  2. 方法的属性增强

        /*2. 方法的属性增强*/
        // ES5的写法
        const ob1 = {
            run: function () {
    
            }
        }
    
        // ES6的写法
        const ob2 = {
            run() {
                
            }
        }
    

1. 介绍

Vue (读音 /vju?/,类似于 view) 是一套用于构建用户界面的渐进式框架

  • 渐进式意味着可以将 Vue 作为应用的一部分嵌入其中,带来更丰富的交互体验。

2. Vue.js初体验

2.1 第一个Vue.js程序




    
    Title


{{message}}

image-20220227170229059

2.2 Vue列表的展示




    
    Title



{{message}}

  • {{movie}}

image-20220227212250428

2.3 计数器

  • 实现功能:小的计数器
    • 点击 + 计数器加1
    • 点击 - 计数器减1



    
    Title



当前记数: {{counter}}

image-20220227213529130

3. Vue中的MVVM

3.1 什么是MVVM

MVVMModel–view–viewmodel的缩写,是一种软件架构模式。

MVVM 有助于将 图形用户界面 的开发与 业务逻辑 或 后端逻辑(数据模型)的开发开来,这是通过 置标语言 或GUI代码实现的。MVVM视图模型是一个值转换器,这意味着视图模型负责从模型中暴露(转换) 数据对象 ,以便轻松管理和呈现对象。在这方面,视图模型比视图做得更多,并且处理大部分视图的显示逻辑。视图模型可以实现 中介者模式 ,组织对视图所支持的 用例 集的后端逻辑的访问。

  • 模型
    • 模型是指代表真实状态内容的领域模型(面向对象),或指代表内容的数据访问层(以数据为中心)。
  • 视图
    • 就像在 MVCMVP 模式中一样,视图是用户在屏幕上看到的结构、布局和外观(UI)
  • 视图模型
    • 视图模型是暴露公共属性和命令的视图的抽象。MVVM没有MVC模式的控制器,也没有MVP模式的presenter,有的是一个绑定器。在视图模型中,绑定器在视图和数据绑定器之间进行通信。
  • 绑定器
    • 声明性数据和命令绑定隐含在 MVVM 模式中。在Microsoft解决方案堆中,绑定器是一种名为 XAML 的标记语言。 绑定器使开发人员免于被迫编写样板式逻辑来同步视图模型和视图。在微软的堆之外实现时,声明性数据绑定技术的出现是实现该模式的一个关键因素。

3.2 Vue中的MVVM

4. Vue的生命周期

img

可以看到在 vue 整个生命周期中会有很多 **钩子函数 **提供给我们在 vue 生命周期不同的时刻进行操作:

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed



  
  
  
  vue生命周期学习
  


  

{{message}}

image-20220228181528411

  1. beforeCreatecreated 钩子函数之间的生命周期
    • 在这个生命周期之间,进行初始化事件,进行数据的观测,可以看到在 created 的时候数据已经和 data 属性进行绑定(放在 data 中的属性当值发生改变的同时,视图也会改变)。
    • 注意看下:此时还是没有 el 选项
  2. created钩子函数和 beforeMount间的生命周期
    • 首先会判断对象是否有el选项如果有的话就继续向下编译,如果没有el选项,则停止编译,也就意味着停止了生命周期,直到在该vue实例上调用vm.$mount(el)。

      • 如果注释掉代码 el: '#app', 运行可以到 created 的时候就停止了:

      • img

      • 如果我们在后面继续调用 vm.$mount(el), 可以发现代码继续向下执行了

      • img

      • 我们往下看,template 参数选项的有无对生命周期的影响。

        • 如果 vue 实例对象中有 template 参数选项,则将其作为模板编译成 render 函数。

        • 如果没有 template 选项,则将外部 HTML 作为模板编译。

        • 可以看到 template 中的模板优先级要高于 outer HTML 的优先级。

        • 举例:在HTML结构中增加了一串html,在vue对象中增加了template选项

        • 
          
          
            
            
            
            vue生命周期学习
            
          
          
            

          {{message + '这是在outer HTML中的'}}

        • 执行后的结果可以看到在页面中显示的是:

        • img

        • vue 对象中 template 的选项注释掉后打印如下信息:

        • img

        • 在vue对象中还有一个render函数,它是以createElement作为参数,然后做渲染操作,而且我们可以直接嵌入JSX.

        • new Vue({
              el: '#app',
              render: function(createElement) {
                  return createElement('h1', 'this is createElement')
              }
          })
          
        • img

      • 所以综合排名优先级:render 函数选项 > template 选项 > outer HTML.

  3. beforeMountmounted 钩子函数间的生命周期
    • img
    • vue 实例对象添加 el成员,并且替换掉挂在的DOM元素。因为在之前console中打印的结果可以看到 **beforeMount **之前 el 上还是 undefined
  4. mounted
    • img
    • mounted 之前 h1 中还是通过 {{message}}进行占位的,因为此时还有挂在到页面上,还是JavaScript中的虚拟DOM形式存在的。在 mounted 之后可以看到 h1 中的内容发生了变化
  5. beforeUpdate 钩子函数和 updated 钩子函数间的生命周期
    • img
    • 当vue发现data中的数据发生了改变,会触发对应组件的重新渲染,先后调用beforeUpdateupdated钩子函数。我们在console中输入 vm.message = '触发组件更新'
    • img
  6. beforeDestroydestroyed 钩子函数间的生命周期
    • img
    • beforeDestroy钩子函数在实例销毁之前调用。在这一步,实例仍然完全可用。
    • destroyed钩子函数在Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁

5. Vue基础语法

5.1 Mustache语法

Mustache 语法即 Vue 中的 {{ }} 语法。

image-20220228162835910

5.2 v-once指令

  • 该指令后不需要跟任何表达式
  • 该指令表示元素和组件只渲染一次,不会随着数据的改变而改变。

{{ message }}

{{ message }}

image-20220228163236610

5.3 v-html指令

解析带有 html 标签的字符串 v-html=""

image-20220228184820786

5.4 v-text指令

v-text 指令的作用与 Mustache 相似,都是用于将数据显示在界面中。

接收的数据类型为 string

image-20220228185237906

5.5 v-pr指令

不解析 {{}} 内部的语句

image-20220228192629843

5.6 v-cloak指令

  • vue 解析之前,div 中存在属性 v-clock
  • vue 解析之后,div 中没有属性 v-clock

5.7 v-bind指令

作用:动态绑定属性

  • 例如动态绑定 a 元素的 href 属性;
  • 例如动态绑定 img 元素的 src 属性;

可以使用 : 代替 v-bind

5.7.1 v-bind简单使用

image-20220228195241621

5.7.2 v-bind动态绑定class

  • 对象语法

    • image-20220228202527463
    • image-20220228203514707
  • 数组语法

    • {{message}}

    • image-20220228203813692

5.8.3 v-bind动态绑定style

对象语法

{{message}}

{{message}}

{{message}}

image-20220301152057665

6.2 计算属性的复杂操作

总价格: {{totalPrice1}}

总价格: {{totalPrice2}}

image-20220301154513741

6.3 计算属性的 setter 和 getter

image-20220301162227779

6.4 计算属性和methods的区别

  • 计算属性会判断数据的值是否发生变化,如果没有发生变化,则不会重新执行,使用缓存的数据;
  • methods 调用几次执行几次,不会去判断数据的值是否发生了改变。
  • 使用 methods

    • {{getFullName()}}

      {{getFullName()}}

      {{getFullName()}}

      {{getFullName()}}

    • image-20220301163131677

    • methods 调用了 4 次

  • 使用计算属性

    • {{fullName}}

      {{fullName}}

      {{fullName}}

      {{fullName}}

    • image-20220301163324978

    • 只有一次

7. v-on

7.1 v-on的基本使用和语法糖

在前端开发中,需要经常和用户交互。在这个时候,就需要监听用户发出的指令,如点击、拖拽、键盘时间等。

Vue 中监听事件使用 v-on 指令。

v-on 介绍:

  • 作用:绑定事件监听器
  • 缩写: @

image-20220301200322508

7.2 v-on的参数传递



  • 如果该方法不需要额外参数,那么方法后的 () 可以不添加。

    •     
          
          
      
    • image-20220301200839221

  • 如果方法本身中有一个参数,那么会默认将原生事件 event 参数传递进去

    •     
          
          
      
    • image-20220301201303543

  • 如果需要同时传入某个参数,同时需要 event 时,可以通过 $event 传入事件

    •     
          
          
          
          
      
    • image-20220301202133531

7.3 v-on修饰符

  • 在某些情况下,我们拿到 event 的目的可能是进行一些事件处理。
  • Vue 提供了修饰符来帮助我们方便的处理一些事件:
    • .stop - 调用 event.stopPropagation()
    • .prevent - 调用 event.preventDefault()
    • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
    • .native - 监听组件根元素的原生事件。
    • .once - 只触发一次回调。

image-20220301202955018

8. v-if 条件判断

8.1 v-if和v-else-if和v-else的使用

image-20220301210056659

8.2 登录切换的小案例

切换账户登录跟邮箱登录

image-20220301212254794

问题描述:

  • 如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入的内容。
  • 但是按道理讲,我们应该切换到另外一个 input 元素中了。
  • 在另一个 input 元素中,我们并没有输入内容。
  • 为什么会出现这个问题呢?

问题解答:

  • 这是因为 Vue 在进行 DOM 渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。
  • 在上面的案例中,Vue 内部会发现原来的 input 元素不再使用,直接作为 else 中的 input 来使用了。

解决方案

  • 如果我们不希望 Vue 出现类似重复利用的问题,可以给对应的 input 添加 key
  • 并且我们需要保证 key 的不同

8.3 v-show

8.3.1 v-show的使用

v-show 的用法和 v-if 非常相似,也用于决定一个元素是否渲染:

8.3.2 v-show与v-if的对比

  • v-ifv-show 都可以决定一个元素是否渲染,那么开发中我们如何选择呢?
  • v-if 当条件为 false 时,压根不会有对应的元素在 DOM 中。
  • v-show 当条件为 false 时,仅仅是将元素的 display 属性设置为 none 而已。

{{message}}

{{message}}

{{message}}, !isShow

{{message}}, !isShow

image-20220302110204417

开发中如何选择呢?

  • 当需要在显示与隐藏之间切片很频繁时,使用 v-show
  • 当只有一次切换时,通过使用 v-if

9. v-for 循环

9.1 v-for遍历数组

image-20220302151437869

9.2 v-for遍历对象

image-20220302152124014

9.3 v-for绑定和非绑定key

image-20220302152450275

所以一句话,key的作用主要是为了高效的更新虚拟DOM。

数组渲染之后-在中间插入元素的渲染过程

9.4 数组中的哪些方式是响应式的

  • 因为 Vue 是响应式的,所以当数据发生变化时,Vue 会自动检测数据变化,视图会发生对应的更新。
  • Vue 中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新
    • push():从数组尾部添加元素
    • pop():删除数组最后一个元素
    • shift(): 删除数组的第一个元素
    • unshift(): 从数组头部添加元素
    • splice():用于添加或删除数组中的元素。
      • splice(index,howmany,item1,.....,itemX)
    • sort()
    • reverse()

示例代码

  • {{item}}
  • push()

    •     methods: {
              btnClick() {
                  /*1 push方法*/
                  this.letters.push(this.counter++);
              }
          }
      
    • image-20220302154450402

10. 购物车案例

10.1 搭建页面




    
    Title
    



书籍名称 出版日期 价格 购买数量 操作
{{book.id}} {{book.name}} {{book.date}} ¥{{book.price | showPrice}} {{book.count}}

总价格:¥{{totalPrice}}

购物车为空

style.css

table {
    border: 1px solid #e9e9e9;
    border-collapse: collapse;
    border-spacing: 0;
}

th, td {
    padding: 8px 16px;
    border: 1px solid #e9e9e9;
    text-align: left;
}

th {
    background-color: #f7f7f7;
    color: #5c6b77;
    font-weight: 600;
}

10.2 过滤器

const vue = new Vue({
    el: '#app',
    data: {
        books: [
            {
                id: 1,
                name: '《算法导论》',
                date: '2006-9',
                price: 85.00,
                count: 1
            },
            {
                id: 2,
                name: '《UNIX编程艺术》',
                date: '2006-2',
                price: 59.00,
                count: 1
            },
            {
                id: 3,
                name: '《编程珠玑》',
                date: '2008-10',
                price: 39.00,
                count: 1
            },
            {
                id: 4,
                name: '《代码大全》',
                date: '2006-3',
                price: 128.00,
                count: 1
            },
        ],
    },
    /*过滤器*/
    filters: {
        showPrice(price) {
            return price.toFixed(2);
        }
    }
})

10.3 改变商品数量+移除功能

const vue = new Vue({
    el: '#app',
    data: {
        ......
    }
    methods: {
        increment(index) {
            this.books[index].count++;
        },
        decrement(index) {
            this.books[index].count--;
        },
        removeClick(index) {
            this.books.splice(index, 1);
        },
    },
    /*过滤器*/
    filters: {
        showPrice(price) {
            return price.toFixed(2);
        }
    }
})

10.4 计算总价格

const vue = new Vue({
    el: '#app',
    data: {
        books: [
            ......
            ]
    },
    computed: {
        totalPrice() {
            let ans = 0;
            for (let i=0;i

11. v-model 表单绑定

11.1 JavaScript高阶函数的使用

  1. filter函数

    • const nums = [10, 20, 111, 222, 444, 40, 50]
      /*filter中的回调函数必须返回boolean值
      * 返回true时 函数内部会自动将这次回调的n加入到新的数组中
      * 返回false时, 函数内部会过滤掉n*/
      let newNums = nums.filter(function (n) {
          return n <= 100;
      })
      console.log(newNums);
      
      /*[10,20,40,50]*/
      
  2. map 函数

    • const newNums = [10,20,40,50]
      let newNums2 = newNums.map(function (n) {
          return n * 2;
      })
      /*[20,40,80,100]*/
      
  3. reduce函数

    • /*3. reduce  对数组中所有内容进行汇总*/
      newNums2.reduce(function (preValue, n) {
          return preValue + n;
      }, 0)
      // 第一次 preValue: 0  n: 20
      // 第二次 preValue: return的返回值 n: 40
      

11.2 v-model的使用与原理

  • 表单控件在实际开发中是非常常见的。特别是对于用户信息的提交,需要大量的表单。
  • Vue 中使用 v-model 指令来实现表单元素和数据的双向绑定。
  • 案例场景
    • 当我们在输入框输入内容时
    • 因为 input 中的 v-model 绑定了 message ,所以会实时将输入的内容传递给 messagemessage发生改变。
    • message 发生改变时,因为上面我们使用 Mustache 语法,将 message 的值插入到 DOM 中,所以DOM 会发生响应的改变。
    • 所以,通过 v-model 实现了双向的绑定。

image-20220302204643052

11.3 v-model原理

  • v-model 其实是一个语法糖,它的背后本质上是包含两个操作:
    1. v-bind 绑定一个 value 属性
    2. v-on 指令给当前元素绑定 input 事件

image-20220302205322339

进阶:

{{message}}

image-20220302205503570

11.4 v-model结合radio类型

  • 您选择的性别是: {{gender}}

  • image-20220302211502117

11.5 v-model结合checkbox

  • 单选框

    •     
          
          

      您选择的是: {{isAgree}}

  • image-20220302212138122image-20220302212148797

  • 复选框

    • 篮球 足球 羽毛球 乒乓球

      您选择是:{{hobbies}}

    • image-20220302212509233

  • 11.6 v-model结合selcet

    • 选择一个

      • 您选择的水果是: {{fruit}}

      • image-20220302213403629

    • 多选

      • 您选择的水果是: {{fruits}}

      • image-20220302213545069

    11.7 v-model值绑定

    动态的给 value 赋值而已

    • 我们前面的 value 中的值,可以回头去看一下,都是在定义 input 的时候直接给定的。
    • 但是真实开发中,这些 input 的值可能是从网络获取或定义在 data 中的。
    • 所以我们可以通过 v-bind:value 动态的给 value 绑定值。

    您选择的是: {{fruits}}

    image-20220303102535908

    11.8 v-model修饰符的私用

    1. lazy 修饰符:

      • 默认情况下,v-model 默认是在 input 事件中同步输入框的数据的。

      • 也就是说,一旦有数据发生改变对应的 data 中的数据就会自动发生改变。

      • lazy 修饰符可以让数据在失去焦点或者回车时才会更新:

      • {{message}}

    2. number 修饰符:

      • 默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。

      • 但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。

      • number 修饰符可以让在输入框中输入的内容自动转成数字类型:

      • {{age}} ---- {{typeof age}}

    3. trim 修饰符:

      • 如果输入的内容首尾有很多空格,通常我们希望将其去除

      • trim 修饰符可以过滤内容左右两边的空格

      • 您输入的name是:{{name}}

    12. 组件化开发

    12.1 认识组件化

    12.1.1 什么是组件化

    • 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
    • 但如果,我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。

    image-20220303110223959

    12.1.2 Vue.js组件思想

    组件化是 Vue.js 中的重要思想

    • 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。
    • 任何的应用都会被抽象成一颗组件树。

    image-20220303110421075

    组件化思想的应用:

    • 有了组件化的思想,我们在之后的开发中就要充分的利用它。
    • 尽可能的将页面拆分成一个个小的、可复用的组件。
    • 这样让我们的代码更加方便组织和管理,并且扩展性也更强。

    12.2 注册组件

    12.2.1 注册组件的基本步骤

    组件的使用分成三个步骤:

    • 创建组件构造器
    • 注册组件
    • 使用组件。

    image-20220303111028675

    12.2.2 注册组件步骤解析

    1. Vue.extend()
      • 调用 Vue.extend() 创建的是一个组件构造器。
      • 通常在创建组件构造器时,传入 template 代表我们自定义组件的模板。
      • 该模板就是在使用到组件的地方,要显示的 HTML 代码。
      • 事实上,这种写法在 Vue2.x 的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。
    2. Vue.component()
      • 调用 Vue.component() 是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
      • 所以需要传递两个参数:1、注册组件的标签名 2、组件构造器。
    3. 组件必须挂载在某个 Vue 实例下

    image-20220303132133289

    12.3 其他组件补充

    12.3.1 全局组件和局部组件

    • 当我们通过调用 Vue.component() 注册组件时,组件的注册是全局的
      • 这意味着该组件可以在任意 Vue 示例下使用。
    • 如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件

    image-20220303143412912

    12.3.2 父组件和子组件的区分

    ------

    image-20220303145923255

    image-20220303145930164

    12.3.3 注册组件的语法糖写法

    • 全局组件的语法糖

      • 
        
    • 局部组件的语法糖

      • 
        

    12.3.4 组件模板分离

    • 方式一:

      • 
        
        
        
    • 方式二:

      • 
        
        
        
        
        

    12.4 组件不能访问Vue实例数据

    • 组件是一个单独功能模块的封装:
      • 这个模块有属于自己的 HTML 模板,也应该有属性自己的数据 data

    组件是不能直接访问 Vue 实例中的数据的

    12.4.1 组件的 data 属性

    Vue 组件应该有自己保存数据的地方

    • 组件对象也有一个 data 属性(也可以有 methods等属性,下面我们有用到)
    • 只是这个 data 属性必须是一个函数
    • 而且这个函数返回一个对象,对象内部保存着数据

    image-20220303200643430

    12.4.2 组件不能访问Vue示例数据的原因

    在于 Vue 让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响

    image-20220303210424280

    13. 父子组件通信

    子组件是不能引用父组件或者Vue实例的数据的。

    但是,在开发中,往往一些数据确实需要从上层传递到下层:

    • 比如在一个页面中,我们从服务器请求到了很多的数据。
    • 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
    • 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)

    如何进行父子组件间的通信呢

    • 通过 props 向子组件传递数据
    • 通过事件向父组件发送消息

    image-20220303211430472

    13.1 父传子 props

    13.1.1 使用方法

    在组件中,使用选项 props 来声明需要从父级接收到的数据。

    props 的值有两种方式:

    • 方式一:字符串数组,数组中的字符串就是传递时的名称。
    • 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。推荐

    方式一:

    image-20220303213816650

    传对象

    支持的数据类型:

    • String
    • Number
    • Boolean
    • Array
    • Object
    • Date
    • Function
    • Symbol

    image-20220304104137256

    13.1.2 props驼峰标识

    不支持驼峰标识

    image-20220304105102914

    解决方法

    ? cInfo 改为 c-info

    
    

    image-20220304105251852

    image-20220304105311525

    image-20220304110941068

    13.2 子传父

    • props 用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中。
    • 我们应该如何处理呢?这个时候,我们需要使用自定义事件来完成。
    • 什么时候需要自定义事件呢?
      • 当子组件需要向父组件传递数据时,就要用到自定义事件了。
      • 我们之前学习的 v-on 不仅仅可以用于监听 DOM 事件,也可以用于组件间的自定义事件。
    • 自定义事件的流程:
      • 在子组件中,通过 $emit() 来触发事件。
      • 在父组件中,通过 v-on 来监听子组件事件。
    
    

    13.3 父子间通信案例1

    • 父组件通过 propscounter 传递给子组件;
    • 子组件通过自定义事件 add-counterdecrement-counter 将点击事件传递给父组件。
    
    

    image-20220304125142083

    13.4 父子间通信案例2

    注意:

    • 子组件中 number1number2 的值初始来源于 num1num2,将 number1number2 双向绑定的时候不能直接绑定 number1number2,需要找一个中间值。

    image-20220304151244724

    13.5 父子组件的访问方式

    有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件。

    • 父组件访问子组件:使用 $children$refs
    • 子组件访问父组件:使用 $parent

    13.5.1 children 方式(不推荐)

    • image-20220304161011762

    13.5.2 refs方式

    image-20220304161619050

    image-20220304161551590

    14. 插槽slot

    组件的插槽

    • 组件的插槽也是为了让我们封装的组件更加具有扩展性。
    • 让使用者可以决定组件内部的一些内容到底展示什么

    14.1 插槽的基本使用

    1. 插槽的基本使用
    2. 插槽的默认值 xxxxx
    3. 如果有多个值,同时放入到组件中进行替换时,一起作为替换元素
    哈哈 呵呵 hahahaha yiyiyiyi
    divdiv

    ppppppppp

    image-20220304184105122

    14.2 具名插槽

    slot 元素一个 name 属性即可

    标题

    image-20220304191712589

    14.3 编译作用域

    父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。

    image-20220304192551618

    14.4 作用域插槽的使用

    目的:父组件替换插槽的标签,但是内容由子组件来提供。

    我们先提一个需求:

    • 子组件中包括一组数据,比如:Languages: ['JavaScript', 'Python', 'Swift', 'Go', 'C++']
    • 需要在多个界面进行展示:
      • 某些界面是以水平方向一一展示的,
      • 某些界面是以列表形式展示的,
      • 某些界面直接展示一个数组
    • 内容在子组件,希望父组件告诉我们如何展示,怎么办呢?
      • 利用 slot 作用域插槽就可以了

    image-20220304194428082

    image-20220304194438906

    15. webpack

    15.1 什么是webpack

    webpack是一个现代的JavaScript应用的静态 模块打包 工具。

    img

    从图中我们可以看出,Webpack 可以将多种静态资源 js、css、less 转换成一个静态文件,减少了页面的请求。

    cnpm install webpack@3.6.0 -g

    webpack --version

    15.2 webpack基本使用

    webpack 3.6.0 的打包命令:

    webpack ./src/main.js ./dist/bundle.js

    // 1.使用commonjs的模块化规范
    const {add, mul} = require('./mathUtil.js')
    
    console.log(add(20, 30));
    console.log(mul(20, 30));
    
    // 2. 使用ES6的模块化规范
    import {
        name, age, height
    } from "./info";
    
    console.log(name);
    console.log(age);
    console.log(height)
    

    15.3 webpack.config.js 和 package.json 配置

    如果每次使用 webpack 的命令都需要写上入口和出口作为参数,就非常麻烦,有没有一种方法可以将这两个参数写到配置中,在运行时,直接读。

    1. npm init

      • 会创建 package.json 文件
    2. 新建 webpack.config.js 文件

      • const path = require('path')
        
        module.exports = {
            entry: './src/main.js',/*入口*/
            output: {
                path: path.resolve(__dirname, 'dist'),/*路径  这里只能填绝对路径*/
                filename: 'bundle.js', /*文件名*/
            },/*出口*/
        }
        
    3. 进阶:使用 npm run build 命令代替 webpack

      • 配置 package.json

      • {
          "name": "meetwebpack",
          "version": "1.0.0",
          "description": "",
          "main": "index.js",
          "scripts": {
            "test": "echo \"Error: no test specified\" && exit 1",
            "build": "webpack" // 添加命令  默认先运行本地的webpack 而非全局webpack
          },
          "author": "",
          "license": "ISC"
        }
        
        

    15.4 webpack中使用css文件的配置

    1. webpack 主要是用来处理编写的 js 代码
      • 但是开发中不仅仅需要处理 js 文件,还需要加载 css、图片等。这个时候就需要给 webpack 扩展对应的 loader 即可。
    2. loader 使用过程
      1. 通过 npm 安装需要使用的 loader
      2. webpack.config.js 中的 mudule 关键字下进行配置

    15.4.1 css-loader

    • 新建一个 css 文件 normal.css

      • body {
            background-color: antiquewhite;
        }
        
    • main.js 中引入该 css文件

      • // 3. 依赖css文件
        require('./css/normal.css')
        
    • 安装 css-loaderstyle-loader

      • npm install --save-dev style-loader
        npm install --save-dev css-loader
        
    • 配置 webpack.config.js

      • const path = require('path')
        
        module.exports = {
            /*处理Js文件*/
            entry: './src/main.js',/*入口*/
            output: {
                path: path.resolve(__dirname, 'dist'),/*路径  这里只能填绝对路径*/
                filename: 'bundle.js', /*文件名*/
            },/*出口*/
            /*处理CSS文件*/
            module: {
                rules: [
                    {
                        test: /\.css$/i,
                        /*css-loader只负责加载css文件
                        style-loader负责将样式添加到DOM中
                        使用多个loader的时候 读取顺序:从右向左
                        * */
                        use: ["style-loader", "css-loader"],
                    },
                ],
            },
        }
        
    • css-loader 只负责加载 css 文件
    • style-loader 负责将样式添加到 DOM
    • 使用多个 loader 的时候 读取顺序:从右向左

    15.4.2 less文件使用

    1. 新建 special.less 文件

      • @fontSize: 50px;
        @fontColor: orange;
        
        body {
          font-size: @fontSize;
          color: @fontColor;
        }
        
        
    2. main.js 中引入该 less文件

      • // 4. 依赖less文件
        require('./css/special.less')
        
    3. 安装 less-loader

      • npm install less less-loader --save-dev
        # 注意对应版本号
        
    4. 配置 webpack.config.js

      • const path = require('path')
        
        module.exports = {
            /*处理Js文件*/
            entry: './src/main.js',/*入口*/
            output: {
                path: path.resolve(__dirname, 'dist'),/*路径  这里只能填绝对路径*/
                filename: 'bundle.js', /*文件名*/
            },/*出口*/
            /*处理CSS文件*/
            module: {
                rules: [
                    {
                        test: /\.css$/i,
                        /*css-loader只负责加载css文件
                        style-loader负责将样式添加到DOM中
                        使用多个loader的时候 读取顺序:从右向左
                        * */
                        use: ["style-loader", "css-loader"],
                    },
                    {
                        test: /\.less$/i,
                        use: [
                            {
                                loader: 'style-loader',
                            },
                            {
                                loader: 'css-loader',
                            },
                            {
                                loader: 'less-loader',
                            },
                        ],
                    },
                ],
            },
        }
        
    5. image-20220305110113185

    15.4.3 webpack图片文件的处理

    1. 添加 图片 文件

    2. 修改 css 文件

      • body {
            background: url("../img/avatar.jpg");
        }
        
    3. 安装 file-loader

      • npm install file-loader --save-dev
        
    4. 配置 webpack.config.js

      • const path = require('path')
        
        module.exports = {
            /*处理Js文件*/
            entry: './src/main.js',/*入口*/
            output: {
                path: path.resolve(__dirname, 'dist'),/*路径  这里只能填绝对路径*/
                filename: 'bundle.js', /*文件名*/
                publicPath: 'dist/', /*配置上这个以后涉及到url的操作会自动把这个路径添加进去*/
            },/*出口*/
            /*处理CSS文件*/
            module: {
                rules: [
                    {
                        test: /\.css$/i,
                        /*css-loader只负责加载css文件
                        style-loader负责将样式添加到DOM中
                        使用多个loader的时候 读取顺序:从右向左
                        * */
                        use: ["style-loader", "css-loader"],
                    },
                    {
                        test: /\.less$/i,
                        use: [
                            {
                                loader: 'style-loader',
                            },
                            {
                                loader: 'css-loader',
                            },
                            {
                                loader: 'less-loader',
                            },
                        ],
                    },
                    {
                        test: /\.(png|jpg|gif)$/,
                        use: [
                            {
                                loader: 'file-loader',
        
                                options: {
                                    name: 'img/[name].[hash:8].[ext]' /*文件的路径已经名字*/
                                },
                                // mode: 'development',
                            },
                        ],
                    },
                ],
            },
        }
        
      • image-20220305122848278

    15.4.4 ES6语法处理

    如果希望将ES6的语法转成ES5,那么就需要使用 babel

    npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
    

    配置 webpack.config.js

    image-20220305123202513

    15.4.5 wepack配置vue

    • cnpm install vue@2.5.21 --save
      '@2.5.21' 指的是vue的版本号 
      
    • main.js

      • // 5. 使用Vue进行开发
        import Vue from 'vue'
        
        const app = new Vue({
            el: '#app',
            data: {
                message: 'Hello webpack',
            }
        })
        
    • 出现问题

      • image-20220305125931313
    • 解决方法

      • webpack.config.js

      • const path = require('path')
        
        module.exports = {
            /*处理Js文件*/
            entry: './src/main.js',/*入口*/
            output: {
                path: path.resolve(__dirname, 'dist'),/*路径  这里只能填绝对路径*/
                filename: 'bundle.js', /*文件名*/
                publicPath: 'dist/', /*配置上这个以后涉及到url的操作会自动把这个路径添加进去*/
            },/*出口*/
            /*处理CSS文件*/
            module: {
                rules: [
                    {
                        test: /\.css$/i,
                        /*css-loader只负责加载css文件
                        style-loader负责将样式添加到DOM中
                        使用多个loader的时候 读取顺序:从右向左
                        * */
                        use: ["style-loader", "css-loader"],
                    },
                    {
                        test: /\.less$/i,
                        use: [
                            {
                                loader: 'style-loader',
                            },
                            {
                                loader: 'css-loader',
                            },
                            {
                                loader: 'less-loader',
                            },
                        ],
                    },
                    {
                        test: /\.(png|jpg|gif)$/,
                        use: [
                            {
                                loader: 'file-loader',
        
                                options: {
                                    name: 'img/[name].[hash:8].[ext]' /*文件的路径已经名字*/
                                },
                                // mode: 'development',
                            },
                        ],
                    },
                ],
            },
            /*指明vue使用runtime-compile版本*/
            resolve: {
                alias: {
                    'vue$': 'vue/dist/vue.esm.js',
                }
            }
        }
        

    15.4.6 el与template的区别

    • 在前面的 Vue 实例中,我们定义了 el 属性,用于和 index.html 中的 #app 进行绑定,让 Vue 实例之后可以管理它其中的内容
    • 这里,我们可以将 div 元素中的 {{message}} 内容删掉,只保留一个基本的 iddiv 的元素
    • 但是如果我依然希望在其中显示 {{message}} 的内容,应该怎么处理呢?
    • 我们可以再定义一个 template属性,代码如下:
    // 5. 使用Vue进行开发
    import Vue from 'vue'
    
    const app = new Vue({
        el: '#app',
        template:
            `

    {{message}}

    {{name}}

    `, data: { message: 'Hello webpack', name: '刘云杰', methods : { btnClick() { console.log('lyj '); } } } })

    15.4.7 vue的终极使用方式

    • 优化第一步:

      • // 5. 使用Vue进行开发
        import Vue from 'vue'
        
        const App = {
            template:
                `

        {{message}}

        {{name}}

        `, data() { return { message: 'Hello webpack', name: '刘云杰', } }, methods: { btnClick() { } } } const app = new Vue({ el: '#app', template: ``, data: { }, components: { App } })
    • 优化第二步:

      • 新建 app.js

        • export default {
              template:
                  `

          {{message}}

          {{name}}

          `, data() { return { message: 'Hello webpack', name: '刘云杰', } }, methods: { btnClick() { } } }
      • 修改 main.js

        • // 5. 使用Vue进行开发
          import Vue from 'vue'
          import App from './vue/app'
          
          const app = new Vue({
              el: '#app',
              template: ``,
              data: {
              },
              components: {
                  App
              }
          })
          
          
    • 优化第三步:

      • 新建 App.vue文件

        • 
          
          
          
          
          

    15.4.8 webpack的plugin的使用

    什么是 plugin

    • plugin 是插件的意思,通常是用于对某个现有的架构进行扩展。
    • webpack 中的插件,就是对 webpack 现有功能的各种扩展,比如打包优化,文件压缩等等。

    loaderplugin 区别

    • loader 主要用于转换某些类型的模块,它是一个转换器。
    • plugin 是插件,它是对 webpack 本身的扩展,是一个扩展器。

    plugin 的使用过程:

    • 步骤一:通过 npm 安装需要使用的 plugins (某些webpack已经内置的插件不需要安装)
    • 步骤二:在 webpack.config.js 中的plugins中配置插件。
    1. 添加版权的 plugin

      • 插件名字叫 BannerPlugin,属于 webpack 自带的插件。

      • 修改 webpack.config.js 文件

      • const webpack = require('webpack')
        
        module.exports = {
            /*......*/
            /*插件*/
            plugins : [
                new webpack.BannerPlugin('版权问题'),
            ]
        }
        
    2. HtmlWebpackPlugin

      • index.html 文件打包到 dist 文件夹中

      • HtmlWebpackPlugin 插件可以为我们做这些事情:

        • 自动生成一个 index.html 文件(可以指定模板来生成)
        • 将打包的 js 文件,自动通过 script 标签插入到 body
      • 安装命令 npm install html-webpack-plugin --save-dev

      • 配置 webpack.config.js 文件

        • const path = require('path')
          const webpack = require('webpack')
          const HtmlWebpackPlugin = require('html-webpack-plugin')
          
          module.exports = {
              /*插件*/
              plugins : [
                  new webpack.BannerPlugin('版权问题'),
                  new HtmlWebpackPlugin({
                     template: 'index.html',
                  }),
              ]
          }
          
    3. uglifyjs-webpack-plugin

      • js 等文件进行压缩处理
      • npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
      • 修改 webpack.config.js 文件
        • image-20220306132928111
    4. webpack-dev-server

      • 作用:搭建本地开发服务器
      • npm install --save-dev webpack-dev-server@2.9.1
        • contentBase:为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
        • port:端口号
        • inline:页面是否实时刷新
        • historyApiFallback:在SPA页面中,依赖HTML5的history模式
      • webpack.config.js 文件配置
        • image-20220306133425237
        • --open 参数表示直接打开浏览器
        • image-20220306133449373

    16. vue-cli 脚手架

    16.1 vue-cli介绍

    CLI是什么意思?

    • CLICommand-Line Interface , 翻译为命令行界面, 但是俗称脚手架.
    • Vue CLI 是一个官方发布 vue.js 项目脚手架
    • 使用 vue-cli 可以快速搭建 Vue 开发环境以及对应的webpack 配置

    使用vue cli的前提

    • Node.js
    • webpack

    安装vue脚手架

    • npm install -g @vue/cli
      • 上面安装的是Vue CLI3的版本,如果需要想按照Vue CLI2的方式初始化项目时不可以的。
    • cnpm install @vue/cli-init -g
    • Vue CLI2初始化项目
      • vue init webpack my-project
    • Vue CLI3初始化项目
      • vue create my-project

    16.2 vue-cli2

    1. 安装过程:image-20220306183008854
    2. 目录结构详情image-20220306183139157

    16.3 Runtime-Compiler和Runtime-only的区别

    • 简单总结
      • 如果在之后的开发中,你依然使用 template,就需要选择 Runtime-Compiler
      • 如果你之后的开发中,使用的是 .vue 文件夹开发,那么可以选择 Runtime-only
    • 项目对比
      • image-20220306194745789
    • Vue程序运行过程
      • image-20220306195313612

    16.3.1 render函数

    1. // The Vue build version to load with the `import` command
      // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
      import Vue from 'vue'
      import App from './App'
      
      Vue.config.productionTip = false
      
      /* eslint-disable no-new */
      new Vue({
        el: '#app',
      
        /*render函数*/
        render: function (createElement) {
          // 1. createElement('标签', {标签的属性}, ['标签的内容'])
          return createElement('h2',
            {class: 'box'},
            ['Hello World', createElement('button', ['按钮'])]);
        }
        // components: { App },
        // template: ''
      })
      
      
      • image-20220306223833819
    2. // The Vue build version to load with the `import` command
      // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
      import Vue from 'vue'
      import App from './App'
      
      Vue.config.productionTip = false
      
      const cpn = {
        template: `
      {{message}}
      `, data() { return { message : '我是组件', } } } /* eslint-disable no-new */ new Vue({ el: '#app', /*render函数*/ render: function (createElement) { // 2. 传入组件 return createElement(cpn); } // components: { App }, // template: '' })
      • image-20220306224226411

      • 进阶

      • // The Vue build version to load with the `import` command
        // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
        import Vue from 'vue'
        import App from './App'
        
        Vue.config.productionTip = false
        
        /* eslint-disable no-new */
        new Vue({
          el: '#app',
          /*render函数*/
          render: function (createElement) {
            // 2. 传入组件
            return createElement(App);
          }
          // components: { App },
          // template: ''
        })
        
        
      • image-20220306224305885

    16.3 vue-cli3

    16.3.1 基础介绍

    • vue-cli 3 与 2 版本有很大区别
      • vue-cli 3 是基于 webpack 4 打造,vue-cli 2 还是 webapck 3
      • vue-cli 3 的设计原则是“0配置”,移除的配置文件根目录下的,build和config等目录
      • vue-cli 3 提供了 vue ui 命令,提供了可视化配置,更加人性化
      • 移除了static文件夹,新增了public文件夹,并且index.html移动到public中

    配置相关

    16.3.2 图像化界面

    1. vue ui
    2. image-20220307111020541
    3. image-20220307111110658
    4. image-20220307111157787

    16.3.2 自定义配置

    1. 创建 vue.config.js
    2. 添加自定义的配置

    16.3.3 箭头函数和 this指向

    const 函数名 = (参数列表)=>{}

    • 例子1:

      • 
        
    • 例子2:只有一个参数

      • 
        

    this 指向

    • 
      

    16.4 路由与映射

    16.4.1 认识路由

    路由routing)就是通过互联的网络把信息从源地址传输到目的地址的活动. --- 维基百科

    • 路由器提供了两种机制: 路由和转送.
      • 路由是决定数据包从来源目的地的路径.
      • 转送将输入端的数据转移到合适的输出端.
    • 路由中有一个非常重要的概念叫路由表.
      • 路由表本质上就是一个映射表, 决定了数据包的指向.

    前端渲染与后端渲染

    • 后端路由是指后端处理 URL 和 页面之间的映射关系。
      • 早期的网站开发整个HTML页面是由服务器来渲染
        • 一个页面有自己对应的网址, 也就是URL.
        • URL会发送到服务器, 服务器会通过正则对该URL进行匹配, 并且最后交给一个Controller进行处理.
        • Controller进行各种处理, 最终生成HTML或者数据, 返回给前端.
        • 这就完成了一个IO操作.
      • 后端路由的缺点:
        • 一种情况是整个页面的模块由后端人员来编写和维护的.
        • 另一种情况是前端开发人员如果要开发页面, 需要通过PHP和Java等语言来编写页面代码.
        • 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情
    • 前后端分离
      • 后端只提供API来返回数据, 前端通过获取数据, 并且可以通过JavaScript将数据渲染到页面中
      • 前端渲染:浏览器中网页内的大部分内容,都是由前端编写的 JS 代码在浏览器中执行,最终渲染出来的网页。
    • 单页面富应用阶段
      • 在前后端分离的基础上加了一层前端路由,即维护前端来维护一套路由规则。
      • 整个网页中只有一个 html 页面。

    urlhash

    • URLhash 也就是锚点(#), 本质上是改变 window.locationhref 属性;
    • 可以通过直接赋值 location.hash 来改变 href , 但是页面不发生刷新。

    16.4.2 vue-router基础

    vue-router是基于路由和组件的

    • 路由用于设定访问路径, 将路径和组件映射起来.
    • 在vue-router的单页面应用中, 页面的路径的改变就是组件的切换.
    1. 步骤一: 安装 vue-router
      1. npm install vue-router --save
    2. 步骤二: 在模块化工程中使用它(因为是一个插件, 所以可以通过 Vue.use() 来安装路由功能)
      1. 第一步:导入路由对象,并且调用 Vue.use(VueRouter)
      2. 第二步:创建路由实例,并且传入路由映射配置
      3. 第三步:在 Vue 实例中挂载创建的路由实例
    /*配置路由的相关信息*/
    import Vue from 'vue'
    import Router from 'vue-router'
    import HelloWorld from '@/components/HelloWorld'
    
    // 1.通过Vue.use(插件) 安装插件
    Vue.use(Router)
    
    // 2. new Router({}) 创建 VueRouter对象 3. export 导出路由
    export default new Router({
      // 配置路由和组件之间的应用关系
      routes: [
        {
          path: '/',
          name: 'HelloWorld',
          component: HelloWorld
        }
      ]
    })
    

    16.4.3 路由映射配置

    第一步: 创建路由组件

    第二步: 配置路由映射: 组件和路径映射关系

    第三步: 使用路由: 通过

    : 该标签是一个 vue-router 中已经内置的组件, 它会被渲染成一个 标签.

    : 该标签会根据当前的路径, 动态渲染出不同的组件.

    网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和 处于同一个等级.

    在路由切换时, 切换的是 挂载的组件, 其他内容不会发生改变.

    举例

    1. 新建 HelloWorld.vueabout.vue

      • 
        
        
        
        
        
        
    2. index.js 中配置映射

      • /*配置路由的相关信息*/
        import Vue from 'vue'
        import Router from 'vue-router'
        import HelloWorld from '@/components/HelloWorld'
        import about from "../components/about";
        
        // 1.通过Vue.use(插件) 安装插件
        Vue.use(Router)
        
        // 2. new Router({}) 创建 VueRouter对象 3. export 导出路由
        export default new Router({
          // 配置路由和组件之间的应用关系
          routes: [
            /*一个映射关系一个对象*/
            {
              path: '/',
              name: 'HelloWorld',
              component: HelloWorld
            },
            {
              path: '/about',
              name: 'about',
              component: about,
            }
          ]
        })
        
    3. App.vue 中使用路由

      • 
        
        
        
        
        

    16.4.4 路由的默认值和修改为history模式

    路由的默认值:

    • path 配置的是根路径: /
    • redirect 是重定向, 也就是我们将根路径重定向到 / 的路径下, 这样就可以得到我们想要的结果了.

    router/index.js 添加一条映射

    /*配置路由的相关信息*/
    import Vue from 'vue'
    import Router from 'vue-router'
    import HelloWorld from '@/components/HelloWorld'
    import about from "../components/about";
    
    // 1.通过Vue.use(插件) 安装插件
    Vue.use(Router)
    
    // 2. new Router({}) 创建 VueRouter对象 3. export 导出路由
    export default new Router({
      // 配置路由和组件之间的应用关系
      routes: [
        /*一个映射关系一个对象*/
        {
          path: '',
          redirect: '/',
        },
        {
          path: '/home',
          name: 'HelloWorld',
          component: HelloWorld
        },
        {
          path: '/about',
          name: 'about',
          component: about,
        }
      ]
    })
    

    将 hash 模式修改为 history 模式

    ? 添加 mode: 'history'

    /*配置路由的相关信息*/
    import Vue from 'vue'
    import Router from 'vue-router'
    import HelloWorld from '@/components/HelloWorld'
    import about from "../components/about";
    
    // 1.通过Vue.use(插件) 安装插件
    Vue.use(Router)
    
    // 2. new Router({}) 创建 VueRouter对象 3. export 导出路由
    export default new Router({
      // 配置路由和组件之间的应用关系
      routes: [
        /*一个映射关系一个对象*/
        {
          path: '',
          redirect: '/home',
        },
        {
          path: '/home',
          name: 'HelloWorld',
          component: HelloWorld
        },
        {
          path: '/about',
          name: 'about',
          component: about,
        }
      ],
      mode: 'history',
    })
    
    

    16.4.5 router-link的其他属性

    16.4.6 vue-router动态路由

    在某些情况下,一个页面的 path 路径可能是不确定的,比如我们进入用户界面时,希望是如下的路径:

    • /user/aaaa/user/bbbb
    • 除了有前面的 /user 之外,后面还跟上了用户的 ID
    • 这种 pathComponent 的匹配关系,我们称之为动态路由(也是路由传递数据的一种方式)。

    例子

    • 新建 User.vue文件

      • 
        
        
        
        
        
    • index.js 配置路由

      • /*配置路由的相关信息*/
        import Vue from 'vue'
        import Router from 'vue-router'
        import HelloWorld from '@/components/HelloWorld'
        import about from "../components/about";
        import User from "../components/User";
        
        // 1.通过Vue.use(插件) 安装插件
        Vue.use(Router)
        
        // 2. new Router({}) 创建 VueRouter对象 3. export 导出路由
        export default new Router({
          // 配置路由和组件之间的应用关系
          routes: [
            {
              path: '/user/:userId',
              name: 'user',
              component: User,
            }
          ],
          mode: 'history'
        })
        
        
    • App.vue传值

      • 
        
        
        
        
        

    16.4.7 路由的懒加载

    • 当打包构建应用时,Javascript 包会变得非常大,影响页面加载。
    • 如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

    概念

    • 首先, 我们知道路由中通常会定义很多不同的页面.
    • 这个页面最后被打包在哪里呢? 一般情况下, 是放在一个js文件中.
    • 但是, 页面这么多放在一个js文件中, 必然会造成这个页面非常的大.
    • 如果我们一次性从服务器请求下来这个页面, 可能需要花费一定的时间, 甚至用户的电脑上还出现了短暂空白的情况.
    • 如何避免这种情况呢? 使用路由懒加载就可以了。

    路由懒加载做了什么?

    • 路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块.
    • 只有在这个路由被访问到的时候, 才加载对应的组件

    懒加载的使用

    1. 方式一:结合Vue的异步组件和Webpack的代码分析;
      • const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })};
    2. 方式二:AMD写法;
      • const About = resolve => require(['../components/About.vue'], resolve);
    3. 方式三:在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割。(推荐)
      • const Home = () => import('../components/Home.vue')

    16.4.8 路由的嵌套

    • 嵌套路由是一个很常见的功能
      • 比如在 home 页面中, 我们希望通过 /home/news/home/message访问一些内容.
      • 一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件.
      • image-20220307205705714
    • 实现嵌套路由有两个步骤:
      • 创建对应的子组件, 并且在路由映射中配置对应的子路由.
      • 在组件内部使用 标签.

    例子:

    • 新建两个子组件 HomeNews.vueHomeMessage.vue

      • 
        
        
        
        
        
        
      • 
        
        
        
        
        
        
    • index.js 中添加映射关系

      • export default new Router({
          // 配置路由和组件之间的应用关系
          routes: [
            /*一个映射关系一个对象*/
            {
              path: '/HelloWorld',
              name: 'HelloWorld',
              component: HelloWorld,
                // children属性
              children: [
                {
                  path: '',
                  redirect: 'news',
                },
                {
                  path: 'news',
                  component: HomeNews,
                },
                {
                  path: 'message',
                  component: HomeMessage,
                },
              ],
            },
            {
              path: '/about',
              name: 'about',
              component: about,
            },
            {
              path: '/user/:userId',
              name: 'user',
              component: user,
            }
          ],
          mode: 'history'
        })
        
    • HelloWorld.vue 配置

      • 
        
        
        
        
        
        

    16.4.9 vue-router的参数传递

    • 传递参数主要有两种类型: paramsquery
      • params 的类型:
        • 配置路由格式: /router/:id
        • 传递的方式: 在 path 后面跟上对应的值
        • 传递后形成的路径: /router/123 , /router/abc
      • query 的类型:
        • 配置路由格式: /router, 也就是普通配置
        • 传递的方式: 对象中使用 querykey 作为传递方式
        • 传递后形成的路径: /router?id=123, /router?id=abc

    query 类型的例子

    • 新建一个 Profile.vue文件

      • 
        
        
        
        
        
    • index.js 配置路由

      • /*配置路由的相关信息*/
        import Vue from 'vue'
        import Router from 'vue-router'
        
        const profile = () => import('../components/Profile')
        
        // 1.通过Vue.use(插件) 安装插件
        Vue.use(Router)
        
        // 2. new Router({}) 创建 VueRouter对象 3. export 导出路由
        export default new Router({
          // 配置路由和组件之间的应用关系
          routes: [
            /*一个映射关系一个对象*/
            {
              path: '/profile',
              component: profile,
            }
          ],
          mode: 'history'
        })
        
        
    • App.vue编写页面

      • 
        
    • image-20220308092857426

    • Profile.vue取出传递过来的数据

      • 
        
        
        
        
        
        
    • image-20220308093039870

    16.4.10 $route$router 是有区别的

    $route$router是有区别的:

    • $routerVueRouter 实例,想要导航到不同 URL ,则使用 $router.push 方法
    • $route 为当前 router 跳转对象里面可以获取 namepathqueryparams

    image-20220308093814683

    16.4.11 导航守卫

    • vue-router 提供的导航守卫主要用来监听路由的进入和离开。
    • vue-router 提供了 beforeEachafterEach 的钩子函数, 它们会在路由即将改变前和改变后触发.

    用处:改变页面的标题

    • 可以利用 beforeEach 来完成标题的修改.

      • 首先, 我们可以在钩子当中定义一些标题, 可以利用 meta 来定义
      • 其次, 利用导航守卫,修改我们的标题.
    • 定义标题:

      • image-20220308101309188
    • 修改标题:

      • image-20220308101330539
      **
      导航钩子的三个参数解析:
      1. to: 即将要进入的目标的路由对象.
      2. from: 当前导航即将要离开的路由对象.
      3. next: 调用该方法后, 才能进入下一个钩子.
      
    • 补充一:如果是后置钩子, 也就是 afterEach, 不需要主动调用 next() 函数.

    • 补充二: 上面我们使用的导航守卫, 被称之为全局守卫.

      • 路由独享的守卫.
      • 组件内的守卫.

    官网链接:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#路由独享的守卫。

    16.4.12 vue-router中的keep-alive

    • keep-aliveVue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
      • 它们有两个非常重要的属性:
      • include - 字符串或正则表达,只有匹配的组件会被缓存
      • exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存
    • router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:image-20220308104101196

    未使用 keep-alive 之前

    • HelloWorld.vue中添加如下代码

      • 
        
      • 每次都是创建新的实例

      • image-20220308110441459

    使用 keep-alive 之后

    • App.vue

    • image-20220308123906880

    • 不会频繁创建与销毁

    17. tabbar案例

    17.1 基本结构的搭建

    1. 新建项目

      • vue init webpack 16-tabbar
    2. 编写前端基本样式

      • body{
          padding: 0;
          margin: 0;
        }
        
        
    3. App.vue 文件中引入样式

      • 
        
        
        
        
        
        

    17.2 TabBar和TabBarItem组件封装

    1. 新建 TabBarItem.vue 将图标和文字进行封装

      • 
        
        
        
        
        
    2. 使用 TabBar.vue 将底部导航栏进行封装

      • 
        
        
        
        
        
    3. 将上面封装完成以后,只需要在 App.vue 写入相关数据即可

      • 
        
        
        
        
        

    image-20220309103015365

    17.3 给 TabBarItem传入active图片

    思路:添加 isActive 属性,true 显示 active 图片;false 不显示 active 图片。

    TabBarItm.vue

    
    
    
    
    
    

    App.vue

    
    
    
    
    
    

    image-20220309104908314

    17.4 TabBarItem和路由结合

    1. 为每一个页面添加对应的 .vue 文件

    2. 配置路由

      • import Vue from "vue";
        import VueRouter from "vue-router";
        
        const home = ()=>import('../views/home/Home')
        const category = ()=>import('../views/category/Category')
        const profile = ()=>import('../views/profile/Profile')
        const card = ()=>import('../views/shopcard/ShopCard')
        
        /*1 安装插件*/
        Vue.use(VueRouter)
        
        /*2 创建路由对象*/
        const routes = [
          {
            path: '',
            redirect: '/home',
          },
          {
            path: '/home',
            component: home,
          },
          {
            path: '/category',
            component: category,
          },
          {
            path: '/profile',
            component: profile,
          },
          {
            path: '/card',
            component: card,
          },
        ]
        const router = new VueRouter({
          routes,
          mode: 'history',
        })
        
        /*3 导出router*/
        export default router
        
        
    3. 要想知道跳转到哪个页面,需要监听 click 事件

      • App.vue

      • 
        
      • TabBarItem.vue

      • 
        
        
        
        
        

    17.5 TabBarItem颜色动态控制

    • TabBarItem.vue 中添加计算属性

      • 
        
    • 自定义修改导航栏的颜色

      • App.vue

      • 
        
      • TabBarItem.vue

      • 
        
        
        
        
        

    18. Proimse

    18.1 Promise的介绍和基本使用

    1. Promise是做什么的?

      • Promise是异步编程的一种解决方案。
    2. 定时器的异步操作

      • 方式一:定时器模拟

      • 
        
      • 方式二:Promise方法

      • // Promise()的参数是一个函数,这个函数有两个参数(resolve, reject)
        // resolve reject也是函数
        new Promise((resolve, reject)=>{
          // 第一次网络请求的代码
          setTimeout(()=>{
            resolve()  // resolve()会执行then
          }, 1000);
        }).then(()=>{
          // 第一次拿到结果的处理代码
          console.log('Hello World1');
          console.log('Hello World2');
          console.log('Hello World3');
          console.log('Hello World4');
          console.log('Hello World5');
          console.log('Hello World6');
          return new Promise((resolve, reject)=>{
            // 第二次网络请求的代码
            setTimeout(()=>{
              resolve()
            }, 1000)
          }).then(()=>{
            // 第二次拿到结果的处理代码
            console.log('Hello Vuejs');
            console.log('Hello Vuejs');
            console.log('Hello Vuejs');
            console.log('Hello Vuejs');
            console.log('Hello Vuejs');
            console.log('Hello Vuejs');
            return new Promise((resolve, reject)=>{
              // 第三次网络请求的代码
              setTimeout(()=>{
                resolve()
              }, 1000)
            }).then(()=>{
              // 第三次拿到结果的处理代码
              console.log('Hello Python');
              console.log('Hello Python');
              console.log('Hello Python');
              console.log('Hello Python');
              console.log('Hello Python');
              console.log('Hello Python');
            })
          })
        })
        
        
    3. 什么时候会用到 Promise

      • 一般情况是有异步操作时,使用 Promise 对异步操作进行封装。
      • new Promise(resolve, reject)
      • 参数 resolvereject 同样是函数。
        • 异步操作成功的时候调用 resolve 函数,执行 resolve 会执行 then
        • 异步操作失败的时候调用 reject 函数,执行 reject 会执行 catch

    18.2 Promise的三种状态

    异步操作之后会有三种状态:

    • ending:等待状态,比如正在进行网络请求,或者定时器没有到时间。
    • fulfill:满足状态,当我们主动回调了 resolve 时,就处于该状态,并且会回调 .then()
    • reject:拒绝状态,当我们主动回调了 reject 时,就处于该状态,并且会回调 .catch()

    其实 .then 可以有两个参数 (函数1,函数2),两个参数同样是函数。当执行 resolve 的时候调用 函数1,执行 reject 的时候调用 函数2

    
    

    image-20220309210124303

    
    

    image-20220309210237767

    18.3 Promise的链式调用

    • 全写

      • 
        
    • 简写

      • 
        
    • 再次简写

      • 
        

    18.4 Promise的all方法

    Promise.all() 参数是一个可迭代的对象,例如数组。

    
    

    只有当 参数里面所有的异步请求都执行完以后,才会执行 .then 中的方法

    19. Vuex

    19.1 Vuex概念和作用解析

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

    状态管理 是什么?

    • 状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。
    • 其实,你可以简单的将其看成 把需要多个组件共享的变量全部存储在一个对象里面
    • 然后,将这个对象放在顶层的 Vue 实例中,让其他组件可以使用。
    • 那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?

    有什么状态时需要我们在多个组件间共享的呢?

    • 如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。
    • 比如用户的登录状态、用户名称、头像、地理位置信息等等。
    • 比如商品的收藏、购物车中的物品等等。
    • 这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的(待会儿我们就可以看到代码了,莫着急)。

    19.2 单界面到多界面的状态切换

    19.2.1 单界面的状态切换

    image-20220310102845454

    • State :不用多说,就是我们的状态。(你姑且可以当做就是 data 中的属性)
    • View :视图层,可以针对 State 的变化,显示不同的信息。(这个好理解吧?)
    • Actions:这里的 Actions 主要是用户的各种操作:点击、输入等等,会导致状态的改变。

    App,vue

    
    
    
    
    
    

    image-20220310121902331

    19.2.2 多页面的状态切换

    功能:HelloVue 也想使用 App.vue 中的 counter

    1. 安装 Vuex

      • npm install vuex --save
        
    2. 新建 store 文件夹以及 index.js

      • index.js

      • import Vue from "vue"
        import Vuex from  'vuex'
        
        // 1. 安装插件
        Vue.use(Vuex)
        
        // 2. 创建对象
        const store = new Vuex.Store({
          /*一共有以下5个参数,每个参数都是对象*/
          state:{
            counter: 1000,
          },
          mutations: {},
          actions: {},
          getters: {},
          modules: {},
        })
        
        // 3.导出
        export default store
        
    3. App.vue以及HelloVue.vue中引入

      • App.vue

      • 
        
        
        
        
        
      • HelloVue.vue

      • 
        
        
        
        
        
        

    image-20220310124451636

    19.3 Vuex的基本使用

    修改 Vuex 中属性的值

    ? 必须通过 mutations 中的方法实现

    • index.js

      • import Vue from "vue"
        import Vuex from  'vuex'
        
        // 1. 安装插件
        Vue.use(Vuex)
        
        // 2. 创建对象
        const store = new Vuex.Store({
          /*一共有以下5个参数,每个参数都是对象*/
          state:{
            // 状态
            counter: 1000,
          },
          mutations: {
            // 方法  默认有一个参数state
            increment(state) {
              state.counter++;
            },
            decrement(state) {
              state.counter--;
            }
          },
          actions: {},
          getters: {},
          modules: {},
        })
        
        // 3.导出
        export default store
        
    • App.vue

      • 
        
        
        
        
        
        

    image-20220310131751607

    19.4 Vuex核心概念

    • State
    • Getters
    • Mutation
    • Action
    • Module

    19.4.1 State 单一状态树

    单一状态树:

    英文名称是 Single Source of Truth,也可以翻译成单一数据源。

    • 如果你的状态信息是保存到多个 Store 对象中的,那么之后的管理和维护等等都会变得特别困难。
    • 所以 Vuex 也使用了单一状态树来管理应用层级的全部状态。
    • 单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护

    19.4.2 Getters 的使用详情

    需要从 store 中获取一些 state 变异后的状态。

    类似于 计算属性。

    import Vue from "vue"
    import Vuex from 'vuex'
    
    // 1. 安装插件
    Vue.use(Vuex)
    
    // 2. 创建对象
    const store = new Vuex.Store({
      /*一共有以下5个参数,每个参数都是对象*/
      state: {
        // 状态
        counter: 1000,
        student: [
          {id: 110, name: 'lyj0', age: 18},
          {id: 111, name: 'lyj1', age: 19},
          {id: 112, name: 'lyj2', age: 20},
          {id: 113, name: 'lyj3', age: 21},
        ],
      },
      mutations: {
        // 方法  默认有一个参数state
        increment(state) {
          state.counter++;
        },
        decrement(state) {
          state.counter--;
        }
      },
      actions: {},
    
      getters: {
        powerCount(state) {
          return state.counter * state.counter;
        },
        more20Stu(state) {
          return state.student.filter(s=>s.age>=20);
        },
          // getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数.
        more20StuLength(state, getters) {
          return getters.more20Stu.length;
        }
      },
      modules: {},
    })
    
    // 3.导出
    export default store
    

    App.vue

    
    

    image-20220310152532578

    getters 传递参数

    index.js

    const store = new Vuex.Store({
      /*一共有以下5个参数,每个参数都是对象*/
      state: {
        // 状态
        counter: 1000,
        student: [
          {id: 110, name: 'lyj0', age: 18},
          {id: 111, name: 'lyj1', age: 19},
          {id: 112, name: 'lyj2', age: 20},
          {id: 113, name: 'lyj3', age: 21},
        ],
      },
      mutations: {
        // 方法  默认有一个参数state
        increment(state) {
          state.counter++;
        },
        decrement(state) {
          state.counter--;
        }
      },
      actions: {},
    
      getters: {
        powerCount(state) {
          return state.counter * state.counter;
        },
        more20Stu(state) {
          return state.student.filter(s=>s.age>=20);
        },
        more20StuLength(state, getters) {
          return getters.more20Stu.length;
        },
        moreAgeStu(state) {
          return function (age) {
            return state.student.filter(s=>s.age>=age);
          }
        },
      },
      modules: {},
    })
    

    App.vue

    
    

    image-20220310155431213

    19.4.3 mutations 状态更新

    • Vuexstore 状态的更新唯一方式:提交Mutation

    • Mutations 主要包括两部分:

      • 字符串的事件类型(type)
      • 一个回调函数(handler),该回调函数的第一个参数就是 state
    • mutations 的定义方式

      • mutations: {
          // 方法  默认有一个参数state
          increment(state) {
            state.counter++;
          },
          decrement(state) {
            state.counter--;
          }
        },
        
    • mutation 的更新方式

      • methods: {
          addition() {
            this.$store.commit('increment');
          },
          substraction() {
            this.$store.commit('decrement');
          },
        }
        

    mutation 传递参数

    • App.vue

      • methods: {
            addition() {
              this.$store.commit('increment');
            },
            substraction() {
              this.$store.commit('decrement');
            },
            addCount(count) {
              this.$store.commit('incCount', count);
            },
            addStudent() {
              const student = {id: 115, name: '刘云杰', age: 20};
              this.$store.commit('addStudent', student);
            }
          }
        
    • index.js

      • mutations: {
          // 方法  默认有一个参数state
          increment(state) {
            state.counter++;
          },
          decrement(state) {
            state.counter--;
          },
          incCount(state, count) {
            state.counter += count;
          },
          addStudent(state, stu) {
            state.student.push(stu);
          }
        },
        
    • image-20220310155405074

    mutation 的提交风格

        addStudent() {
          // 1. 普通的提交风格
          const student = {id: 115, name: '刘云杰', age: 20};
          // this.$store.commit('addStudent', student);
    
          //2. 特殊的提交风格
          this.$store.commit({
            type: 'addStudent',
            student,
          })
        }
    

    这种情况下,index.js 中的 addStudent() 方法会接收到一个 对象。

    image-20220310170635972

    19.4.4 mutations的响应规则

    • Vuexstore 中的 state 是响应式的, 当 state 中的数据发生改变时, Vue 组件会自动更新.
    • 属性被添加到响应式系统中
    • 这就要求我们必须遵守一些 Vuex 对应的规则:
      • 提前在 store 中初始化好所需的属性.
      • 当给 store 中的对象添加新属性时, 使用下面的方式:
        • 方式一: 使用 Vue.set(obj, 'newProp', 123)
        • 方式二: 用新对象给旧对象重新赋值
        • image-20220310172228470

    19.4.5 actions 的使用详解

    • Vuex 要求我们 Mutation 中的方法必须是同步方法.
      • 主要的原因是当我们使用 devtools 时, devtools 可以帮助我们捕捉 mutation 的快照.
      • 但是如果是异步操作, 那么 devtools 将不能很好的追踪这个操作什么时候会被完成.
    • 异步操作必须要在 Action 中进行
    • image-20220310223418251
    • image-20220310223430456

    context 是什么?

    • context 是和 store 对象具有相同方法和属性的对象.
    • 也就是说,我们可以通过 context 去进行 commit 相关的操作, 也可以获取 context.state 等.

    19.4.6 modules的使用详情

    • Vue 使用单一状态树,那么也意味着很多状态都会交给 Vuex 来管理.
    • 当应用变得非常复杂时, store 对象就有可能变得相当臃肿.
    • 为了解决这个问题, Vuex 允许我们将 store 分割成模块( Module ), 而每个模块拥有自己的 statemutationsactionsgetters 等。

    image-20220311122608039

    19.5 文件夹目录结构

    image-20220311124400535

    20. 网络请求模块-Axios

    20.1 请求方式

    axios 支持多种请求方式:

    • axios(config)
    • axios.request(config)
    • axios.get(url[, config])
    • axios.delete(url[, config])
    • axios.head(url[, config])
    • axios.post(url[, data[, config]])
    • axios.put(url[, data[, config]])
    • axios.patch(url[, data[, config]])

    举例

    ? axios(config) 默认使用 get 请求,可以通过 methods 修改。

    import Vue from 'vue'
    import App from './App'
    import axios from 'axios'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      render: h => h(App)
    })
    
    axios({
      url: 'http://123.207.32.32:8000/home/multidata',
      // methods: 'post', 请求方式
    }).then(res=>{
      console.log(res);
    })
    

    image-20220311131856648

    20.2 axios 并发请求

    有时候, 我们可能需求同时发送两个请求

    • 使用 axios.all , 可以放入多个请求的数组.
    • axios.all([]) 返回的结果是一个数组,使用 axios.spread 可将数组 [res1,res2] 展开为 res1, res2
    import Vue from 'vue'
    import App from './App'
    // import router from './router'
    import axios from 'axios'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      // router,
      // axios,
      render: h => h(App)
    })
    
    /* 1基本使用*/
    // axios({
    //   url: 'http://123.207.32.32:8000/home/multidata',
    //   // methods: 'post', 请求方式
    // }).then(res => {
    //   console.log(res);
    // })
    
    /*2 并发请求*/
    axios.all([axios({
      url: 'http://123.207.32.32:8000/home/multidata',
    }), axios({
      url: 'http://123.207.32.32:8000/home/data',
      params: {
        type: 'sell',
        page: 2,
      }
    })]).then(axios.spread((res1, res2)=>{
      console.log(res1);
      console.log(res2);
    }))
    

    image-20220311140150066

    20.3 axios的配置信息相关

    在上面的示例中, 我们的 BaseURL 是固定的

    • 事实上, 在开发中可能很多参数都是固定的.
    • 这个时候我们可以进行一些抽取, 也可以利用 axios 的全局配置
    // 公共配置
    axios.defaults.baseURL = 'http://123.207.32.32:8000',
    axios.defaults.timeout = 1000
    
    /*2 并发请求*/
    axios.all([axios({
      url: '/home/multidata',
    }), axios({
      url: '/home/data',
      params: {
        type: 'sell',
        page: 2,
      }
    })]).then(axios.spread((res1, res2)=>{
      console.log(res1);
      console.log(res2);
    }))
    

    20.4 axios的实例和模块封装

    • 新建 request.js 存储所有 axios 代码

      • import axios from "axios";
        
        export function request(config) {
        
          // 1. 创建 axios 实例
            
            // return new Promise的缩写
          const instance = axios.create({
            baseURL: 'http://123.207.32.32:8000',
            timeout: 5000
          })
          return instance(config)
        
        /*  
        return new Promise(((resolve, reject) => {
            // 1. 创建 axios 实例
            const instance = axios.create({
              baseURL: 'http://123.207.32.32:8000',
              timeout: 5000
            })
        
            // 发送网络请求
            instance(config)
              .then(res=>{
                resolve(res)
              })
              .catch(err=>{
                reject(err)
              })
          }))*/
        }
        
    • main.js 文件处理

      • import Vue from 'vue'
        import App from './App'
        // import router from './router'
        import axios from 'axios'
        
        Vue.config.productionTip = false
        
        /* eslint-disable no-new */
        new Vue({
          el: '#app',
          // router,
          // axios,
          render: h => h(App)
        })
        
        // 封装 request 模块
        import {request} from "./network/request";
        request({
          url: '/home/multidata',
        }).then(res=>{
          console.log(res);
        }).catch(err=>{
          console.log(err);
        })
        

    20.5 拦截器

    axios 提供了拦截器,用于我们在发送每次请求或者得到相应后,进行对应的处理。

    1. 如何使用拦截器?
    2. 拦截器可以做的事情。
    3. 拦截成功
    4. 拦截失败
      // 2. axios 的拦截器
      /*拦截请求*/
      /*请求拦截的作用:
      * 1. config中的一些信息不符合服务器的要求
      * 2. 每次发送网络请求时,希望在界面中显示一个请求的图标
      * 3. 某些网络请求(比如登录 token)必须携带一些特殊的信息
      * */
      axios.interceptors.request.use(config=>{
        console.log('axios拦截请求成功');
      }, error => {
        console.log('axios拦截请求失败');
      });
      /*拦截响应*/
    
      axios.interceptors.response.use(result=>{
        console.log('axios拦截响应成功');
      }, error => {
        console.log('axios拦截响应失败');
      });