首页 > 其他 > 详细

Vue

时间:2020-07-07 17:30:39      阅读:61      评论:0      收藏:0      [点我收藏+]

0、实战项目结构划分

技术分享图片

一、Vue作用

Vue (读音 /vju?/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

如果你想在深入学习 Vue 之前对它有更多了解,我们制作了一个视频,带您了解其核心概念和一个示例工程。

如果你已经是有经验的前端开发者,想知道 Vue 与其它库/框架有哪些区别,请查看对比其它框架

Vue安装方式

  1. 直接下载引用
  2. CDN引用
  3. npm 安装

二、Vue简单入门

Vue使用步骤

  1. 创建Html文件
  2. 引入Vue.js文件
  3. 创建Vue({})对象,并在html中指定Vue要渲染的容器

2.1、声明式渲染

2.1.1、文本值插入

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>初识Vue</title>
    <script src="js/vue.js"></script>
  </head>
  <body>
    <div id="app">{{message}}</div>
    <script>
      let app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘小红‘,
        },
      })
    </script>
  </body>
</html>
  1. Vue与DOM建立了关联,所有东西都是响应式的,在浏览器中修改app.message的值,DOM上就会立即显示更新

2.1.2、绑定attribute元素

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>初识Vue</title>
    <script src="js/vue.js"></script>
    <style>
      span {
        margin: 50px 50px;
        background-color: bisque;
      }
    </style>
  </head>
  <body>
    <div id="app">
      {{message}}
      <br />
      <span v-bind:title="title"
        >鼠标悬停几秒钟查看此处动态绑定的提示信息!</span
      >
    </div>
    <script>
      let app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘小红‘,
          title: ‘页面加载与‘ + new Date().toLocaleDateString(),
        },
      })
    </script>
  </body>
</html>

总结

  1. 这里使用到了一个新东西,v-bind:title被称为指令,指令带有前缀v-,表示他们由Vue提供的指令
  2. 他们在渲染DOM的时候是响应式行为,该指令意思就是“将这个元素节点的title attributeVue实例的title保持一致”

如果你再次打开浏览器的 JavaScript 控制台,输入 app2.message = ‘新消息‘,就会再一次看到这个绑定了 title attribute 的 HTML 已经进行了更新。

2.2、条件与循环

2.2.1、条件

绑定DOM结构,动态判断来确定一个html标签执行否

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>条件</title>
    <script src="js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      要显示当前时间吗?<br />
      <span v-if="flag">{{dateNow}}</span>
      <button type="button" v-on:click="funAdd">显示</button>
      <button type="button" v-on:click="funNo">不显示</button>
    </div>
    <script>
      let app = new Vue({
        el: ‘#app‘,
        data: {
          dateNow: ‘当前时间为:‘ + new Date().toLocaleDateString(),
          flag: false,
        },
        methods: {
          funAdd: function () {
            this.flag = true
          },
          funNo: function () {
            this.flag = false
          },
        },
      })
    </script>
  </body>
</html>

2.2.2、循环

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表展示</title>
    <script src="js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{message}}
      <br />
      <ul>
        <li v-for="i in list">{{i}}</li>
      </ul>
    </div>
    <script>
      let app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘列表展示‘,
          list: [‘Java‘, ‘C‘, ‘C++‘, ‘Go‘],
        },
      })
    </script>
  </body>
</html>

2.3、插值操作

就是从Vue的属性中获取值,然后插入对应HTML中

2.3.1、Musatche语法

又叫做胡须语法,两边像胡子

{{message}}

代码示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Mustache语法</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <h1>Mustache语法,又叫做胡须语法</h1>
    <div id="app">
      <h3>{{message}}</h3>
      <h3>{{message}}, 小新</h3>
      <h3>{{message + " " + temp}}</h3>
      <h3>{{message}} {{temp}}</h3>
    </div>
    <script>
      let app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么?‘,
          temp: ‘Vue.js‘,
        },
      })
    </script>
  </body>
</html>

2.3.2、v-once指令渲染属性后不修改,不响应式

渲染属性之后,不会根据响应式来改变值

技术分享图片

代码示例

<!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>v-once指令,不想响应式改变值</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <h3>{{message}}</h3>
      <h3 v-once>{{message}}</h3>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么‘,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.3.3、v-html指令渲染html标签

有时候,后端返回的是一个标签,并不是一个连接,这种情况下想把服务器返回的标签插入的Html页面中,就需要用到v-html指令了

代码示例

<!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>Document</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <h3>{{url}}</h3>
      <h3 v-html="url"></h3>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          url: ‘<a href="http://www.baidu.com">百度一下</a>‘,
        },
        methods: {},
      })
    </script>
  </body>
</html>

显示结果

技术分享图片

2.3.4、v-text指令与Musatche语法类似,但不灵活

代码示例

<!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>v-text指令使用,与Mustache类似,但不够灵活</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <!-- 追加覆盖-->
      <h3>{{message}},新酱</h3>
      <!-- 直接覆盖-->
      <h3 v-text="message">新酱</h3>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么‘,
        },
        methods: {},
      })
    </script>
  </body>
</html>

图片示例

技术分享图片

2.3.5、v-pre指令,标识渲染属性不渲染

图片

技术分享图片

代码示例

<!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>v-pre指令,不替换值</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <h3>{{message}}</h3>
      <!-- 原样展示 -->
      <h3 v-pre>{{message}}</h3>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么‘,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.3.6、v-cloak指令,等待Js执行完成后渲染HTML

有种情况JS代码执行不了

  • JS代码加载不出来
  • JS代码卡主了

这就导致HTML页面原样显示数据的话,太别丑

所以需要v-cloak指令

代码示例

<!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>v-cloak指令,js代码加载完成后显示内容</title>
    <script src="../js/vue.js"></script>

    <style>
      [v-cloak] {
        display: none;
      }
    </style>
  </head>

  <body>
    <!-- 
      Vue的执行流程,比如下面的代码,h3中,它是原样的展示,直到执行对应的Js代码之后,才会将内容替换成要显示的数据。
      这个时候,有一个种情况,就是js代码加载不出来,或卡主了,为了防止原样的展示,所以需要v-cloak指令,让js代码执行完毕后在显示Html代码
     -->
    <div id="app" v-cloak>
      <h3>{{message}}</h3>
    </div>

    <script>
      // 模拟Js代码卡主,页面是还没有渲染的
      setInterval(function () {
        var app = new Vue({
          el: ‘#app‘,
          data: {
            message: ‘你好么‘,
          },
          methods: {},
        })
      }, 5000)
    </script>
  </body>
</html>

2.4、动态数值绑定

2.4.1、v-bind()标签属性绑定

之前,学习的都是标签体内容替换,这次来替换标签的属性,那么该如何使用呢?

代码示例

<!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>v-bind属性绑定</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <!-- 之前的数据绑定,对标签属性而言是错误的
      <img src="{{imgURL}}" alt="" />
      <a href="{{url}}"></a> 
      
    -->
      <img v-bind:src="imgURL" alt="" />
      <a v-bind:href="url">百度一下</a>

      <!-- 语法糖书写方式,简略方式 -->
      <img :src="imgURL" alt="" />
      <a :href="url">百度一下</a>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么‘,
          imgURL:
            ‘https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1950846641,3729028697&fm=111&gp=0.jpg‘,
          url: ‘http://www.baidu.com‘,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.4.2、v-bind()绑定标签体属性class

动态修改标签体class属性的值

2.4.2.1、v-bind()绑定属性(class类名)-对象语法

语法:<div v-bind:class="{类名1: boolean 类名2: boolean}"></div>

动态添加,一个标签体的class属性

代码示例

<!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>动态绑定v-bind(class类名)对象语法</title>
    <script src="../js/vue.js"></script>
    <style>
      .acction {
        color: red;
      }
      .flage {
        color: blue;
        font-size: 18px;
      }
    </style>
  </head>

  <body>
    <div id="app">
      <div :class="temp">{{message}}</div>
      <br /><br /><br />

      <!-- 实际应用,复杂的语法 -->
      <!-- 
                          如果为true,才会显示类名
        <div v-bind:class="{类名1: boolean 类名2: boolean}"></div> 
      -->
      <div :class="{acction: isAcction, flage: isFlage}">{{message}}</div>

      <button type="button" v-on:click="fun1">显示添加样式</button>

      <!-- 所以实际开发中 -->
      <div class="写固定的类名" :class="动态显示的类名"></div>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么‘,
          temp: ‘acction‘,
          isAcction: true,
          isFlage: true,
        },
        methods: {
          fun1: function () {
            this.isFlage = !this.isFlage
          },
        },
      })
    </script>
  </body>
</html>

补充,还可以是函数

<!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>v-bind对象语法,补充-方法获取class类名</title>
    <script src="../js/vue.js"></script>
    <style>
      .accone {
        color: red;
      }
    </style>
  </head>

  <body>
    <div id="app">
      <div :class="getClass()">{{message}}</div>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么‘,
        },
        methods: {
          getClass: function () {
            return { accone: true }
          },
        },
      })
    </script>
  </body>
</html>

2.4.2.2、v-bind()绑定属性(class类名)-数组语法

<!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>v-bind数组</title>
    <script src="../js/vue.js"></script>
    <style>
      .accone {
        color: red;
      }
      .line {
        font-size: 20px;
      }
    </style>
  </head>

  <body>
    <div id="app">
      <!-- 不带引号是变量,带引号就是字符串 -->
      <div :class="[active, line]">{{message}}</div>
      <div :class="getClass()">{{message}}</div>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          active: ‘accone‘,
          line: ‘line‘,
          message: ‘你好么‘,
        },
        methods: {
          getClass: function () {
            return [this.active, this.line]
          },
        },
      })
    </script>
  </body>
</html>

2.4.3、v-bind()绑定属性(Style)

更改标签体的Style属性

2.4.3.1、对象语法

直接插入对象,对象是CSS格式,key是驼峰命名

代码示例

<!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>动态绑定Style</title>
    <script src="../js/vue.js"></script>
    <style>
      .cals {
        font-size: 10px;
        color: red;
      }
    </style>
  </head>

  <body>
    <div id="app">
      <!-- <div :style="{key(Style语法): ‘值‘| 变量}">你好吗</div> -->
      <div :style="{finalSize: findSize, color: finalColor}">你好吗</div>
      <div :style="{finalSize: findSize, color: finalColor}">你好吗</div>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          finalColor: ‘red‘,
          finalSize: "100px"
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.4.3.2、数组语法

数组的元素是,已经定义好的样式,在data

<!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>动态绑定v-bind(数组)</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <div :style="[baseStyle, baseFonse]">{{message}}</div>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么?‘,
          baseStyle: {
            backgroundColor: ‘red‘,
          },
          baseFonse: {
            fontSize: ‘30px‘,
          },
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.4.4、Demo-点击li标签内容变为红色

代码示例

    <title>作业</title>
    <script src="../js/vue.js"></script>
    <style>
      .acc {
        color: red;
      }

      li {
        padding-top: 10px;
      }
    </style>
  </head>

  <body>
    <div id="app">
      <ul>
        <li
          v-for="(m,index) in list"
          v-on:click="fun1(index)"
          :class="{acc: currentIndex === index}"
        >
          {{index}}-{{m}}
        </li>
      </ul>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          list: [‘小新‘, ‘小白‘, ‘夏红‘, ‘小明‘],
          currentIndex: -1,
        },
        methods: {
          fun1: function (index) {
            this.currentIndex = index
          },
        },
      })
    </script>
  </body>
</html>

2.5、计算属性

2.5.1、计算属性获取Mustache语法方式

获取data对象里,两个变量的内容

代码示例

<!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>计算属性</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <div>{{message + ‘ ‘ + lastName}}</div>
      <div>{{message}} {{lastName}}</div>
      <div>{{getName()}}</div>
      <!-- 计算属性 -->
      <div>{{NameAll}}</div>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么?‘,
          lastName: ‘小新‘,
        },
        // 计算属性
        computed: {
          NameAll: function () {
            return this.message + ‘ ‘ + this.lastName
          },
        },
        // 方法
        methods: {
          getName() {
            return this.message + ‘ ‘ + this.lastName
          },
        },
      })
    </script>
  </body>
</html>

总结:

计算属性,放在computed中,他不是一个方法或函数,只是属性,写方上省略了set方法

2.5.2、实战Demo-负责的计算属性操作

代码示例

<!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>计算属性,复杂操作</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <div>书总价格:{{priceAll}}</div>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          books: [
            { id: 10001, name: ‘Unix编程艺术‘, price: 110 },
            { id: 10002, name: ‘代码大全‘, price: 119 },
            { id: 10003, name: ‘深入理解计算机原理‘, price: 89 },
          ],
        },
        computed: {
          priceAll: function () {
            let sum = 0
            for (let i = 0; i < this.books.length; i++) {
              sum += this.books[i].price
            }
            return sum
          },
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.5.3、计算属性的get/set方法

  • 在浏览器中,默认情况下,直接访问计算属性的话是调用它的get方法

  • 如果要在修改属性的值的话,浏览器默认调用set方法

<!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>计算属性的get和set方法</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <h3>{{funALl}}</h3>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          lastName: ‘张‘,
          firstName: ‘红‘,
        },
        methods: {},
        computed: {
          
          // 属性的全写,有get和set方法,在输出的时候默认自动调用get方法;在修改的时候调用set方法
          funALl: {
            get: function () {
              return ‘abc‘
            },
            set: function (temp) {
              console.log(temp)
            },
            
          },
          // 以下为简写,省略了set方法
          funALl: function () {
            return this.lastName + ‘ ‘ + this.firstName
          },
        },
      })
    </script>
  </body>
</html>

证明上面说的话

<!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>Mustache(函数)优势对比</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <!-- 
        函数:每次遇到了就会调用
        计算属性:只执行一次,然后不会再调用,只有被修改才会调用
       -->
      <h3>{{funAll()}}</h3>
      <h3>{{funAll()}}</h3>
      <h3>{{funAll()}}</h3>
      <h3>{{funAll()}}</h3>

      <h3>{{funName}}</h3>
      <h3>{{funName}}</h3>
      <h3>{{funName}}</h3>
      <h3>{{funName}}</h3>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          lastName: ‘张‘,
          firstName: ‘红‘,
        },
        methods: {
          funAll: function () {
            console.log(‘方法执行‘)

            return this.lastName + ‘ ‘ + this.firstName
          },
        },
        computed: {
          funName: function () {
            console.log(‘计算属性执行‘)
            return this.lastName + ‘ ‘ + this.firstName
          },
        },
      })
    </script>
  </body>
</html>

2.6、事件监听

2.6.1、v-on基本使用

使用方式

v-on:事件名
语法糖:@事件名

代码示例

    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>v-on基本使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      {{count}} <br />
      <!-- 第一种写法 -->
      <button type="button" v-on:click="count++">+</button>
      <button type="button" v-on:click="count--">-</button>
      <br /><br /><br />
      <!-- 第二中写法 -->
      <button type="button" v-on:click="add">+</button>
      <button type="button" v-on:click="sub">-</button>
      <br /><br /><br />
      <!-- 语法糖写法 -->
      <button type="button" @click="add">+</button>
      <button type="button" @click="sub">-</button>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          count: 0,
        },
        methods: {
          add() {
            this.count++
          },
          sub() {
            this.count--
          },
        },
      })
    </script>
  </body>
</html>

2.6.2、v-on事件回调参数问题

触发事件,函数回调传参问题

自动封装event参数(事件对象)

  1. 默认不带()号调用函数,
  2. 参数多个,还要封装event对象,需要这么玩$event

代码示例

<!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>v-on参数使用问题</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <!-- 1. 无参,不写括号:没有区别都可以执行函数 -->
      <button type="button" @click="fun1">按钮1</button>
      <button type="button" @click="fun1()">按钮1</button>
      <br /><br /><br />
      <!-- 2. 有参数,写上参数或不写参数
          带括号无参数:正常执行函数,但是参数是个undefined
          不带括号,无参数:Vue自动封装事件对象event传输到参数中
      -->
      <button type="button" @click="fun2()">按钮2带括号</button>
      <button type="button" @click="fun2">按钮2不带</button>
      <br /><br /><br />
      <!-- 3. 有参数,要事件对象也要参数 
          不自动封装事件对象
          加上`$`符号会封装事件对象
      -->
      <button type="button" @click="fun3(123)">按钮5有参数,</button>
      <button type="button" @click="fun3(abc, $event)">
        按钮6,有参数,当做变量,并封装事件对象
      </button>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          abc: ‘Xiaoxin‘,
        },
        methods: {
          fun1() {
            console.log(‘fun1‘)
          },
          fun2(obj) {
            console.log(‘fun2‘, obj)
          },
          fun3(abc, event) {
            console.log(‘abc:‘ + abc + ‘\nevent:‘, event)
          },
        },
      })
    </script>
  </body>
</html>

2.6.3、v-on事件的常用修饰符

事件修饰符

.stop 解决事件冒泡
.prevent 阻止默认事件,可以阻止默认事件,比如表单默认提交
.{keyCode|keyAlias} 只当事件从特定键触发才触发回调
.native 监听组件根元素的原生事件
.once 只触发一次回调

代码示例

<!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>v-on事件修饰符的使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <div @click="divClick">
        <!-- 这样会有时间冒泡,就是按钮被单击,div也会触发单击事件
          解决办法是:
        -->
        <button @click.stop="buttonClick">按钮</button>
      </div>

      <form action="/baidu">
        <!-- 阻止表单自动提交,阻止默认事件 -->
        <input type="submit" value="提交" @click.prevent />
      </form>

      <input type="text" name="" id="" @keyup="key" /> <br />
      <!-- 键盘特定事件来触发函数 -->
      <input type="text" name="" id="" @keyup.enter="Mykeyup" />

      <br /><br /><br />
      <!-- 只触发一次事件 -->
      <button type="button" @click.once="Myonce">once触发一次事件</button>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {
          divClick() {
            console.log(‘divClick‘)
          },
          buttonClick() {
            console.log(‘buttonClick‘)
          },
          Mykeyup() {
            console.log(‘按回车键了 ‘)
          },
          key() {
            console.log(‘键盘按键抬起触发‘)
          },
          Myonce() {
            console.log(‘只触发一次‘)
          },
        },
      })
    </script>
  </body>
</html>

2.7、条件判断

条件表达式如果为true那么就会显示

2.7.1、v-if使用

<!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>v-if使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <h2 v-if="isShow">
        <div>abc</div>
        <div>abc</div>
        <div>abc</div>
        <div>abc</div>
        {{message}}
      </h2>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么‘,
          isShow: true,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.7.2、v-else使用

<!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>v-else使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <h2 v-if="isShow">
        <div>abc</div>
        <div>abc</div>
        <div>abc</div>
        <div>abc</div>
        {{message}}
      </h2>
      <h3 v-else>isShow为false显示我</h3>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么‘,
          isShow: false,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.7.3、v-else-if使用

<!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>v-else-if的使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <!-- 不建议这么玩,太麻烦,使用计算属性是最佳解决办法 -->
      <h3 v-if="score>80">优秀</h3>
      <h3 v-else-if="score>60">及格</h3>
      <h3 v-else>不及格</h3>

      <h1>{{showScore}}</h1>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          score: 100,
        },
        computed: {
          showScore() {
            let showStr = ‘‘
            if (this.score > 80) {
              showStr = ‘优秀‘
            } else if (this.score > 60) {
              showStr = ‘及格‘
            } else {
              showStr = ‘不及格‘
            }
            return showStr
          },
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.7.4、v-show使用

  • v-if:不一样的就是v-if结果为false的时候hmlt文件中就不会生成DOM
  • v-show:只是影藏标签
<!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>v-show的使用和与v-if的不同</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <h3 v-if="isUser">v-if</h3>
      <br />
      <h3 v-show="isUser">v-show</h3>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          isUser: true,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.7.5、案例-切换登录方式

<!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>用户登录方式切换案例</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <span v-if="isUser">
        <label for="username">用户名登录</label><br />
        <input type="text" id="username" placeholder="用户名" />
      </span>
      <span v-else>
        <label for="email">邮箱登录</label><br />
        <input type="text" id="email" placeholder="用户邮箱" />
      </span>
      <button @click="isUser = !isUser">切换登录方式</button>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          isUser: true,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.7.6、案例Bug抛出以及修复

  1. Bug说明: 输入了内容,然后点击切换,发现内容还是存在,正常来说内容应该消失了,
  2. 原因:Vue在渲染的时候回对DOM进行优化,像这种差不多的DOM,它都会更换名称(个人现在理解)
  3. 解决办法:给input加入key属性标明不一致,Vue就会生成两个,而不是修改属性了

代码示例

<!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>案例-01小bug说明以及修复</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <!-- 
      Bug说明: 输入了内容,然后点击切换,发现内容还是存在,正常来说内容应该消失了,
      原因:Vue在渲染的时候回对DOM进行优化,像这种差不多的DOM,它都会更换名称(个人现在理解)
      解决办法:给input加入key属性标明不一致,Vue就会生成两个,而不是修改属性了
     -->
    <div id="app">
      <span v-if="isUser">
        <label for="username">用户名登录</label><br />
        <input type="text" id="username" placeholder="用户名" key="username" />
      </span>
      <span v-else>
        <label for="email">邮箱登录</label><br />
        <input type="text" id="email" placeholder="用户邮箱" key="email" />
      </span>
      <button @click="isUser = !isUser">切换登录方式</button>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          isUser: true,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.8、循环

建议:

每个元素加:key ,如果在运行期间,我们往里面加一个元素,Vue会如何处理?

会,一个一个替代,比如在3号位置插入F,其余的3号变4号,4号变5号,依次类推,这样会繁琐, 建议是加:key属性,值建议是:元素值,不过如果是相同的值,就会报一个警告,但是如果保证数据的唯一性,这样的问题也不会出现

2.8.1、v-for遍历数组

<!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>v-for遍历数组</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <ul>
        <li v-for="item in names" :key="item">{{item}}</li>
      </ul>

      <hr />
      <ul>
        <li v-for="(item, index) in names">{{item}} - {{index+1}}</li>
      </ul>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          names: [‘java‘, ‘python‘, ‘C‘, ‘C++‘, ‘C‘],
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.8.2、v-for遍历对象

<!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>v-for遍历对象</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <ul>
        <li v-for="item in info">{{item}}</li>
      </ul>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          info: {
            name: ‘张三‘,
            age: 21,
            sex: ‘男‘,
          },
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.9、购物车小案例

样式

技术分享图片

代码示例

index.html

<!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>书籍购物车案例</title>
    <script src="../js/vue.js"></script>
    <link rel="stylesheet" href="style.css" />
  </head>

  <body>
    <div id="app">
      <div v-if="list.length > 0">
        <table>
          <thead>
            <tr>
              <th></th>
              <th>书籍名称</th>
              <th>出版日期</th>
              <th>价格</th>
              <th>购买数量</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <tr
              v-if="list != null || list.length == 0"
              v-for="(item, index) in list"
            >
              <td>{{item.id}}</td>
              <td>{{item.name}}</td>
              <td>{{item.date}}</td>
              <td>{{getPrice(item.price)}}</td>
              <td>
                <button
                  type="button"
                  @click="subPrice(index)"
                  v-bind:disabled="item.number <= 1"
                >
                  -
                </button>
                {{item.number}}
                <button type="button" @click="addPrice(index)">+</button>
              </td>
              <td>
                <button type="button" @click="deleteBook(index)">移除</button>
              </td>
            </tr>
          </tbody>
        </table>
        <span>总价格:{{priceSum}}</span>
      </div>
      <h3 v-else>购物车为空</h3>
    </div>

    <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;
}

main.js

const app = new Vue({
  el: ‘#app‘,
  data: {
    list: [
      {
        id: 1,
        name: ‘Java编程思想‘,
        date: ‘2020-01‘,
        price: 89,
        number: 1,
        Oprice: 89,
      },
      {
        id: 2,
        name: ‘Linux从入门到入土‘,
        date: ‘2020-02‘,
        price: 59.8,
        number: 1,
        Oprice: 59.8,
      },
      {
        id: 3,
        name: ‘现代计算机‘,
        date: ‘2020-03‘,
        price: 79,
        number: 1,
        Oprice: 79,
      },
      {
        id: 4,
        name: ‘Java核心技术I/II‘,
        date: ‘2020-01‘,
        price: 150,
        number: 1,
        Oprice: 150,
      },
    ],
  },
  methods: {
    addPrice(index) {
      this.list[index].price =
        ++this.list[index].number * this.list[index].Oprice
    },
    subPrice(index) {
      // 最少一本书

      if (this.list[index].number < 0) {
        this.list[index].number = 1
      }
      this.list[index].price =
        --this.list[index].number * this.list[index].Oprice
    },
    deleteBook(index) {
      this.list.splice(index, 1)
    },
    getPrice(price) {
      return ‘¥‘ + price.toFixed(2)
    },
  },
  computed: {
    priceSum() {
      // let sum = 0
      // 第一种遍历方式
      // i是下标
      // for (let i = 0; i < this.list.length; i++) {
      //   sum += this.list[i].price
      // }

      // 第二种遍历方式
      // i还是下标
      // for (let i in this.list) {
      //   console.log(i)
      // }

      // 第三种遍历方式
      // item是数组中的值, `of`关键字
      // for (let item of this.list) {
      //   sum += item.price
      // }
      let sum = this.list.reduce(function (prevValue, book) {
        return (prevValue += book.price)
      }, 0)
      console.log(sum)

      return sum
    },
  },
})

2.10、表单绑定v-model

表单绑定

  • 表单控件在实际开发中是非常常见的,特别是对用户信息的提交需要大量表单
  • Vue中使用v-model指令来实现表单元素和数据双向绑定

双向绑定

  • 修改数据可以修改到input:value中数据
  • 修改Input中的数据可以修改到data数据中

其实使用v-bind指令和事件也可以实现v-model的作用

2.10.1、v-model基本使用

代码示例

<!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>v-model基本使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <input type="text" v-model="message" />
      <br /><br /><br />
      {{message}}
      <br /><br /><br />
      <hr />
      密码
      <input type="password" name="" id="" v-model="password" />
      <br />
      <h2>密码为:{{password}}</h2>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么‘,
          password: ‘‘,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.10.2、使用v-bind和事件实现v-model

代码示例

<!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>v-model原理,使用其他指令完成</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <input
        type="text"
        :value="message"
        @input="message = $event.target.value"
      />
      <br /><br /><br />
      {{message}}
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘你好么‘,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.10.3、v-model结合input:radio单选框使用

代码示例

<!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>v-model结合radio单选框使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <label for="gender"
        ><input
          type="radio"
          name="sex"
          id="gender"
          value="男"
          v-model="gender"
        />
        男
      </label>
      <label for="nv"
        ><input type="radio" name="sex" id="nv" value="女" v-model="gender" />
        女</label
      >
      <hr />
      <hr />
      <h2>您选中的性别是: {{gender}}</h2>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          gender: ‘‘,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.10.4、v-model结合input:checkbox使用

单选

属性是truefalse

多选

多选框value是选项,也可以是数组,具体看情况

代码示例

<!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>v-model结合check多选框使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <!-- 
        1. 单选框使用,单选框value是布尔类型
            同意协议
       -->
      <label for="agreement">
        <input type="checkbox" name="" id="agreement" v-model="isAgreement" />
        同意协议
      </label>
      <h2>您的选择是: {{isAgreement}}</h2>
      <hr />
      <hr />
      <!-- 
        2. 多选框使用,多选框value是选项
       -->
      <label for="Basketball">
        <input
          type="checkbox"
          name=""
          id="Basketball"
          value="篮球"
          v-model="hobbys"
        />篮球
      </label>
      <label for="Football">
        <input
          type="checkbox"
          name=""
          id="Football"
          value="足球"
          v-model="hobbys"
        />足球
      </label>
      <label for="music">
        <input
          type="checkbox"
          name=""
          id="music"
          value="音乐"
          v-model="hobbys"
        />音乐
      </label>
      <label for="Playgames">
        <input
          type="checkbox"
          name=""
          id="Playgames"
          value="打游戏"
          v-model="hobbys"
        />打游戏
      </label>
      <br /><br />
      <h2>您的选择是{{hobbys}}</h2>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          isAgreement: false,
          hobbys: [],
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.10.5、v-model结合input:select使用

单选

value的值是data属性中的值

多选

<select>标签加上multiple属性

代码示例

<!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>v-model结合select单选和多选使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <select name="abc" id="" v-model="fruits">
        <option value="苹果">苹果</option>
        <option value="香蕉">香蕉</option>
        <option value="葡萄">葡萄</option>
        <option value="橘子">橘子</option>
      </select>
      <br /><br />
      <h2>您选中的水果是:{{fruits}}</h2>

      <hr />
      <h1>多选</h1>
      <br />
      <select name="abc" id="" v-model="list" multiple>
        <option value="苹果">苹果</option>
        <option value="香蕉">香蕉</option>
        <option value="葡萄">葡萄</option>
        <option value="橘子">橘子</option>
      </select>
      <br /><br />
      <h2>您选中的水果是{{list}}</h2>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          fruits: ‘苹果‘,
          list: [],
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.10.6、值绑定

像前面的04-check,爱好都是写死的,在项目中肯定是需要从后台拿的,那么这样该这么玩,也叫值绑定

我们前面的value中的值,都是写死的,实际开发中,这些input值是从后台过来的,所以我们可以通过v-bind:value动态绑定给value绑定值,这不就是v-bind吗

代码示例

<!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>值绑定</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <!-- 
      像前面的04-check,爱好都是写死的,在项目中肯定是需要从后台拿的,那么这样该这么玩,也叫值绑定
      我们前面的value中的值,都是写死的,
      实际开发中,这些input值是从后台过来的
      所以我们可以通过v-bind:value动态绑定给value绑定值
      这不就是v-bind吗
     -->
    <div id="app">
      <h2>选择您的爱好:{{temps}}</h2>
      <label v-for="item in hobbtys" :for="item">
        <input
          type="checkbox"
          name=""
          :id="item"
          :value="item"
          v-model="temps"
        />
        {{item}}
      </label>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          hobbtys: [‘篮球‘, ‘足球‘, ‘乒乓球‘, ‘打游戏‘, ‘吃鸡‘],
          temps: [],
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.10.7、v-model修饰符的使用

  • lazy:就是说与input:text控件绑定的话,文本框内容变动,data里就变太浪费,可以让他只有在文本框失去焦点或按下回车才会更新data中的数据
  • number:保证绑定data数据中的数据是一个number类型
  • trim:输出字符串两边空格

代码示例

<!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>v-model修饰符的使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <!-- 
        1. lazy 懒
        就是说与input:text控件绑定的话,文本框内容变动,data里就变太浪费,
        需求:想要文本框输入之后按回车或者文本框失去焦点在更新
       -->
      <input type="text" name="" id="" v-model.lazy="message" />
      <br />
      <h2>您输入的内容为: {{message}}</h2>
      <br /><br />
      <hr />
      <!-- 
        2. number修饰符 ,
          默认情况下:文本框输入的内容是string类型,即使你绑定的数据时number也会转换为string,
          需求:输入的转换为number类型,加上修饰符即可完成
       -->

      <input type="text" v-model="num" /><!--string 类型-->
      <br />
      <input type="text" v-model.number="num" />
      <!-- number类型 -->
      <br />
      <h2>您输入的是:{{num}},类型为:{{typeof num}}</h2>
      <br /><br />
      <hr />
      <!-- 
        3. trim修饰符,删除多余空格
       -->
      <input type="text" v-model="name" />
      <!--有空额-->
      <br />
      <input type="text" v-model.trim="name" />
      <!-- 无空格 -->
      <h2>您输入的内容为:{{name}}</h2>
    </div>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘‘,
          num: ‘‘,
          name: ‘‘,
        },
        methods: {},
      })
    </script>
  </body>
</html>

2.11、组件化开发

组件的使用步骤

  1. 创建组件

    const pnC = Vue.extend({
            template: `<div>
                        <h2>我是标题</h2>
                        <p>我是内容哈哈哈哈哈。</p>
                        <p>嘿嘿嘿,嘻嘻嘻</p>
                      </div>`,
          })
    
  2. 注册组件

    Vue.component(‘my-cpn‘, pnC)
    
  3. 使用组件

    <div id="app">
        <!-- 3. 在这里使用组件 -->
            <my-cpn></my-cpn>
    </div>
    

2.11.1、组件化开发的基本使用

个人概念

说白了,就相当于是一个将HTML代码转化成一个函数一样的东西,Vue里呢就是定义一个新标签,然后编写模块里的内容即可。

  1. 创建组件,如果使用语法糖,这步可以省略
  2. 注册组件
  3. 使用组件

代码示例

<!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>组件的基本使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <!-- 3. 在这里使用组件 -->
      <my-cpn></my-cpn>
    </div>

    <script>
      // 组件的使用方式
      // 1. 创建组件构造器
      const pnC = Vue.extend({
        template: `<div>
                    <h2>我是标题</h2>
                    <p>我是内容哈哈哈哈哈。</p>
                    <p>嘿嘿嘿,嘻嘻嘻</p>
                  </div>`,
      })
      // 2. 注册组件
      Vue.component(‘my-cpn‘, pnC)

      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {},
      })
    </script>
  </body>
</html>

2.11.2、全局组件和局部组件

  1. 全局组件:使用Vue.component(‘my-cpn‘, pnC)注册的默认都是全局组件
  2. 局部组件:在Vue实例里面注册components,只有这一个实例才可以使用

代码示例

<!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>全局组件和局部组件</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <!-- 
      Vue中使用 Vue.component(‘my-cpn‘, pnC) 注册的组件,默认都是全局组件。 
            局部组件的注册方式是在Vue实例对象中注册组件才会是局部组件

      全局组件:使用函数注册Vue.component(‘my-cpn‘, pnC),注册之后哪里都可以使用
      局部组件:在Vue实例里面注册components,只有这一个实例才可以使用
    -->
    <div id="app">
      <cpn></cpn>
      <cpn></cpn>
    </div>

    <div id="app2">
      <!-- 不起效果 -->
      <cpn></cpn>
    </div>

    <script>
      // 1. 创建组件
      let cpnC = Vue.extend({
        template: `
          <div><h2>您好啊</h2>
              <p>哈哈您习</p>
          </div>
        `,
      })

      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {},
        components: {
          cpn: cpnC, // 注册局部组件
        },
      })

      var app2 = new Vue({
        el: ‘#app2‘,
      })
    </script>
  </body>
</html>

2.11.3、父组件和子组件的使用和区别

  1. 父组件:除了使用Vue.component(‘my-cpn‘, pnC)注册的全局组件,之外,谁给你注册的谁就是你的父组件
  2. 子组件:谁帮你注册,你就是谁的子组件

子组件注册之后只能在父组件的范围中使用,如果在别的地方使用,那么就会注册全局组件或者在它下面再注册一次

代码示例

<!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>父组件和子组件的使用和区别</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <cpn></cpn>
      <!-- 不能够使用,如果想使用,那么只能在Vue示例中在注册一次,所以Vue可见也可以当做是一个组件 -->
      <cpn1></cpn1>
    </div>

    <script>
      // 是cpnC2的子组件
      let cpnC1 = Vue.extend({
        template: `
                <div>
                  <h2>我是标题1</h2>
                  <p>哈哈哈</p>
                </div>
        `,
      })
      // 父组件
      let cpnC2 = Vue.extend({
        template: `
                <div>
                  <h2>我是标题2</h2>
                  <p>呵呵</p>
                  <cpn1></cpn1>
                </div>
        `,
        components: {
          cpn1: cpnC1,
        },
      })
      // 可以当做根组件(root)来看
      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {},
        components: {
          cpn: cpnC2,
        },
      })
    </script>
  </body>
</html>

2.11.4、语法糖的全局组件和局部组件

  1. 全局组件:省略了创建组件
  2. 局部组件:在components中可以直接定义它的组件

代码示例

<!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>语法糖的全局组件和局部组件的注册于使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <my-cpn2></my-cpn2>
      <cpn3></cpn3>
    </div>

    <script>
      // 以前的组件注册方式
      let cpnC1 = Vue.extend({
        template: `
                <div>
                  <h2>我是标题1</h2>
                  <p>哈哈哈</p>
                </div>
        `,
      })
      Vue.component(‘my-cpn‘, cpnC1)

      // 语法糖写法
      Vue.component(‘my-cpn2‘, {
        template: `
                <div>
                  <h2>语法糖注册全局组件</h2>
                  <p>哈哈哈</p>
                </div>
        `,
      })

      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          cpn3: {
            template: `
                <div>
                  <h2>语法糖注册局部组件</h2>
                  <p>嘿嘿嘿</p>
                </div>
            `,
          },
        },
      })
    </script>
  </body>
</html>

2.11.5、组件模板的分离写法

  1. 使用<template id="cpn">标签,在注册组件的时候指定id名称即可 推荐使用
  2. 使用<script type="text/x-template" id="cpn">标签,指定上id值与type

代码示例

<!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>组件模板分离写法</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <cpn></cpn>
      <cpn2></cpn2>
    </div>
    <!-- 1. 分离写法一: 一定要套一个div -->
    <script type="text/x-template" id="cpn">
      <div>
        <h2>组件分离写法1</h2>
        <p>哈哈哈哈哈哈哈哈哈哈或或或或或</p>
      </div>
    </script>

    <!-- 2. 第二种写法 template标签使用 一定要套一个div -->
    <template id="cpn2">
      <div>
        <h2>组件分离写法2</h2>
        <p>嘻嘻嘻黑恶Hi好IE</p>
      </div>
    </template>

    <script>
      // 注册组件
      Vue.component(‘cpn‘, {
        template: ‘#cpn‘,
      })

      Vue.component(‘cpn2‘, {
        template: ‘#cpn2‘,
      })
      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {},
      })
    </script>
  </body>
</html>

2.11.6、组件可访问实例中的数据吗,组件如何拥有属于自己的数据

组件可以访问实例中的数据吗?

答案:不可以,从定义上来说组件是独立的,所以它应该有属于自己的数据模型。

组件如何拥有属于自己的数据模型?

答案:

  1. 在注册组件的时候,给他加一个data()函数,并返回的是一个对象,这样组件才会用于自己的数据,
  2. 既然组件是独立的,那么它应该也会有方法,函数,计算属性等等

代码示例

子组件没有数据,访问出错

<!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>组件可以访问实例中的数据吗</title>
    <script src="../js/vue.js"></script>
  </head>

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

    <template id="cpn">
      <div>
        <h2>{{totial}}</h2>
        <p>哈哈哈哈哈</p>
      </div>
    </template>
    <script>
      Vue.component(‘cpn‘, {
        template: ‘#cpn‘,
      })
      var app = new Vue({
        el: ‘#app‘,
        data: {
          totial: ‘我是一个标题‘,
        },
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {},
      })
    </script>
  </body>
</html>

给子组件创建属于组件自己的数据模型

<!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>组件访问实例中的数据</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <!-- 
      1. 组件如何拥有属于自己的数据区:data(){}函数  返回对象实例,  对象实例就是数据,这样。组件才会独立,一个页面有多个相同组件才不会受干扰,还是说组件的独立性
      2. 如何访问 就是Mustache语法插值
    
    -->
    <div id="app">
      <cpn></cpn>
    </div>

    <template id="cpn">
      <div>
        <h2>{{totial}}</h2>
        <p>哈哈哈哈哈</p>
      </div>
    </template>

    <script>
      Vue.component(‘cpn‘, {
        template: ‘#cpn‘,
        data() {
          return {
            totial: ‘我是标题哈哈‘,
          }
        },
      })

      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {},
      })
    </script>
  </body>
</html>

2.11.6.1、组件的date为什么是一个函数

组件的data为什么是函数

  1. 组件像是一个独立的工具,一个网页中可以使用多次,但是你并不希望他们使用同一块数据,毕竟组件要独立
  2. 看图技术分享图片
  3. 只有函数返回的东西 才会不是同一个内存单元,如果多个相同的组件使用同一个内存空间,那么他们就不会独立

代码示例

<!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>组件的data为什么是函数</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <cpn></cpn>
      <cpn></cpn>
      <cpn></cpn>
      <cpn></cpn>
    </div>

    <template id="cpn">
      <div>
        <h3>当前数字为:{{count}}</h3>
        <br />
        <button type="button" @click="count++">+</button>
        <button type="button" v-on:click="count--">-</button>
      </div>
    </template>

    <script>
      Vue.component(‘cpn‘, {
        template: ‘#cpn‘,
        data() {
          return {
            count: 0,
          }
        },
      })

      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {},
      })
    </script>
  </body>
</html>

2.11.7、父子组件通信

父组件给子组件通信:props属性

子组件给父组件通新:子组件中自定义事件,父组件监听

2.11.7.1、父给子组件通信-proes属性

为什么需要组件之间的通信?

在实际开发中,可以能一个网页是由多个组件来组成的,按照规定,一般由root组件发送网页请求,然后子组件来对返回的数据进行展示,这个时候就需要父子组件的通信

子组件是如何表明哪些数据是从父组件传输过来的?

子组件标明props属性来标明

如何使用?

在使用子组件的时候传递参数,一定要使用v-bind来绑定,不然Vue会当做字符串来处理

props属性可以是什么类型的数据?

  • 数组:使用数组,只能限制类型
  • 对象,使用对象,限制可以多一点,比如类型、默认值,必须值requied

代码示例

<!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>组件通信,父给子组件通信</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <!-- 
      为什么需要组件之间通信?
        在实际开发中,可能一个网页是由多个组件组成的,按照规范,一般root组件来发送网页请求,然后子组件来对返回到的数据进行处理并显示
      这个时候就需要父组件来对子组件传输信息
      子组件是如何表明哪些数据是从父组件传输过来的?
        通过props属性来表明的
      那么如何使用?
        在使用子组件的时候传递参数 一定要使用v-bind来绑定,不然Vue会当做字符串来处理
      props属性可以有什么
        - 数组
        - 对象

     -->
    <div id="app">
      <cpn :cmessage="message" :clist="list"></cpn>
      <hr />
      <cpn2 :cmessage="message"></cpn2>
    </div>

    <template id="cpn">
      <div>
        <h2>{{cmessage}}</h2>
        <ul>
          <li v-for="item in clist">{{item}}</li>
        </ul>
      </div>
    </template>

    <template id="cpn2">
      <div>
        <h2>字符串是{{cmessage}}</h2>
        <ul>
          <li v-for="item in clist">{{item}}</li>
        </ul>
      </div>
    </template>

    <script>
      // 自定义类型
      function Preson() {}

      const cpn = {
        template: ‘#cpn‘,
        props: [‘cmessage‘, ‘clist‘],
      }

      const cpn2 = {
        template: ‘#cpn2‘,
        props: {
          // 基本数据类型规定,表明传输过来的一定要是字符串类型
          cmessage: String,
          clist: {
            type: Array, // 类型
            default() {
              return [] // 如果默认值是对象  default必须是函数
            },
            // 默认值
            requied: false, // 必须传输
          },
          author: Preson, // 自定义类型
        },
      }

      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘电影排行榜‘,
          list: [‘阿凡达‘, ‘寄生虫‘, ‘少年与海‘, ‘绅士们‘],
        },
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          cpn,
          cpn2,
        },
      })
    </script>
  </body>
</html>
2.11.7.1.1、父给子组件通信,(props驼峰命名问题)

问题:在子组件的props属性的变量命令,如果是驼峰,比如cMessage那么在绑定的时候就必须这么写:c-message="xx"

问题原因:由于HTML没有大小写规范,所以只能在大写字母前面加-来分明

代码示例

<!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>Document</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <!-- 
      props与驼峰命名:
          对于子组件中的props属性的命名,如果是驼峰,那么就得在HTML中写c-list这样才行  大写转小写 前面+ -
          这是因为HTML是没有大小写区分的
     -->
    <div id="app">
      <npc :c-message="message" :c-list="list"></npc>
    </div>

    <template id="npc">
      <div>
        <h2>{{cMessage}}</h2>
        <ul>
          <li v-for="item in cList">{{item}}</li>
        </ul>
      </div>
    </template>

    <script>
      const npc = {
        template: ‘#npc‘,
        props: {
          cMessage: String,
          cList: {
            type: Array,
            default() {
              return []
            },
            requied: true,
          },
        },
      }

      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘鬼泣5‘,
          list: [‘小红‘, ‘小明‘, ‘小新‘, ‘小白‘],
        },
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          npc,
        },
      })
    </script>
  </body>
</html>

2.11.7.2、子组件给父组件通信-事件

案例:手机京东

技术分享图片

如何让父组件知道子组件选择的是什么分类,然后由父组件来发送信息

子组件通过事件传输数据到父组件中?

子组件通过子定义事件,然后父组件通过v-on来监听事件,完成子穿父的通信

  • 什么时候需要自定义事件呢

    • 当子组件需要向父组件传输数据,就需要用到自定义事件了

    • 我们之前学习过的v-on不仅可以用于监听DOM的默认事件,还可以监听组件的自定义事件

  • 自定义事件流程

    • 子组件,通过$emit()来触发时间
    • 父组件,通过v-on来监听子组件发出的事件

代码示例

<!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>父子组件通信-子组件给父组件通信-通过自定义事件来完成</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <npc @item-click="btClick"></npc>
      <!-- 这里监听事件,由于是自定义事件,Vue会自动传输事件 -->
    </div>

    <template id="npc">
      <div>
        <button type="button" v-for="item in categorys" @click="cpnClick(item)">
          {{item.name}}
        </button>
      </div>
    </template>

    <script>
      const npc = {
        template: ‘#npc‘,
        data() {
          return {
            categorys: [
              { id: ‘aaa‘, name: ‘热门推荐‘ },
              { id: ‘bbb‘, name: ‘电脑办公‘ },
              { id: ‘ccc‘, name: ‘家用家电‘ },
              { id: ‘ddd‘, name: ‘手机数码‘ },
            ],
          }
        },
        methods: {
          cpnClick(item) {
            // 发射事件给父组件
            // this.$emit("自定义事件名", 要传输的参数)
            this.$emit(‘item-click‘, item)
          },
        },
      }
      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {
          btClick(item) {
            console.log(item)
          },
        },
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          npc,
        },
      })
    </script>
  </body>
</html>

2.11.7.3、案例-子组件触发事件修改父组件的值

<!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>子组件发出事件修改父组件的值</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <h2>{{count}}的值</h2>
      <cpn @sub-click="count--" @add-click="count++"></cpn>
    </div>

    <template id="cpn">
      <div>
        <button type="button" @click="subClick">-</button>
        <button type="button" @click="addClick">+</button>
      </div>
    </template>

    <script>
      const cpn = {
        template: ‘#cpn‘,
        methods: {
          subClick() {
            this.$emit(‘sub-click‘)
          },
          addClick() {
            this.$emit(‘add-click‘)
          },
        },
      }

      var app = new Vue({
        el: ‘#app‘,
        data: {
          count: 0,
        },
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          cpn,
        },
      })
    </script>
  </body>
</html>

2.11.7.4、案例-父子组件中双向绑定案例

<!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>13-案例-父子组件中双向绑定案例.html</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <!-- 
      不难,关系搞清楚就行,
        如果直接使用v-model绑定的话,会报错误Vue,意思就是说你修该props中的属性没啥意义,
			因为即使你修该的,它还是根据父组件中传输过来再修该的,

      所以,想要完成父组件的修改,
        根据v-model原理来完成  先来:value绑定data中数据,然后根据事件来修改data中数据并发送事件
			到父组件,父组件监听然后再修改
     -->
    <div id="app">
      <h1>{{um1}}</h1>
      <br />
      <h1>{{um2}}</h1>
      <npc
        :num1="um1"
        :num2="um2"
        @num1-input="updateUm1"
        @num2-input="updateUm2"
      ></npc>
    </div>

    <template id="npc">
      <div>
        <h3>{{num1}}</h3>
        <h3>data{{dnum1}}</h3>
        <!-- 如果还想修该父组件的值 那么就写事件 v-model根据原理分开来写 写事件-->
        <!-- <input type="text" name="" id="" v-model="num1" /> 不推荐这么玩 
            因为: 如果直接使用v-model绑定的话,会报错误Vue,意思就是说你修该props中的属性没啥意义,
            因为即使你修该的,它还是根据父组件中传输过来再修该的,-->
        <input type="text" name="" id="" :value="dnum1" @input="num1Input" />
        <br />
        <h3>{{num2}}</h3>
        <h3>data{{dnum2}}</h3>
        <input type="text" name="" id="" :value="dnum2" @input="num2Input" />
      </div>
    </template>

    <script>
      const npc = {
        template: ‘#npc‘,
        props: {
          num1: {
            type: Number,
          },
          num2: Number,
        },
        data() {
          return {
            dnum1: this.num1,
            dnum2: this.num2,
          }
        },
        methods: {
          num1Input(event) {
            this.dnum1 = event.target.value
            this.$emit(‘num1-input‘, parseInt(this.dnum1))
          },
          num2Input(event) {
            this.dnum2 = event.target.value
            this.$emit(‘num2-input‘, parseInt(this.dnum2))
          },
        },
      }

      var app = new Vue({
        el: ‘#app‘,
        data: {
          um1: 0,
          um2: 0,
        },
        methods: {
          updateUm1(num1) {
            this.um1 = num1
          },
          updateUm2(num2) {
            this.um2 = num2
          },
        },
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          npc,
        },
      })
    </script>
  </body>
</html>

2.11.7.5、案例-父子组件中双向绑定案例(watch实现)

<!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>14-案例-父子组件中双向绑定案例(watch实现)</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <!-- 
      不难,关系搞清楚就行,
        如果直接使用v-model绑定的话,会报错误Vue,意思就是说你修该props中的属性没啥意义
			,因为即使你修该的,它还是根据父组件中传输过来再修该的,

      所以,想要完成父组件的修改,
        根据v-model原理来完成  先来:value绑定data中数据,然后根据事件来修改data中数据
			并发送事件到父组件,父组件监听然后再修改
     -->
    <div id="app">
      <h1>{{um1}}</h1>
      <br />
      <h1>{{um2}}</h1>
      <npc
        :num1="um1"
        :num2="um2"
        @num1-input="updateUm1"
        @num2-input="updateUm2"
      ></npc>
    </div>

    <template id="npc">
      <div>
        <h3>{{num1}}</h3>
        <h3>data{{dnum1}}</h3>
        <input type="text" name="" id="" v-model="dnum1" />
        <br />
        <h3>{{num2}}</h3>
        <h3>data{{dnum2}}</h3>
        <input type="text" name="" id="" v-model="dnum2" />
      </div>
    </template>

    <script>
      const npc = {
        template: ‘#npc‘,
        props: {
          num1: {
            type: Number,
          },
          num2: Number,
        },
        data() {
          return {
            dnum1: this.num1,
            dnum2: this.num2,
          }
        },
        // 值发生改变自动触发
        watch: {
          dnum1(newDnum1) {
            this.dnum1 = newDnum1
            this.$emit(‘num1-input‘, parseInt(this.dnum1))
          },
          dnum2(newDnum2) {
            this.dnum2 = newDnum2
            this.$emit(‘num2-input‘, parseInt(this.dnum2))
          },
        },
      }

      var app = new Vue({
        el: ‘#app‘,
        data: {
          um1: 0,
          um2: 0,
        },
        methods: {
          updateUm1(num1) {
            this.um1 = num1
          },
          updateUm2(num2) {
            this.um2 = num2
          },
        },
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          npc,
        },
      })
    </script>
  </body>
</html>

2.11.8、组件的访问,直接操作父或子组件

  • 父组件直接操作子组件(拿到子组件引用)

    • this.$children获取到的是一个数组,包含着该实例下的所有子组件[不推荐使用]
    • this.$refs通过给子组件设置ref属性(相当于子组件的key),然后通过this.$refs.key拿到对应的子组件
  • 子组件直接操作父组件(拿到父组件的引用)

    • this.$parent拿到父组件
    • this.$root直接拿到祖宗组件

2.11.8.1、组件访问方式-父组件操作子组件中的数据(方式一:children)

直接操作子组件,相当于指针来操作子组件

获取子组件两种方式:

  1. children 不推荐 获取到的是组件的数组, 因为在开发中,可能突然往组件之间插入一个,那么他们下标就会改变 所以不贵贱
  2. 给子组件 来一个ref属性 然后通过this.$refs.子组件的ref属性(key)来访问指定的子组件

代码示例

<!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>15-组件访问方式-父组件操作子组件中的数据(方式一:children)</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <cpn></cpn>
      <button @click="btnClick">获取子组件</button>
    </div>

    <template id="cpn">
      <div>
        <h2>name: {{name}}</h2>
      </div>
    </template>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {
          btnClick() {
            console.log(this.$children) // 获取的是子组件的数据,因为子组件可能存在多个
            // 开始直接操作子组件,
            // 调用方法
            this.$children[0].showMessage()
            console.log(this.$children[0].name)
          },
        },
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          cpn: {
            template: ‘#cpn‘,
            methods: {
              showMessage() {
                console.log(‘我是子组件‘)
              },
            },
            data() {
              return {
                name: ‘why ?‘,
              }
            },
          },
        },
        // 属性修改自动触发
        watch: {},
      })
    </script>
  </body>
</html>

2.11.8.2、父组件操作子组件中的数据(方式二:refs)

<!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>16-组件访问方式-父组件操作子组件中的数据(方式二:refs)</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <cpn ref="aaa"></cpn>
      <button @click="btnClick">获取子组件</button>
    </div>

    <template id="cpn">
      <div>
        <h2>name: {{name}}</h2>
      </div>
    </template>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {
          btnClick() {
            // console.log(this.$children) // 获取的是子组件的数据,因为子组件可能存在多个
            // 开始直接操作子组件,
            // 调用方法
            // this.$children[0].showMessage()
            // console.log(this.$children[0].name)
            this.$refs.aaa.showMessage()
            console.log(this.$refs.aaa.name)
          },
        },
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          cpn: {
            template: ‘#cpn‘,
            methods: {
              showMessage() {
                console.log(‘我是子组件‘)
              },
            },
            data() {
              return {
                name: ‘why ?‘,
              }
            },
          },
        },
        // 属性修改自动触发
        watch: {},
      })
    </script>
  </body>
</html>

2.11.8.3、子访问父组件中的数据(parent,root)操作父组件

代码示例

<!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>
      17-组件访问方式-子访问父组件中的数据(parent,root)操作父组件
    </title>
    <script src="../js/vue.js"></script>
  </head>

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

    <template id="cpn">
      <div>
        <h2>root的子组件小红</h2>
        <ccpn></ccpn>
      </div>
    </template>

    <template id="ccpn">
      <div>
        <h2>小红的子组件</h2>
        <button @click="btnClick">访问父组件</button>
      </div>
    </template>

    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          message: ‘我是Vue的示例‘,
        },
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          cpn: {
            template: ‘#cpn‘,
            data() {
              return {
                message: ‘我是小红‘,
              }
            },
            components: {
              ccpn: {
                template: ‘#ccpn‘,
                methods: {
                  btnClick() {
                    console.log(‘父组件‘, this.$parent) // 访问父组件,也就是小红
                    console.log(‘父组件的属性‘, this.$parent.message)
                    console.log(‘祖宗root组件‘, this.$root) // 访问祖宗 也就是Vue的示例
                    console.log(‘祖宗root组件的属性‘, this.$root.message) // 访问祖宗 也就是Vue的示例
                  },
                },
              },
            },
          },
        },
        // 属性修改自动触发
        watch: {},
      })
    </script>
  </body>
</html>

2.11.9、组件的高级

  1. 插槽slot
  2. 具名插槽
  3. 作用域

2.11.9.1、slot插槽的基本使用

插槽:

就相当于是一个接口,用于扩展自定义的东西,就像电脑的USB一样,可以插手机、U盘。

Vue

Vue的插槽定义:就是在组件中留一个<slot></slot>标签

插槽作用

父组件提供标签,但是内容由子组件来提供

插槽使用步骤-slot

  1. 在使用组件直接插入,就相当于使用了插槽
  2. 可以插入很多标签
  3. 插槽可以有默认值

代码示例

<!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>slot插槽的基本使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <!-- 
      插槽的基本使用
      插槽:就相当于是一个接口,用于扩展自定义的东西,就像电脑的USB一样,可以插手机、U盘。
      Vue 的插槽定义:就是在组件中留一个<slot></slot>标签

      插槽作用:
          父组件提供标签,但是内容由子组件来提供

      1. 在使用组件直接插入,就相当于使用了插槽
      2. 可以插入很多标签
      3. 插槽可以有默认值
     -->
    <div id="app">
      <cpn><button>按钮</button></cpn>
      <hr />
      <cpn><span>我是span</span></cpn>
      <hr />
      <cpn><i>哈哈</i></cpn>
      <hr />
      <cpn>
        <h1>h1</h1>
        <b>b</b>
      </cpn>
      <hr />
      <cpn></cpn>
    </div>

    <template id="cpn">
      <div>
        <h2>我是标题</h2>
        <p>我是段落</p>
        <slot><button>按钮</button></slot>
      </div>
    </template>

    <script>
      const cpn = {
        template: ‘#cpn‘,
      }

      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          cpn,
        },
        // 属性修改自动触发
        watch: {},
      })
    </script>
  </body>
</html>

2.11.9.2、具名插槽

具名插槽的使用:就是给插槽起个名字,再使用的时候告诉组件我们要使用哪个插槽

  1. 如果有三个插槽都没有名字,那么在使用的时候就会全部替换
  2. 如果插槽都有名字,那么你替换没有指定名字,那么就会替换没有名字的插槽
  3. 指定名称替换插槽

代码示例

<!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>具名插槽的使用</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <!-- 
      具名插槽的使用:就是给插槽起个名字,再使用的时候告诉组件我们要使用哪个插槽

      1. 如果有三个插槽都没有名字,那么在使用的时候就会全部替换
      2. 如果插槽都有名字,那么你替换没有指定名字,那么就会替换没有名字的插槽
      3. 指定名称替换插槽
     -->
    <div id="app">
      <cpn>
        <!-- 指定名称 告诉VUe我要替换中间的那个插槽-->
        <h2 slot="center">标题2</h2>
        <!-- 会自动提换没有名字的插槽  -->
        <strong>粗体</strong>
      </cpn>
    </div>

    <template id="cpn">
      <div>
        <slot name="left"><span>左边</span></slot>
        <slot name="center"><span>中间</span></slot>
        <slot name="right"><span>右边</span></slot>
      </div>
    </template>

    <script>
      const cpn = {
        template: ‘#cpn‘,
      }

      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          cpn,
        },
        // 属性修改自动触发
        watch: {},
      })
    </script>
  </body>
</html>

2.11.9.3、作用域

<!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>变量作用域</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <!-- 看Vue实例中的isShow变量-->
      <cpn v-show="isShow"></cpn>
    </div>
    <!-- 为什么呢?
          官网一句话:父组件模板的所有东西都会在父级作用域内编译,子组件模板所有东西都会在子组件作用域编译
    -->
    <template id="cpn">
      <div>
        <h2>我是子组件</h2>
        <!-- 看组件的isShow变量-->
        <button v-show="isShow">按钮</button>
      </div>
    </template>
    <script>
      var app = new Vue({
        el: ‘#app‘,
        data: {
          isShow: true,
        },
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          cpn: {
            template: ‘#cpn‘,
            data() {
              return {
                isShow: false,
              }
            },
          },
        },
        // 属性修改自动触发
        watch: {},
      })
    </script>
  </body>
</html>

2.11.9.4、作用域插槽

<!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>作用域插槽的案例-父组件拿到子组件数据来展示</title>
    <script src="../js/vue.js"></script>
  </head>
  <!-- 

    插槽作用:
          父组件提供标签,但是内容由子组件来提供

    需求:
      不想按照子组件的方式去展示数据,父组件中替换展示方式,、
      那么第一点就是拿到子组件的数据,该如何拿到???
   -->
  <body>
    <div id="app">
      <cpn></cpn>

      <!-- 换一种展示方式 -->
      <cpn>
        <template slot-scope="slot" slot="aaa">
          <span>{{slot.abc.join(" - ")}}</span>
        </template>
      </cpn>
    </div>

    <template id="cpn">
      <div>
        <slot :abc="dataList" name="aaa">
          <ul>
            <li v-for="item in dataList">{{item}}</li>
          </ul>
        </slot>
      </div>
    </template>

    <script>
      const cpn = {
        template: ‘#cpn‘,
        data() {
          return {
            dataList: [‘java‘, ‘C‘, ‘C++‘, ‘Go‘, ‘javaScript‘, ‘Python‘],
          }
        },
      }

      var app = new Vue({
        el: ‘#app‘,
        data: {},
        methods: {},
        // 计算属性
        computed: {},
        // 组件注册
        components: {
          cpn,
        },
        // 属性修改自动触发
        watch: {},
      })
    </script>
  </body>
</html>

2.11.10、ES6的模块化开发

导出

导出:export 变量/函数/类/数组 可以多个,中间逗号分隔

还可以exprot default 变量/函数/类/数组不过一个js文件里只能这么写一次

导入

import 变量/函数/类/数组 from 路径 不过有限制,只能导出是什么名导入是什么名

还可以全部导入import * as 类名 from 路径这种方式是把导出的全部都封装成类

有两个人写js代码小明,小红

h5

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <!-- 必须带type="module" 这才是ES6的模块开发 -->
    <script src="xiaoming.js" type="module"></script>
    <script src="xiaohong.js" type="module"></script>
  </body>
</html>

小明代码

// 小明在开发
var name = ‘小明‘
var age = 18
let sex = ‘男‘
var flag = true

function sum(num1, num2) {
  return num1 + num2
}

// 导出
export { name, age, sex, sum }

// 定义时到处
export let list = [‘Java‘, ‘C#‘]

// 定时时到处类/函数
export function add() {

}

// 上面的几种到处方式,在导入的时候,变量名或方法名都必须和到处的一致,才行
// 带上default 关键字 就可以在导入的时候自定义名  有一个export default这样的导出
export default function (num1, num2) {
  return num1 - num2
}

小红代码

// 小红在开发

// 导入
import { age, sex,sum } from ‘./xiaoming.js‘

var name = ‘小红‘
console.log(name)

console.log(age);

console.log(sum(10 ,20))

import {list} from "./xiaoming.js";

console.log(list)

import a from "./xiaoming.js";

console.log(a(10 ,1))

// 导入所有,自动封装成为类 并重命名关键字
import * as aaa from ‘./xiaoming.js‘

console.log(aaa)

三、webpack

技术分享图片

3.1、webpack使用

安装

npm install webpack -g
-g 表示全局安装
本地安装 npm install webpack@3.6.0 --save-dev 开发环境

验证是否安装成功
webpack --version

webpack打包过程

技术分享图片

3.1.1、webpack安装

一定要安装webpack@3.6.0

  1. 下载node.js
  2. npm install webpack@版本 -g安装
  3. 在vs code里,创建目录
目录 作用
src 开发的js/style/img
dist 打包好的js文件
  1. 创建webpack.config.js

    const path = require("path")
    module.exports = {
      // 入口,
      entry: "./src/main.js",
      // 出口,生成位置
      output: {
        path: path.resolve(__dirname, "dist"), // 1. 这里一定要动态的获取绝对路径,  __driname得到的是当前文件的路径
        filename: "bundle.js"
      }
    }
    

是因为每次打包都是webpack ./src/main.js ./dist/bundle.js命令太长,繁琐,所以创建步骤4这个文件,来指定出口入口

但是因为出口:要的是绝对路径,那么就只能通过node来动态获取绝对路径。所以

  1. npm init初始化node。初始化之后就会有一个package.json文件。作用相当于Maven差不多,这个时候如果有依赖的话,可以npm install安装依赖

  2. 安装好之后,在script标签,写上命令build指定下即可

    {
      "name": "meetwebpack",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack"
      },
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "webpack": "^3.6.0"
      }
    }
    

    以后执行直接npm run build来打包

  3. 但是这个时候使用的是全局的webpack,要安装局部的npm install webpack@3.6.0 --save-dev

3.2、loader的使用

webpack只是起到打包作用,问题是前端有各种文件,那么针对不同文件的打包处理需要不同的方式,这个概念就是loader

技术分享图片

loader的安装具体可看

https://www.webpackjs.com/loaders/

3.2.1、css的loader安装

  1. 安装css-loader
  2. 安装style-loader

分别配置

  1. css配置
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ ‘style-loader‘, ‘css-loader‘ ]
      }
    ]
  }
}

注意事项

  1. css-loader 只是导入css代码,并不会应用
  2. style-loader 应用cssDOM
  3. 使用多个loader时,是从从右向左读取,所以才会是先是使用样式 再导入css代码了

3.2.2、图片的loader使用

  1. 安装url-loader
  2. 安装file-loader

注意事项

  1. webpack通过main.js找到了img的依赖
  2. url-loader会有一设置limit: 8192,大小的设置,如果超过8k就会使用file-loadre打包图片。
  3. 所以也需要file-loader
  4. 图片的名称,在开发中,名称一般是img/原图片名称+hash值+原来的后缀 , 语法是img[name].[hash].[ext]

配置

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif|jpeg)$/,
        use: [
          {
            loader: ‘url-loader‘,
            options: {
              limit: 8192,
              name: "img/[name].[hash].[ext]"
            }
          }
        ]
      },
    ]
  }
}

3.2.3、less安装

一定要指定版本安装不然会出错

npm install --save-dev less-loader@4.1.0 less

想知道的看:https://blog.csdn.net/shujiaw/article/details/105863069

3.2.4、ES6转ES5的babel-loader

安装:

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

配置

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: ‘babel-loader‘,
        options: {
          presets: [‘es2015‘]
        }
      }
    }
  ]
}

3.3、webpack配置Vue

webpack配置vue前提

Vue有两个版本

  1. runtime-only版本:代码中不可以有任何template
  2. runtime-compiler版本:代码可有,因为用于编译template

webpack使用Vue步骤

  1. 安装Vue,因为是不管任何环境都用所以

    npm install vue --save

  2. webpack.config.js中配置

module.exports = {
  // ...
  resolve: {
    alias: {
      ‘vue$‘: ‘vue/dist/vue.esm.js‘ // 用 webpack 1 时需用 ‘vue/dist/vue.common.js‘
    }
  }
}

详细看这里

https://cn.vuejs.org/v2/guide/installation.html#webpack

如果不配置,报错的信息

bundle.js:1451 [Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

(found in <Root>)

3.3.1、webpack中Vue的开发方式

  1. 当完成3.2.5的时候,以后在webpack开发Vue的方式是,在html中只新建<div> id="app"</div>即可。

  2. .js文件里

    // 导入Vue
    import Vue from ‘vue‘
    
    new Vue({
      el: "#app",
      template: `
        <div>
          <h2>{{message}}</h2>
          <button @click="btnClick">按钮</button>
        </div>
      `,
      data: {
        message: "你好么 webpack"
      },
      methods: {
        btnClick () {
          console.log("按钮被单击了");
    
        }
      }
    })
    
  3. Vue实例里面有eltemplate的时候template里的东西会把el的东西替换掉

  4. 这么写即可,Vue会把template中内容替换到html<div id="app">里去

3.3.2、webpack开发vue,组件抽取-重点

因为要解析vue文件所以需要vue的loader

  1. 安装vue-loader
  2. 安装vue-template-compiler
npm install vue-loader vue-template-compiler --save-dev
  1. 修改webpack.config.js配置

    module.exports = {
      entry: "./src/main.js",
      output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js",
        publicPath: "dist/"  // 图片需要的
      },
      module: {
        rules: [
          {
            test: /\.vue$/,
            use: [‘vue-loader‘]
          }
        ]
      },
    }
    

3.3.2.1、抽取方式一:从js文件抽取出一个对象

main.js

原版

new Vue({
  el: "#app",
  template: `<AppAndVue/>`,
  components: {
    Cpn: {
      template: `<div>
        <h2>{{message}}</h2>
        <button @click="btnClick">按钮</button>
      </div>`,
      data () {
        return {
          message: "你好么 webpack"
        }
      },
      methods: {
        btnClick () {
          console.log("按钮被单击了");

        }
      }
    }
})

抽取后

// 1. 抽取第一步 当前文件抽取
const Cpn = {
  template: `<div>
        <h2>{{message}}</h2>
        <button @click="btnClick">按钮</button>
      </div>`,
  data () {
    return {
      message: "你好么 webpack"
    }
  },
  methods: {
    btnClick () {
      console.log("按钮被单击了");

    }
  }
}

new Vue({
  el: "#app",
  template: `<AppAndVue/>`,
  components: {
   Cpn,// 本文件抽取,没有脱离
  }
})

3.3.2.2、方式二:抽取单独js文件

  1. 一定要是默认导出
export default {
  template: `<div>
        <h2>{{message}}</h2>
        <button @click="btnClick">按钮</button>
      </div>`,
  data () {
    return {
      message: ".js文件外部抽取"
    }
  },
  methods: {
    btnClick () {
      console.log("按钮被单击了");

    }
  }
}

main.js中导入

// 抽取方式二:抽取为Cpn.js
import App from ‘./js/Cpn.js‘;

new Vue({
  el: "#app",
  template: `<AppAndVue/>`,
    App,  // 已抽取,但是是js文件,只能以ES6默认导出 再导入
  }
})

3.3.2.3、方式三:单独vue文件

AppAndVue.vue

// 定义模板
<template>
  <div>
    <h2 class=".tile">{{ message }}</h2>
    <button @click="tnbClick">按钮</button>
  </div>
</template>
// 定义代码
<script>
export default {
  name: ‘AppAndVue‘,
  data() {
    return {
      message: ‘Vue方式抽取‘,
    }
  },
  methods: {
    tnbClick() {
      console.log(‘Vue方式按钮抽取‘)
    },
  },
}
</script>
// 编写样式
<style>
.tile {
  color: gold;
}
</style>

main.js

// 抽取方式三:抽取为vue文件
import AppAndVue from ‘./vue/AppAndVue.vue‘

new Vue({
  el: "#app",
  template: `<AppAndVue/>`,
  components: {
    // Cpn, 本文件抽取,没有脱离
    // App,  // 已抽取,但是是js文件,只能以ES6默认导出 再导入
    AppAndVue,
  }
})

3.6、plugin

技术分享图片

3.6.1、webpack自带插件-添加版权-BannerPlugin

由于是webpack自带插件,不需要安装。直接使用

webpack.config.js

const webpack = require("webpack")

module.exports = {
  lugins: [
    new webpack.BannerPlugin("最终版权归测试所有")
  ]
}

3.6.2、打包HTML的插件HtmlWebpackPlugin-将html文件编译到dist目录下

  1. 安装插件npm install html-webpack-plugin@3.2.0 -save-dev

  2. 配置webpack.config.js内容

    const hmtlWebpackPlugin = require("html-webpack-plugin")
    
    module.exports = {
        entry: "./src/main.js",
      output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js",
        // publicPath: "dist/"  这里不要,因为插件会自动引用生成的js文件的
      },
        plugins: [
             new hmtlWebpackPlugin({
          template: "index.html",
        })    // 这个意思是根据src下的index.html作为模板生成
        ]
    }
    

3.6.3、uglifyjs-webpack-plugin插件js文件压缩

  1. 安装npm install uglifyjs-webpack-plugin@1.1.1 --save-dev

  2. webpack.config.js配置

    const UglifyjsWebpackPlugin = require("uglifyjs-webpack-plugin")
    
    module.exports = {
      entry: "./src/main.js",
      output: {
        path: path.resolve(__dirname, "dist"), 
        filename: "bundle.js",
      },
      plugins: [
        new UglifyjsWebpackPlugin(),
      ]
    }
    

3.6.4、搭建本地服务器

webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改的结果

  1. 安装npm install --save-dev webpack-dev-server@2.9.1

  2. 配置webpack.config.js

    module.exports = {
      entry: "./src/main.js",
      output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js",
      },
      
      devServer: {
        contentBase: "./dist",
        inline: true
      }
    }
    

参数作用

参数 作用
contentBase 为哪一个文件夹提供本地服务,默认是根文件夹,我们这里写./dist
port 端口号
inline 页面实时刷新
historyApiFallback 在SPA页面中,依赖HTML5的history模式

另外还可以在package.js中加上参数,默认自动打开浏览器

  • --open 参数表示直接打开浏览器
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "bundle": "webpack",
    "dev": "webpack-dev-server --open"
  },

3.6.5、配置文件分离

概念

就是开发一个配置文件,发布一个配置文件。两个配置文件都一样的,分开写的话不乱

步骤

  1. 安装合并工具npm install webpack-merge -save-dev
  2. 根目录下建立文件夹build
  3. 创建两个环境一样的配置到base.config.js
  4. 创建开发时需要的配置文件dev.config.js
  5. 创建发布时需要的配置文件prod.config.js

具体配置文件代码

base.config.js

const path = require("path")
const webpack = require("webpack")
const hmtlWebpackPlugin = require("html-webpack-plugin")

module.exports = {
  entry: "./src/main.js",
  output: {
    path: path.resolve(__dirname, "../dist"), // 上级目录
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [‘style-loader‘, ‘css-loader‘]
      },
      {
        test: /\.(png|jpg|gif|jpeg)$/,
        use: [
          {
            loader: ‘url-loader‘,
            options: {
              limit: 8192,
              name: "img/[name].[hash].[ext]"
            }
          }
        ]
      },
      {
        test: /\.less$/,
        use: [{
          loader: "style-loader" // creates style nodes from JS strings
        }, {
          loader: "css-loader" // translates CSS into CommonJS
        }, {
          loader: "less-loader" // compiles Less to CSS
        }]
      },
      {
        test: /\.vue$/,
        use: [‘vue-loader‘]
      },
    ]
  },
  resolve: {
    alias: {
      "vue$": "vue/dist/vue.esm.js"
    }
  },
  plugins: [
    new webpack.BannerPlugin("最终版权归测试所有"),
    new hmtlWebpackPlugin({
      template: "index.html",
    }),
  ],
}

dev.config.js

const webpackMerge = require("webpack-merge")
const baseConfig = require("./base.config.js")
module.exports = (baseConfig, {
  devServer: {
    contentBase: "./dist",
    inline: true
  }
})

prod.config.js

const UglifyjsWebpackPlugin = require("uglifyjs-webpack-plugin")
const webpackMerge = require("webpack-merge")
const baseConfig = require("./base.config")
module.exports = webpackMerge(baseConfig, {
  plugins: [
    new UglifyjsWebpackPlugin(),
  ],
})
  1. 修改package.json配置文件,指定配置文件地址
"scripts": {
    "bundle": "webpack --config ./build/prod.config.js",
    "dev": "webpack-dev-server --open --config ./build/dev.config.js"
  },

四、Vue CLI

技术分享图片

4.1、项目安装

4.1.1、Vue CLI脚手架安装

前提

电脑上有装node.js8.9或者更高的版本

安装Vue Cli

npm install -g @vue/cli

如果出现问题

npm 装东西出现错误那就去把该路径下的目录删除掉C:\Users\HiWin10\AppData\Roamingnpm-cache目录删除,重新安装

4.1.2、项目的创建

自定义创建项目,每个插件的作用

技术分享图片

4.1.2.1、ui界面创建

cmd窗口输入vue ui,自动打开本地服务器,根据提示创建即可

4.1.2.2、命令创建

1. vue create 项目名
2. 选择自定义创建,还是默认创建

4.1.3、插件

在创建后的项目中,package.json查看已经添加的插件或依赖

4.1.3.1、vue ui添加或管理插件

根据提示,添加依赖后添加插件

4.1.3.2、命令方式

详细看这里吧:https://cli.vuejs.org/zh/guide/plugins-and-presets.html#%E9%A1%B9%E7%9B%AE%E6%9C%AC%E5%9C%B0%E7%9A%84%E6%8F%92%E4%BB%B6

4.1.4、runtime-compiler和runtime-only的区别

区别

runtime-compiler

编译过程:temlate->ast->render->vdom->UI

runtime-only推荐使用,性能高,代码量更少

编译过程:render->vdom->UI

技术分享图片

五、Vue-Router

5.1、前后端的三个阶段,以及什么是路由

阶段1:后端渲染

典型代表技术:Java的Servlet和JSP

技术分享图片

阶段2:前端渲染

代表技术:ajax异步请求

1个页面一组html+css+js,可以让专门一个服务器提供这些静态数据。

另一个服务器提供API接口

技术分享图片

阶段3:前端路由和SPA

SPA页面:就是单页面富应用,整个网站都是使用同一个html页面。

一个页面,通过不同的URL,来显示不同的内容,比如点这个我让这个组件显示,点那个我让那个组件显示,数据的话是通过ajax服务器动态该来获取

技术分享图片 技术分享图片

5.1.1、url的hash和H5的history

前面已经知道,现在基本都是SPA应用,那么我们需要了解的就是,如何改变URL,但是页面不刷新

5.1.2、hsah的修改方式

在浏览器的交互里输入

location.hash = "xxx"

5.1.3、history方式

// 更改路径
history.pushState({}, "", "url")

// 后退
history.back()

// 直接根据pushState入栈的路径可以跳转
histoyr.go()  如果是负数后退,整数前进

5.2、Vue-Router的安装和使用

概述

  1. 安装该插件
  2. 编写自定义组件
  3. router目录下的index.js中注册Vue-Router的插件和路径映射关系
  4. App.js中导入注册Vue-Router,并使用<router-link to="/home"></router-link>、<router-view></router-view>,两个标签

安装

vue的界面里安装,先导入依赖,然后在插件页面,安装插件即可

使用

安装好之后会在src目录下多一个router目录,该目录下有一个index.js文件,该文件记录有Vue-Router的注册和使用

  1. 编写自己的Vue组件
<template>
  <div>
    <h2>我是主页</h2>
    <p>哈哈哈哈,我是主页</p>
  </div>
</template>

<script>
export default {
  name: "Home"
}
</script>

<style>
</style>
  1. 导入写好的Vue组件
// 1. 配置路由相关信息 导入路由
import VueRouter from "vue-router"
import Vue from ‘vue‘

import Home from ‘../components/Home‘
import About from ‘../components/About‘

// 2. 通过Vue.use(),安装要使用的插件
Vue.use(VueRouter)

// 3. 创建VueRouter对象
// 3.1 路由和组件的映射关系
const routes = [
  {
    // 用于打开的时候就首页就显示/home的东西
    path: "",  // 默认,缺省
    redirect: "/home",
  },
  {
    // 路径
    path: "/home",
    // 组件
    component: Home,
  },
  {
    path: "/about",
    component: About
  }
]

const router = new VueRouter({
  // 配置路由和组件的映射关系
  routes,
  // mode默认是hash值切换, 可以换成history
  mode: "history"
})

// 4. 将VUeRouter出入到Vue实例中
export default router
  1. Vue-Router使用
  1. 导入Vue-Router
  2. 注册Vue-Router
  3. 配置映射路径,就是什么路径显示组件
  4. 导入对应编写好的组件
  1. APP.js修改
  1. 写上<router-link to="/home">标签,有按钮才行,
    • to属性是路径
  2. <router-view></router-view>添加这个标签,作用:自定义组件会显示在这里
<template>
  <div id="app">
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-view></router-view>
  </div>
</template>

<style>
</style>

<script lang="ts">
import Vue from "vue";
export default Vue.extend({});
</script>

最后一步

应该根路径,用于打开网页的时候,主页显示主页的东西,重定向redirect


import VueRouter from "vue-router"
import Vue from ‘vue‘

import Home from ‘../components/Home‘
import About from ‘../components/About‘


Vue.use(VueRouter)

const routes = [
  {
    // 用于打开的时候就首页就显示/home的东西
    path: "",  // 默认,缺省
    redirect: "/home",
  },
  {
    path: "/home",
    component: Home,
  },
  {
    path: "/about",
    component: About
  }
]

const router = new VueRouter({
  routes,
  mode: "history"
})

export default router

提示

mode属性是url的修改方式historyhash

5.3、<router-link>的补充

参数

  • tag表示指定<router-link>之后要渲染成什么组件,比如默认是<a>,可以修改成button
  • replace:preplace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中
  • active-class:当<router-link>对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称.
    • 在进行高亮显示的导航菜单或者底部tabbar时, 会使用到该类
    • 但是通常不会修改类的属性, 会直接使用默认的router-link-active即可.
技术分享图片

修改linkActiveClass

class具体的名称也可以通过router实例的属性进行修改

技术分享图片 技术分享图片

5.4、路由代码跳转-通过代码方式来跳转

当用随意的标签替代<router-link>后,其他的标签,要写一个单击事件,然后不跳转的情况下,修改url

技术分享图片

代码示例

<template>
  <div id="app">
    <!-- <router-link to="/home" tag="button" replace>首页</router-link>
    <router-link to="/about" tag="button">关于</router-link> -->
    <!-- 代码跳转 -->
    <button @click="btnClick">首页</button>
    <button @click="btnClick2">关于</button>
    <router-view></router-view>
  </div>
</template>

<style>
</style>

<script lang="ts">
import Vue from "vue";
export default Vue.extend({
  methods: {
    btnClick() {
      this.$router.push("/home");
      //  this.$router.replace("/hemo")
    },
    btnClick2() {
      this.$router.push("/about");
    }
  }
});
</script>

5.5、动态路由

某些情况下,一个网站的path是不固定的,比如

  • /user/aaa
  • /user/zhangsan
  • 更或者是id

这种方式叫做动态路由,如何使用呢?

  1. 在路由映射那么加上:变量名
  2. 可以通过this.$route.params.变量名取出即可

代码示例

index.js 配置:变量名

// 1. 配置路由相关信息 导入路由
import VueRouter from "vue-router"
import Vue from ‘vue‘

// 这种方式不是懒加载
// import Home from ‘../components/Home‘
// import About from ‘../components/About‘
// import User from ‘../components/User‘

// 懒加载
const User = () => import("../components/User")



Vue.use(VueRouter)

const routes = [
  {
    path: "/user/:id",
    component: User,
    meta: {
      title: "用户"
    }
  },

]


// 4. 将VUeRouter出入到Vue实例中
export default router

User.vue

<template>
  <div>
    <h2>我是用户标题</h2>
    <p>Hahahahha</p>
    {{userId}}
    <hr>
    {{getid}}
  </div>
</template>

<script>
export default {
  name: "User",
  computed: {
    userId: function () {
      return this.$route.params.id
    },
    getid () {
      return this.$route.params.id
    }
  }
}
</script>

<style>
</style>

5.6、路由懒加载

概述

  1. 正常情况下,Vue将我们写的东西打包之后,有三个.js文件:
    • 我们自己的js文件
    • 第三方的js文件包括vue
    • 底层支持的js文件,比如ES6转ES5

按照上面这种情况下,如果我们的项目很大,js代码都写一个文件里,那么这个js文件就会非常大,加载时间如果过长的话,页面就会出现空白,这对用户是不友好的体验

  1. 懒加载:将js文件分开,用到哪个加载哪个。

懒加载实现方式

在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割

// 懒加载
const Home = () => import("../components/Home")
const HomeNews = () => import("../components/HomeNews")
const HoemMessage = () => import("../components/HomeMessage")

5.7、嵌套路由

什么是嵌套路由

  1. 比如在home页面中,我们系统通过/home/news/home/message访问一些内容
  2. 一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件
技术分享图片

实现嵌套路由有两个步骤:

  1. 创建对应的子组件, 并且在路由映射中配置对应的子路由
  2. 在组件内部使用<router-view>标签

代码示例

  1. 定义新的组件HemoNews

  2. home路径下,添加children属性来注册路由

  3. Hemo中使用<router-link><router-view>标签

  4. <template>
      <div>
        <h2>我只嵌套组件-HomeNews</h2>
        <ul>
          <li>新闻1</li>
          <li>新闻2</li>
          <li>新闻3</li>
          <li>新闻4</li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      name: "HomeNews",
    }
    </script>
    
    <style>
    </style>
    
  5. const routes = [
      {
        // 用于打开的时候就首页就显示/home的东西
        path: "",  // 默认,缺省
        redirect: "/home",
      },
      {
        // 路径
        path: "/home",
        meta: {
          title: "首页"
        },
        // 组件
        component: Home,
        children: [
          // {
          //   path: "",
          //   redirect: ‘news‘
          // },
          {
            path: "news",
            component: HomeNews
          },
          {
            path: "message",
            component: HoemMessage
          }
        ]
      },
    ]
    
  6. <template>
      <div>
        <h2>我是主页</h2>
        <p>哈哈哈哈,我是主页</p>
        <router-link to="/home/news">新闻</router-link>
        <router-link to="/home/message">娱乐</router-link>
        <router-view></router-view>
      </div>
    </template>
    

5.8、传递参数

5.8.1、方式一:url-params的类型

  1. 在路由映射中写上:变量名
  2. 然后在组件中就可以获取this.$route.params.变量名
const routes = [  {
    path: "/user/:id",
    component: User,
    meta: {
      title: "用户"
    }
  },
]
<template>
  <div>
    <h2>我是用户标题</h2>
    <p>Hahahahha</p>
    {{userId}}
    <hr>
    {{getid}}
  </div>
</template>

<script>
export default {
  name: "User",
  computed: {
    userId: function () {
      return this.$route.params.id
    },
    getid () {
      return this.$route.params.id
    }
  }
}
</script>

5.8.2、方式二:to标签属性query对象

<router-link :to="{path: ‘/profile‘, query: {name:‘小红‘, age: 18, heigh: 1.78}}">档案</router-link>

对应的组件上

<template>
  <div>
    <h2>参数传递</h2>
    <h3>{{$route.query.name}}</h3>
    <h3>{{$route.query.age}}</h3>
    <h3>{{$route.query.heigh}}</h3>
  </div>
</template>

<script>
export default {
  name: "Profile"
}
</script>

<style>
</style>

5.8.3、方式三:JavaScript实现

<template>
  <div id="app">
    <!-- <router-link to="/home" tag="button" replace>首页</router-link>
    <router-link to="/about" tag="button">关于</router-link>
     代码跳转 
     <button @click="btnClick">首页</button>
    <button @click="btnClick2">关于</button> -->

    <router-link to="/home" tag="button">首页</router-link>
    <router-link to="/about" tag="button">关于</router-link>
    <!--    <router-link :to="{path: ‘/profile‘, query: {name:‘小红‘, age: 18, heigh: 1.78}}">档案</router-link>
  <router-link :to="‘/user/‘ + userId">用户</router-link> -->

    <!-- 换一种方式实现 -->
    <button @click="userClick">用户</button>
    <button @click="proClick">档案</button>

    <!-- keep-alive作用就是防止组件的销毁和创建   这个标签对应着`activated`/`deactivated` 判断当前状态是否处于活跃 -->
    <keep-alive>
      <router-view />
    </keep-alive>

  </div>
</template>

<style>
</style>

<script lang="ts">
import Vue from "vue";
export default Vue.extend({
  methods: {
    btnClick() {
      this.$router.push("/home");
      //  this.$router.replace("/hemo")
    },
    btnClick2() {
      this.$router.push("/about");
    },
    userClick() {
      this.$router.push("/user/" + this.userId);
    },
    proClick() {
      this.$router.push({
        path: "/profile",
        query: {
          name: "夏红",
          age: 18,
          heigh: 18.9
        }
      });
    }
  },
  data() {
    return {
      userId: "zhangsan"
    };
  }
});
</script>

<style scoped>
.router-link-active {
  color: red;
}
</style>

5.9、$route和$router是有区别的

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

5.10、导航守卫-全局守卫

什么是导航守卫?

vue-router提供的导航守卫主要是用来监听路由的进入和离开的。

pvue-router提供了beforeEachafterEach的钩子函数, 它们会在路由即将改变前和改变后触发

beforeEach:改变前

afterEach:改变后

导航守卫的实际应用

案例:我们来考虑一个需求: 在一个SPA应用中, 如何改变网页的标题呢?

  1. 网页标题是通过<title>来显示的, 但是SPA只有一个固定的HTML, 切换不同的页面时, 标题并不会改变
  2. 但是我们可以通过JavaScript来修改<title>的内容window.document.title = ‘新的标题‘
  3. 那么在Vue项目中, 在哪里修改? 什么时候修改比较合适呢

普通的解决方案:

  1. 我们比较容易想到的修改标题的位置是每一个路由对应的组件.vue文件中
  2. 通过mounted生命周期函数, 执行对应的代码进行修改即可
  3. 但是当页面比较多时, 这种方式不容易维护(因为需要在多个页面执行类似的代码).

导航守卫的解决办法

使用beforeEach来完成标题的修改

  1. 首先给每个路由映射定义标题

    const routes = [
      {
        path: "/about",
        component: About,
        meta: {
          title: "关于"
        }
      },
    ]
    
  2. 利用导航守卫完成修改

router.beforeEach((to, from, next) => {
  // 从 from 跳到 to
  //console.log(to);

  document.title = to.matched[0].meta.title;
  next()  // 必须调用,不调用的话,就不会往下走:不会触发组件
})
  1. beforeEach参数的说明:
    • to: 即将要进入的目标的路由对象
    • from: 当前导航即将要离开的路由对象
    • next: 调用该方法后, 才能进入下一个钩子

5.10.1、导航守卫的其他使用方式

  1. 如果是后置守卫,那么就不需要调用next()方法
  2. 还有路由独享守卫
  3. 组件内部守卫

具体学习看:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#路由独享的守卫

5.10.2、keep-alive遇到vue-router

什么是kepp-alive

keep-alive Vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染

组件的声明周期

vue-router中组件都是根据路由的点击,来创建和删除,访问一个path的时候,这个组件就会创建,再点击其他path的时候,当前的组件就会删除,这样的话非常浪费资源。而keep-alive就是为了解决这么个问题。

当然通过createdestroyed分别是Vue对象的构建和析构执行的回调,可以验证组件的创建与销毁

keep-alive带来的事件

名称 作用
activated 当前组件活跃
deactivaed 当前组件不活跃

只有使用keep-alive才可以使用

使用方式

<!-- 
	keep-alive作用就是防止组件的销毁和创建   
	这个标签对应着`activated`/`deactivated` 判断当前状态			是否处于活跃 
-->
<keep-alive>
    <router-view />
</keep-alive>

keep-valive重要的属性

include - 字符串或正则表达,只有匹配的组件会被缓存

exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存

字符串都是组件的name属性
比如:exclude="Profile,User"

六、vuex

Vuex 是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

6.1、状态管理模式概念

状态管理到底是什么

状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透

  1. 其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。
  2. 然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用
  3. 那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢

6.1.1、管理状态的实例举例

实际开发中,我们一般使用状态管理技术,都用于什么场景呢?

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

6.2、单页面的状态管理

技术分享图片
  • ·state·,驱动应用的数据源;
  • ·view·,以声明方式将 ·state· 映射到视图
  • ·actions·,响应在 view 上的用户输入导致的状态变化。

state好比Vue的data数据,state的数据显示在view中,然后view上触发了某些行为(actions),而行为修改了state

技术分享图片
  • 在这个案例中,我们有木有状态需要管理呢?没错,就是个数counter。
  • counter需要某种方式被记录下来,也就是我们的State。
  • counter目前的值需要被显示在界面中,也就是我们的View部分。
  • 界面发生某些操作时(我们这里是用户的点击,也可以是用户的input),需要去更新状态,也就是我们的Actions

6.3、多页面状态管理

多页面的缺陷

多页面管理如果还是用上面单页面的流程图那样的话,那肯定是不合适的比如:

  1. 多个视图依赖于同一状态。
  2. 来自不同视图的行为需要变更同一状态

对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!

通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。

Vuex的流程图
技术分享图片

流程:
state:共享状态的数据
Vue Components:有用到共享数据的组件
Actions:网络异步请求,必须要经过Actions,才可以被devtools记录到
Mutations:网络请求到之后,通过此属性的方法来完成对state的修改
Devtools:是记录着之间发生什么事情的工具,前提是你所完成的操作必须按照图中流程一步一步走,如果有跳过的那么Devtools就记录不到了。

6.4、Vuex的Dome案例

案例说明
两个组件使用Vuex共享的数据,我想通过一个组件的Click事件来完成,共享值的修改,然后把新的值立刻在两个组件中显示出来

开发规范,Vuex的存放位置

  • 在src目录下,建立store文件夹,用来存放index.js
  • Vuex的代码都是写在sotre目录下

编写两个组件
组件1

<template>
  <div id="app">
    <h2>---App---</h2>
    <h5>{{ $store.state.count }}</h5>
    <button type="button" @click="addCount">+</button>
    <button type="button" @click="addTemp">+5</button>
    <button type="button" @click="subCount">-</button>
    <HelloVuex></HelloVuex>
  </div>
</template>

<script>
import HelloVuex from ‘./components/HelloVuex‘

export default {
  name: ‘App‘,
  components: {
    HelloVuex,
  },
  methods: {
    addCount() {
      this.$store.commit(‘increase‘)
    },
    subCount() {
      this.$store.commit(‘reduction‘)
    },
    addTemp() {
      const temp = 5
      // 第二种mutations提交方式
      this.$store.commit({
        type: ‘addTemp‘,
        temp,
      })
    },
  },
}
</script>

<style></style>

组件2

<template>
  <div>
    <hr />
    <h2>---HelloVuex---</h2>
    <h5>{{ $store.state.count }}</h5>
  </div>
</template>

<script>
export default {
  name: ‘HelloVUex‘,
}
</script>

<style></style>

Vuex-index.js代码

import Vue from ‘vue‘
import Vuex from ‘vuex‘

Vue.use(Vuex)

const store = new Vuex.Store({
    // 保存状态
    state: {
        count: 0,
    },
    actions: {},
    mutations: {
        // increase 事件类型  (){}是回调函数
        increase () {
            this.state.count++
        },
        reduction (state) {
            this.state.count--
        },
        // 第二种提交方式
        addTemp (state, payload) {
            console.log(payload);

            state.count += payload.temp
        },
    },
    getters: {},
    modules: {}
}
    

挂载Vue实例中

import Vue from ‘vue‘
import App from ‘./App.vue‘

import store from ‘./store‘

Vue.config.productionTip = false

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

6.5、Vuex的核心概年

  • State :你要共享的数据
  • Getters :相当于计算属性,如果共享的数据需要执行某些逻辑才能显示的话,方法可以写这个属性中
  • Mutation:对State里的共享数据修改,方法可以写在这里
  • Action:如果要执行异步操作,那么外边的组件就必须通过这个属性中方法来完成回调(对共享数据的修改)
  • Module:由于Vuex是单一数据元素树,大型项目的共享数据一定是按照模块划分的,而模快分的数据可以写在此属性中

6.5.1、Getter的使用

获取State数据

获取年龄大于18的信息

const store = new Vuex.Store({
  // 保存状态
  state: {
    count: 0,
    students: [
      { id: 1, name: "小红", age: 18 },
      { id: 2, name: "小明", age: 28 },
      { id: 3, name: "小白", age: 4 },
      { id: 4, name: "小新", age: 5 },
      { id: 5, name: "正男", age: 26 },

    ],
    info: {
      name: "codewhy",
      age: 21
    }
  },
  getters: {
    get18Age (state) {

      return state.students.filter(function (value, index, array) {
        return value.age > 18
      })
    },
}
<p>年龄大于18的学生</p>
<p>{{ $store.getters.get18Age }}</p>

6.5.1.1、Getters作为参数和传递参数

Getter作为参数
如果我们已经有了一个获取所有年龄大于20岁学生列表的getters, 那么代码可以这样来写
技术分享图片

Getter传输参数
getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数
比如上面的案例中,我们希望获取指定姓名的学生信息

 // 查询姓名的学生,但是不允许带参数,解决办法是:返回一个有参数的函数
    byName (state) {
      return (name) => {
        return state.students.filter((s) => s.name == name)
      }
    }
	<p>指定小新的学生</p>
    <p>{{ $store.getters.byName(‘小新‘) }}</p>

6.5.2、Mutation的使用

一个参数
包含两部分:

  1. 字符串的时间类型
  2. 一个回调函数
mutations: {
	addCount(){
		state.count++
	}
}

addCount:事件类型
(){state.count++}:回调函数

6.5.2.1、Mutation传递参数

  • 在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数
  • 参数被称为是mutation的载荷(Payload)

代码示例

// 组件中代码
 update() {
      this.$store.dispatch(‘aupdate‘).then(() => {
        console.log(‘update指行完成‘)
      })
    },


// Mutation中的代码
update (state, name) {
      state.info.name = name
    }

多个参数传递
代码示例

// 带有参数的mutations
    addStudent() {
      const stu = { id: 14, name: ‘风间‘, age: 21 }
      this.$store.commit(‘addStu‘, stu)
    },
// Mutations中代码
    addStu (state, stu) {
      state.students.push(stu)
    },

另一种提交风格

// 组件中代码
addTemp() {
      const temp = 5
      // 第二种mutations提交方式
      this.$store.commit({
        type: ‘addTemp‘,
        temp,
      })
    },
    
    // Mutations中代码
    // 第二种提交方式
    addTemp (state, payload) {
      console.log(payload);

      state.count += payload.temp
    },

响应规则

  1. 提前在store中初始化好所需的属性
  2. 通过Vue.set(要添加的对象, 添加的key, value)
  3. 删除Vue.delete("删除对象", key)

6.5.4、Action使用

  • 但是某些情况, 我们确实希望在Vuex中进行一些异步操作, 比如网络请求, 必然是异步的. 这个时候怎么处理呢?
  • Action类似于Mutation, 但是是用来代替Mutation进行异步操作的.

context是什么
上下文对象,这里的上下文对象值的是store

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

代码示例,使用定时器模拟网络请求

// APP中代码
update() {
    this.$store.dispatch(‘aupdate‘).then(() => {
        console.log(‘update指行完成‘)
    })
},

// Actionve代码
// 做异步操作
  actions: {
    // aupdate (context) {
    //   setInterval(() => {
    //     let name = "颜韵"
    //     context.commit("update", name)
    //   }, 1000)
    // 修改,APP里要知道调用完成没有
    aupdate (context) {
      new Promise((resolve, reject) => {
        setInterval(() => {
          let name = "颜韵"
          context.commit("update", name)  // 指行到这里代表数据修改完成
          resolve(); // 走then
        })
      })
    }
  },
  1. 为什么返回类型是Promise,是为用来监听回调业务代码是否处理完成
  2. actions是用dispatch方法调用提交
  3. mutations是用commit方法提交

6.5.4、Module使用

  • Module是模块的意思, 为什么在Vuex中我们要使用模块呢?

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

  • 看下面代码

技术分享图片

上面的代码中, 我们已经有了整体的组织结构, 下面我们来看看具体的局部模块中的代码如何书写

  1. 我们在moduleA中添加state、mutations、getters
  2. mutation和getters接收的第一个参数是局部状态对象
技术分享图片 技术分享图片

注意:

  1. 虽然, 我们的doubleCount和increment都是定义在对象内部的
  2. 但是在调用的时候, 依然是通过this.$store来直接调用的

局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
技术分享图片

如果getters中也需要使用全局的状态, 可以接受更多的参数
如果传输的是对象的话,那么接受函数的参数是对象的key那么就会自动拆分,这是ES6的语法
技术分享图片

6.6、Vuex项目结构组织

技术分享图片

七、网络请求封装(axios)

7.1、axios的基本使用

// 1. 导入axios
import axios from ‘axios‘

// 2. 发送请求
axios({
  url: ‘http://123.207.32.32:8000/home/multidata‘,
  // method: ‘‘  发送格式  post get
}).then((request) => {
  console.log(request);

})

7.2、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]])

7.3、发送并发请求

axios.all([数组])


axios.all([axios.get("http://123.207.32.32:8000/home/multidata"),
axios.get("http://123.207.32.32:8000/home/data")
]).then(axios.spread((resq1, resq2) => {
  console.log(resq1);
  console.log(resq2);

}))

7.4、全局配置

在实际开发中,很多路径前面都是一致的,这个时候我们就可以给axios设置一个基础的URL

axios.defaults.baseURL = ‘123.207.32.32:8000‘

// 下面的路径就可以这么写了
axios.all([axios.get("/home/multidata"),
axios.get("/home/data")
]).then(axios.spread((resq1, resq2) => {
  console.log(resq1);
  console.log(resq2);

}))

7.5、常见配置

  • 请求地址

    • url: ‘/user‘
  • 请求类型

    • method:‘get‘
  • 请跟路径

    • baseURL:‘http://www.mt.com/api‘
  • 请求前的数据处理

    • transformRequest:[function(data){}]
  • 请求后的数据处理

    • transformResponse: [function(data){}]
  • 自定义的请求头

    • headers:{‘x-Requested-With‘:‘XMLHttpRequest‘},
  • URL查询对象

    • params:{ id: 12 },
  • 查询对象序列化函数

    • paramsSerializer: function(params){ }
  • request bodyPOST请求

    • data: { key: ‘aa‘},
  • 超时设置

    • timeout: 1000
  • 跨域是否带Token

    • withCredentials: false
  • 自定义请求处理

    • adapter: function(resolve, reject, config){},
  • 身份验证信息

    • auth: { uname: ‘‘, pwd: ‘12‘},
  • 响应的数据格式 json / blob /document /arraybuffer / text / stream

    • responseType: ‘json‘,

7.6、axios的实例

为什么要创建axios的实例呢

  1. 当我们从axios模块中导入的时候,使用的是默认实例
  2. 当我们给他一些基本默认配置时,这些配置就会固定下来,在后面开发中,可能有些不太一样的地方,这样修改就不会不方面
  3. 所以我们需要创建一个axios的实例

代码示例


const axiosInstance = axios.create()

axiosInstance.get(‘http://123.207.32.32:8000/home/multidata‘).then((req) => {
  console.log(req);

})

const axiosInstance1 = axios.create({
  baseURL: ‘http://123.207.32.32:8000‘
})

axiosInstance1.get(‘/home/multidata‘).then((req) => {
  console.log(req);

})

7.4、拦截器

  • 请求拦截器
    • 请求成功拦截器
    • 请求失败拦截
  • 响应拦截器
    • 响应成功拦截
    • 响应失败拦截

注意:不论是请求或响应拦截,都是在前拦截的,比如:请求拦截是在发送请求前拦截。响应是接受响应信息前拦截

// 拦截请求
axiosInstance1.interceptors.request.use(config => {
  console.log("拦截到了");

  console.log(config);
  return config
}, err => {
  // 出错
})


// 响应拦截
axiosInstance1.interceptors.response.use((req) => {
  console.log(req);

  console.log("拦截响应");
  return req
}, err => {
  // 出错
})

拦截器实际中一般的应用

请求

  1. 当发送网络请求时,在页面添加一个loading组件,作为动画
  2. 某些请求要求用户必须登录,判断用户是否有token,如果没有token跳转到login页面
  3. 对请求参数序列化

响应

  1. 可以根据状态码判断是否响应成功,如果有错跳转到提示页面
  2. 对响应数据进行过滤

7.5、实际开发中第三方框架的用法

实际开发中,因为用到第三方框架,所以要想到以后,万一框架不维护了,或者后面要换发送网络请求的框架了。这都是要考虑的情况。

如果说,你的每一个网页里都是导入axios直接用,那么你的程序太过依赖第三方框架,也就是高耦合,这种情况造成的后果上面也说了,而解决这种问题的思路是。

在src目录下新建network文件夹,然后写在里面导入axios框架,然后我们写函数导出

request.js

import axios from ‘axios‘

export function requset (config) {
  const instance = axios.create({
    baseURL: "http://123.207.32.32:8000",
    timeout: 5000
  })

  return instance(config)
}

main.js应用

import { requset } from ‘./netrwork/request‘

requset({
  url: "/home/multidata",
  mode: "get"
}).then((req) => {
  console.log(req);

})

options-Vue的类型

  • el :
    • 类型: string | HTMLElement
    • 作用:决定之后Vue实例会管理哪个DOM
  • data:
    • 类型:Object | Function
    • 作用:Vue实例对应的数据对象
  • methods:
    • 类型:key:string Function
    • 作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用
  • computed
    • 计算属性
    • 作用:data的数据需要通过某些计算才能得到
  • components
    • 作用:组件注册
  • watch
    • 作用:属性修改自动触发
  • destroyed
    • 作用:当前组件被销毁的回调函数
  • activated
    • 作用:当前组件处于活跃状态
  • deactivated
    • 作用:当前组件处于不活跃状态

ES的增强

ES6增强

块级作用域

变量有了块级作用域,关键字分别是letconst;分别是局部变量,常量(不能修改指向)

代码示例,以及没有块级作用域的BUG

<!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>块级作用域</title>
    <script src="../js/vue.js"></script>
  </head>

  <body>
    <!-- 
      ES6的增强:
        变量由作用域了。用let关键字定义。
        var定义变量是没有作用域的,相当于全局变量,所以有一个缺陷,比如点击按钮输出,这是第几个按钮
        
      没有作用域会产生的问题,以及解决和增强之后的解决

     -->
    <div id="app">
      <button>按钮1</button>
      <button>按钮2</button>
      <button>按钮3</button>
      <button>按钮4</button>
      <button>按钮5</button>
    </div>

    <script>
      var buttons = document.getElementsByTagName(‘button‘)
      // 输出结果是直接是第5个按钮被单击,原因是在执行js的时候,程序为每个按钮绑定一个click事件,
      // 当全部好,i的值就是5,而js没有作用域,所以使用的都是同一个i变量,解决办法使用函数(闭包)
      for (var i = 0; i < buttons.length; i++) {
        ;(function (num) {
          buttons[i].addEventListener(‘click‘, function () {
            console.log(‘第‘ + num + ‘个按钮被单击‘)
          })
        })(i)
      }

      // --------------------增强之后ES6解决办法
      var buttons = document.getElementsByTagName(‘button‘)
      for (let i = 0; i < buttons.length; i++) {
        buttons[i].addEventListener(‘click‘, function () {
          console.log(‘di-‘ + i + ‘被单击‘)
        })
      }

      // ------------ 循环生成的程序
      {
        i = 0
        ;(function (num) {
          buttons[i].addEventListener(‘click‘, function () {
            console.log(‘第‘ + num + ‘个按钮被单击‘)
          })
        })(i) // 这里是直接函数调用
      }
      {
        i = 1
        ;(function (num) {
          buttons[i].addEventListener(‘click‘, function () {
            console.log(‘第‘ + num + ‘个按钮被单击‘)
          })
        })(i)
      }
    </script>
  </body>
</html>

const修饰符

代码示例

<!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>Document</title>
    <script src="../js/vue.js"></script>
  </head>

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

    <script>
      // 规则1:在使用的时候,一定要初始化
      const obj = ‘xiaobai‘

      // 规则2:建议使用const关键字,可以很明确告诉别人这个变量作用

      // 规则3:可以修改指向内容
      const obj1 = {
        name: ‘小白‘,
        sex: ‘男‘,
      }
      obj1.name = ‘小红‘
    </script>
  </body>
</html>

ES6对对象属性和方法的增强

代码示例

<!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>ES6对属性和方法的增强</title>
    <script src="../js/vue.js"></script>
  </head>

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

    <script>
      // ES5定义对象
      var obj = {
        name: ‘小红‘,
        age: 15,
        sex: ‘男‘,
      }

      name = ‘小红‘
      sex = ‘女‘
      age = 20
      var obj = {
        name: name,
        age: age,
        sex: sex,
      }

      // ES6增强  可以直接写变量名,会把变量名作为key,内容为value
      var obj = {
        name,
        age,
        sex,
      }

      // ES6对方法的增强
      var obj = {
        eat: function () {
          console.log(‘吃东西‘)
        },
      }
      // 增强
      var obj = {
        eat() {
          console.log(‘吃东西‘)
        },
      }
    </script>
  </body>
</html>

高阶函数的使用

Filter

见名知意,过滤函数。回调函数中返回如果为true那么就会存储在新的数组中

代码示例

const list = [12, 34, 555, 1223, 343, 55, 66, 98]

// 求小于100的数字
// filter 回调函数
//          参数,是每个元素的值
//          返回值:布尔类型,如果是true那么会装到新数组中
let newList01 = list.filter(function (number) {
  return number < 100
})
console.log(newList01)

Map

替换,回调函数中,遍历每个元素,在回调中操作,并返回值,生成新的数组

// 小于100的数字 乘2倍
// map: 回调函数
//      参数:是每个元素的值
//      返回值是:每个元素的处理
//    更像是:替换数组中所有元素
let newList02 = newList01.map(function (number) {
  return number * 2
})
console.log(newList02)

Reduce

计算和,回调函数中两个参数

  • 参数1:上一个值/初始值
  • 参数2:下一个值

代码示例

// 求和
// reduce:回调函数
//    参数1:总数(初始值/先前返回的值)
//    参数2:下一个元素的值
//  初始值,不写也可以,
// 返回值:先前返回的值+下一个元素的值
let newList03 = newList02.reduce(function (preValue, n) {
  return preValue + n
}, 0)
console.log(newList03)

let sum = list
  .filter(function (number) {
    return number < 100
  })
  .map(function (n) {
    return n * 2
  })
  .reduce(function (p, next) {
    return p + next
  })
console.log(sum)

Vue

原文:https://www.cnblogs.com/q2210075017/p/13261604.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!