Javascript

vue笔记

本文主要是介绍vue笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

为什么学习vue

1可能你的公司正要将原有的项目使用Vue进行重构。
2也可能是你的公司新项目决定使用Vue的技术栈。
3当然,如果你现在正在换工作,你会发现招聘前端的需求中,10个有8个都对Vue有或多或少的要求。
4当然,作为学习者我们知道Vuejs目前非常火,可以说是前端必备的一个技能。

认识vue

Vue (读音 /vjuː/,类似于 view),不要读错。

Vue是一个渐进式的框架,什么是渐进式的呢?

渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,一点一点来开发,带来更丰富的交互体验。
或者如果你希望将更多的业务逻辑使用Vue实现,那么Vue的核心库以及其生态系统,比如【Vue核心+Vue-router+Vuex】,也可以满足你各种各样的需求。

Vue有很多特点和Web开发中常见的高级功能

  • 解耦视图和数据

  • 可复用的组件

  • 前端路由技术

  • 状态管理

  • 虚拟DOM

Vue的MVVM

View层 -视图层:

在我们前端开发中,通常就是DOM层。

主要的作用是给用户展示各种信息。

Model层-数据层:

数据可能是我们固定的死数据,更多的是来自我们服务器,从网络上请求下来的数据。

VueModel层-视图模型层:

视图模型层是View和Model沟通的桥梁。

一方面它实现了Data Binding,也就是数据绑定,将Model的改变实时的反应到View中

另一方面它实现了DOM Listener,也就是DOM监听,当DOM发生一些事件(点击、滚动、touch等)时,可以监听到,并在需要的情况下改变对应的Data。

Vue的安装

1-直接CDN引入

<!-- 开发环境版本,包含了有帮助的命令行警告 --> 
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

2-下载和引入

开发环境 https://vuejs.org/js/vue.js 生产环境 https://vuejs.org/js/vue.min.js

3-NPM安装

Vue初体验

<div id="app">
    <h2>{{message}}</h2>
    <h1  >{{name}}</h1>
</div>

<div>{{message}}</div>

<script src="../js/vue.js"></script>
<script>
    // let(变量)/const(常量)
    // 编程范式: 声明式编程
    const app = new Vue({
        el: '#app', // 用于挂载要管理的元素
        data: { // 定义数据
            message: '你好啊,!',
            name: 'zy'
        }
        
    })
    
    
    // 原生js的做法(编程范式: 命令式编程)
    // 1.创建div元素,设置id属性
    // 2.定义一个变量叫message
    // 3.将message变量放在前面的div元素中显示
    // 4.修改message的数据: 今天天气不错!
    // 5.将修改后的数据再次替换到div元素
</script>

创建Vue实例传入的options

el

  • 类型:string | HTMLElement

  • 作用:决定之后Vue实例会管理哪一个DOM。

data

  • 类型:Object | Function (组件当中data必须是一个函数)

  • 作用:Vue实例对应的数据对象。

methods

  • 类型:{ [key: string]: Function }

  • 作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。

Vue的生命周期

vue实例从创建到销毁的过程中有很多个阶段,在每个阶段都有一些函数会被自动执行,可以方便的帮助我们完成自动被执行调用的逻辑。

八个生命周期

<body>
<!-- 生命周期 -->
        <div id="demodiv">
            <h1>{{text}}</h1>
            <button v-on:click="text='我改了'">点我修改</button>
        </div>
        <script>
            new Vue({
                el:"#demodiv",
                data:{
                    text:"生命周期"
                },
                methods:{},
                watch:{},
                computed:{},
                filters:{},
                beforeCreate(){
                    console.log("实例创建之前")
                },
                created(){
                    console.log("实例创建之后")
                },
                beforeMount(){
                    console.log("开始创建模板")挂载前
                },
                mounted(){
                    console.log("创建模板ok")挂载后
                },
                beforeUpdate(){
                    console.log("开始更新")
                },
                updated(){
                    console.log("更新完成")
                },
                beforeDestroy(){
                    console.log("开始销毁")
                },
                destroyed(){
                    console.log("销毁完成")
                },      
            })
        </script>
</body>

描述每个生命周期

  • beforeCreate(创建前) 在数据观测和初始化事件还未开始
  • created(创建后) 完成数据观测,属性和方法的运算,初始化事件,实例中的el属性还没有显示出来
  • beforeMount(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。
  • mounted(载入后) 在el 被新创建的 vue.el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。
  • beforeUpdate(更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
  • updated(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
  • beforeDestroy(销毁前) 在实例销毁之前调用。实例仍然完全可用。
  • destroyed(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

什么是vue生命周期?

Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。

vue生命周期的作用是什么?

生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易完成指定逻辑。 U�

vue生命周期总共有几个阶段?

它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后

第一次页面加载会触发哪几个钩子?

第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子

DOM 渲染在 哪个周期中就已经完成?

DOM 渲染在 mounted 中就已经完成了。

Vue插值的操作

mustache语法

 <!--mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式-->
 <!--Mustache: 胡子/胡须-->

  <h2>{{firstName + lastName}}</h2>
  <h2>{{firstName + ' ' + lastName}}</h2>
  <h2>{{firstName}} {{lastName}}</h2>
  <h2>{{counter * 2}}</h2>

v-once 只渲染一次

在某些情况下,我们可能不希望界面随意的跟随改变

这个时候,我们就可以使用一个Vue的指令

v-once:

该指令后面不需要跟任何表达式(比如之前的v-for后面是由跟表达式的)

该指令表示元素和组件(组件后面才会学习)只渲染一次,不会随着数据的改变而改变。

 <h2 v-once>{{message}}</h2>

v-html

某些情况下,我们从服务器请求到的数据本身就是一个HTML代码

如果我们直接通过{{}}来输出,会将HTML代码也一起输出。

但是我们可能希望的是按照HTML格式进行解析,并且显示对应的内容。

如果我们希望解析出HTML展示

​ 可以使用v-html指令

​ 该指令后面往往会跟上一个string类型

​ 会将string的html解析出来并且进行渲染

<div id="app">
  <h2>{{url}}</h2>
<!--	v-html指令把数据按照html标签来解析-->
  <h2 v-html="url"></h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      url: '<a href="http://www.baidu.com">百度一下</a>'
    }
  })
</script>

v-text

v-text作用和Mustache比较相似:都是用于将数据显示在界面中

v-text通常情况下,接受一个string类型

<h2>{{message}}, 李银河!</h2>
<h2 v-text="message">, 李银河!</h2>

v-pre

v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法。

比如下面的代码:

<div id="app">
  <h2>{{message}}</h2>
  <h2 v-pre>{{message}}</h2>
</div>

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

第一个h2元素中的内容会被编译解析出来对应的内容

第二个h2元素中会直接显示{{message}}

v-cloak

在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签。 ncloak: 斗篷。

用v-cloak先不让它显示出来,加载完成后再显示出来

<div id="app" v-cloak>
  <h2>{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  // 在vue解析之前, div中有一个属性v-cloak
  // 在vue解析之后, div中没有一个属性v-cloak
  setTimeout(function () {
    const app = new Vue({
      el: '#app',
      data: {
        message: '你好啊'
      }
    })
  }, 1000)
</script>

v-bind动态绑定属性

为什么有v-bind

除了内容需要动态来决定外,某些属性我们也希望动态来绑定。

比如动态绑定a元素的href属性

比如动态绑定img元素的src属性

这个时候,我们可以使用v-bind指令:

作用:动态绑定属性

缩写::

预期:any (with argument) | Object (without argument)

参数:attrOrProp (optional)

v-bind基本使用

<body>
<div id="app">
  <!-- 错误的做法: 这里不可以使用mustache语法-->
  <!--<img src="{{imgURL}}" alt="">-->
  <!-- 正确的做法: 使用v-bind指令 -->
  <img v-bind:src="imgURL" alt="">
  <a v-bind:href="aHref">百度一下</a>
  <!--<h2>{{}}</h2>-->
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      imgURL: 'https://img11.360buyimg.com/mobilecms/s350x250_jfs/t1/20559/1/1424/73138/5c125595E3cbaa3c8/74fc2f84e53a9c23.jpg!q90!cc_350x250.webp',
      aHref: 'http://www.baidu.com'
    }
  })
</script>
</body>

v-bind语法糖

就是冒号

<div id="app">
  <!--语法糖的写法-->
  <img :src="imgURL" alt="">
  <a :href="aHref">百度一下</a>
</div

v-bind动态绑定class属性

很多时候,我们希望动态的来切换class,比如:

当数据为某个状态时,字体显示红色。

当数据另一个状态时,字体显示黑色。

对象语法

绑定方式:对象语法

对象语法的含义是:class后面跟的是一个对象。

用法一:直接通过{}绑定一个类
<h2 :class="{'active': isActive}">Hello World</h2>

用法二:也可以通过判断,传入多个值
<h2 :class="{'active': isActive, 'line': isLine}">Hello World</h2>

用法三:和普通的类同时存在,并不冲突
注:如果isActive和isLine都为true,那么会有title/active/line三个类
<h2 class="title" :class="{'active': isActive, 'line': isLine}">Hello World</h2>

用法四:如果过于复杂,可以放在一个methods或者computed中
注:classes是一个计算属性
<h2 class="title" :class="classes">Hello World</h2>

如果对象语法过与复杂,可以将这个放到函数里或者计算属性里

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .active {
      color: red;
    }
  </style>
</head>
<body>
<div id="app">
  <h2 class="title" v-bind:class="{active: isActive, line: isLine}">{{message}}</h2>
  <h2 class="title" v-bind:class="getClasses()">{{message}}</h2>
  <button v-on:click="btnClick">按钮</button>
</div>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      isActive: true,
      isLine: true
    },
    methods: {
      btnClick: function () {
        this.isActive = !this.isActive
      },
      getClasses: function () {
        return {active: this.isActive, line: this.isLine}
      }
    }
  })
</script>
</body>
</html>

数组语法

绑定方式:数组语法

数组语法的含义是:class后面跟的是一个数组。数组里面如果是常量要加单引号,否则常量会被当成变量来解析,是变量不用加

用法一:直接通过{}绑定一个类
<h2 :class="['active']">Hello World</h2>

用法二:也可以传入多个值
<h2 :class=“[‘active’, 'line']">Hello World</h2>

用法三:和普通的类同时存在,并不冲突
注:会有title/active/line三个类
<h2 class="title" :class=“[‘active’, 'line']">Hello World</h2>

用法四:如果过于复杂,可以放在一个methods或者computed中
注:classes是一个计算属性
<h2 class="title" :class="classes">Hello World</h2>

<body>
<div id="app">
  <h2 class="title" :class="[active, line]">{{message}}</h2>
  <h2 class="title" :class="getClasses()">{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      active: 'aaaaaa',
      line: 'bbbbbbb'
    },
    methods: {
      getClasses: function () {
        return [this.active, this.line]
      }
    }
  })
</script>
</body>

v-bind动态绑定style

对象语法

  • :style="{color: currentColor, fontSize: fontSize + ‘px’}"

  • style后面跟的是一个对象类型

  • 对象的key是CSS属性名称

  • 对象的value是具体赋的值,值可以来自于data中的属性

  • value如果是具体的值,必须加上单引号,否则会被当成变量来解析

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .title {
      font-size: 50px;
      color: red;
    }
  </style>
</head>
<body>

<div id="app">
  <!--<h2 :style="{key(属性名): value(属性值)}">{{message}}</h2>-->

  <!--'50px'必须加上单引号, 否则是当做一个变量去解析-->
  <!--<h2 :style="{fontSize: '50px'}">{{message}}</h2>-->

  <!--finalSize当成一个变量使用-->
  <!--<h2 :style="{fontSize: finalSize}">{{message}}</h2>-->
  <h2 :style="{fontSize: finalSize + 'px', backgroundColor: finalColor}">{{message}}</h2>
  <h2 :style="getStyles()">{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      finalSize: 100,
      finalColor: 'red',
    },
    methods: {
      getStyles: function () {
        return {fontSize: this.finalSize + 'px', backgroundColor: this.finalColor}
      }
    }
  })
</script>
</body>
</html>

数组语法

<div v-bind:style="[baseStyles, overridingStyles]"></div>
  • style后面跟的是一个数组类型
  • 多个值以,分割即可
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

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

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      baseStyle: {backgroundColor: 'red'},
      baseStyle1: {fontSize: '100px'},
    }
  })
</script>

</body>
</html>

计算属性

什么是计算属性

  • 我们知道,在模板中可以直接通过插值语法显示一些data中的数据。
  • 但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示
  • 比如我们有firstName和lastName两个变量,我们需要显示完整的名称。
  • 但是如果多个地方都需要显示完整的名称,我们就需要写多个{{firstName}} {{lastName}},可以将这样的代码换成计算属性
  • 计算属性是写在实例的computed选项中的。
  • 希望将我们的数据进行某种变化再来显示,就可以用计算属性,给他重新定义一个属性,并且返回变化之后的数据

计算属性的基本使用

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

<div id="app">
  <!-- 普通变化 -->
  <h2>{{firstName + ' ' + lastName}}</h2>
  <h2>{{firstName}} {{lastName}}</h2>
  <!-- 调用方法 -->
  <h2>{{getFullName()}}</h2>
  <!-- 计算属性返回的 -->
  <h2>{{fullName}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      firstName: 'Lebron',
      lastName: 'James'
    },
    // computed: 计算属性()
    computed: {
      fullName: function () {
        return this.firstName + ' ' + this.lastName
      }
    },
    methods: {
      getFullName() {
        return this.firstName + ' ' + this.lastName
      }
    }
  })
</script>
</body>
</html>

计算属性的复杂操作

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <h2>总价格: {{totalPrice}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      books: [
        {id: 110, name: 'Unix编程艺术', price: 119},
        {id: 111, name: '代码大全', price: 105},
        {id: 112, name: '深入理解计算机原理', price: 98},
        {id: 113, name: '现代操作系统', price: 87},
      ]
    },
    methods: {
      getTotalPrice: function () {
        let result = 0
        for (let i=0; i < this.books.length; i++) {
          result += this.books[i].price
        }
        return result
      }
    },
    computed: {
      totalPrice: function () {
        let result = 0
        for (let i=0; i < this.books.length; i++) {
          result += this.books[i].price
        }
        return result
          
          
          
        //es6用法
        // for (let i in this.books) {
        //   this.books[i]
        // }
        //
        // for (let book of this.books) {
        //
        // }
      }
    }
  })
</script>
</body>
</html>

计算属性的setter和getter

  computed: {
       fullName: function () {
         return this.firstName + ' ' + this.lastName
       }
    }
  • 上面是计算属性一般写法:可以称为简写,但是其实每个属性对应保存的是一个对象,里面有set方法和get方法
  • 每次需要改变数据形式的代码都在get方法里面写的,一般不想在set方法写入数据
  • 只利用get方法只读属性来编写代码,所以一般set方法省略
  • 而只有一个get方法也可以简写省略,用属性名代替get,所以每次调用的时候对应的属性后面都不用加括号,但是本质上写属性其实是调用其本身对应的get函数。
   computed: {
      fullName: {
        set: function(newValue) {
        },
        get: function () {
        }
    }
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <h2>{{fullName}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      firstName: 'Kobe',
      lastName: 'Bryant'
    },
    computed: {
      // fullName: function () {
      //   return this.firstName + ' ' + this.lastName
      // }
      // name: 'coderwhy'
      // 计算属性一般是没有set方法, 只读属性.
      fullName: {
        set: function(newValue) {
          // console.log('-----', newValue);
          const names = newValue.split(' ');
          this.firstName = names[0];
          this.lastName = names[1];
        },
        get: function () {
          return this.firstName + ' ' + this.lastName
        }
      },

      // fullName: function () {
      //   return this.firstName + ' ' + this.lastName
      // }
    }
  })
</script>

</body>
</html>

计算属性和methods的对比

methods和computed看起来都可以实现我们的功能,那么为什么还要多一个计算属性这个东西呢?

原因:

  • 计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。
  • 方法不会进行缓存,多次使用时,方法会被多次调用。

事件监听

在前端开发中,我们需要经常和用于交互。

这个时候,我们就必须监听用户发生的时间,比如点击、拖拽、键盘事件等等

在Vue中如何监听事件呢?使用v-on指令

v-on介绍

  • 作用:绑定事件监听器
  • 缩写:@
  • 预期:Function | Inline Statement | Object
  • 参数:event

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

  • 可以直接写表达式
  • 可以写函数调用
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <h2>{{counter}}</h2>
  <!--<button v-on:click="counter++">+</button>-->
  <!--<button v-on:click="counter--;">-</button>-->
    
  <!--<button v-on:click="increment">+</button>-->
  <!--<button v-on:click="decrement">-</button>-->
  
    <!-- 语法糖-->
  <button @click="increment">+</button>
  <button @click="decrement">-</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      counter: 0
    },
    methods: {
      increment() {
        this.counter++
      },
      decrement() {
        this.counter--
      }
    }
  })
</script>
</body>
</html>

v-on的参数传递问题

当通过methods中定义方法,以供@click调用时,需要注意参数问题:

  • 如果该方法不需要额外参数,那么方法后的()可以不添加,如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去

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

  • 在事件定义时, 写方法时省略了小括号, 但是方法本身是需要一个参数的, 这个时候, Vue会默认将浏览器生产的event事件对象作为参数传入到方法

  • 如果函数需要参数,但是没有传入, 那么函数的形参为undefined

  • 方法定义时, 我们需要event对象, 同时又需要其他参数,在调用方式, 就需要手动的获取到浏览器参数的事件对象,给绑定的函数传入 $event参数就行。

     <button @click="btn3Click("abc",$event)">按钮3</button>
    

v-on的修饰符

Vue提供了修饰符来帮助我们方便的处理一些事件:

  • .stop - 调用 event.stopPropagation()。阻止事件冒泡
  • .prevent - 调用 event.preventDefault()。阻止浏览器默认行为
  • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
  • .native - 监听组件根元素的原生事件。
  • .once - 只触发一次回调。
  <!--1. .stop修饰符的使用-->
  <div @click="divClick">
    aaaaaaa
    <button @click.stop="btnClick">按钮</button>
  </div>

  <!--2. .prevent修饰符的使用-->
  <br>
  <form action="baidu">
    <input type="submit" value="提交" @click.prevent="submitClick">
  </form>

  <!--3. .监听某个键盘的键帽-->
  <input type="text" @keyup.enter="keyUp">

  <!--4. .once修饰符的使用-->
  <button @click.once="btn2Click">按钮2</button>

条件判断

  • v-if、v-else-if、v-else
  • 这三个指令与JavaScript的条件语句if、else、else if类似。
  • Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件

v-if的使用

v-if的原理:

  • v-if后面的条件为false时,对应的元素以及其子元素不会渲染。也就是根本不会有对应的标签出现在DOM中。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

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

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      isShow: true
    }
  })
</script>
</body>
</html>

v-if和v-else的使用

<div id="app">
  <h2 v-if="isShow">
    {{message}}
  </h2>
  <h1 v-else>isShow为false时, 显示我</h1>
</div>

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

<div id="app">
  <h2 v-if="score>=90">优秀</h2>
  <h2 v-else-if="score>=80">良好</h2>
  <h2 v-else-if="score>=60">及格</h2>
  <h2 v-else>不及格</h2>
  <h1>{{result}}</h1>
</div>

v-show的使用

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

<h2 v-show="isShow" id="bbb">{{message}}</h2>

v-show和v-if的区别

v-if和v-show对比

  • v-if当条件为false时,压根不会有对应的元素在DOM中。
  • v-show当条件为false时,仅仅是将元素的display属性设置为none而已。

开发中如何选择呢?

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

循环遍历

v-for遍历数组

  • 当有一组数据需要进行渲染时,我们就可以使用v-for来完成。
  • v-for的语法类似于JavaScript中的for循环。
  • 格式如下:item in items的形式。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <!--1.在遍历的过程中,没有使用索引值(下标值)-->
  <ul>
    <li v-for="item in names">{{item}}</li>
  </ul>

  <!--2.在遍历的过程中, 获取索引值-->
  <ul>
    <li v-for="(item, index) in names">
      {{index+1}}.{{item}}
    </li>
  </ul>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      names: ['why', 'kobe', 'james', 'curry']
    }
  })
</script>
</body>
</html>

v-for遍历对象

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <!--1.在遍历对象的过程中, 如果只是获取一个值, 那么获取到的是value-->
  <ul>
    <li v-for="item in info">{{item}}</li>
  </ul>

  <!--2.获取key和value 格式: (value, key) -->
  <ul>
    <li v-for="(value, key) in info">{{value}}-{{key}}</li>
  </ul>

  <!--3.获取key和value和index 格式: (value, key, index) -->
  <ul>
    <li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}</li>
  </ul>
</div>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      info: {
        name: 'why',
        age: 18,
        height: 1.88
      }
    }
  })
</script>
</body>
</html

v-for绑定和非绑定key的区别

官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性。这个其实和Vue的虚拟DOM的Diff算法有关系。当某一层有很多相同的节点时,也就是列表节点时,比如ABCDE,我们希望插入一个新的节点,我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的。即把C更新成F,D更新成C,E更新成D,最后再插入E,很没有效率所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。所以一句话,key的作用主要是为了高效的更新虚拟DOM。

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

数组的响应式方法有哪些

数组方法

可以响应式

data:{
    letters: ['a', 'b', 'c', 'd']
}
  • push方法,可以一次性添加多个值,类似ES6可变参数语法
this.letters.push('aaa') 
this.letters.push('aaaa', 'bbbb', 'cccc') 
  • pop(): 删除数组中的最后一个元素
this.letters.pop();
  • shift(): 删除数组中的第一个元素
this.letters.shift();
  • unshift(): 在数组最前面添加元素
this.letters.unshift()
this.letters.unshift('aaa', 'bbb', 'ccc')
  • splice
// 删除元素: 第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素)
// 替换元素: 第二个参数, 表示我们要替换几个元素, 后面是用于替换前面的元素
// 插入元素: 第二个参数, 传入0, 并且后面跟上要插入的元素
  • sort() 排序
this.letters.sort()
  • reverse() 翻转
this.letters.reverse()

不能响应式

this.letters[0] = 'bbbbbb';//通过下标不能响应式修改

this.letters.splice(0, 1, 'bbbbbb')//可以响应式

//set(要修改的对象, 索引值, 修改后的值)
Vue.set(this.letters, 0, 'bbbbbb')//可以响应式

ES6可变参数语法

function sum(...num) {
  console.log(num);
 }
 sum(20, 30, 40, 50, 601, 111, 122, 33)

过滤器

格式化展示数据,把展示的数据快速的展示成我们想要的内容

vue2x的时候,vue取消了内置过滤器,如果想使用过滤器就必须自定义过滤器

全局过滤器

在所有vue实例都可以使用的叫全局过滤器,必须定义在实例之前

//全局过滤器的定义方法  位置:创建实例之前
Vue.filter(‘sum’, function(val){
    	return val + 4;
});

<body>
<!-- 全局过滤器
    写在实例之前
    Vue.filter("过滤器的名字随便写的",(你要过滤的数据会自动传入)=>{
        return  逻辑
    })
-->
        <div id="demodiv">
            aaaaaaaaa
            <h1>{{text|xiaoming}}</h1>
        </div>
        <div id="demodivb">
            bbbbbbbbbbb
            {{text|xiaoming}}
            <h1>{{num|xiaohong}}</h1>
        </div>
        <script>
            Vue.filter("xiaoming",(val)=>{
                return "《《"+val+"》》"
            })
            Vue.filter("xiaohong",(val)=>{
                return "¥"+val
            })
            new Vue({
                el:"#demodiv",
                data:{
                  text:"demodiv"
                                }
            })
            new Vue({
                el:"#demodivb",
                data:{
                    text:"demodivb",
                  num:19.9
                }
            })
        </script>
</body>

局部过滤器

只能在一个指定vue实例中使用的叫局部过滤器,定义在指定vue实例中,与el同级。

只能在当前vue注册内容中使用
在vue实例中与el属性data属性同级定义
filters:{
过滤器名字:function(val){
 return 输出内容
   }
}
<body>
        <div id="demodiv">
            aaaaaaaaa
            <h1>{{text|xiaoming}}</h1>
            <h1>{{textb|xiaoming}}</h1>
        </div>


        <div id="demodivb">
            bbbbbbbbbbb
            {{text}}
        </div>
        <script>

            new Vue({
                el:"#demodiv",
                data:{
                  text:"demodiv",
                  textb:"abcdefgbhjfhf"
                },
                filters:{
                    xiaoming:(val)=>{
                        return val.substr(0,4)+"..."
                    }
                }
            })

            new Vue({
                el:"#demodivb",
                data:{
                    text:"demodivb"
                }
            })
        </script>
</body>

v-model 双向绑定

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

v-model的基本使用

<body>

<div id="app">
  <input type="text" v-model="message">
  {{message}}
</div>

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

</body>

v-model双向绑定的原理

v-model其实是一个语法糖,它的背后本质上是包含两个操作

  • 1 v-bind绑定一个value属性
  • 2 v-on指令给当前元素绑定input事件
  • input事件用来动态监听用户输入东西的行为
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <!-- 1-可以这样做 -->
  <input type="text" v-model="message">
  <input type="text" :value="message" @input="valueChange">
  <!-- 2-也可以这样写,不用写方法,写表达式 -->
  <input type="text" :value="message" @input="message = $event.target.value">
  <h2>{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    methods: {
      valueChange(event) {
        this.message = event.target.value;
      }
    }
  })
</script>
</body>
</html>

v-model结合radio使用

  • v-model和radio结合,可以将radio对应的内容,动态的赋值给v-model绑定的变量
  • name可以省略
  • 可以先给变量一个展示的值,默认显示的就是这个内容
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <label for="male">
    <input type="radio" id="male" value="男" v-model="sex">男
  </label>
  <label for="female">
    <input type="radio" id="female" value="女" v-model="sex">女
  </label>
  <h2>您选择的性别是: {{sex}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      sex: '女'
    }
  })
</script>
</body>
</html>

v-model结合checkbox使用

单个勾选框

  • v-model即为布尔值。
  • 此时input的value并不影响v-model的值。
  • 一个变量可以控制两个东西的状态
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
 <label for="agree">
    <input type="checkbox" id="agree" v-model="isAgree">同意协议
 </label>
  <h2>您选择的是: {{isAgree}}</h2>
  <button :disabled="!isAgree">下一步</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      isAgree: false, 
    }
  })
</script>
</body>
</html>

多个勾选框

  • 当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组。
  • 当选中某一个时,就会将input的value添加到数组中。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <input type="checkbox" value="篮球" v-model="hobbies">篮球
  <input type="checkbox" value="足球" v-model="hobbies">足球
  <input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
  <input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
  <h2>您的爱好是: {{hobbies}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      hobbies: [], // 多选框,
    }
  })
</script>

</body>
</html>

v-model结合select使用

单选

  • 只选中一个值。
  • v-model绑定的是一个值。
  • 当我们选中option中的一个时,会将它对应的value赋值到mySelect中
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <!--1.选择一个-->
  <select name="abc" v-model="fruit">
    <option value="苹果">苹果</option>
    <option value="香蕉">香蕉</option>
    <option value="榴莲">榴莲</option>
    <option value="葡萄">葡萄</option>
  </select>
  <h2>您选择的水果是: {{fruit}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      fruit: '香蕉',
    }
  })
</script>

</body>
</html>

多选

  • 选中多个值。
  • v-model绑定的是一个数组。
  • 当选中多个值时,就会将选中的option对应的value添加到数组mySelects中
  • 给select添加multiple可以选中多个
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <!--2.选择多个-->
  <select name="abc" v-model="fruits" multiple>
    <option value="苹果">苹果</option>
    <option value="香蕉">香蕉</option>
    <option value="榴莲">榴莲</option>
    <option value="葡萄">葡萄</option>
  </select>
  <h2>您选择的水果是: {{fruits}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      fruits: []
    }
  })
</script>
</body>
</html>

v-model修饰符的使用

lazy修饰符

  • 默认情况下,v-model默认是在input事件中同步输入框的数据的。
  • 也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变。
  • lazy修饰符可以让数据在失去焦点或者回车时才会更新

number修饰符

  • 默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。
  • 但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
  • number修饰符可以让在输入框中输入的内容自动转成数字类型

trim修饰符

  • 如果输入的内容首尾有很多空格,通常我们希望将其去除
  • trim修饰符可以过滤内容左右两边的空格
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <!--1.修饰符: lazy-->
  <input type="text" v-model.lazy="message">
  <h2>{{message}}</h2>

  <!--2.修饰符: number-->
  <input type="number" v-model.number="age">
  <h2>{{age}}-{{typeof age}}</h2>
    
  <!--3.修饰符: trim-->
  <input type="text" v-model.trim="name">
  <h2>您输入的名字:{{name}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      age: 0,
      name: ''
    }
  })
  var age = 0
  age = '1111'
  age = '222'
</script>
</body>
</html>

组件化简介

为什么有组件化

  • 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
  • 但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。
  • 组件化是Vue.js中的重要思想
  • 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。
  • 任何的应用都会被抽象成一颗组件树。

组件化的好处

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

注册组件的三个步骤

  1. 创建组件构造器
  2. 注册组件
  3. 使用组件。
  • Vue.extend():

调用Vue.extend()创建的是一个组件构造器。 通常在创建组件构造器时,传入template代表我们自定义组件的模板。该模板就是在使用到组件的地方,要显示的HTML代码。

  • Vue.component():

调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。所以需要传递两个参数:1、注册组件的标签名 2、组件构造器

  • 组件必须挂载在某个Vue实例下,否则它不会生效。

组件化基础开发

组件的基本使用

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

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



<script src="../js/vue.js"></script>
<script>
  // 1.创建组件构造器对象
  const cpnC = Vue.extend({
    template: `
      <div>
        <h2>我是标题</h2>
        <p>我是内容, 哈哈哈哈</p>
        <p>我是内容, 呵呵呵呵</p>
      </div>`
  })

  // 2.注册组件
  Vue.component('my-cpn', cpnC)

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

全局组件和局部组件

全局组件

通过调用Vue.component()注册组件时,组件的注册是全局的,这意味着该组件可以在任意Vue示例下使用。

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

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

<script src="../js/vue.js"></script>
<script>
  // 1.创建组件构造器
  const cpnC = Vue.extend({
    template: `
      <div>
        <h2>我是标题</h2>
        <p>我是内容,哈哈哈哈啊</p>
      </div>
    `
  })
  // 2 注册局部组件
   Vue.component('cpn', cpnC)

  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
  })

</script>
</body>
</html>

局部组件

如果注册的组件是挂载在某个实例中, 那么就是一个局部组件

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

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

<script src="../js/vue.js"></script>
<script>
  // 1.创建组件构造器
  const cpnC = Vue.extend({
    template: `
      <div>
        <h2>我是标题</h2>
        <p>我是内容,哈哈哈哈啊</p>
      </div>
    `
  })


  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
     // 2注册局部组件
    components: {
      // cpn使用组件时的标签名
      cpn: cpnC
    }
  })

</script>
</body>
</html>

父组件和子组件

  • 组件和组件之间存在层级关系
  • 而其中一种非常重要的关系就是父子组件的关系
  • 最大的组件可以理解为是通过vue实例创建出来的对象,挂载的那个元素,也可以一个看成最顶层的一个组件
  • 在父组件里定义的子组件只能在该组件里面使用
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <cpn2></cpn2>
  <!--<cpn1></cpn1>-->
</div>

<script src="../js/vue.js"></script>
<script>
  // 1.创建第一个组件构造器(子组件)
  const cpnC1 = Vue.extend({
    template: `
      <div>
        <h2>我是标题1</h2>
        <p>我是内容, 哈哈哈哈</p>
      </div>
    `
  })


  // 2.创建第二个组件构造器(父组件)
  const cpnC2 = Vue.extend({
      //在父组件模板里面使用子组件,<cpn1></cpn1>
    template: `
      <div>
        <h2>我是标题2</h2>
        <p>我是内容, 呵呵呵呵</p>
        <cpn1></cpn1>
      </div>
    `,
      //在父组件里面注册子组件
    components: {
      cpn1: cpnC1
    }
  })

  // 根组件
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn2: cpnC2
    }
  })
</script>
</body>
</html>

组件的语法糖注册方式

  • 注册组件有点麻烦,Vue为了简化这个过程,提供了注册的语法糖。
  • 主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

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

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

  // 正常创建组件构造器
  // const cpn1 = Vue.extend({
    template: `
      <div>
        <h2>我是标题1</h2>
        <p>我是内容, 哈哈哈哈</p>
      </div>
    `
  }
    
    )

  //语法糖分别注册全局组件和局部组件
  // 1 注册全局组件,
  Vue.component('cpn1', {
    template: `
      <div>
        <h2>我是标题1</h2>
        <p>我是内容, 哈哈哈哈</p>
      </div>
    `
  })

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

组件模板的分离写法

如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰。

Vue提供了两种方案来定义HTML模块内容

使用script标签

搞一个id,注册组件的时候放对应的id,:类型必须是text/x-template

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

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

<!--script标签, 注意:类型必须是text/x-template-->
<script type="text/x-template" id="cpn">
<div>
  <h2>我是标题</h2>
  <p>我是内容,哈哈哈</p>
</div>
</script>

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

  // 1.注册一个全局组件
  Vue.component('cpn', {
    template: '#cpn'
  })

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

</body>
</html>

使用template标签

搞一个id,注册组件的时候放对应的id

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

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


<!--template标签-->
<template id="cpn">
  <div>
    <h2>我是标题</h2>
    <p>我是内容,呵呵呵</p>
  </div>
</template>

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

  // 1.注册一个全局组件
  Vue.component('cpn', {
    template: '#cpn'
  })

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

组件中数据存放的问题

  • 组件是一个单独功能模块的封装
  • 这个模块有属于自己的HTML模板,也应该有属性自己的数据data
  • Vue组件应该有自己保存数据的地方
  • 组件对象也有一个data属性,也可以有methods等属性
  • 只是这个data属性必须是一个函数
  • 而且这个函数返回一个对象,对象内部保存着数据

组件中的data为什么是函数?

  • 首先,如果不是一个函数,Vue直接就会报错。

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

  • 当一个组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。

  • 如果 data 是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!

  • 通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。

  • 函数在每次调用的时候会重新创建,所以data是函数,以后每次用组件的时候,数据都是单独绑定的,不会相互影响。

  • 如果像下面这样写,直接定义一个对象放在return后面,那么每次调用组件,用的都是同一个数据,改一个就会全部改动

    const obj = {
      counter: 0
    }
    

// 注册组件
Vue.component(‘cpn’, {
template: ‘#cpn’,
// data() {
// return {
// counter: 0
// }
// },
data() {
return obj
},
methods: {
increment() {
this.counter++
},
decrement() {
this.counter–
}
}
})

const app = new Vue({
el: ‘#app’,
data: {
message: ‘你好啊’
}
})

# 父子组件的通信

子组件是不能引用父组件或者Vue实例的数据的。但是,在开发中,往往一些数据确实需要从上层传递到下层:比如在一个页面中,我们从服务器请求到了很多的数据。其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。如何进行父子组件间的通信呢?Vue官方提到

- 通过props向子组件传递数据(properties-属性的意思)
- 通过事件向父组件发送消息

## 父组件向子组件传递数据props

### props第一种用法:props直接用

1. 字符串数组,数组中的字符串就是传递时的名称 

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn v-bind:cmovies="movies"></cpn>
</div>
<!--子组件-->
<template id="cpn">
<div>
  <ul>
    <li v-for="item in cmovies">{{item}}</li>
  </ul>
  <h2>{{cmessage}}</h2>
</div>
</template>

<script src="../js/vue.js"></script>
<script>
// 父传子: 在子组件中用props定义接受
const cpn = {
  template: '#cpn',
  props: ['cmovies', 'cmessage'],
  },
  data() {
    return {}
  },
}
//父组件
const app = new Vue({
  el: '#app',
  data: {
    message: '你好啊',
    movies: ['海王', '海贼王', '海尔兄弟']
  },
  components: {
    cpn
  }
})
</script>

</body>
</html>

props第二种用法:props数据验证

  1. 对象,对象可以设置传递时的类型,也可以设置默认值。
  2. 当需要对props进行类型等验证时,就需要对象写法了。
  3. 通过type来设置数据类型
  4. 可以通过default: ‘aaaaaaaa’,来设置默认值,当父组件没有传值的时候显示默认值。
  5. 加上required为必须传递的值,不传就会报错。
  6. 设置默认值的时候,当type类型是对象或者数组时, 默认值default后面必须是一个函数(如果不是函数,vue2.5之前没事,之后会报错)
  7. 当我们有自定义构造函数时,验证也支持自定义的类型
  8. 验证都支持哪些数据类型呢?
  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
<body>
<div id="app">
  <cpn :cmessage="message" :cmovies="movies"></cpn>
</div>
    
<template id="cpn">
  <div>
    <ul>
      <li v-for="item in cmovies">{{item}}</li>
    </ul>
    <h2>{{cmessage}}</h2>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  // 父传子: props
  const cpn = {
    template: '#cpn',
    // props: ['cmovies', 'cmessage'],
    props: {
      // 1.类型限制
      // cmovies: Array,
      // cmessage: String,

      // 2.提供一些默认值, 以及必传值
      cmessage: {
        type: String,
        default: 'aaaaaaaa',
        required: true
      },
      // 类型是对象或者数组时, 默认值必须是一个函数
      cmovies: {
        type: Array,
        default() {
          return []
        }
      }
    },
    data() {
      return {}
    },
    methods: {

    }
  }

  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      movies: ['海王', '海贼王', '海尔兄弟']
    },
    components: {
      cpn
    }
  })
</script>
</body>

父传子(props中的驼峰标识)

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

<div id="app">
  <cpn :c-info="info" :child-my-message="message" v-bind:class></cpn>
</div>

<template id="cpn">
  <div>
    <h2>{{cInfo}}</h2>
    <h2>{{childMyMessage}}</h2>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  const cpn = {
    template: '#cpn',
    props: {
      cInfo: {
        type: Object,
        default() {
          return {}
        }
      },
      childMyMessage: {
        type: String,
        default: ''
      }
    }
  }

  const app = new Vue({
    el: '#app',
    data: {
      info: {
        name: 'why',
        age: 18,
        height: 1.88
      },
      message: 'aaaaaa'
    },
    components: {
      cpn
    }
  })
</script>

</body>
</html>

子组件向父组件传递数据,自定义事件

  1. 什么时候需要自定义事件呢
  • 当子组件需要向父组件传递数据时,就要用到自定义事件了。

  • v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。

    2.自定义事件的流程

  • 在子组件中,通过$emit()来触发事件。

  • 在父组件中,通过v-on来监听子组件事件。

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

<!--父组件模板-->
<div id="app">
    <!--以前的事件不传参数,默认把事件对象传递进去,现在不传参数默认把子组件发送过来的数据传递进去,需要在定义的时候在参数里接受-->
  <cpn @item-click="cpnClick"></cpn>
</div>

<!--子组件模板-->
<template id="cpn">
  <div>
    <button v-for="item in categories"
            @click="btnClick(item)">
      {{item.name}}
    </button>
  </div>
</template>

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

  // 1.子组件
  const cpn = {
    template: '#cpn',
    data() {
      return {
        categories: [
          {id: 'aaa', name: '热门推荐'},
          {id: 'bbb', name: '手机数码'},
          {id: 'ccc', name: '家用家电'},
          {id: 'ddd', name: '电脑办公'},
        ]
      }
    },
    methods: {
      btnClick(item) {
        // 发射事件: 自定义事件
        this.$emit('item-click', item)
      }
    }
  }

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

父子组件直接访问方式

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

父组件访问子组件:使用$children或$refs reference(引用)

子组件访问父组件:使用$parent

父访问子-$children

数组类型,取值需要用下标取,不太方便

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

<div id="app">
  <cpn></cpn>
  <cpn></cpn>
    
  <my-cpn></my-cpn>
  <y-cpn></y-cpn>
    
  <cpn></cpn>
    
  <button @click="btnClick">按钮</button>
</div>

<template id="cpn">
  <div>我是子组件</div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    methods: {
      btnClick() {
        // $children 数组类型
        console.log(this.$children);
          
         for (let c of this.$children) {
           console.log(c.name);
           c.showMessage();
         }
         console.log(this.$children[3].name);
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            name: '我是子组件的name'
          }
        },
        methods: {
          showMessage() {
            console.log('showMessage');
          }
        }
      },
    }
  })
</script>

</body>
</html>

父访问子-$refs

是一个对象类型,必须在组件上加上ref属性,随便等于一个值,比如aaa,通过this.$refs.aaa,就可以拿到这个对象,再通过对象打点数据的名字,就可以拿到子组件的数据。

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

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

  <my-cpn></my-cpn>
  <y-cpn></y-cpn>

  <cpn ref="aaa"></cpn>
  <button @click="btnClick">按钮</button>
</div>

<template id="cpn">
  <div>我是子组件</div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    methods: {
      btnClick() {
        // $refs => 对象类型, 默认是一个空的对象 ref='bbb'
        console.log(this.$refs.aaa.name);
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            name: '我是子组件的name'
          }
        },
        methods: {
          showMessage() {
            console.log('showMessage');
          }
        }
      },
    }
  })
</script>
</body>
</html>

子访问父-$parent

如果我们想在子组件中直接访问父组件,可以通过$parent
注意事项:
尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

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

<template id="cpn">
  <div>
    <h2>我是cpn组件</h2>
    <ccpn></ccpn>
  </div>
</template>

<template id="ccpn">
  <div>
    <h2>我是子组件</h2>
    <button @click="btnClick">按钮</button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            name: '我是cpn组件的name'
          }
        },
        components: {
          ccpn: {
            template: '#ccpn',
            methods: {
              btnClick() {
                //访问父组件$parent
                 console.log(this.$parent);
                 console.log(this.$parent.name);
              }
            }
          }
        }
      }
    }
  })
</script>

</body>
</html>

访问根组件-$root

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

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

<template id="cpn">
  <div>
    <h2>我是cpn组件</h2>
    <ccpn></ccpn>
  </div>
</template>

<template id="ccpn">
  <div>
    <h2>我是子组件</h2>
    <button @click="btnClick">按钮</button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            name: '我是cpn组件的name'
          }
        },
        components: {
          ccpn: {
            template: '#ccpn',
            methods: {
              btnClick() {
                // 访问根组件$root
                console.log(this.$root);
                console.log(this.$root.message);
              }
            }
          }
        }
      }
    }
  })
</script>
</body>
</html>

slot插槽

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

slot-插槽的基本使用

1.插槽的基本使用 <slot></slot>
2.插槽的默认值 <slot>button</slot>
3.如果有多个值, 同时放入到组件进行替换时, 一起作为替换元素
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>


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

  <cpn><span>哈哈哈</span></cpn>
  <cpn><i>呵呵呵</i></cpn>
  <cpn>
    <i>呵呵呵</i>
    <div>我是div元素</div>
    <p>我是p元素</p>
  </cpn>

  <cpn></cpn>
  <cpn></cpn>
  <cpn></cpn>
  <cpn></cpn>
</div>


<template id="cpn">
  <div>
    <h2>我是组件</h2>
    <p>我是组件, 哈哈哈</p>
    <slot><button>按钮</button></slot>
    <!--<button>按钮</button>-->
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn: {
        template: '#cpn'
      }
    }
  })
</script>

</body>
</html>

slot-具名插槽的使用

 <slot name="left"><span>左边</span></slot>
 <cpn><button slot="left">返回</button></cpn>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <cpn><span slot="center">标题</span></cpn>
  <cpn><button slot="left">返回</button></cpn>
</div>


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

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn: {
        template: '#cpn'
      }
    }
  })
</script>
</body>
</html>

什么是编译的作用域

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

isShow这个变量在父子组件都有,不过父组件只会去父组件的作用域去找,子组件只会去子组件的作用域去找

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

<div id="app">
  <cpn v-show="isShow"></cpn>
  <cpn v-for="item in names"></cpn>
</div>

<template id="cpn">
  <div>
    <h2>我是子组件</h2>
    <p>我是内容, 哈哈哈</p>
    <button v-show="isShow">按钮</button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      isShow: true
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            isShow: false
          }
        }
      },
    }
  })
</script>
</body>
</html>

作用域插槽

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

在子组件绑定属性
<slot :data="pLanguages">
在父组件使用我们的子组件时,从子组件中拿到数据:
1-先用template标签包裹住,2.5版本后可以使用任意标签
2-我们通过<template slot-scope="slotProps">获取到slotProps属性
3-在通过slotProps.data就可以获取到刚才我们传入的data了
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <cpn></cpn>
  <cpn>
    <!--2-目的是获取子组件中的pLanguages-->
    <template slot-scope="slot">
      <!--<span v-for="item in slot.data"> - {{item}}</span>-->
      <span>{{slot.data.join(' - ')}}</span>
    </template>
  </cpn>

  <cpn>
    <!--2-目的是获取子组件中的pLanguages-->
    <template slot-scope="slot">
      <!--<span v-for="item in slot.data">{{item}} * </span>-->
      <span>{{slot.data.join(' * ')}}</span>
    </template>
  </cpn>
  <!--<cpn></cpn>-->
</div>

<template id="cpn">
  <div>
      <!--1-把子组件的pLanguages绑定到任意属性上,属性名可以随便起-->
    <slot :data="pLanguages">
      <ul>
        <li v-for="item in pLanguages">{{item}}</li>
      </ul>
    </slot>
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            pLanguages: ['JavaScript', 'C++', 'Java', 'C#', 'Python', 'Go', 'Swift']
          }
        }
      }
    }
  })
</script>
</body>
</html>

ES6的模块化实现

通过模块化的方式开发,type属性等于这个

<script src="aaa.js" type="module"></script>

导出/导入方式-基本使用

导入接受的名字和导出的要一样

//文件名是aaa.js
var name = '小明'
var age = 18
var flag = true

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

if (flag) {
  console.log(sum(20, 30));
}

// 1.导出方式一:
export {
  flag, sum
}

// 1.导入的{}中定义的变量
//导入接受的名字和导出的要一样
import {flag, sum} from "./aaa.js";

导出/导入方式-直接导出变量

导入接受的名字和导出的要一样

// 2.导出方式二:
export var num1 = 1000;
export var height = 1.88
// 2.直接导入export定义的变量
import {num1, height} from "./aaa.js";
console.log(num1);
console.log(height);

导出/导入方式-导出函数/类

导入接受的名字和导出的要一样

// 3.导出函数/类
export function mul(num1, num2) {
  return num1 * num2
}

export class Person {
  run() {
    console.log('在奔跑');
  }
}
// 3.导入 export的function/class
import {mul, Person} from "./aaa.js";

console.log(mul(30, 50));

const p = new Person();
p.run()

导出/导入方式-export default

  • 一个模块中包含某个的功能,我们并不希望给这个功能命名,而且让导入者可以自己来命名 ,这个时候就可以使用export default
  • pexport default在同一个模块中,不允许同时存在多个。
export default function (argument) {
  console.log(argument);
}
// 4.导入 export default中的内容
import addr from "./aaa.js";
addr('你好啊');

导出/导入方式-全部导入*

// 5.统一全部导入
// import {flag, num, num1, height, Person, mul, sum} from "./aaa.js";
import * as aaa from './aaa.js'
console.log(aaa.flag);
console.log(aaa.height);

Webpack

什么是webpack?
这个webpack还真不是一两句话可以说清楚的。
我们先看看官方的解释:
At its core, webpack is a static module bundler for modern JavaScript applications. 
从本质上来讲,webpack是一个现代的JavaScript应用的静态模块打包工具。
但是它是什么呢?用概念解释概念,还是不清晰。
我们从两个点来解释上面这句话:模块 和 打包

前端模块化的一些方案:AMD、CMD、CommonJS、ES6。
在ES6之前,我们要想进行模块化开发,就必须借助于其他的工具,让我们可以进行模块化开发。
并且在通过模块化开发完成了项目后,还需要处理模块间的各种依赖,并且将其进行整合打包。
而webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系。
而且不仅仅是JavaScript文件,我们的CSS、图片、json文件等等在webpack中都可以被当做模块来使用(在后续我们会看到)。
这就是webpack中模块化的概念。

打包如何理解呢?
理解了webpack可以帮助我们进行模块化,并且处理模块间的各种复杂关系后,打包的概念就非常好理解了。
就是将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)。
并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。
但是打包的操作似乎grunt/gulp也可以帮助我们完成,它们有什么不同呢?
grunt/gulp的核心是Task
我们可以配置一系列的task,并且定义task要处理的事务(例如ES6、ts转化,图片压缩,scss转成css)
之后让grunt/gulp来依次执行这些task,而且让整个流程自动化。
所以grunt/gulp也被称为前端自动化任务管理工具。

什么时候用grunt/gulp呢?
如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念。
只需要进行简单的合并、压缩,就使用grunt/gulp即可。

但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack了。

所以,grunt/gulp和webpack有什么不同呢?
grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心。
webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能。
安装webpack首先需要安装Node.js,Node.js自带了软件包管理工具npm
查看自己的node版本:
node -v

全局安装webpack(这里我先指定版本号3.6.0,因为vue cli2依赖该版本)
npm install webpack@3.6.0 -g

局部安装webpack(后续才需要)
--save-dev`是开发时依赖,项目打包后不需要继续使用的。
cd 对应目录
npm install webpack@3.6.0 -g --save-dev

为什么全局安装后,还需要局部安装呢?
在终端直接执行webpack命令,使用的全局安装的webpack
当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack



Vue-router

路由模式-URL的hash

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

路由模式-HTML5的history模式

history.pushState()

//是将每个添加的地址压入的栈里面,现进后出,前进后退网页类似于进出栈的操作
history.pushState({},"","/home")
history.pushState({},"","/abt")
history.pushState({},"","/demmt")

history.replaceState ()

//直接替换地址,不能返回
history.replaceState ({},"","/home")

history.go()

history.go(-1) //后退一个
history.go(0) //原地刷新
history.go(1) //向前走一个

history.back()

history.back() 等价于 history.go(-1) 

history.forward()

history.forward() 则等价于 history.go(1)

Vue-router安装配置

在router中创建index.js,在里面配置

// 配置路由相关的信息
import VueRouter from 'vue-router'
import Vue from 'vue'

import Home from '../components/Home'
import About from '../components/About'

// 1.通过Vue.use(插件), 安装插件
Vue.use(VueRouter)

// 2.创建VueRouter对象
const routes = []
const router = new VueRouter({
  // 配置路由和组件之间的应用关系
  routes,
  mode: 'history',
  linkActiveClass: 'active'
})

// 3.将router对象传入到Vue实例
export default router

在main.js中

import Vue from 'vue'
import App from './App'
//会自动找到index.js的文件,可以不用写后面的目录
import router from './router' 

Vue.config.productionTip = false

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

Vue-router路由映射配置-加重定向

// 配置路由相关的信息
import VueRouter from 'vue-router'
import Vue from 'vue'

import Home from '../components/Home'
import About from '../components/About'

// 1.通过Vue.use(插件), 安装插件
Vue.use(VueRouter)

// 2.创建VueRouter对象
const routes = [
  {
    path: '',
    // redirect重定向
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]
const router = new VueRouter({
  // 配置路由和组件之间的应用关系
  routes,
  mode: 'history',
  linkActiveClass: 'active'
})

// 3.将router对象传入到Vue实例
export default router

展示配置

<template>
  <div id="app">
    <router-link to="/home" >首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-view></router-view>
  </div>
</template>
<router-link>: 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个<a>标签.
<router-view>: 该标签会根据当前的路径, 动态渲染出不同的组件.
网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和<router-view>处于同一个等级.
在路由切换时, 切换的是<router-view>挂载的组件, 其他内容不会发生改变.

修改路由模式

修改mode属性值就好

const router = new VueRouter({
  // 配置路由和组件之间的应用关系
  routes,
  mode: 'history',
  linkActiveClass: 'active'
})

router-link其他属性

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

修改路由活跃的类名,在index.js中修改

const router = new VueRouter({
  // 配置路由和组件之间的应用关系
  routes,
  mode: 'history',
  linkActiveClass: 'active'//修改类名
})

通过代码跳转路由

<template>
  <div id="app">
    <button @click="homeClick">首页</button>
    <button @click="aboutClick">关于</button>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    homeClick() {
      // 通过代码的方式修改路由 vue-router
      // push => pushState
      // this.$router.push('/home')
      this.$router.replace('/home')
      console.log('homeClick');
    },
    aboutClick() {
      // this.$router.push('/about')
      this.$router.replace('/about')
      console.log('aboutClick');
    }
  }
}
</script>

Vue-router动态路由的使用

在某些情况下,一个页面的path路径可能是不确定的,比如我们进入用户界面时,希望是如下的路径:
/user/aaaa或/user/bbbb
除了有前面的/user之外,后面还跟上了用户的ID
这种path和Component的匹配关系,我们称之为动态路由(也是路由传递数据的一种方式)。
{
    path: '/user/:id',
    component: User,
  },
 <router-link :to="'/user/'+userId">用户</router-link>
 <script>
   data() {
    return {
      userId: 'zhangsan',
    }
  }
 </script>
  userId() {
        return this.$route.params.id
    }

路由懒加载

结合Vue的异步组件和Webpack的代码分析

const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })};

AMD写法

const About = resolve => require(['../components/About.vue'], resolve);

ES6

const Home = () => import('../components/Home.vue')

路由的嵌套

{
    path: '/home',
    component: Home,
    meta: {
      title: '首页'
    },
    children: [
      // {
      //   path: '',
      //   redirect: 'news'
      // },
      {
        path: 'news',
        component: HomeNews
      },
      {
        path: 'message',
        component: HomeMessage
      }
    ]
  },
    <router-link to="/home/news">新闻</router-link>
    <router-link to="/home/message">消息</router-link>

    <router-view></router-view>

参数传递

传递参数主要有两种类型: params和query
params的类型:
配置路由格式: /router/:id
传递的方式: 在path后面跟上对应的值
传递后形成的路径: /router/123, /router/abc

query的类型:
配置路由格式: /router, 也就是普通配置
传递的方式: 对象中使用query的key作为传递方式
传递后形成的路径: /router?id=123, /router?id=abc

导航守卫

// 配置路由相关的信息
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 Home = () => import('../components/Home')
const HomeNews = () => import('../components/HomeNews')
const HomeMessage = () => import('../components/HomeMessage')

const About = () => import('../components/About')
const User = () => import('../components/User')
const Profile = () => import('../components/Profile')

// 1.通过Vue.use(插件), 安装插件
Vue.use(VueRouter)

// 2.创建VueRouter对象
const routes = [
  {
    path: '',
    // redirect重定向
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home,
    meta: {
      title: '首页'
    },
    children: [
      // {
      //   path: '',
      //   redirect: 'news'
      // },
      {
        path: 'news',
        component: HomeNews
      },
      {
        path: 'message',
        component: HomeMessage
      }
    ]
  },
  {
    path: '/about',
    component: About,
    meta: {
      title: '关于'
    },
    beforeEnter: (to, from, next) => {
      // console.log('about beforeEnter');
      next()
    }
  },
  {
    path: '/user/:id',
    component: User,
    meta: {
      title: '用户'
    },
  },
  {
    path: '/profile',
    component: Profile,
    meta: {
      title: '档案'
    },
  }
]
const router = new VueRouter({
  // 配置路由和组件之间的应用关系
  routes,
  mode: 'history',
  linkActiveClass: 'active'
})

// 前置守卫(guard)
router.beforeEach((to, from, next) => {
  // 从from跳转到to
  document.title = to.matched[0].meta.title
  // console.log(to);
  // console.log('++++');
  next()
})

// 后置钩子(hook)
router.afterEach((to, from) => {
  // console.log('----');
})

// 3.将router对象传入到Vue实例
export default router


keep-alive

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
它们有两个非常重要的属性:
include - 字符串或正则表达,只有匹配的组件会被缓存
exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存
router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:
通过create声明周期函数来验证
 <keep-alive exclude="Profile,User">
      <router-view/>
 </keep-alive>
这篇关于vue笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!