Vue基础知识大全

一、Vue 介绍

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

1.1 MVVM

二、安装Vue

2.1 使用CDN引入

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


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


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


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

Vue 也可以在 unpkg 和 cdnjs 上获取 (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会把前面的值传入到后面的过滤器中
    
    

    Total Price: {{totalPrice | showTwoDecimal}} 元

    // 实现价格保留2位小数 const app = new Vue({ data:{ totalPrice:58 }, filters:{ showTwoDecimal(price){ return '¥' + price.toFixed(2) } } })

3.2 Vue 生命周期

Vue基础知识大全_第1张图片

3.3 指令

1. 文本内容插值操作

  • 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

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

      , 李银河!

  • v-pre

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

      {{message}}

  • 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

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

      
      
      data:{
      	imgUrl:"www.baidu.com"
      }
      
      # 语法糖写法
      
      
      data:{
      	imgUrl:"www.baidu.com"
      }
      
    • 动态绑定class(对象语法)

      # class 调用的是一个对象
      
      <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 调用的是一个函数
      

      {{message}}

      methods: { getClasses: function () { return {active: this.isActive, line: this.isLine} } }
    • 动态绑定class(数组语法)-- 使用较少

      {{message}}

      {{message}}

      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">
        
        <button @click="btn1Click()">按钮1button>
        <button @click="btn1Click">按钮1button>
      
        
        
        
        <button @click="btn2Click">按钮2button>
      
        
        
        <button @click="btn3Click(abc, $event)">按钮3button>
      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">
        
        <div @click="divClick">
          aaaaaaa
          <button @click.stop="btnClick">按钮button>
        div>
      
        
        <br>
        <form action="baidu">
          <input type="submit" value="提交" @click.prevent="submitClick">
        form>
      
        
        <input type="text" @keyup.enter="keyUp">
      
        
        <button @click.once="btn2Click">按钮2button>
      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>
      

Vue基础知识大全_第2张图片

  • v-show

    v-showv-if 的区别

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

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

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

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

      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

    • 遍历数组

      
      <ul>
          <li v-for="item in names">{{item}}li>
      ul>
      
      
      <li v-for="(item, index) in names">
          {{index+1}}.{{item}}
      li>
      
    • 遍历对象

      
      <ul>
          <li v-for="item in info">{{item}}li>
      ul>
      
      
      <ul>
          <li v-for="(value, key) in info">{{value}}-{{key}}li>
      ul>
      
      
      <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

      Vue基础知识大全_第3张图片

4. v-model

  • 基本使用

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

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

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

    • 不使用vue实现单选按钮

        
    • 使用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框

      
      
    • 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修饰符可以让数据在失去焦点或者回车时才会更新

        
        
        

        {{message}}

    • .number

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

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

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

        
        
        

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

    • tirm

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

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

        
        
        

        您输入的名字:{{name}}

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 实现多本书籍价格统计

    总价格: {{totalPrice}}


    总价格: {{getTotalPrice()}}

  • 计算属性深入理解

    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 数据会存储在缓存中。

    {{firstName}} {{lastName}}

    {{getFullName()}}

    {{getFullName()}}

    {{getFullName()}}

    {{getFullName()}}

    {{fullName}}

    {{fullName}}

    {{fullName}}

    {{fullName}}

    ...

    通过对比发现,methods在console中加载了四次,而computed只加载了一次。
    Vue基础知识大全_第4张图片

3.5 组件

1. 组件的创建流程

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

  1. 创建组件构造器

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

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

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

    Vue基础知识大全_第5张图片

  • 一个完整的组件demo

    <div id="app">
        // 3.使用组件
        <my-cpn></my-cpn>
    </div>
    
    // 1. 创建组件
        const cpn = Vue.extend({	// 注意extend没有s
            template:`				// 注意template没有s
                

    这是组件的标题

    这是组件内容
    `
    }) // 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:`
            

    这是局部组件的标题

    这是局部组件内容
    `
    }) const app = new Vue({ el: '#app', data: {}, methods: {}, components:{ // 此处有s // cpn2_tag 表示标签名 cpn2_tag:cpn2 } });

    3. 父组件与子组件

    • 在父组件需要根实例中注册
    • 子组件只能在父组件中被识别的。
      // 1.创建第一个组件构造器(子组件)
      const cpnC1 = Vue.extend({
        template: `
          

    我是标题1

    我是内容, 哈哈哈哈

    ` }) // 2.创建第二个组件构造器(父组件) const cpnC2 = Vue.extend({ template: `

    我是标题2

    我是内容, 呵呵呵呵

    `, components: { cpn1: cpnC1 } }) // root组件 const app = new Vue({ el: '#app', data: { message: '你好啊' }, components: { cpn2: cpnC2 } })

4. 组件的语法糖

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

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

    // 全局注册组件
    Vue.component('cpn1', {
        template: `
          <div>
            <h2>我是标题1h2>
            <p>我是内容, 哈哈哈哈p>
          div>
        `
      })
      // 第二个参数的对象,其实就是调用Vue.extend返回的对象被component接收了
      
    // 局部注册组件语法糖
    const app = new Vue({
        el: '#app',
        data: {
          message: '你好啊'
        },
        components: {
          'cpn2': {
            template: `
              <div>
                <h2>我是标题2h2>
                <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. 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。

Vue基础知识大全_第6张图片

  • 父组件向子组件传递 props

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

      			// cmovies 要不全小写命名,要不写成c-movies
      <cpn v-bind:cmovies="movies">cpn>
      // 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**进行类型等验证时,就需要对象写法了。
    

    Vue基础知识大全_第7张图片

  • 子组件向父组件传递 $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)
                }
            }
        });
    

    Vue基础知识大全_第8张图片

  • 父子组件访问方式

    • 父组件→子组件,使用$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组件。
一旦有了这个组件,我们就可以在多个页面中复用了。

Vue基础知识大全_第9张图片

1. 普通插槽

  • 基本使用

    1.插槽的基本使用 
    2.插槽的默认值  button
    3.如果有多个值, 同时放入到组件进行替换时, 一起作为替换元素
    
    <div id="app">
      
      <cpn><button>左侧button>button>cpn>	//此处内容会替换slot标签的内容
    
      
      <cpn>cpn>		// 若为空则会显示slot标签中的内容
    
      	 
      <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. 编译作用域

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

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

Vue基础知识大全_第10张图片

4. 插槽获取数据

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

​ 我们通过