云小杰

相对于绝对成功的汲汲渴求,越无杂质的奔赴,越是动人。

要一个黄昏, 满是风, 和正在落下的夕阳。如此, 足够我爱这破碎泥泞的人间。


Download the theme

学习vuejs

学习Vue.js

0. ES6补充

0.1 var 没有块级作用域

iffor 中会出错

<script>
    {
        var name = 'Hello Vue';
        console.log(name);
    }
    console.log(name);
</script>

image-20220301165743759

<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
    var btns = document.getElementsByTagName("button");
    for (var i =0;i<btns.length;++i) {
        btns[i].addEventListener('click', function () {
            console.log('' + i + '个按钮被点击')
        })
    }
</script>

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程序

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

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

<body>
<script src="../js/vue.js"></script>
<script>
    // let定义变量 const定义常量
    // 编程范式:声明式编程
    /*好处:数据与界面完全分离*/
    let app = new Vue({
        el: '#app', // 定义要挂载的元素
        data: { // 定义数据
            message: 'Hello Vue.js',
        }
    })
</script>
</body>
</html>

image-20220227170229059

2.2 Vue列表的展示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="app">
    <p></p>
    <ul>
        <li v-for="movie in movies"></li>
    </ul>
</div>

<script src="../js/vue.js"></script>
<script>
    let app = new Vue({
        el: "#app",
        data: {
            message: 'Hello Vue.js',
            movies: ['星际穿越', '大话西游', '少年派', '盗梦空间'],
        }
    })
</script>

</body>
</html>

image-20220227212250428

2.3 计数器

  • 实现功能:小的计数器
    • 点击 + 计数器加1
    • 点击 - 计数器减1
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="app">
    <h2>当前记数: </h2>
    <!--方法一:-->
<!--    <button v-on:click="++counter">+</button>
    <button v-on:click="&#45;&#45;counter">-</button>-->
    <!--方法二:-->
    <button v-on:click="add"></button>
    <button v-on:click="sub"></button>
</div>

<script src="../js/vue.js"></script>
<script>
    let app = new Vue({
        el: "#app",
        data: {
            counter: 0,
        },
        methods: {
            add: function () {
                console.log("add被执行");
                this.counter++;
            },
            sub: function () {
                console.log("sub被执行");
                this.counter--;
            }
        }
    })
</script>

</body>
</html>

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
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>vue生命周期学习</title>
  <script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
  <div id="app">
    <h1></h1>
  </div>
</body>
<script>
  var vm = new Vue({
    el: '#app',
    data: {
      message: 'Vue的生命周期'
    },
    beforeCreate: function() {
      console.group('------beforeCreate创建前状态------');
      console.log("%c%s", "color:red" , "el     : " + this.$el); //undefined
      console.log("%c%s", "color:red","data   : " + this.$data); //undefined 
      console.log("%c%s", "color:red","message: " + this.message) 
    },
    created: function() {
      console.group('------created创建完毕状态------');
      console.log("%c%s", "color:red","el     : " + this.$el); //undefined
      console.log("%c%s", "color:red","data   : " + this.$data); //已被初始化 
      console.log("%c%s", "color:red","message: " + this.message); //已被初始化
    },
    beforeMount: function() {
      console.group('------beforeMount挂载前状态------');
      console.log("%c%s", "color:red","el     : " + (this.$el)); //已被初始化
      console.log(this.$el);
      console.log("%c%s", "color:red","data   : " + this.$data); //已被初始化  
      console.log("%c%s", "color:red","message: " + this.message); //已被初始化  
    },
    mounted: function() {
      console.group('------mounted 挂载结束状态------');
      console.log("%c%s", "color:red","el     : " + this.$el); //已被初始化
      console.log(this.$el);    
      console.log("%c%s", "color:red","data   : " + this.$data); //已被初始化
      console.log("%c%s", "color:red","message: " + this.message); //已被初始化 
    },
    beforeUpdate: function () {
      console.group('beforeUpdate 更新前状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el);   
      console.log("%c%s", "color:red","data   : " + this.$data); 
      console.log("%c%s", "color:red","message: " + this.message); 
    },
    updated: function () {
      console.group('updated 更新完成状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el); 
      console.log("%c%s", "color:red","data   : " + this.$data); 
      console.log("%c%s", "color:red","message: " + this.message); 
    },
    beforeDestroy: function () {
      console.group('beforeDestroy 销毁前状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el);    
      console.log("%c%s", "color:red","data   : " + this.$data); 
      console.log("%c%s", "color:red","message: " + this.message); 
    },
    destroyed: function () {
      console.group('destroyed 销毁完成状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el);  
      console.log("%c%s", "color:red","data   : " + this.$data); 
      console.log("%c%s", "color:red","message: " + this.message)
    }
  })
</script>
</html>

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选项

        • <!DOCTYPE html>
          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta http-equiv="X-UA-Compatible" content="ie=edge">
            <title>vue生命周期学习</title>
            <script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
          </head>
          <body>
            <div id="app">
              <!--html中修改的-->
              <h1></h1>
            </div>
          </body>
          <script>
            var vm = new Vue({
              el: '#app',
              template: "<h1></h1>", //在vue配置项中修改的
              data: {
                message: 'Vue的生命周期'
              }
          </script>
          </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 中还是通过 ``进行占位的,因为此时还有挂在到页面上,还是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指令

  • 该指令后不需要跟任何表达式
  • 该指令表示元素和组件只渲染一次,不会随着数据的改变而改变。
<div id="app">
    <h2></h2>
    <h2 v-once></h2>
</div>

<script src="../js/vue.js"></script>
<script>
    let app = new Vue({
        el: "#app",
        data: {
            counter: 0,
            message: ' Hello Vue,js',
            firstName: 'kobe',
            lastName: 'bryant',
        },
        methods: {
            add: function () {
                console.log("add被执行");
                this.counter++;
            },
            sub: function () {
                console.log("sub被执行");
                this.counter--;
            }
        }
    })
</script>

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
  • 数组语法

    • <div id="app">
          <h2 class="title" :class="['active', 'line']"></h2>
      </div>
      
    • image-20220228203813692

5.8.3 v-bind动态绑定style

对象语法

<div id="app">
<!--    <h2 :style="{属性名:属性值}"></h2>-->

    <!--'0px'必须加上单引号 否则是当作变量去解析
    驼峰标识的时候 fontSize 不需要加单引号
    -标识的时候 'font-size'也需要加单引号
    -->
    <h2 :style="{fontSize: '50px'}"></h2>
    <h2 :style="{'font-size': '50px'}"></h2>
    <h2 :style="{fontSize: size}"></h2>
</div>
<script src="../js/vue.js"></script>
<script>
    let app = new Vue({
        el: "#app",
        data: {
            message: '你好呀',
            size: '100px'
        },
    })

image-20220301150004990

数组语法

image-20220301150353552

6. 计算属性

6.1 计算属性的简单操作

computed 类似于 methods

<div id="app">
    <h2></h2>
    <h2> </h2>
    <h2></h2>
    <!--计算属性-->
    <h2></h2>
</div>

<script src="../js/vue.js"></script>
<script>
    let app = new Vue({
        el: "#app",
        data: {
            firstName: '',
            lastName: '撷思',
        },
        /*计算属性*/
        computed: {
            fullName: function () {
                return this.firstName + ' ' + this.lastName;

            }
        },
        methods: {
            getFullName: function () {
                return this.firstName + ' ' + this.lastName;
            }
        }
    })
</script>

image-20220301152057665

6.2 计算属性的复杂操作

<div id="app">
    <h2>总价格: </h2>
    <h2>总价格: </h2>
</div>

<script src="../js/vue.js"></script>
<script>
    let app = new Vue({
        el: "#app",
        data: {
            books: [
                {id: 110, name: '深入理解计算机原理', price: 100},
                {id: 111, name: '现代操作系统', price: 105},
                {id: 112, name: '深入理解Java虚拟机', price: 110},
            ]
        },
        /*计算属性*/
        computed: {
            totalPrice1: function () {
                let ans = 0;
                for (let i=0;i<this.books.length;i++) {
                    ans += this.books[i].price;
                }
                return ans;
            },
            totalPrice2: function () {
                let ans = 0;
                for (let book of this.books) {
                    ans += book.price;
                }
                return ans;
            },
        },
    })
</script>

image-20220301154513741

6.3 计算属性的 setter 和 getter

image-20220301162227779

6.4 计算属性和methods的区别

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

    • <div id="app">
          <!--方法二:调用methods-->
          <h2></h2>
          <h2></h2>
          <h2></h2>
          <h2></h2>
      </div>
          
      <script src="../js/vue.js"></script>
      <script>
          let app = new Vue({
              el: "#app",
              data: {
                  firstName: '',
                  lastName: '撷思',
              },
              methods: {
                  getFullName: function () {
                      console.log('getFullName');
                      return this.firstName + ' ' + this.lastName;
                  }
              }
          })
      </script>
      
    • image-20220301163131677

    • methods 调用了 4 次

  • 使用计算属性

    • <div id="app">
      <!--    方法三:计算属性-->
          <h2></h2>
          <h2></h2>
          <h2></h2>
          <h2></h2>
      </div>
          
      <script src="../js/vue.js"></script>
      <script>
          let app = new Vue({
              el: "#app",
              data: {
                  firstName: '',
                  lastName: '撷思',
              },
              /*计算属性*/
              computed: {
                  fullName: function () {
                      console.log('fullName');
                      return this.firstName + ' ' + this.lastName;
                  }
              },
          })
      </script>
      
    • image-20220301163324978

    • 只有一次

7. v-on

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

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

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

v-on 介绍:

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

image-20220301200322508

7.2 v-on的参数传递

<script src="../js/vue.js"></script>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            counter: 0,
        },
        methods: {
            btn1Click() {
                console.log("btn1Click");
            }
        }
    })
</script>
  • 如果该方法不需要额外参数,那么方法后的 () 可以不添加。

    •     <!--事件调用的方法没有参数-->
          <button @click="btn1Click()">按钮1</button>
          <button @click="btn1Click">按钮1-1</button>
      
    • image-20220301200839221
  • 如果方法本身中有一个参数,那么会默认将原生事件 event 参数传递进去

    •     <!--事件调用的方法需要参数-->
          <button @click="btn2Click(123)">按钮2</button>
          <button @click="btn2Click">按钮2-2</button>
      
    • image-20220301201303543
  • 如果需要同时传入某个参数,同时需要 event 时,可以通过 $event 传入事件

    •     <!--事件调用的方法需要event对象,也需要其他参数-->
          <button @click="btn3Click">按钮3</button>
          <button @click="btn3Click()">按钮3-1</button>
          <button @click="btn3Click(123)">按钮3-2</button>
          <button @click="btn3Click(123, $event)">按钮3-4</button>
      
    • 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 登录切换的小案例

切换账户登录跟邮箱登录

<div id="app">
    <span v-if="type=='username'">
        <label>用户账号:</label>
        <input placeholder="用户账号">
    </span>
    <span v-else>
        <label>邮箱地址:</label>
        <input placeholder="邮箱地址">
    </span>
    <button @click="handle">切换类型</button>
</div>
<script src="../js/vue.js"></script>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            message: "你好呀",
            score: 98,
            type: 'username',
        },
        methods: {
            handle() {
                this.type = this.type == 'username' ? 'email' : 'username';
            }
        }
    })
</script>

image-20220301212254794

问题描述:

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

问题解答:

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

解决方案

  • 如果我们不希望 Vue 出现类似重复利用的问题,可以给对应的 input 添加 key
  • 并且我们需要保证 key 的不同
<div id="app">
    <span v-if="type=='username'">
        <label>用户账号:</label>
        <input placeholder="用户账号" key="username-key">
    </span>
    <span v-else>
        <label>邮箱地址:</label>
        <input placeholder="邮箱地址" key="email-key">
    </span>
    <button @click="handle">切换类型</button>
</div>

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 而已。
<div id="app">
    <h2 v-if="isShow" id="1"></h2>
    <h2 v-show="isShow" id="2"></h2>
    <h2 v-if="!isShow" id="3">, !isShow</h2>
    <h2 v-show="!isShow" id="4">, !isShow</h2>
</div>
<script src="../js/vue.js"></script>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            message: "你好呀",
            isShow: true,
        },
    })
</script>

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()

示例代码

<div id="app">
    <ul>
        <li v-for="item in letters" :key="item"></li>
        <button @click="btnClick">按钮</button>
    </ul>
</div>
<script src="../js/vue.js"></script>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            letters: ['a', 'b', 'c', 'd'],
            counter: 0,
        },
        methods: {
            btnClick() {
            }
        }
    })
</script>
  • push()

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

10. 购物车案例

10.1 搭建页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>

<div id="app">
    <div v-if="books.length">
        <table>
            <thead>
            <tr>
                <th></th>
                <th>书籍名称</th>
                <th>出版日期</th>
                <th>价格</th>
                <th>购买数量</th>
                <th>操作</th>
            </tr>
            </thead>
            <tbody>
            <tr v-for="(book, index) in books">
                <!--<td v-for="value in book"></td>-->
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td>
                    <button @click="decrement(index)" v-bind:disabled="book.count <= 1">-</button>
                    
                    <button @click="increment(index)">+</button>
                </td>
                <td>
                    <button @click="removeClick(index)">移除</button>
                </td>
            </tr>
            </tbody>
        </table>
        <h2>总价格:¥</h2>
    </div>
    <div v-else>
        购物车为空
    </div>
</div>

<script src="../js/vue.js"></script>
<script src="main.js"></script>
</body>
</html>

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<this.books.length;i++) {
                ans += this.books[i].price * this.books[i].count;
            }
            return ans.toFixed(2);
        }
    },
    methods: {
        ......
    },
    /*过滤器*/
    filters: {
        ......
    }
})

11. v-model 表单绑定

11.1 JavaScript高阶函数的使用

  1. filter函数

    • ```js 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

进阶:

<div id="app">
    <input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
    
</div>

image-20220302205503570

11.4 v-model结合radio类型

  • <div id="app">
        <label for="gentle">
            <input type="radio" id="gentle" name="gender" value="男" v-model="gender"></label>
        <label for="lady">
            <input type="radio" id="lady" name="gender" value="女" v-model="gender"></label>
        <h2>您选择的性别是: </h2>
    </div>
    <script src="../js/vue.js"></script>
    <script>
        const vue = new Vue({
            el: '#app',
            data: {
                message: '你好啊',
                gender: '',
            },
        })
    </script>
    
  • image-20220302211502117

11.5 v-model结合checkbox

  • 单选框

    •     <!--单选框-->
          <label for="agree">
              <input type="checkbox" id="agree" v-model="isAgree">同意协议
          </label>
          <h2>您选择的是: </h2>
          <button :disabled="!isAgree">下一步</button>
      </div>
      <script src="../js/vue.js"></script>
      <script>
          const vue = new Vue({
              el: '#app',
              data: {
                  message: '你好啊',
                  isAgree: false,
              },
          })
      </script>
      
    • image-20220302212138122image-20220302212148797
  • 复选框

    • <div>  
      <!--多选框-->
          <input type="checkbox" value="篮球" v-model="hobbies">篮球
          <input type="checkbox" value="足球" v-model="hobbies">足球
          <input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
          <input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
          <h2>您选择是:</h2>
      </div>
      <script src="../js/vue.js"></script>
      <script>
          const vue = new Vue({
              el: '#app',
              data: {
                  message: '你好啊',
                  isAgree: false,
                  hobbies: [],
              },
          })
      </script>
      
    • image-20220302212509233

11.6 v-model结合selcet

  • 选择一个

    • <div id="app">
          <!--1. 选择一个-->
          <select name="abc" v-model="fruit">
              <option value="苹果">苹果</option>
              <option value="香蕉">香蕉</option>
              <option value="草莓">草莓</option>
              <option value="菠萝">菠萝</option>
              <option value="榴莲">榴莲</option>
          </select>
          <h2>您选择的水果是: </h2>
      </div>
      <script src="../js/vue.js"></script>
      <script>
          const vue = new Vue({
              el: '#app',
              data: {
                  message: '你好啊',
                  fruit: '',
              },
          })
      </script>
      
    • image-20220302213403629
  • 多选

    • <div> 
      <select name="abc" v-model="fruits" multiple>
              <option value="苹果">苹果</option>
              <option value="香蕉">香蕉</option>
              <option value="草莓">草莓</option>
              <option value="菠萝">菠萝</option>
              <option value="榴莲">榴莲</option>
          </select>
          <h2>您选择的水果是: </h2>
          <!--1. 选择多个-->
      </div>
      <script src="../js/vue.js"></script>
      <script>
          const vue = new Vue({
              el: '#app',
              data: {
                  message: '你好啊',
                  fruit: '',
                  fruits: [],
              },
          })
      </script>
      
    • image-20220302213545069

11.7 v-model值绑定

动态的给 value 赋值而已

  • 我们前面的 value 中的值,可以回头去看一下,都是在定义 input 的时候直接给定的。
  • 但是真实开发中,这些 input 的值可能是从网络获取或定义在 data 中的。
  • 所以我们可以通过 v-bind:value 动态的给 value 绑定值。
<div id="app">
    <label v-for="item in originHobbies">
        <input type="checkbox" :value="item" v-model="fruits">
    </label>
    <h2>您选择的是: </h2>
</div>
<script src="../js/vue.js"></script>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
            fruit: '',
            fruits: [],
            originHobbies: ['篮球', '足球', '乒乓球', '羽毛球', '台球', '悠悠球']
        },
    })
</script>

image-20220303102535908

11.8 v-model修饰符的私用

  1. lazy 修饰符:

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

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

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

    • <div id="app">
          <!--1. lazy-->
          <input type="text" v-model.lazy="message">
          <h2></h2>
      </div>
      <script src="../js/vue.js"></script>
      <script>
          const vue = new Vue({
              el: '#app',
              data: {
                  message: '你好啊',
              },
               
          })
      </script>
      
  2. number 修饰符:

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

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

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

    • <div id="app">
          <!--2. number-->
          <input type="number" v-model.number="age">
          <h2> ---- </h2>
      </div>
      <script src="../js/vue.js"></script>
      <script>
          const vue = new Vue({
              el: '#app',
              data: {
                  message: '你好啊',
                  age: 0,
              },
          })
      </script>
      
  3. trim 修饰符:

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

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

    • <div id="app">
          <!--3. 修饰符 trim-->
          <input type="text" v-model.trim="name">
          <h2>您输入的name是:</h2>
      </div>
      <script src="../js/vue.js"></script>
      <script>
          const vue = new Vue({
              el: '#app',
              data: {
                  message: '你好啊',
                  age: 0,
                  name: '',
              },
          })
      </script>
      

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 父组件和子组件的区分

<div id="app">
<!--    <cpn1></cpn1>-->
    <cpn2></cpn2>
    <p>------</p>
    <cpn1></cpn1>
</div>
<script src="../js/vue.js"></script>

<script>
    <!--1. 第一个组件 子组件-->
    const cpnC1 = Vue.extend({
        template: `
        <div>
            <h2>我是标题1</h2>
            <p>我是内容 哈哈哈哈</p>
        </div>
        `
    })

    /*2. 第二个组件 父组件*/
    const cpnC2 = Vue.extend({
        template: `
        <div>
            <h2>我是标题2</h2>
            <p>我是内容 嘿嘿嘿嘿</p>
            <cpn1></cpn1>
        </div>
        `,
        components: {
            cpn1: cpnC1
        }
    })

    const vue = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
        },
        components: {
            // cpn1: cpnC1,
            cpn2: cpnC2,
        }
    })
</script>

image-20220303145923255

image-20220303145930164

12.3.3 注册组件的语法糖写法

  • 全局组件的语法糖

    • <script>
              /*1 创建组件构造器对象*/
          const cpnC = Vue.extend({
              template:
                  `<div>
                      <h2>我是标题</h2>
                      <p>我是内容,哈哈哈哈</p>
                      <p>我是内容,呵呵呵呵</p>
                  </div>`
          })
          /*2. 注册组件*/
          Vue.component('my-cpn', cpnC)
          
          /*上面两步等价于下面*/
          Vue.component('mu-cpn', {
              template:
                  `<div>
                      <h2>我是标题</h2>
                      <p>我是内容,哈哈哈哈</p>
                      <p>我是内容,呵呵呵呵</p>
                  </div>`
          })
      </script>
      
  • 局部组件的语法糖

    • <script>    
      /*注册局部组件的语法糖*/
          const vue = new Vue({
              el: '#app',
              data: {
                  message: '你好啊',
              },
              component: {
                  'cpn2': {
                      template:
                          `<div>
                      <h2>我是标题</h2>
                      <p>我是内容,哈哈哈哈</p>
                      <p>我是内容,呵呵呵呵</p>
                  </div>`
              }
          })
      </script>
      

12.3.4 组件模板分离

  • 方式一:

    • <!--1 方式一:script标签 类型必须为text/x-template-->
      <script type="text/x-template" id="cpn">
          <div>
              <h2>我是标题</h2>
              <p>我是内容</p>
          </div>
      </script>
      <script>
          /*1 注册全局组件*/
          Vue.component('my-cpn', {
              template: `#cpn`
          })
      </script>
      
  • 方式二:

    •     
      <!--方式二:template-->
      <template id="cpn">
          <div>
              <h2>我是标题2</h2>
              <p>我是内容2</p>
          </div>
      </template>
          
      <script src="../js/vue.js"></script>
      <script>
          /*1 注册全局组件*/
          Vue.component('my-cpn', {
              template: `#cpn`
          })
      </script>
      
<div id="app">
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
</div>

<!--1 方式一:script标签 类型必须为text/x-template-->
<script type="text/x-template" id="cpn">
    <div>
        <h2>我是标题</h2>
        <p>我是内容</p>
    </div>
</script>

<!--方式二:template-->
<template id="cpn">
    <div>
        <h2>我是标题2</h2>
        <p>我是内容2</p>
    </div>
</template>

<script src="../js/vue.js"></script>
<script>
    /*1 注册全局组件*/
    Vue.component('my-cpn', {
        template: `#cpn`
    })

    const vue = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
        }
    })
</script>

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 的值有两种方式:

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

方式一:

<div id="app">
    <cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
    <div>
        <lu>
            <li v-for="item in cmovies"></li>
        </lu>
        <h2></h2>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>

    const vue = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
            movies: ['进击的巨人', '海贼王', '火影忍者', '鬼灭之刃'],
        },
        components: {
            'cpn': {
                template: '#cpn',
                props: ['cmovies', 'cmessage'],
            }
        }
    })
</script>

image-20220303213816650

传对象

<div id="app">
    <cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
    <div>
        <lu>
            <li v-for="item in cmovies"></li>
        </lu>
        <h2></h2>
    </div>
</template>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
            movies: ['进击的巨人', '海贼王', '火影忍者', '鬼灭之刃'],
        },
        components: {
            'cpn': {
                template: '#cpn',
                // props: ['cmovies', 'cmessage'],
                props: {
                    cmessage: {
                        type: String,   /*类型限制*/
                        default: '',  /*默认值*/
                        required: true, /*true表示使用cmessage这个属性的时候必须要传值*/
                    },
                    cmovies: {
                        type: Array,
                        /*类型是数组或者对象的时候,默认值必须要是一个函数*/
                        default() {
                            return [];
                        }
                    }
                }
            }
        }
    })
</script>

支持的数据类型:

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

image-20220304104137256

13.1.2 props驼峰标识

不支持驼峰标识

<div id="app">
    <cpn :cInfo="info"></cpn>
</div>

<template id = "cpn">
    <h2></h2>
</template>

<script src="../js/vue.js"></script>
<script>

    const cpn = {
        template: '#cpn',
        props: {
            cInfo: {
                type: Object,
                default() {
                    return {};
                }
            }
        }
    }

    const vue = new Vue({
        el: '#app',
        data: {
            info: {
                name: '刘云杰',
                age: 18,
                height: 188,
            }
        },
        components: {
            cpn
        }
    })
</script>

image-20220304105102914

解决方法

cInfo 改为 c-info


image-20220304105251852

image-20220304105311525

image-20220304110941068

13.2 子传父

  • props 用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中。
  • 我们应该如何处理呢?这个时候,我们需要使用自定义事件来完成。
  • 什么时候需要自定义事件呢?
    • 当子组件需要向父组件传递数据时,就要用到自定义事件了。
    • 我们之前学习的 v-on 不仅仅可以用于监听 DOM 事件,也可以用于组件间的自定义事件。
  • 自定义事件的流程:
    • 在子组件中,通过 $emit() 来触发事件。
    • 在父组件中,通过 v-on 来监听子组件事件。
<!--父组件模板-->
<div id="app">
    <!--这里不支持驼峰标识-->
    <vpn @item-click="apcClick"></vpn>
</div>
<!--子组件模板-->
<template id="cpn">
    <div>
        <button v-for="item in categories"
                @click="btnClick(item)">
            
        </button>
    </div>
</template>

<script src="../js/vue.js"></script>
<script>
    /*1. 子组件*/
    const vpn = {
        template: '#cpn',
        data() {
            return {
                categories: [
                    {id: 'aaa', name: '热门推荐'},
                    {id: 'bbb', name: '手机数码'},
                    {id: 'ccc', name: '家用家电'},
                    {id: 'ddd', name: '电脑办公'},
                ]
            }
        },
        methods: {
            btnClick(item) {
                /*向父组件通信  自定义事件
                * item-click是事件的名称
                * item是事件要传递的参数
                */
                this.$emit('item-click', item)
            }
        }
    }

    /*2. 父组件*/
    const vue = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
        },
        components: {
            vpn
        },
        methods: {
            apcClick(item) {
                console.log(item);
            }
        }
    })
</script>

13.3 父子间通信案例1

  • 父组件通过 propscounter 传递给子组件;
  • 子组件通过自定义事件 add-counterdecrement-counter 将点击事件传递给父组件。
<!--父组件模板-->
<div id="app">
    <!--这里不支持驼峰标识-->
    <vpn @add-counter="addCounter" @decrement-counter="decrementCounter" :child-counter="counter"></vpn>
</div>
<!--子组件模板-->
<template id="cpn">
    <div>
        <h2></h2>
        <button @click="increment()">+</button>
        <button @click="decrement()">-</button>
    </div>
</template>

<script src="../js/vue.js"></script>
<script>
    /*1. 子组件*/
    const vpn = {
        template: '#cpn',
        props: {
            childCounter: {
                required: true,
            }
        },
        methods: {
            increment() {
                this.$emit('add-counter');
            },
            decrement() {
                this.$emit('decrement-counter')
            }
        }
    }

    /*2. 父组件*/
    const vue = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
            counter: 0,
        },
        components: {
            vpn
        },
        methods: {
            addCounter() {
                this.counter++;
            },
            decrementCounter() {
                this.counter--;
            }
        }
    })
</script>

image-20220304125142083

13.4 父子间通信案例2

注意:

  • 子组件中 number1number2 的值初始来源于 num1num2,将 number1number2<input> 双向绑定的时候不能直接绑定 number1number2,需要找一个中间值。
<div id="app">
    <cpn :number1="num1"
         :number2="num2"
         @num1change="num1change"
         @num2change="num2change"/>
</div>
<template id="cpn">
    <div>
        <h2>props: </h2>
        <h2>data: </h2>
        <input type="text" :value="dnumber1" @input="num1Input">
        <h2>props: </h2>
        <h2>data: </h2>
        <input type="text" :value="dnumber2" @input="num2Input">
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            num1: 1,
            num2: 0,
        },
        methods: {
            num1change(value) {
                this.num1 = parseFloat(value);
            },
            num2change(value) {
                this.num2 =  parseFloat(value);
            },
        },
        components: {
            cpn: {
                template: '#cpn',
                props: {
                    number1: Number,
                    number2: Number,
                },
                data() {
                    return {
                        dnumber1: this.number1,
                        dnumber2: this.number2,
                    }
                },
                methods: {
                    num1Input(event) {
                        this.dnumber1 = event.target.value;
                        this.$emit("num1change", this.dnumber1);

                        this.dnumber2 = this.dnumber1 * 100;
                        this.$emit("num2change", this.dnumber2);
                    },
                    num2Input(event) {
                        this.dnumber2 = event.target.value;
                        this.$emit("num2change", this.dnumber2);

                        this.dnumber1 = this.dnumber2 / 100;
                        this.$emit("num1change", this.dnumber1);
                    },
                }
            },
        }
    })
</script>

image-20220304151244724

13.5 父子组件的访问方式

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

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

13.5.1 children 方式(不推荐)

  • <div id="app">
        <cpn></cpn>
        <button @click="btnClick">按钮</button>
    </div>
    <template id="cpn">
        <div>我是子组件</div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const vue = new Vue({
            el: '#app',
            data: {
                message: '你好啊',
            },
            methods: {
                btnClick() {
                    console.log(this.$children);
                    this.$children[0].showMessage();
                }
            },
            components: {
                cpn: {
                  template: '#cpn',
                  methods: {
                      showMessage() {
                          console.log('showMessage');
                      }
                  }
                },
            }
        })
    </script>
    
  • image-20220304161011762

13.5.2 refs方式

image-20220304161619050

image-20220304161551590

14. 插槽slot

组件的插槽

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

14.1 插槽的基本使用

  1. 插槽的基本使用 <slot></slot>
  2. 插槽的默认值 <slot>xxxxx</slot>
  3. 如果有多个值,同时放入到组件中进行替换时,一起作为替换元素
<div id="app">
    <cpn><button>按钮</button></cpn>
    <cpn><span>哈哈</span></cpn>
    <cpn><i>呵呵</i></cpn>
    <cpn><button>按钮2</button></cpn>
    <cpn>
        <i>hahahaha</i>
        <b>yiyiyiyi</b>
        <div>divdiv</div>
        <p>ppppppppp</p>
    </cpn>
    <cpn></cpn>
    <cpn></cpn>
    <cpn></cpn>
    <cpn></cpn>
</div>
<template id="cpn">
    <div>
        <h2>我是插槽</h2>
        <p>我是组件</p>
        <slot><button>button</button></slot>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
        },
        components: {
            cpn: {
                template: '#cpn',
            }
        }
    })
</script>

image-20220304184105122

14.2 具名插槽

slot 元素一个 name 属性即可

<slot name='myslot'></slot>

<div id="app">
    <cpn>
        <span slot="center">标题</span>
    </cpn>
    <cpn>
        <button slot="left">按钮</button>
    </cpn>
</div>
<template id="cpn">
    <div>
        <slot name="left"><span>左边</span></slot>
        <slot name="center"><span>中间</span></slot>
        <slot name="right"><span>右边</span></slot>
        <slot><span>哈哈哈</span></slot>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
        },
        components: {
            cpn: {
                template: '#cpn',
            }
        }
    })
</script>

image-20220304191712589

14.3 编译作用域

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

<div id="app">
    <cpn v-slot="isShow"></cpn>
</div>
<template id="cpn">
    <div>
        <h2>我是子组件</h2>
        <p>我是内容 哈哈哈</p>
        <button v-show="isShow">按钮</button>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
            isShow: true,
        },
        components: {
            cpn: {
                template: '#cpn',
                data() {
                    return {
                        isShow: false,
                    }
                }
            }
        },
    })
</script>

image-20220304192551618

14.4 作用域插槽的使用

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

我们先提一个需求:

  • 子组件中包括一组数据,比如:Languages: ['JavaScript', 'Python', 'Swift', 'Go', 'C++']
  • 需要在多个界面进行展示:
    • 某些界面是以水平方向一一展示的,
    • 某些界面是以列表形式展示的,
    • 某些界面直接展示一个数组
  • 内容在子组件,希望父组件告诉我们如何展示,怎么办呢?
    • 利用 slot 作用域插槽就可以了
<div id="app">
    <cpn></cpn>
    <cpn>
        <!--目的是获取子组件中的pLanguages-->
        <template slot-scope="slot">
            <span v-for="item in slot.data">-</span>
        </template>
    </cpn>
    <cpn>
        <!--目的是获取子组件中的pLanguages-->
        <template slot-scope="slot">
            <span></span>
        </template>
    </cpn>
</div>
<template id="cpn">
    <div>
        <slot :data="pLanguages">
            <ul>
                <li v-for="item in pLanguages"></li>
            </ul>
        </slot>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
        },
        components: {
            cpn: {
                template: '#cpn',
                data() {
                    return {
                        pLanguages: ['JavaScript', 'Python', 'Swift', 'Go', 'C++'],
                    }
                }
            },
        },
    })
</script>

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 元素中的 `` 内容删掉,只保留一个基本的 iddiv 的元素
  • 但是如果我依然希望在其中显示 `` 的内容,应该怎么处理呢?
  • 我们可以再定义一个 template属性,代码如下:
// 5. 使用Vue进行开发
import Vue from 'vue'

const app = new Vue({
    el: '#app',
    template:
        `<div>
          <h2></h2>
        <button @click="btnClick">按钮</button>
        <h2></h2>
        </div>`,
    data: {
        message: 'Hello webpack',
        name: '刘云杰',
        methods : {
            btnClick() {
                console.log('lyj ');
            }
        }
    }
})

15.4.7 vue的终极使用方式

  • 优化第一步:

    • // 5. 使用Vue进行开发
      import Vue from 'vue'
          
      const App = {
          template:
              `<div>
                <h2></h2>
              <button @click="btnClick">按钮</button>
              <h2></h2>
              </div>`,
          data() {
              return {
                  message: 'Hello webpack',
                  name: '刘云杰',
              }
          },
          methods: {
              btnClick() {
              }
          }
      }
          
      const app = new Vue({
          el: '#app',
          template: `<App/>`,
          data: {
          },
          components: {
              App
          }
      })
      
  • 优化第二步:

    • 新建 app.js

      • export default {
            template:
                `<div>
                  <h2></h2>
                <button @click="btnClick">按钮</button>
                <h2></h2>
                </div>`,
            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: `<App/>`,
            data: {
            },
            components: {
                App
            }
        })
              
        
  • 优化第三步:

    • 新建 App.vue文件

      • <template>
          <div>
            <h2 class="title"></h2>
            <button @click="btnClick">按钮</button>
            <h2></h2>
          </div>
        </template>
              
        <script>
        # import cpn from './cpn'
        #components: {
        #  cpn
        #}
        export default {
          name: "App",
          data() {
            return {
              message: 'Hello webpack',
              name: '刘云杰',
            }
          },
          methods: {
            btnClick() {
            }
          }
        }
        </script>
              
        <style scoped>
        .title {
          color: aquamarine;
        }
        </style>
        

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: '<App/>'
    })
       
    
    • 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: `<div></div>`,
      data() {
        return {
          message : '我是组件',
        }
      }
    }
       
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      /*render函数*/
      render: function (createElement) {
        // 2. 传入组件
        return createElement(cpn);
      }
      // components: { App },
      // template: '<App/>'
    })
       
    
    • 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: '<App/>'
      })
           
      
    • 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:

    • <script>
          /*箭头函数:定义函数的一种方式*/
          // 1. function定义函数
          const aaa = function () {
          }
          // 2. 对象自变量中定义函数
          const obj = {
              bbb() {
                      
              }
          }
          // 3. ES6中的箭头函数
          // const 函数名 = (参数列表) => {}
          const ccc = () => {
              /*箭头函数最基础的使用
              * 
              * 1. 没有参数
              * 2. 没有返回值
              * */
          } 
      </script>
      
  • 例子2:只有一个参数

    • <script>
          // 1. 参数问题
          // 1.1 放入两个参数
          const sum = (num1, num2) => {
              return num1 + num2;
          }
          
          // 1.2 放入一个参数的时候 ()可以省略
          const power = num => {
              return num * num;
          }
      </scripts>
      
  • 例子3:只有一行代码

    • <script>
          // 2.返回值问题
          // 2.1 函数代码块中有多行代码时  正常写
          const test = () => {
              /*1. 打印Hello World*/
              console.log('Hello World');
              /*2. 打印Hello Vuejs*/
              console.log('Hello Vue.js');
          }
          
          // 2.2 函数代码块中只有一行代码
          /*
          * const mul = (num1, num2) => {
              return num1 * num2;
          }
          * 等价于下面
          * */
          const mul = (num1, num2) => num1 * num2;
          
          const demo = () => console.log('hello world');
      </script>
      

this 指向

  • <script>
        /*什么时候使用*/
        const onj = {
            aaa() {
                setTimeout(function () {
                    console.log(this); // windows
                })
                /* 箭头函数的this引用的是最近作用域中的this
                *
                * 箭头函数中的this是如何查找的?向外层作用域一层层查找this,直到有this的定义。
                *
                * */
                setTimeout(() => {
                    console.log(this); // Obj对象
                })
            }
        }
        onj.aaa();
    </script>
    

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 路由映射配置

第一步: 创建路由组件

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

第三步: 使用路由: 通过 <router-link><router-view>

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

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

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

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

举例

  1. 新建 HelloWorld.vueabout.vue

    • <template>
        <div>
          <h2>我是about.js</h2>
          <p>我是about.js的内容, 哈哈哈</p>
        </div>
      </template>
           
      <script>
      export default {
        name: "about"
      }
      </script>
           
      <style scoped>
           
      </style>
           
      
  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 中使用路由

    • <template>
        <div id="app">
          <router-link to="/home">HelloWorld</router-link>
          <router-link to="/about">about</router-link>
           
        <!-- <router-view> 作用是占位 页面要渲染在哪个位置 -->
          <router-view></router-view>
        </div>
      </template>
           
      <script>
      export default {
        name: 'App'
      }
      </script>
           
      <style>
      #app {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      </style>
      

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的其他属性

  • 在前面的 <router-link> 中, 我们只是使用了一个属性: to, 用于指定跳转的路径.
  • <router-link> 还有一些其他属性:
    • tag: tag可以指定 <router-link> 之后渲染成什么组件, 比如 <router-link to='/home' tag='li'> 会被渲染成一个 <li> 元素, 而不是 <a>
    • replace: replace不会留下 history 记录, 所以指定 replace 的情况下,后退键返回不能返回到上一个页面中。
    • active-class: 当 <router-link> 对应的路由匹配成功时, 会自动给当前元素设置一个 router-link-activeclass
      • 设置 active-class 可以修改默认的名称.在进行高亮显示的导航菜单或者底部 tabbar 时, 会使用到该类;
      • 但是通常不会修改类的属性, 会直接使用默认的 router-link-active 即可。

16.4.6 vue-router动态路由

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

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

例子

  • 新建 User.vue文件

    • <template>
      <div>
        <h2>我是用户界面</h2>
        <p>我是用户的相关信息,嘿嘿嘿</p>
        <h2></h2>
      </div>
      </template>
          
      <script>
      export default {
        name: "User",
        computed: {
          userId() {
            return this.$route.params.userId; // 注意:这里的userId对应的是App.vue中的userId
          }
        }
      }
      </script>
          
      <style scoped>
          
      </style>
      
  • 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传值

    • <template>
        <div id="app">
          <router-link to="/">HelloWorld</router-link>
          <router-link to="/about">about</router-link>
          <router-link :to="'/user/'+userId">user</router-link>
        <!-- <router-view> 作用是占位 页面要渲染在哪个位置 -->
          <router-view></router-view>
        </div>
      </template>
          
      <script>
      export default {
        name: 'App',
        data() {
          return {
            userId: 'lyj',
          }
        }
      }
      </script>
          
      <style>
      #app {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      </style>
      

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
  • 实现嵌套路由有两个步骤:
    • 创建对应的子组件, 并且在路由映射中配置对应的子路由.
    • 在组件内部使用 <router-view> 标签.

例子:

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

    • <template>
      <div>
        <ul>
          <li>新闻1</li>
          <li>新闻2</li>
          <li>新闻3</li>
          <li>新闻4</li>
        </ul>
      </div>
      </template>
          
      <script>
      export default {
        name: "HomeNews"
      }
      </script>
          
      <style scoped>
          
      </style>
          
      
    • <template>
      <div>
        <ul>
          <li>消息1</li>
          <li>消息2</li>
          <li>消息3</li>
          <li>消息4</li>
        </ul>
      </div>
      </template>
          
      <script>
      export default {
        name: "HomeMessage"
      }
      </script>
          
      <style scoped>
          
      </style>
          
      
  • 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 配置

    • <template>
        <div>
          <h2>Hello World</h2>
          <router-link to="/HelloWorld/news">新闻</router-link>
          <router-link to="/HelloWorld/message">消息</router-link>
          <router-view></router-view>
        </div>
      </template>
          
      <script>
      export default {
        name: 'HelloWorld',
        data () {
          return {
            msg: 'Welcome to Your Vue.js App'
          }
        }
      }
      </script>
          
      <!-- Add "scoped" attribute to limit CSS to this component only -->
      <style scoped>
          
      </style>
      

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文件

    • <template>
      <div>
        <h2>我是Profile组件</h2>
      </div>
      </template>
          
      <script>
      export default {
        name: "Profile"
      }
      </script>
          
      <style scoped>
          
      </style>
      
  • 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编写页面

    • <template>
        <div id="app">
          <router-link to="/HelloWorld">HelloWorld</router-link>
          <router-link to="/about">about</router-link>
          <router-link :to="'/user/'+userId">user</router-link>
          <router-link :to="{
                             path: '/profile',
                             query: {name: '刘云杰', age: 18, height: 188}}">
            档案</router-link>
          <!-- <router-view> 作用是占位 页面要渲染在哪个位置 -->
          <router-view></router-view>
        </div>
      </template>
      
  • image-20220308092857426

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

    • <template>
      <div>
        <h2>我是Profile组件</h2>
        <p></p>
        <p></p>
        <p></p>
        <p></p>
      </div>
      </template>
          
      <script>
      export default {
        name: "Profile"
      }
      </script>
          
      <style scoped>
          
      </style>
          
      
  • 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#%E8%B7%AF%E7%94%B1%E7%8B%AC%E4%BA%AB%E7%9A%84%E5%AE%88%E5%8D%AB。

16.4.12 vue-router中的keep-alive

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

未使用 keep-alive 之前

  • HelloWorld.vue中添加如下代码

    • <script>
      export default {
        name: 'HelloWorld',
        data () {
          return {
            msg: 'Welcome to Your Vue.js App'
          }
        },
        created() {
          console.log('Hello World created');
        },
        destroyed() {
          console.log('Hello World destroyed');
        },
      }
      </script>
      
    • 每次都是创建新的实例

    • image-20220308110441459

使用 keep-alive 之后

  • App.vue

    • <template>
        <div id="app">
          <router-link to="/HelloWorld">HelloWorld</router-link>
          <router-link to="/about">about</router-link>
          <router-link :to="'/user/'+userId">user</router-link>
          <router-link :to="{
                             path: '/profile',
                             query: {name: '刘云杰', age: 18, height: 188}}">
            档案</router-link>
          <!-- <router-view> 作用是占位 页面要渲染在哪个位置 -->
          <keep-alive>
            <router-view></router-view>
          </keep-alive>
      <!--    <router-view></router-view>-->
        </div>
      </template
      
  • image-20220308123906880

  • 不会频繁创建与销毁

17. tabbar案例

17.1 基本结构的搭建

  1. 新建项目

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

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

    • <template>
        <div id="app">
          <div id="tab-bar">
            <div class="tab-bar-item">首页</div>
            <div class="tab-bar-item">分类</div>
            <div class="tab-bar-item">购物车</div>
            <div class="tab-bar-item">我的</div>
          </div>
        </div>
      </template>
           
      <script>
        export default {
          name: 'App',
        }
      </script>
           
      <style>
      @import "assets/css/base.css";
      #tab-bar {
        display: flex;
        background: #f6f6f6;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        box-shadow: 0px -2px 1px rgba(100,100,100,0.1);
      }
      .tab-bar-item {
        flex: 1;
        text-align: center;
        height: 49px;
      }
      </style>
           
      

17.2 TabBar和TabBarItem组件封装

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

    • <template>
        <div class="tab-bar-item">
          <slot name="item-icon"></slot>
          <slot name="item-text"></slot>
        </div>
      </template>
           
      <script>
      export default {
        name: "TabBarItem"
      }
      </script>
           
      <style scoped>
      .tab-bar-item {
        flex: 1;
        text-align: center;
        height: 49px;
        font-size: 14pt;
      }
           
      .tab-bar-item img {
        width: 24px;
        height: 24px;
        margin-top: 30px;
        vertical-align: middle;
        margin-bottom: 2px;
      }
      </style>
      
  2. 使用 TabBar.vue 将底部导航栏进行封装

    • <template>
        <div id="tab-bar">
          <slot></slot>
        </div>
      </template>
           
      <script>
      export default {
        name: "TabBar",
      }
      </script>
           
      <style scoped>
      #tab-bar {
        /* 本身的样式 */
        background-color: #f6f6f6;
        height: 49px;
        border-top: 1px solid #eee;
        box-shadow: 0px -1px 1px rgba(150,150,150,.08);
           
        /* 定位相关 */
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
           
        /* 利用flex进行布局 */
        display: flex;
        text-align: center;
      }
           
      </style>
      
  3. 将上面封装完成以后,只需要在 App.vue 写入相关数据即可

    • <template>
        <div id="app">
          <TabBar>
            <TabBarItem>
              <img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="首页">
              <div slot="item-text">首页</div>
            </TabBarItem>
            <TabBarItem>
              <img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="分类">
              <div slot="item-text">分类</div>
            </TabBarItem>
            <TabBarItem>
              <img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="购物车">
              <div slot="item-text">购物车</div>
            </TabBarItem>
            <TabBarItem>
              <img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="我的">
              <div slot="item-text">我的</div>
            </TabBarItem>
          </TabBar>
        </div>
      </template>
           
      <script>
        import TabBar from "./components/tabbar/TabBar";
        import TabBarItem from "./components/tabbar/TabBarItem";
           
        export default {
          name: 'App',
          components: {
            TabBar,
            TabBarItem,
          }
        }
      </script>
           
      <style>
        @import "assets/css/base.css";
      </style>
      

image-20220309103015365

17.3 给 TabBarItem传入active图片

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

TabBarItm.vue

<template>
  <div class="tab-bar-item">
    <div v-if="!isActive"><slot name="item-icon"></slot></div>
    <div v-else-if="isActive"><slot name="item-icon-active"></slot></div>
    <div :class="{'active': isActive}"><slot name="item-text"></slot></div>
  </div>
</template>

<script>
export default {
  name: "TabBarItem",
  data() {
    return {
      isActive: true,
    }
  }
}
</script>

<style scoped>
.tab-bar-item {
  flex: 1;
  text-align: center;
  height: 49px;
  font-size: 14pt;
}

.tab-bar-item img {
  width: 24px;
  height: 24px;
  margin-top: 3px;
  vertical-align: middle;
  margin-bottom: 2px;
}
.active {
  color: red;
}
</style>

App.vue

<template>
  <div id="app">
    <TabBar>
      <TabBarItem>
        <img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="首页">
        <img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="首页">
        <div slot="item-text">首页</div>
      </TabBarItem>
      <TabBarItem>
        <img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="分类">
        <img slot="item-icon-active" src="./assets/img/tabbar/category_active.svg" alt="分类">
        <div slot="item-text">分类</div>
      </TabBarItem>
      <TabBarItem>
        <img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="购物车">
        <img slot="item-icon-active" src="./assets/img/tabbar/shopcart_active.svg" alt="购物车">
        <div slot="item-text">购物车</div>
      </TabBarItem>
      <TabBarItem>
        <img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="我的">
        <img slot="item-icon-active" src="./assets/img/tabbar/profile_active.svg" alt="我的">
        <div slot="item-text">我的</div>
      </TabBarItem>
    </TabBar>
  </div>
</template>

<script>
  import TabBar from "./components/tabbar/TabBar";
  import TabBarItem from "./components/tabbar/TabBarItem";

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

<style>
  @import "assets/css/base.css";
</style>

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

    • <template>
        <div id="app">
          <router-view></router-view>
          <TabBar>
            <TabBarItem path="/home">
              <img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="首页">
              <img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="首页">
              <div slot="item-text">首页</div>
            </TabBarItem>
            <TabBarItem path="/category">
              <img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="分类">
              <img slot="item-icon-active" src="./assets/img/tabbar/category_active.svg" alt="分类">
              <div slot="item-text">分类</div>
            </TabBarItem>
            <TabBarItem path="/card">
              <img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="购物车">
              <img slot="item-icon-active" src="./assets/img/tabbar/shopcart_active.svg" alt="购物车">
              <div slot="item-text">购物车</div>
            </TabBarItem>
            <TabBarItem path="/profile">
              <img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="我的">
              <img slot="item-icon-active" src="./assets/img/tabbar/profile_active.svg" alt="我的">
              <div slot="item-text">我的</div>
            </TabBarItem>
          </TabBar>
        </div>
      </template>
      
    • TabBarItem.vue

    • <template>
        <div class="tab-bar-item" @click="itemClick">
          <div v-if="!isActive"><slot name="item-icon"></slot></div>
          <div v-else-if="isActive"><slot name="item-icon-active"></slot></div>
          <div :class="{'active': isActive}"><slot name="item-text"></slot></div>
        </div>
      </template>
           
      <script>
      export default {
        name: "TabBarItem",
        props: {
          path: String
        },
        data() {
          return {
            isActive: true,
          }
        },
        methods: {
          itemClick() {
            this.$router.push(this.path).catch((error)=>error);
          }
        }
      }
      </script>
           
      <style scoped>
      .tab-bar-item {
        flex: 1;
        text-align: center;
        height: 49px;
        font-size: 14pt;
      }
           
      .tab-bar-item img {
        width: 24px;
        height: 24px;
        margin-top: 3px;
        vertical-align: middle;
        margin-bottom: 2px;
      }
      .active {
        color: red;
      }
      </style>
      

17.5 TabBarItem颜色动态控制

  • TabBarItem.vue 中添加计算属性

    • <script>
      export default {
        name: "TabBarItem",
        props: {
          path: String,
          activeColor: {
            type: String,
            default: 'red',
          }
        },
        data() {
          return {
            // isActive: true,
          }
        },
        computed: {
          isActive() {
            return this.$route.path.indexOf(this.path) !== -1;
          },
        },
        methods: {
          itemClick() {
            this.$router.push(this.path).catch((error) => error);
          }
        }
      }
      </script>
      
  • 自定义修改导航栏的颜色

    • App.vue

    • <template>
        <div id="app">
          <router-view></router-view>
          <TabBar>
            <TabBarItem path="/home" activeColor="pink">
              <img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="首页">
              <img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="首页">
              <div slot="item-text">首页</div>
            </TabBarItem>
            <TabBarItem path="/category" activeColor="pink">
              <img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="分类">
              <img slot="item-icon-active" src="./assets/img/tabbar/category_active.svg" alt="分类">
              <div slot="item-text">分类</div>
            </TabBarItem>
            <TabBarItem path="/card" activeColor="pink">
              <img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="购物车">
              <img slot="item-icon-active" src="./assets/img/tabbar/shopcart_active.svg" alt="购物车">
              <div slot="item-text">购物车</div>
            </TabBarItem>
            <TabBarItem path="/profile" activeColor="pink">
              <img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="我的">
              <img slot="item-icon-active" src="./assets/img/tabbar/profile_active.svg" alt="我的">
              <div slot="item-text">我的</div>
            </TabBarItem>
          </TabBar>
        </div>
      </template>
      
    • TabBarItem.vue

    • <template>
        <div class="tab-bar-item" @click="itemClick">
          <div v-if="!isActive">
            <slot name="item-icon"></slot>
          </div>
          <div v-else-if="isActive">
            <slot name="item-icon-active"></slot>
          </div>
          <div :style="itemColor">
            <slot name="item-text"></slot>
          </div>
        </div>
      </template>
          
      <script>
      export default {
        name: "TabBarItem",
        props: {
          path: String,
          activeColor: {
            type: String,
            default: 'red',
          }
        },
        data() {
          return {
            // isActive: true,
          }
        },
        computed: {
          isActive() {
            return this.$route.path.indexOf(this.path) !== -1;
          },
          itemColor() {
            return this.isActive ? {'color': this.activeColor} : {};
          }
        },
        methods: {
          itemClick() {
            this.$router.push(this.path).catch((error) => error);
          }
        }
      }
      </script>
          
      <style scoped>
      .tab-bar-item {
        flex: 1;
        text-align: center;
        height: 49px;
        font-size: 14pt;
      }
          
      .tab-bar-item img {
        width: 24px;
        height: 24px;
        margin-top: 3px;
        vertical-align: middle;
        margin-bottom: 2px;
      }
      </style>
      

18. Proimse

18.1 Promise的介绍和基本使用

  1. Promise是做什么的?

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

    • 方式一:定时器模拟

    • ```html
      1. 使用setTimeout setTimeout(()=>{ console.log(‘Hello World’); }, 1000); </script> ```
    • 方式二: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');
            })
          })
        })
      </script>
      
  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

<script>
    new Promise((resolve, reject)=>{
        resolve('Hello');
    }).then((data)=>{
        console.log(data);
    }), (error)=>{
        console.log(error);
    })
</script>

image-20220309210124303

<script>
    new Promise((resolve, reject)=>{
        // resolve('Hello');
        reject('出现错误');
    }).then((data)=>{
        console.log(data);
    }, (error)=>{
        console.log(error);
    })
</script>

image-20220309210237767

18.3 Promise的链式调用

  • 全写

    • <script>
      new Promise(((resolve, reject) => {
          setTimeout(()=>{
              resolve('aaa');
          }, 1000)
      })).then((data) => {
          console.log(data);
          return new Promise(resolve => {
              resolve(data + '111');
          }).then(data=>{
              console.log(data);
              return new Promise(resolve => {
                  resolve(data + '222');
              }).then(data=>{
                  console.log(data);
              })
          })
      })
      </script>
      
  • 简写

    • <script>
      new Promise((resolve, reject) => {
          setTimeout(()=>{
              resolve('aaa');
          }, 1000)
      }).then((data) => {
          console.log(data);
          return Promise.resolve(data + '111')
          }).then(data=>{
          console.log(data);
          return Promise.resolve(data + '222')
          }).then(data=>{
          console.log(data);
      })
      </script>
      
  • 再次简写

    • <script>
          new Promise((resolve, reject) => {
          setTimeout(()=>{
              resolve('aaa');
          }, 1000)
      }).then((data) => {
          console.log(data);
          return data + '111'
      }).then(data=>{
          console.log(data);
          return data + '222'
      }).then(data=>{
          console.log(data);
      })
       </script>
      

18.4 Promise的all方法

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

<script>
   Promise.all([
    {异步请求1},
    {异步请求2},
    ...,
    {异步请求n}
]).then(()=>{})
</script>

只有当 参数里面所有的异步请求都执行完以后,才会执行 .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

<template>
  <div id="app">
    <h2></h2>
    <h2></h2>
    <button @click="counter++">加一</button>
    <button @click="counter--">减一</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      message: '我是App的组件',
      counter: 0,
    }
  }
}
</script>

<style>
</style>

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

    • <template>
        <div id="app">
          <h2></h2>
          <h2></h2>
          <button @click="$store.state.counter++">加一</button>
          <button @click="$store.state.counter--">减一</button>
          <h2>HelloVue</h2>
          <HelloVue></HelloVue>
      <!--  父子组件的方式  <HelloVue :counter="counter"></HelloVue>-->
        </div>
      </template>
           
      <script>
      import HelloVue from "./components/HelloVue";
      export default {
        name: 'App',
        components: {
          HelloVue,
        },
        data() {
          return {
            message: '我是App的组件',
            counter: 0,
          }
        }
      }
      </script>
           
      <style>
      </style>
      
    • HelloVue.vue

    • <template>
      <div>
        <h2></h2>
      </div>
      </template>
           
      <script>
      export default {
        name: "HelloVue",
      }
      </script>
           
      <style scoped>
           
      </style>
           
      

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

    • <template>
        <div id="app">
          <h2></h2>
          <h2></h2>
          
          <button @click="addition">加一</button>
          <button @click="substraction">减一</button>
          <h2>HelloVue</h2>
          <HelloVue></HelloVue>
      <!--  父子组件的方式  <HelloVue :counter="counter"></HelloVue>-->
        </div>
      </template>
          
      <script>
      import HelloVue from "./components/HelloVue";
      export default {
        name: 'App',
        components: {
          HelloVue,
        },
        data() {
          return {
            message: '我是App的组件',
            counter: 0,
          }
        },
        methods: {
          addition() {
            this.$store.commit('increment');
          },
          substraction() {
            this.$store.commit('decrement');
          },
        }
      }
      </script>
          
      <style>
      </style>
          
      

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

<template>
  <div id="app">
    <h2></h2>
    <h2></h2>
    <button @click="addition">加一</button>
    <button @click="substraction">减一</button>

    <h2>----------------App内容:getters使用--------------------</h2>
    <h3></h3>
    <h3></h3>
    <h3></h3>

    <h2>--------------------HelloVue-----------------------</h2>
    <HelloVue></HelloVue>
  </div>
</template>

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

<template>
  <div id="app">
    <h2></h2>
    <h2></h2>
    <button @click="addition">加一</button>
    <button @click="substraction">减一</button>

    <h2>----------------App内容:getters使用--------------------</h2>
    <h3></h3>
    <h3></h3>
    <h3></h3>
    <h3></h3>

    <h2>--------------------HelloVue-----------------------</h2>
    <HelloVue></HelloVue>
<!--  父子组件的方式  <HelloVue :counter="counter"></HelloVue>-->
  </div>
</template>

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拦截响应失败');
  });
最近的文章

Z 字形变换

6. Z 字形变换题目描述将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:P A H NA P L S I I GY I R之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。请你实现这个将字符串进行指定行数变换的函数:string convert(string s, int numRows);示例 ...…

继续阅读
更早的文章

万万没想到之聪明的编辑

万万没想到之聪明的编辑题目描述我叫王大锤,是一家出版社的编辑。我负责校对投稿来的英文稿件,这份工作非常烦人,因为每天都要去修正无数的拼写错误。但是,优秀的人总能在平凡的工作中发现真理。我发现一个发现拼写错误的捷径: 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> hello 两对一样的字母(AABB型)连在一起,一定是拼写错误,去掉第二对的一个字母就好啦:比如 helloo -> hello 上面的规则优...…

继续阅读