Vue基础知识大全

一、Vue 介绍

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

1.1 MVVM

二、安装Vue

2.1 使用CDN引入

对于制作原型或学习,你可以这样使用最新版本:

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

对于生产环境,我们推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>

如果你使用原生 ES Modules,这里也有一个兼容 ES Module 的构建文件:

<script type="module">
  import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.esm.browser.js'
</script>

你可以在 cdn.jsdelivr.net/npm/vue 浏览 NPM 包的源代码。

Vue 也可以在 unpkgcdnjs 上获取 (cdnjs 的版本更新可能略滞后)。

请确认了解不同构建版本并在你发布的站点中使用生产环境版本,把 vue.js 换成 vue.min.js。这是一个更小的构建,可以带来比开发环境下更快的速度体验。

2.2 下载引入

2.3 NPM

三、Vue 使用入门

3.1 Vue中的options

  • el:

    类型:string | HTMLElement
    作用:决定之后Vue实例会管理哪一个DOM
    
  • data:

    类型:Object | Function (组件当中data必须是一个函数)
    作用:Vue实例对应的数据对象。
    
  • methods:

    类型:{ [key: string]: Function }
    作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。
    
  • computed

    计算属性,实现data中一些数据的整理运算,详细看3.4小节
    
  • filters

    过滤器,书写与methods类似,使用时在变量后面加 ‘|’ ,Vue会把前面的值传入到后面的过滤器中
    
    <h3>Total Price: {{totalPrice | showTwoDecimal}} 元</h3>
    
    // 实现价格保留2位小数
    const app = new Vue({
    	data:{
    		totalPrice:58
    	},
        filters:{
            showTwoDecimal(price){
                return '¥' + price.toFixed(2)
            }
        }
    })
    
    

3.2 Vue 生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-inCkw49z-1645673949244)(Z:\Study Note\Typora笔记图库\image-20211206205538362.png)]

3.3 指令

1. 文本内容插值操作

  • mustache(双大括号)

    • 简单的表达式操作(拼接、倍乘、除法、减法)

      <!--mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式-->
      <h2>{{firstName + lastName}}</h2>
      <h2>{{firstName + ' ' + lastName}}</h2>
      <h2>{{firstName}} {{lastName}}</h2>
      <h2>{{counter * 2}}</h2>
      
      <script src="../js/vue.js"></script>
      data: {
          firstName: 'kobe',
          lastName: 'bryant',
          counter: 100
      }
      
  • v-once

    • 只渲染第一次后,不受data中的数据影响

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

    • 可以对data中的数据,在携带有标签时进行html渲染,不会单独的显示字符串内容。

      <h2 v-html="baidu">{{baidu}}</h2>
      
      data:{
      	baidu:'<a href="www.baidu.com">百度</a>'
      }
      
  • v-text

    • 只会显示便是指定的值后面的值会进行覆盖

      <h2 v-text="message">, 李银河!</h2>
      
  • v-pre

    • 不进行vue的渲染,原封不动的显示标签中字符串内容

      <h2 v-pre>{{message}}</h2>
      
  • c-cloak(cloak:斗篷,解析前斗篷存在,解析后斗篷摘掉了)

    • 在vue解析前,div中v-cloak属性会存在

    • 在vue解析后,div中v-cloak属性会被删除

      # 开始时没有解析vue前,网页文本内容为空(display:none)
      # 1秒后解析了vue,v-cloak属性去掉了,故style样式就不生效了。网页出现内容
      <style>
          [v-cloak] {
              display: none;
          }
      </style>
      
      <h2>{{message}}</h2>
      
      <script>
        // 在vue解析之前, div中有一个属性v-cloak
        // 在vue解析之后, div中没有一个属性v-cloak
        setTimeout(function () {
          const app = new Vue({
            el: '#app',
            data: {
              message: '你好啊'
            }
          })
        }, 1000)
      </script>
      

2. 标签属性动态绑定操作

  • v-bind

    • 动态绑定标签的属性(基本使用)

      <img v-bind:src="imgUrl">
      
      data:{
      	imgUrl:"www.baidu.com"
      }
      
      # 语法糖写法
      <img :src="imgUrl">
      
      data:{
      	imgUrl:"www.baidu.com"
      }
      
    • 动态绑定class(对象语法)

      # class 调用的是一个对象
      <!--<h2 v-bind:class="{类名1: true, 类名2: boolean}">{{message}}</h2>-->
      <h2 class="title" v-bind:class="{active: isActive, line: isLine}">{{message}}</h2>
      
      <button v-on:click="btnClick">按钮</button>
      
      <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>
      
    • 动态绑定class(函数语法)

      # class 调用的是一个函数
      <h2 class="title" v-bind:class="getClasses()">{{message}}</h2>
          methods: {
            getClasses: function () {
              return {active: this.isActive, line: this.isLine}
            }
          }
      
    • 动态绑定class(数组语法)-- 使用较少

      <h2 class="title" :class="[active, line]">{{message}}</h2>
      <h2 class="title" :class="getClasses()">{{message}}</h2>
        
        const app = new Vue({
          el: '#app',
          data: {
            message: '你好啊',
            active: 'aaaaaa',
            line: 'bbbbbbb'
          },
          methods: {
            getClasses: function () {
              return [this.active, this.line]
            }
          }
        })
      
    • 动态绑定style

      <h2 :style="{fontSize: finalSize + 'px', backgroundColor: finalColor}">{{message}}</h2>			// 对象调用style
      
      <h2 :style="getStyles()">{{message}}</h2>  // 函数调用style
      
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            message: '你好啊',
            finalSize: 100,
            finalColor: 'red',
          },
          methods: {
            // 函数调用style
            getStyles: function () {
              return {fontSize: this.finalSize + 'px', backgroundColor: this.finalColor}
            }
          }
        })
      </script>
      
      
  • v-on

    • 事件监听,比如点击,拖拽,键盘事件等

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

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

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

      作用:绑定事件监听器
      缩写:@ (语法糖)
      预期:Function | Inline Statement | Object
      参数:event
      
    • 基础使用

      <button @click="increment">+</button>
      <button @click="decrement">-</button>
      methods: {
            increment() {
              this.counter++
            },
            decrement() {
              this.counter--
            }
          }
      
    • 带参数使用

      <div id="app">
        <!--1.事件调用的方法没有参数-->
        <button @click="btn1Click()">按钮1</button>
        <button @click="btn1Click">按钮1</button>
      
        <!--2.在事件定义时, 写方法时省略了小括号, 但是方法本身是需要一个参数的, 这个时候, Vue会默认将浏览器生产的event事件对象作为参数传入到方法-->
        <!--<button @click="btn2Click(123)">按钮2</button>-->
        <!--<button @click="btn2Click()">按钮2</button>-->
        <button @click="btn2Click">按钮2</button>
      
        <!--3.方法定义时, 我们需要event对象, 同时又需要其他参数-->
        <!-- 在调用方式, 如何手动的获取到浏览器参数的event对象: $event-->
        <button @click="btn3Click(abc, $event)">按钮3</button>
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            message: '你好啊',
            abc: 123
          },
          methods: {
            btn1Click() {
              console.log("btn1Click");
            },
            btn2Click(event) {
              console.log('--------', event);
            },
            btn3Click(abc, event) {
              console.log('++++++++', abc, event);
            }
          }
        })
      
        // 如果函数需要参数,但是没有传入, 那么函数的形参为undefined
        // function abc(name) {
        //   console.log(name);
        // }
        //
        // abc()
      </script>
      
    • v-on 修饰符

      @click.stop 阻止冒泡

      @click.prevent 把一些浏览器自动处理时修改为手动处理(例如form表单)

      @click.enter 监听某个键盘的键帽

      @click.once 只监听一次

      <div id="app">
        <!--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>
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            message: '你好啊'
          },
          methods: {
            btnClick() {
              console.log("btnClick");
            },
            divClick() {
              console.log("divClick");
            },
            submitClick() {
              console.log('submitClick');
            },
            keyUp() {
              console.log('keyUp');
            },
            btn2Click() {
              console.log('btn2Click');
            }
          }
        })
      </script>
      

在这里插入图片描述

  • v-show

    v-showv-if 的区别

      ` v-show` 当条件为false时, v-show只是给我们的元素添加一个行内样式: display: none
      
      `v-if `当条件为false时, 包含v-if指令的元素, 根本就不会存在dom中
    
    <div id="app">
      <!--v-if: 当条件为false时, 包含v-if指令的元素, 根本就不会存在dom中-->
      <h2 v-if="isShow" id="aaa">{{message}}</h2>
    
      <!--v-show: 当条件为false时, v-show只是给我们的元素添加一个行内样式: display: none-->
      <h2 v-show="isShow" id="bbb">{{message}}</h2>
    </div>
        data: {
          message: '你好啊',
          isShow: true
        }
    

3. 条件循环动态绑定操作

  • v-if / v-else / v-else-if

    • v-if 判断是否显示,传入布尔值

      <h2 v-if="isShow">
      data: {
            isShow: true
          }
      
    • v-if / v-else

      <h2 v-if="isShow">
          {{message}}
      </h2>
      <h1 v-else>isShow为false时, 显示我</h1>
      
      data: {
            message: 'isShow为true时, 显示我',
            isShow: true
          }
      
    • v-if / v-else-if / v-else

      在第一个if条件失败,会执行了第二个else-if,所有条件判断完了就会执行else

      <div id="app">
         // 方式一 通过操作使用 v-if / v-else-if / v-else 可读写不高
        <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>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            score: 99
          },
          // 方式二采用了计算属性来显示文字内容
          computed: {
            result() {
              let showMessage = '';
              if (this.score >= 90) {
                showMessage = '优秀'
              } else if (this.score >= 80) {
                showMessage = '良好'
              }
              // ...
              return showMessage
            }
          }
        })
      </script>
      
  • v-for

    • 遍历数组

      <!--1.在遍历的过程中,没有使用索引值(下标值)-->
      <ul>
          <li v-for="item in names">{{item}}</li>
      </ul>
      
      <!--2.在遍历的过程中, 获取索引值-->
      <li v-for="(item, index) in names">
          {{index+1}}.{{item}}
      </li>
      
    • 遍历对象

      <!--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>
      
      data: {
            info: {
              name: 'why',
              age: 18,
              height: 1.88
            }
          }
      
    • v-for 添加 key属性

      在没有添加key时,如果在中间插入元素,则dom会把下标的值修改为插入的值,后续的下标的值依次往后移一个位置。例如在C处添加F, 则DOM会把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?

      Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。

      总结:key的作用主要是为了高效的更新虚拟DOM

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GaO4Q8KY-1645673949246)(Z:\Study Note\Typora笔记图库\image-20211207220422819.png)]

4. v-model

  • 基本使用

    使用在inputtextare等可以表单类型的标签中

    v-model指令来实现表单元素和数据的双向绑定

    <div id="app">
      <input type="text" v-model="message">
      {{message}}
    </div>
    
    data:{
    	message:''
    }
    
  • v-model 结合radio类型

    • 不使用vue实现单选按钮

        <div>
          <label for="male">
            <input type="radio" value="male" name="sex" id="male" checked>男
          </label>
          <label for="female">
            <input type="radio" value="female" name="sex" id="female">女
          </label>
        </div>
      
    • 使用vue实现单选按钮

      <div id="app">
        <label for="male">
          <input type="radio" id="male" value="" v-model="sex" checked></label>
        <label for="female">
          <input type="radio" id="female" value="" v-model="sex"></label>
        <h2>您选择的性别是: {{sex}}</h2>
      </div>
      
          data: {
            sex: '男'
          }
      
  • v-model 结合checkbox类型

    • html式checkbox框

      <label for="agree">
        <input type="checkbox" id="agree" name="agree" >同意协议
      </label>
      
    • Vue式checkbox实现单选框

      <label for="agree">-->
          <input type="checkbox" id="agree" v-model="isAgree">同意协议
      </label>
      <h2>您选择的是: {{isAgree}}</h2>
      // 如果isAgree为true,那么disable就会生效,下一步将无法选择,注意前面同意为true,下一步这里取反了
      <button :disabled="!isAgree">下一步</button>	
      
      data:{
      	isAgree: false, // 单选框
      }
      
    • Vue式checkbox实现多选框

      // 方式一
      <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>
      
      // 方式二(推荐)
        <label v-for="item in originHobbies" :for="item">
          <input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
        </label>
      </div>
      
        data: {
            hobbies: [], // 多选框,
            originHobbies: ['篮球', '足球', '乒乓球', '羽毛球', '台球', '高尔夫球']
          }
        })
      
  • v-model 结合select类型

    • 选择一个

      <select name="abc" v-model="fruit">
          <option value="苹果">苹果</option>
          <option value="香蕉">香蕉</option>
          <option value="榴莲">榴莲</option>
          <option value="葡萄">葡萄</option>
      </select>
      <h2>您选择的水果是: {{fruit}}</h2>
        data:{
        	fruit:''
        }
      
    • 选择多个

      <select name="abc" v-model="fruits" multiple>
          <option value="苹果">苹果</option>
          <option value="香蕉">香蕉</option>
          <option value="榴莲">榴莲</option>
          <option value="葡萄">葡萄</option>
      </select>
      <h2>您选择的水果是: {{fruits}}</h2>
        data:{
        	fruits:[]
        }
      
  • v-model 原理

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

    • v-bind绑定一个value属性

    • v-on指令给当前元素绑定input事件

    • 流程解析

      1. 因为input中的v-model绑定了message,所以会实时将输入的内容传递给message,message发生改变
      2. 当message发生改变时,因为上面我们使用Mustache语法,将message的值插入到DOM中,所以DOM会发生响应的改变。
      3. 所以,通过v-model实现了双向的绑定

      🍺不适用v-model实现数据双向绑定

      <input type="text" v-model="message">
      <input type="text" :value="message" @input="valueChange">
      
      data:{
      	message:''
      },
      methods:{
      	// 这里传参的event,在标签如果不写的话,默认会把该input事件传入进来
      	valueChange(event){
      		this.message = this.event.target.value
      	}
      }
      

      🍺简洁写法实现v-model效果

      <input type="text" :value="message" @input="message = $event.target.value">
      data:{
      	message:''
      },
      
  • v-model 修饰符

    • .lazy

      1. v-model默认是在input事件中同步输入框的数据的。

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

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

        <!--1.修饰符: lazy-->
        <input type="text" v-model.lazy="message">
        <h2>{{message}}</h2>
        
    • .number

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

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

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

        <!--2.修饰符: number-->
        <input type="number" v-model.number="age">
        <h2>{{age}}-{{typeof age}}</h2>
        
    • tirm

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

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

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

5. Vue检测数组更新

因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新。

/ 1.push方法
// this.letters.push('aaa')
// this.letters.push('aaaa', 'bbbb', 'cccc')

// 2.pop(): 删除数组中的最后一个元素
// this.letters.pop();

// 3.shift(): 删除数组中的第一个元素
// this.letters.shift();

// 4.unshift(): 在数组最前面添加元素
// this.letters.unshift()
// this.letters.unshift('aaa', 'bbb', 'ccc')

// 5.splice作用: 删除元素/插入元素/替换元素
// 删除元素: 第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素)
// 替换元素: 第二个参数, 表示我们要替换几个元素, 后面是用于替换前面的元素
// 插入元素: 第二个参数, 传入0, 并且后面跟上要插入的元素
// splice(start)
// splice(start):
this.letters.splice(1, 3, 'm', 'n', 'l', 'x')
// this.letters.splice(1, 0, 'x', 'y', 'z')

// 5.sort()
// this.letters.sort()

// 6.reverse()
// this.letters.reverse()

// 注意: 通过索引值修改数组中的元素不会使Vue的值更新
// this.letters[0] = 'bbbbbb';
// this.letters.splice(0, 1, 'bbbbbb')

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

3.4 计算属性

  • 基本使用

    下面代码实现了四种显示英文姓和名的写法,使用计算属性来书写使得代码可读性更好。

    <div id="app">
      // 字符串拼接方式
      <h2>{{firstName + ' ' + lastName}}</h2>
      //  模板变量组合方式
      <h2>{{firstName}} {{lastName}}</h2>
      // 使用 methods方法
      <h2>{{getFullName()}}</h2>
      // 使用computed计算属性
      <h2>{{fullName}}</h2>
    </div>
    <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>
    
  • computed 实现多本书籍价格统计

    <h2>总价格: {{totalPrice}}</h2>
    <br>
    <h2>总价格: {{getTotalPrice()}}</h2>
    
    <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
    
            // for (let i in this.books) {
            //   this.books[i]
            //   .....
            // }
            //
            // for (let book of this.books) {
            //
            // }
          }
        }
      })
    </script>
    
  • 计算属性深入理解

    computed 包含 set 函数和get函数

    set方法主要对数据是否变化进行监听,如果数据发生变化则会触发set方法

    get方法主要用于数据值的返回,并被Vue接收渲染

    注意:计算属性一般没有set方法,只有属性

    # 基本结构   
    computed: {
        fullName: {
            set: function(*arg) {
    			pass
            },
            get: function () {
              pass
            }
          }
    

    由于set方法一般不会进行书写,所以computed进行了简写

        #  fullname属性取代了get方法
        computed: {
          fullName: function () {
            console.log('fullName');
            return this.firstName + ' ' + this.lastName
          }
        }
    
  • methods 与 computed 的对比

    method 在每次调用时都会去进行一次,没有缓存

    computed 则是对值进行检测,如果值没有发生改变,则每次调用都只会加载一次,computed 数据会存储在缓存中。

    <div id="app">
      <!--1.直接拼接: 语法过于繁琐-->
      <h2>{{firstName}} {{lastName}}</h2>
    
      <!--2.通过定义methods-->
      <h2>{{getFullName()}}</h2>
      <h2>{{getFullName()}}</h2>
      <h2>{{getFullName()}}</h2>
      <h2>{{getFullName()}}</h2>
    
      <!--3.通过computed-->
      <h2>{{fullName}}</h2>
      <h2>{{fullName}}</h2>
      <h2>{{fullName}}</h2>
      <h2>{{fullName}}</h2>
    </div>
    ...
    <script>
      const app = new Vue({
        el: '#app',
        data: {
          firstName: 'Kobe',
          lastName: 'Bryant'
        },
        methods: {
          getFullName: function () {
            console.log('methods getFullName');
            return this.firstName + ' ' + this.lastName
          }
        },
        computed: {
          fullName: function () {
            console.log('computed fullName');
            return this.firstName + ' ' + this.lastName
          }
        }
      })
    
    </script>
    

    通过对比发现,methods在console中加载了四次,而computed只加载了一次。
    在这里插入图片描述

3.5 组件

1. 组件的创建流程

✒️ 这是原始的书写方式,后面会有更加便捷的写法(语法糖)

  1. 创建组件构造器

    Vue.extend()
    - 调用Vue.extend()创建的是一个组件构造器。 
    - 通常在创建组件构造器时,传入template代表我们自定义组件的模板。
    - 该模板就是在使用到组件的地方,要显示的HTML代码。
    - 事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。
    
  2. 注册组件

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

    组件必须挂载在某个Vue实例下,否则它不会生效。
    我们来看下面我使用了三次<my-cpn></my-cpn>
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UJAW2WAM-1645673949247)(Z:\Study Note\Typora笔记图库\image-20211208200433294.png)]

  • 一个完整的组件demo

    <div id="app">
        // 3.使用组件
        <my-cpn></my-cpn>
    </div>
    
    // 1. 创建组件
        const cpn = Vue.extend({	// 注意extend没有s
            template:`				// 注意template没有s
                <div>
                    <h3>这是组件的标题</h3>
                    <span>这是组件内容</span>   
                </div>
            `
        })
    // 2. 注册组件
    	Vue.component('my-cpn',cpn)
    

2. 全局组件和局部组件

  • 全局组件

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

    当我们使用Vue 创建多个实例时,全局组件可以在任何一个实例中使用

    // my-cpn 可以在app1和app2中使用
    Vue.component('my-cpn',cpn)
    
    const app1 = new Vue({ ... })
    const app1 = new Vue({ ... })
    
  • 局部组件

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

    <div id="app">
        <cpn2_tag></cpn2_tag>
    </div>
    
    	const cpn2 = Vue.extend({
            template:`
            <div>
                <h3>这是局部组件的标题</h3>
                <span>这是局部组件内容</span>   
            </div>
            `
        })
        const app = new Vue({
            el: '#app',
            data: {},
            methods: {},
            components:{	// 此处有s
                // cpn2_tag 表示标签名
                cpn2_tag:cpn2
            }
        });
    

    3. 父组件与子组件

    • 在父组件需要根实例中注册
    • 子组件只能在父组件中被识别的。
      // 1.创建第一个组件构造器(子组件)
      const cpnC1 = Vue.extend({
        template: `
          <div>
            <h2>我是标题1</h2>
            <p>我是内容, 哈哈哈哈</p>
          </div>
        `
      })
    
    
      // 2.创建第二个组件构造器(父组件)
      const cpnC2 = Vue.extend({
        template: `
          <div>
            <h2>我是标题2</h2>
            <p>我是内容, 呵呵呵呵</p>
            <cpn1></cpn1>
          </div>
        `,
        components: {
          cpn1: cpnC1
        }
      })
    
      // root组件
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好啊'
        },
        components: {
          cpn2: cpnC2
        }
      })
    

4. 组件的语法糖

  • 方式一:全局组件注册的语法糖

    这种方式书写的代码过于臃肿,不够简洁美观

    // 全局注册组件
    Vue.component('cpn1', {
        template: `
          <div>
            <h2>我是标题1</h2>
            <p>我是内容, 哈哈哈哈</p>
          </div>
        `
      })
      // 第二个参数的对象,其实就是调用Vue.extend返回的对象被component接收了
      
    // 局部注册组件语法糖
    const app = new Vue({
        el: '#app',
        data: {
          message: '你好啊'
        },
        components: {
          'cpn2': {
            template: `
              <div>
                <h2>我是标题2</h2>
                <p>我是内容, 呵呵呵</p>
              </div>
        	`
          }
        }
      })
    
  • ⭐️方式二:组件模板分离(推荐)

    分工更加明确,可读写更高。

    // 1. 书写模板标签,指定id用以被template获取
    <template id="cpn">
      <div>
        <h2>我是标题</h2>
        <p>我是内容,呵呵呵</p>
      </div>
    </template>
    
    // 2. 注册组件,
      Vue.component('cpn', {
        template: '#cpn'	// 指定template标签的id
      })
    

5. 组件数据获取

  • 使用data()函数来获取数据,注意组件中使用变量来获取数据时,根实例的数据是不能引用的。

    组件对象也有一个data属性(也可以有methods等属性)

    只是这个data属性必须是一个函数

    格式:data(){ return {} } -> 函数再返回一个对象,在对象中定义数据
    
    <template id="cpn">
      <div>
        <h2>{{title}}</h2>
        <p>我是内容,呵呵呵</p>
      </div>
    </template>
    
    Vue.component('cpn', {
        template: '#cpn',
        data() {	
          return {
            title: 'abc'	
          }
        }
      })
    const app = new Vue({
        el: '#app',
        data: {
          message: '你好啊',
          title: '我是标题'     // 不会引用该变量
        }
      })
    
  • 为什么data用函数来书写,而不是直接用对象来书写

    仔细对比发现,在组件中data是通过函数返回对象,并在对象中定义数据

    而在根实例中data是直接使用对象来定义数据的。

    为什么会这么定义呢?

    • 解析

      1. 如果不是一个函数,Vue直接就会报错
      2. 如果使用对象直接返回,则每一个组件都是调用的同一个内存地址,会导致一处地方的数据修改,其他的地方相同的组件中的数据也会修改
      3. 采用函数返回对象,则每次调用组件时,都会产生新的对象,使每个组件中的数据相互独立,互不干扰。
      • 组件同一对象调用伪代码

         const obj = {
            name: 'why',
            age: 18
          }
          Vue.component('cpn', {
            template: '#cpn',
            data() {
              return obj	// 以后每一个生成组件,数据都是引用同一个地址
            },
        
      • 组件不同对象调用

         const obj = 
          Vue.component('cpn', {
            template: '#cpn',
            data() {
              return {  // 以后每一个生成组件,数据都是引用不同的地址
                name: 'why',
                age: 18
              }	
            },
        
  • 封装组件demo

    <template id="cpn">
      <div>
        <h2>当前计数: {{counter}}</h2>
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
      </div>
    </template>
    <script>
      Vue.component('cpn', {
        template: '#cpn',
        data() {
          return {
            counter: 0
          }
        },
        methods: {
          increment() {
            this.counter++
          },
          decrement() {
            this.counter--
          }
        }
      })
    
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好啊'
        }
      })
    </script>
    

6.组件通信

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-43ol7cZC-1645673949247)(Z:\Study Note\Typora笔记图库\image-20211208233831795.png)]

  • 父组件向子组件传递 props

    注意驼峰命名,在脚手架中可以使用,在原生Vue中需要使用`-`来区分来,或者全部小写
    
    • 方式一:字符串数组,数组中的字符串就是传递时的名称

      			// cmovies 要不全小写命名,要不写成c-movies
      <cpn v-bind:cmovies="movies"></cpn>
      // <cpn v-bind:cmovies=['海王', '海贼王', '海尔兄弟']></cpn> 上面代码就等同这种格式
      
      const cpn = {
          template: '#cpn',
          props: ['cmovies', 'cmessage'],
      } 
      const app = new Vue({
          el: '#app',
          data: {
            message: '你好啊',
            movies: ['海王', '海贼王', '海尔兄弟']
          },
          components: {
            cpn
      }
        })
      
    
    - :star:`方式二`:对象,对象可以设置传递时的类型,也可以设置默认值等。
    
    
       const cpn = {
          template: '#cpn',
          props: {
            // 1.类型限制
            // cmovies: Array,
            // cmessage: String,
      
            // 2.提供一些默认值, 以及必传值
            cmessage: {
              type: String,
              default: 'aaaaaaaa',
              required: true
            },
            // 类型是对象或者数组时, 默认值必须是一个函数
            cmovies: {
              type: Array,
              default() {
                return []
              }
            }
          }
        }
        
      const app = new Vue({
          el: '#app',
          data: {
            message: '你好啊',
            movies: ['海王', '海贼王', '海尔兄弟']
          },
          components: {
            cpn
      }
        })
    
    
    
  • props 数据验证

      如上代码,当需要对**props**进行类型等验证时,就需要对象写法了。
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3MYnPCQs-1645673949248)(Z:\Study Note\Typora笔记图库\image-20211208234815934.png)]

  • 子组件向父组件传递 $emit()

    使用自定义事件来完成子组件向父组件传递

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

      格式:.$emit('EventName',data)
      			(事件名称,数据)
      
    2. 在父组件中,通过v-on来监听子组件事件。

    <!-- 父组件 -->
    <div id="app">
        	 // cat-click就是子组件定义事件的名称,catClick是父组件实现的方法,可以简单的理解为cat-click就是一个点击事件,后者是实现的方法,
        	 // 注意在父子传递的过程中,事件名称不可以使用【驼峰命名】
        <cpn @cat-click="catClick"></cpn>
    </div>
    
    <!-- 子组件模板 -->
    <template id="cpn">
        <div>
            <ul>
                <li v-for="item in categories">
                    // 子组件调用了catClick()方法
                    <button @click="catClick(item)">{{item.name}}</button>
                </li>
            </ul>
            <h6>
                {{desc}}
            </h6>
        </div>
    </template>
    
    
    const cpn = {				// 这里被根实例的components调用了,就变成了子组件
            template:'#cpn',
            data(){
                return{
                    categories:[
                    {
                        'id':0001,
                        'name':'数码',
                    },
                    {
                        'id':0002,
                        'name':'图书',
                    },
                    {
                        'id':0003,
                        'name':'家电',
                    },
                    {
                        'id':0004,
                        'name':'服饰',
                    },
                ],
                   desc:'每天24开始更新数据' 
                }
            },
            methods:{
                catClick(item){
                    //  (自定义的事件方法名,传递的数据)
                    this.$emit('cat-click',item)  // 发射事件:自定义事件
                }
            }
        }
    
    const app = new Vue({
            el: '#app',
            data: {},
            methods: {},
            components:{
                cpn			// 增强写法就等于 cpn:cpn  -》(标签名:对象)
            },
            methods:{
                catClick(item){
    				// 此处接收的item就是子组件中的item数据
                    console.log(item)
                }
            }
        });
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oPqnNZQH-1645673949248)(Z:\Study Note\Typora笔记图库\image-20211209133956474.png)]

  • 父子组件访问方式

    • 父组件→子组件,使用$children$refs

      • $children使用

        1. 通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
        2. 但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
          const app = new Vue({
            el: '#app',
            data: {
              message: '你好啊'
            },
            methods: {
              btnClick() {
                // 1.$children
                console.log(this.$children);
                for (let c of this.$children) {
                  console.log(c.name);
                }
              }
            },
            components: {		// 子组件
              cpn: {
                template: '#cpn',
                data() {
                  return {
                    name: ['Jack','Rion','Lucy']
                  }
                },
        
      • ⭐️$refs 使用

        1. $refs和ref指令通常是一起使用的。
        2. 我们通过ref给某一个子组件绑定一个特定的ID
        3. 通过this.$refs.ID就可以访问到该组件了
        <div id="app">
          <cpn ref="aaa"></cpn>			// 绑定一个特定的ID
          <button @click="btnClick">按钮</button>
        </div>
        
            methods: {
              btnClick() {
                // 2.$refs => 对象类型, 默认是一个空的对象 ref='bbb'
                console.log(this.$refs.aaa.name)	// 使用$refs.ID来获取组件中的数据
              }
            },
            components: {
              cpn: {
                template: '#cpn',
                data() {
                  return {
                    name: ['Jack','Rion','Lucy']
                  }
                },
        
    • 🛑子组件→父组件,使用$parent$root

      • 注意:

        1.尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做
        2.子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
        3.如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
        4.另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。
        
        data() {
            return {
            name: '我是cpn组件的name'
            }
        },
        components: {
            cpn: {
            	template: '#cpn',
            	methods: {
            	btnClick() {
                // 1.访问父组件$parent
                console.log(this.$parent);
                console.log(this.$parent.name);
        
                // 2.访问根组件$root
                console.log(this.$root);
                console.log(this.$root.message);
            }
        

3.6 插槽 slot

插槽(slot)是什么?

  1. ​ 插槽的目的是让我们原来的设备具备更多的扩展性
  2. ​ 比如电脑的USB我们可以插入U盘、硬盘、手机、音响、键盘、鼠标等等。

组件的插槽有什么好处

  1. 组件的插槽也是为了让我们封装的组件更加具有扩展性。
  2. 让使用者可以决定组件内部的一些内在这里插入图片描述
    容到底展示什么。

🌸 栗子:移动网站中的导航栏。

移动开发中,几乎每个页面都有导航栏。显示内容也不一致
导航栏我们必然会封装成一个插件,比如nav-bar组件。
一旦有了这个组件,我们就可以在多个页面中复用了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SKgF4JTG-1645673949248)(Z:\Study Note\Typora笔记图库\image-20211209162146381.png)]

1. 普通插槽

  • 基本使用

    1.插槽的基本使用 <slot></slot>
    2.插槽的默认值  <slot>button</slot>
    3.如果有多个值, 同时放入到组件进行替换时, 一起作为替换元素
    
    <div id="app">
      <!-- 第一个cpn -->
      <cpn><button>左侧</button></button></cpn>	//此处内容会替换slot标签的内容
    
      <!-- 第二个cpn -->
      <cpn></cpn>		// 若为空则会显示slot标签中的内容
    
      <!-- 第三个cpn -->	 
      <cpn>		// 组件中的全部内容一起替换slot标签中的内容
        <span>第一段内容</span>
        <span>第一段内容</span>
        <span>第一段内容</span>
      </cpn>
    </div>
    
    <template id="cpn">
      <div>
        <h2>我是组件,这是共同部分</h2>
        <slot><h4>这时插槽中的默认值</h4></slot>
      </div>
    </template>
    

2. 具名插槽

  1. 当子组件的功能复杂时,子组件的插槽可能有多个时。
  2. 比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。
  3. 那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?
  4. 非常简单,只要给slot元素一个name属性即可
// slot属性用来指定加载到模板中哪个slot标签,没有被指定name的slot标签就不会加载
<div id="app">
  <cpn><span slot="center">标题</span></cpn>
  <cpn><button slot="left">返回</button></cpn>
</div>

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

3. 编译作用域

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iXFN6hnq-1645673949249)(Z:\Study Note\Typora笔记图库\image-20211209164213629.png)]

4. 插槽获取数据

​ 当我们在使用插槽时,想要同一组数据不同方式展示,如何将数据传递给slot中呢

​ 我们通过<template slot-scope="slotProps">获取到slotProps属性

​ 通过slotProps.data就可以获取到刚才我们传入的data了

  • slot标签中可以指定数据对象
  • 流程
    1. 书写模板,定义slot标签,并指定变量引用的数据 :DateName="pLanguages"
    2. 实例app中使用组件,若为空则显示模板中slot的默认样式
    3. 若不为空,则使用自定义标签➕属性slot-scope="SlotName" 来获取数据
    4. 数据通过SlotName.DateName 来使用数据
<div id="app">
  <cpn></cpn>	// 默认显示方式,即 ul>li 的方式来显示
  
  <cpn>	
    <!--目的是获取子组件中的pLanguages-->
    <template slot-scope="slot">	// template 名字是可以修改的,slot-scope获取了 模板slot标签的属性,即获取了数据 slot.data = pLanguages
      <span v-for="item in slot.data"> {{item}} | </span>
    </template>
  </cpn>

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

<template id="cpn">
  <div>
    <slot :data="pLanguages">	//:data  data名字可以随意修改,后续调用通过改名字获取数据
      <ul>
        <li v-for="item in pLanguages">{{item}}</li>
      </ul>
    </slot>
  </div>
</template>

// 子组件中定义了pLanguages数组
components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            pLanguages: ['JavaScript', 'C++', 'Java', 'C#', 'Python', 'Go', 'Swift']
          }
        }
      }
    }

网页效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GQAK56R8-1645673949249)(Z:\Study Note\Typora笔记图库\image-20211209170442661.png)]

四、Vue CLI

CLICommand-Line Interface,翻译为命令行界面, 但是俗称脚手架,使用 vue-cli 可以快速搭建Vue开发环境以及对应的webpack配置。

  • Vue程序运行过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JpcjVehU-1645673949252)(Z:\Study Note\Typora笔记图库\image-20211212124042048.png)]

4.1 安装node

  • 下载

    http://nodejs.cn/download/
    
  • 安装检测

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b8o7O46U-1645673949252)(Z:\Study Note\Typora笔记图库\image-20211211214953983.png)]

4.2 NPM

  1. NPM的全称是Node Package Manager
  2. 是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。
  • 配置下载源

    // 如出现下载缓慢请配置 npm 淘宝镜像
    npm config set registry https://registry.npm.taobao.org
    
  • npm 使用

    在使用npm之前,需要先下载node安装包。

    npm(node package Manager)

    • node 环境配置

      npm init
      
    • 安装包

      // npm安装
      npm install vue 
      npm install --save
      npm install --save-dev
      npm install -g
      npm search		// 搜索
      npm update		// 更新
      npm list -g --depth 0 // 查看全局包,–depth 0:列表搜索深度0级 不加这参数 会默认所有
      npm ls vue/cli -g  // 查询单独一个包文件是否安装
      npm install -g cnpm --registry
      npm uninstall  -g 包名称		// 卸载
      
  • npm run build调用流程

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zUjAxzrj-1645673949252)(Z:\Study Note\Typora笔记图库\image-20211212124152265.png)]

  • npm run dev 运行流程

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wlaxwuNE-1645673949253)(Z:\Study Note\Typora笔记图库\image-20211212124223753.png)]

4.3 Vue CLI 使用

  • 安装Vue CLI

    npm install -g @vue/cli
    
  • 创建 vue 项目

    vue crate projectname
    
  • 启动 vue 项目

    npm run serve
    

五、Vue-Router

5.1 url中的hash和history

​ 一个是带有#/ 一个不带

  • hash

    URL的hash也就是锚点(#), 本质上是改变window.location的href属性

    我们可以通过直接赋值location.hash来改变href, 但是页面不发生刷新

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aVQ68fB7-1645673949253)(Z:\Study Note\Typora笔记图库\image-20211212130042351.png)]

  • history

    history接口是HTML5新增的, 它有五种模式改变URL而不刷新页面.

    • history.pushState()

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SxGv4xXh-1645673949253)(Z:\Study Note\Typora笔记图库\image-20211212130929279.png)]

    • history.replaceState()

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kHlzGBRo-1645673949254)(Z:\Study Note\Typora笔记图库\image-20211212130942407.png)]

    • history.go()

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

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

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-th98HyAz-1645673949254)(Z:\Study Note\Typora笔记图库\image-20211212131048487.png)]

5.2 Vue-Router 使用

1. 基本使用

  • 安装

    npm install vue-router --save
    
  • vue-router的简单使用

    第一步: 创建路由组件
    第二步: 配置路由映射: 组件和路径映射关系
    第三步: 使用路由: 通过<router-link>和<router-view>
    
  • 实例

    • 创建router实例

      //src/router/index.js

      
      import VueRouter from "vue-router";
      import Vue from "vue";
      
      import Home from '../components/home'
      import Order from '../components/order'
      
      // 第一步: 创建路由组件
      Vue.use(VueRouter)
      
      // 第二步: 配置路由映射: 组件和路径映射关系
      const routes = [
          {
              path:"/home",
              component:Home
          },
          {
              path:"/order",
              component:Order
          },
      ]
      
      const router = new VueRouter({
          routes,
      	mode:"history"
      })
      
      // 第三步:将router对象传入到Vue实例
      

    export default router
    ```

    • 挂在到main.js

      import Vue from 'vue'
      import App from './App.vue'
      import router from './router'
      
      Vue.config.productionTip = false
      
      new Vue({
        router,
        render: h => h(App),
      

    }).$mount(’#app’)
    ```

    • 实现component中的组件

      // home.vue
      <template>
        <div>
            <h2>Home</h2>
            <span>这是Home页面</span>
        </div>
      </template>
      
      <script>
      export default {
          name:'Home'
      }
      </script>
      
      // order.vue
      <template>
        <div>
            <h2>Home</h2>
            <span>这是Home页面</span>
        </div>
      </template>
      
      <script>
      export default {
          name:'Order'
      }
      </script>
      

5.2 router-link属性

1. router-link属性

  • to属性

    实现组件之间的切换,即各组件之间的跳转

  • tag属性

    tag可以指定之后渲染成什么组件, 比如上面的代码会被渲染成一个

  • 元素, 而不是 。默认是渲染成a标签
  • <router-link to="home" tag="h3">Home</router-link>
    
  • replace属性

    replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中

    <router-link to="home" tag="h3" replace>Home</router-link>
    
  • active-class

    当对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称.

    • 方式一:加载标签上

      <router-link to="home" active-class="activeName">Home</router-link>
      
    • 方式一:加载VueRouter实例中

      // /src/router/index.js
      const router = new VueRouter({
          routes,
          linkActiveClass:'activeName',
          mode:'history'
      })
      

      设置类样式,实现点击字体变绿

      // src/App.vue
      <style>
      	.activeName{
        	color: yellowgreen;
      }
      </style>
      

      实现效果

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YcOrh5b7-1645673949254)(Z:\Study Note\Typora笔记图库\image-20211212182040372.png)]

2. $router 实现路由跳转

​ 标签通过点击事件来实现路由跳转,在实现的methods中,调用$router$router这是一个系统自带的对象,属于全局的vuerouter实例。

  • 使用push方法,则网页可以进行前进和后退

  • 使用repalce方法,则不允许网页进行前进或者后退

    <button @click="clickHome">Home</button>
    <button  @click="clickOrder">Order</button>
    
    methods:{
        clickHome(){
          // return this.$router.push('/home')
          return this.$router.replace('/home')
        },
        clickOrder(){
          // return this.$router.push('/order')
          return this.$router.replace('/order')
        }
      }
    

5.3 动态路由

  • 设置接收参数

    // router/index.js
    {
        path: '/user/:id',
        component: User,
      }
    
  • 组件解析id

    <template>
      <div>
        <h2>我是用户界面</h2>
        <h2>{{userId}}</h2>
      </div>
    </template>
    
  • 视图传值

    <router-link :to="'/user/'+userId">用户</router-link>
    

5.4 路由懒加载

  1. 当打包构建应用时,Javascript 包会变得非常大,影响页面加载。
  2. 如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
  3. 路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块.
  4. 只有在这个路由被访问到的时候, 才加载对应的组件
  • 未使用懒加载

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RZXbqQJ5-1645673949255)(Z:\Study Note\Typora笔记图库\image-20211212211239182.png)]

  • ⭐️使用懒加载

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7UqpZMPy-1645673949255)(Z:\Study Note\Typora笔记图库\image-20211212211257170.png)]

  • 其他方式使用懒加载

    方式一: 结合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中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割.
    const Home = () => import('../components/Home.vue')
    
    

5.5 路由嵌套

  1. 比如在home页面中, 我们希望通过/home/news和/home/message访问一些内容
  2. 一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z2ddVFPF-1645673949255)(Z:\Study Note\Typora笔记图库\image-20211212211632833.png)]

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

    1. 创建对应的子组件(), 并且在路由映射中配置对应的子路由.
    2. 在组件内部使用标签.
    // 1. 创建对应的HomeNews.vue 和 HomeMessage.vue 组件
    {
        path: '/home',
        component: Home,
        meta: {
          title: '首页'
        },
        // 2. 在home页面创建对应的子组件(children)
        children: [
          // {			设置子组件的默认显示
          //   path: '',
          //   redirect: 'news'
          // },
          {
            path: 'news',
            component: HomeNews
          },
          {
            path: 'message',
            component: HomeMessage
          }
        ]
      },
      
      // 3. 路由视图书写
      <router-link to="/home/news">关于</router-link>
      <router-link to="/home/message">关于</router-link>
    

5.6 参数传递

在网页URL后面传递query,如:http://www.baidu.com/?type=1&name=rion

​ 获取参数通过$route对象获取的

​ 在使用了 vue-router 的应用中,路由对象会被注入每个组件中,赋值为 this.$route ,并且当路由切换时,路由对象会被更新。

  • router-link 传递

    // App.vue
    <router-link :to="{path: '/profile', query: {name: 'why', age: 18, height: 1.88}}"> 档案</router-link>
    
    // profile.vue
    <template>
      <div>
        <h2>我是Profile组件</h2>
        <h2>{{$route.query.name}}</h2>
        <h2>{{$route.query.age}}</h2>
        <h2>{{$route.query.height}}</h2>
      </div>
    </template>
    
  • methods方法传递

    // App.vue
    <button @click="profileClick">档案</button>
    
    methods: {
        profileClick() {
          this.$router.push({
            path: '/profile',
            query: {
              name: 'kobe',
              age: 19,
              height: 1.87
            }
          })
        }
    }
    
    // profile.vue
    <template>
      <div>
        <h2>我是Profile组件</h2>
        <h2>{{$route.query.name}}</h2>
        <h2>{{$route.query.age}}</h2>
        <h2>{{$route.query.height}}</h2>
      </div>
    </template>
    
  • router & route 的区别

    1. $routerr为VueRouter实例,想要导航到不同URL,则使用$router.push方法
    2. $route为当前router跳转对象里面可以获取name、path、query、params

5.7 导航守卫

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

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

可以在路由发生改变时执行一些操作,比如修改网页的title等

  • beforeEach

    格式:beforeEach(to,from,next) => {}
        to: 即将要进入的目标的路由对象.
        from: 当前导航即将要离开的路由对象.
        next: 调用该方法后, 才能进入下一个钩子
    

    首先, 我们可以在钩子当中定义一些标题, 可以利用meta来定义

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NMj0R3l0-1645673949256)(Z:\Study Note\Typora笔记图库\image-20211212213950738.png)]

    其次, 利用导航守卫,修改我们的标题.

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pAGYHJLI-1645673949256)(Z:\Study Note\Typora笔记图库\image-20211212213958813.png)]

// 子组件中获取元数据的title的方式
document.title = to.matched[0].meta.title
```

**补充**

```
如果是后置钩子, 也就是afterEach, 不需要主动调用next()函数.
补充二: 上面我们使用的导航守卫, 被称之为全局守卫.
        路由独享的守卫.
        组件内的守卫.
```

5.8 keep-alive

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

它们有两个非常重要的属性:

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

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

router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hz5oIFlp-1645673949256)(Z:\Study Note\Typora笔记图库\image-20211213115516393.png)]

🌴实现切换标签保持组件的alive状态

  • 注意,以下都是基于keep-alive状态下才能实现

  • 定义path路径

  • 使用生命周期create函数,在创建组件是保存path的路径

  • 在切换组件时,使用组件守卫来保持path路径,实现保存保持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值