• Vue知识点整理(待更新)


    Vue知识点整理(待更新)

    参考Vue.js中文官网Vue 知识点汇总(上)–附案例代码及项目地址Vue 知识点汇总(下)–附案例代码及项目地址Vue知识点汇总【持更】

    一、基础知识

    • 【问】JS中attribute和property有什么区别?(attribute属性在HTML上,property属性值在js上;两者的区别是attribute不会同步property的值,而property可以同步attribute的值)

      Note

      • attributeHTML标签上的特性,它的值只能够是字符串attribute可以简单理解成dom节点自带的属性,例如html中常用的id、class、title、alignattributes是属于property的一个子集。attribute的赋值:

            //注意参数1,2均是字符串
         div1.setAttribute('class', 'a');
         div1.setAttribute('title', 'b');
         div1.setAttribute('title1', 'c');
         div1.setAttribute('title2', 'd');
        
        • 1
        • 2
        • 3
        • 4
        • 5
      • property是DOM中的属性,是JavaScript里的对象;property取值如下:

        //取任何属性的只,用“.”即可 
        var id = div1.id;
        var className = div1.className;
        var childNodes = div1.childNodes;
        var attrs = div1.attributes;
        
        //此处再次强调:
        //1) class特性在变成属性时,名字改成了“className”,因此div1.className和div1.getAttrbute('class')相同。
        //2) 上面代码中的div1.attributes是取的attributes这一属性,取出来保存到attrs变量中,attrs就成了一个NamedNodeList类型的对象,里面存储了若干个Attr类型。
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
      • 在js中,attribute和property关系如下:

        • property能够从attribute中得到同步

        • attribute不会同步property上的值;

        • attribute和property之间的数据绑定是单向的,attribute->property;

        • 更改property和attribute上的任意值,都会将更新反映到HTML页面中;

    • 【问】Vue.js是什么?(只关注于视图层,渐进式框架)

      Note

      • Vue (读音类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用

      • Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。

    • 【问】MVVM是什么?谈谈Vue的工作原理?

      Note

      • MVVMModel - View - ViewModel 的缩写 可以看到他和之前的MVC很像,的确有人称之为一个加强优化版的MVC。 是一种模块化开发代码分层的思想或者框架!

        • 模型(Model):模型和业务数据绑定,方便数据的使用和传递。

        • 视图(View): 视图是应用程序中用户界面相关的部分,是用户看到并与之交互的界面

        • ViewModel:首先它的创建需要将Model中的数据绑定在他身上(为模型到视图的映射提供桥梁)。将原来MVC中的业务逻辑剥离出来写在ViewModel中,简化viewcontroller
          在这里插入图片描述

      • 使用步骤
        1、模块中需要的数据,通过网络层请求得到 ,然后将数据绑定到Model层中
        2、将model层中的数据转化到ViewModel中,然后在ViewModel中处理一些逻辑问题
        3、将ViewModel中的数据绑定到控制器的View上,然后更新界面。

        MVVM模型适合作为前端的框架,用来实现前后端分离。

      • Vue官网对Vue的MVVM模型做出了解释:参考Vue中的MVVM

        在这里插入图片描述

        各层的作用:

        • View层

          ①视图层
          ②在前端开发中就是DOM层
          ③主要作用是给用户展示各种信息

        • Model层

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

        • ViewModel层

          ①视图模型层
          ②视图模型层是View和Model沟通的桥梁
          ③一方面它实现了Data Bindings来进行数据的绑定,将Model的改变实时反映到View中
          ④另一方面它实现了DOM Listener,也就是DOM监听,当DOM发生一些事件(点击,滚动,touch)时,可以监听到,并在需要的情况下改变对应的Data。

    • 【问】Vue实例的生命周期分为哪几个阶段?(创建、运行和销毁;常见的生命周期函数包括createdmountedupdated等)

      Note

      • 生命周期概念:生命周期是指一个组件从创建 > 运行 > 销毁的整个过程,强调的是一个时间段

        生命周期函数概念:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行

        注意:生命周期强调的是时间段,生命周期函数强调的是时间点

      • 组件生命周期函数的分类:

        在这里插入图片描述

      • 官网提供的Vue实例生命周期解释如下:
        在这里插入图片描述

    • 【问】关于Vue的特性有哪些?(数据驱动视图,双向数据绑定)

      Note

      • 数据驱动视图:数据(model)的变化会驱动视图(View)自动更新

      • 双向数据绑定:在网页中,form负责采集数据,Ajax负责提交数据。数据源的变化,会被自动渲染到页面上;页面上表单采集的数据发生变化的时候,会被 vue 自动获取到,并更新到数据源中。

    • 【问】如何编写一个简单的Vue应用?(Vue实例挂载到某个元素上)

      Note

      • Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统

        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
            <!-- 开发环境版本,包含了有帮助的命令行警告 -->
        <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
        </head>
        <body>
            <div id="app">  
              {{ message }}  <!-- 声明式渲染 -->
            </div>
        </body>
            <script>
                var app = new Vue({  //创建Vue实例
                  el: '#app',  //挂载点为id = "app"的DOM元素
                  data: {
                    message: 'Hello Vue!'
                  }
                })
            </script>
        </html>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22

        现在数据和 DOM 已经被建立了关联,所有东西都是响应式的,即在当前页面的控制台上修改app.message = "Hello world",html页面会更新内容。
        在这里插入图片描述

    二、基础语法(渲染和绑定)

    • 【问】Vue实例对象中常用属性有哪些?

      Note

      • el:"" :指定vue所操作的dom范围,属性值是你获取的节点;

      • data:{}:就是vue的model,是存放数据的,属性值是一个对象或者是一个函数,在组件中的data是一个函数

      • watch:{}vue中的侦听器

      • computed:{}vue中的计算属性,看起来像methods,用起来像data

      • methods:{}:是vue中的事件方法

    • 【问】内容渲染指令有哪些?(插值表达式,v-text=‘’,v-html=‘’)

      Note

      • 内容渲染指令用来辅助开发者渲染 DOM 元素的文本内容。常用的内容渲染指令有如下 3 个:

        • {{}}插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容。注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中;

        • v-text:会覆盖元素内部原有的内容

        • v-html:不仅会覆盖原来的内容,而且可以把带有标签的字符串,渲染成真正的HTML内容

        比如:

        <div id = "box1">
           <p v-text="text2">这是我原本的内容</p>
           <div v-html="text3">这是我原本的内容</div>
        </div>
        <script>
           const vm1=new Vue({
              el:'#box1',
              data:{
                text1:'插值表达式的结果',
                text2:'v-text的结果',
                text3:'a href="http://www.baidu.com">v-html的结果'
              }
        </script>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
    • 【问】属性绑定指令如何使用?(v-bind:属性=,常配合class,style,src和href属性使用)

      Note

      • v-bind:可为元素的属性动态绑定属性值,可简写为:v-bind:语法糖)。

      • 在开发中,有哪些属性需要动态进行绑定:比如图片的链接src、网站的链接href动态绑定一些类、样式等等,比如

        <!--html-->
        <a :href="'https://www.runoob.com/vue2/'+url">点击跳转vue菜鸟教程</a>
        
        <!--script-->
        const vm2=new Vue({
             el:'#box2',
             data:{
                 url:'vue-tutorial.html'
             }
        })
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
      • v-bind:class=:动态切换class,比如:

        • 数据为某个状态时,字体显示红色。
        • 数据另一个状态时,字体显示黑色。

        绑定class有两种方式:

        • 对象语法class后面跟的是一个对象

          用法一:直接通过{}绑定一个类
          <h2 :class="{'active': isActive}">Hello World</h2>
          
          用法二:也可以通过判断,传入多个值
          <h2 :class="{'active': isActive, 'line': isLine}">Hello World</h2>
          
          • 1
          • 2
          • 3
          • 4
          • 5
        • 数组语法class后面跟的是一个数组

          用法一:直接通过{}绑定一个类
          <h2 :class="['active']">Hello World</h2>
          
          用法二:也可以传入多个值
          <h2 :class="['active', 'line']">Hello World</h2>
          
          • 1
          • 2
          • 3
          • 4
          • 5
      • v-bind:style来绑定一些CSS内联样式,绑定style有两种方式:

        • 对象语法:style后面跟的是一个对象类型,对象的key是CSS属性名称,
          对象的value是具体赋的值
          ,值可以来自于data中的属性:

          :style="{color: currentColor, fontSize: fontSize + 'px'}"
          
          • 1
        • 数组语法:style后面跟的是一个数组类型,多个值以“,“分割即可

          <div v-bind:style="[baseStyles, overridingStyles]"></div>
          
          • 1
    • 【问】事件绑定指令如何使用?(v-on:事件=方法

      Note

      • vue提供了v-on事件绑定指令,用于为DOM元素绑定事件监听。原生DOM对象有onclickoninputonkeyup原生事件替换为vue的事件绑定形式后,分别为v-on:clickv-on:inputv-on:keyup

      • v-on:可以用@进行简写(v-on:语法糖)

      • v-on指令所绑定的事件处理函数,可以接收事件参数对象event

      • $event是vue提供的特殊变量,用来表示原生的事件参数对象event

      • 参考代码如下:

        <button @click="add">自增</botton>
        <button @click="changeColor">变色</botton>
        data(){
            return{
                count:'',
            }
        }
        methods:{
            add(){
                this.count++;    
            },
            changeColor(e){
                    e.target.style.backgroundColor='red';
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
    • 【问】事件修饰符,按钮修饰符是什么?

      Note

      • 事件修饰符:在事件处理函数中调用event.preventDefault()取消在浏览器中事件的默认动作)或event.stopPropagagation()阻止事件冒泡和捕获)是非常常见的需求。参考stopPropagation, preventDefault的区别
        vue提供了事件修饰符的概念,来辅助程序员更方便的对事件的触发进行控制。常用的5个事件修饰符如下:参考[事件冒泡概念]((https://baike.baidu.com/item/%E4%BA%8B%E4%BB%B6%E5%86%92%E6%B3%A1/4211429?fr=aladdin)

        .prevent阻止默认行为(例如:阻止a连接的跳转、阻止表单的提交等)
        .stop阻止事件冒泡(即阻止传播到责任链中下一个事件处理器)
        .capture以捕获模式触发当前的事件处理函数
        .once绑定的事件只触发1次
        .self只有在event.target是当前元素自身时触发事件处理函数
    • 按钮修饰符:在监听键盘事件时,我们经常需要判断详细的按键。此时,可以为键盘相关的事件添加按键修饰符,例如

      <!--只有在"key"'Enter'时调用'vm.submit()'...>
      <input @keyup.enter="submit">
      
      <!--只有在'key''Esc'时调用'vm.clearInput()'...>
      <input @keyup.esc="clearInput">
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • 【问】双向绑定指令如何使用?(v-model 原理为v-on:input + v-bind:value;v-model对radio,checkbox,select等进行双向绑定)

      Note

      • vue提供了v-model双向数据绑定指令,用来辅助开发者在不操作DOM的前提下,快速获取表单的数据

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

        1. v-bind绑定一个value属性

        2. v-on指令给当前元素绑定input事件在普通input对应的input事件中,vue已经帮我们写好逻辑了

        也就是说下面的代码:等同于下面的代码:

        <input type="text" v-model="message">
        等同于
        <input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
        
        • 1
        • 2
        • 3
      • v-model:radio复选框分为两种情况:单个勾选框和多个勾选框

        单个勾选框v-model即为布尔值。此时inputvalue并不影响v-model的值。参考https://jiuaidu.com/jianzhan/832729/

        <template>
             <div id="app">
                 <input type="radio" id="male" value="male" v-model="gender"> male
                 <input type="radio" id="female" value="female" v-model="gender"> femalea
        
                <p>{{gender}}</p>
             </div>
        </template>
        
        <script>
        export default {
             name: 'app',
             data () {
                 return {
                  gender: ''
                 }
             }
        }
        </script>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
      • v-model:checkbox

        • 单个勾选框:

          • v-model即为布尔值。

          • 此时input的value并不影响v-model的值。

        • 多个复选框:当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组。当选中某一个时,就会inputvalue添加到数组中

          代码:

          <div id = "app">
            <!--单个复选框-->
            <label for="check">
                <input type="checkbox" v-model="checked" id="check">同意协议
            </label>
            <p>是否选中:{{checked}} <p>
          
            <!--多个复选框-->
            <label><input type="checkbox" v-model="hobbies"value="篮球">篮球</Label>
            <label><input type="checkbox" v-model="hobbies"value="足球">足球</Label>
            <label><input type="checkbox" v-model="hobbies"value="台球">台球</Label>
            <p>您选中的爱好:{{hobbies}}</p>
          </div>
          
          <script>
          let app = new Vue ({
            el:'#app',
            data:{
                checked : false ,
                hobbies: []
            }
          })
          </script >
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
      • v-model:select:和checkbox一样,select也分单选和多选两种情况。

        • 单选:只能选中一个值

          • v-model绑定的是一个值

          • 当我们选中option中的一个时,会将它对应的value赋值到mySelect中

          代码:

          <!--选择一个值-->
          <select v-model="mySelect">
              <option value="apple">苹果</option>
              <option value="orange">橘子</option>
              <option value="banana">香蕉</option>
          </select>
          <p>您最喜欢的水果:{{mySelect}} </p>
          ...
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
        • 多选:可以选中多个值

          • v-model绑定的是一个数组

          • 当选中多个值时,就会将选中的option对应的value添加到数组mySelects中

          代码:

          <select v-model="mySelect" multiple>
              <option value="apple">苹果</option>
              <option value="orange">橘子</option>
              <option value="banana">香蕉</option>
          </select>
          <p>您最喜欢的水果:{{mySelect}} </p>
          
          <script>
          let app = new Vue ({
              el:'#app',
              data:{
                  mySelect:'apple',
                  mySelects : []
              }
          })
          </script>
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
    • 【问】条件渲染指令如何使用?它们之间有什么区别(v-if动态添加DOM元素,有更高的切换开销;v-show动态添加样式,有更高的初始渲染开销)

      Note

      • 条件渲染指令包括:v-ifv-show

      • 实现原理不同:

        • v-if指令会动态地创建或移除DOM元素,从而控制元素在页面上的显示与隐藏;

        • v-show指令会动态为元素添加或移除style="display:none;"样式,从而控制元素的显示与隐藏;

      • 性能消耗不同:

        v-if更高的切换开销,而v-show更高的初始渲染开销。因此:

        • 如果需要非常频繁地切换,则使用v-show较好
        • 如果在运行时条件很少改变,则使用v-if 较好
      • 两者使用的区别:

        • v-ifv-elsev-else-if条件性的渲染某元素,判定为true时渲染,否则不渲染;

        • v-show条件不满足令其displaynone

        <div v-if="score<60">不及格</div>
        <div v-else-if="60<=score&&score<90">中等</div>
        <div v-else="score>=90">优秀</div>
        
        <div v-show="true">display:block显示</div>
        <div v-show="false">display:none隐藏</div>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
    • 【问】列表渲染指令如何使用?(v-for常配合checkbox、options、li标签使用)

      Note

      • vue提供了v-for列表渲染指令,用来辅助开发者基于一个数组来循环渲染一个列表结构v-for指令需要使用item in items形式的特殊语法,其中:

        • items是待循环的数组
        • item是被循环的每一项

        代码:

        <ul id='app'>
            <li v-for="item in list"> 姓名是:{{item.name}} </li>
        </ul>
        
        <script>
        let app = new Vue ({
            el:'#app',
            data:{
                list: [
                    {'id' : 1, 'name': 'wang'},
                    {'id' : 2, 'name': 'liu'}
                ]
            }
        })
        </script>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
      • v-for 指令还支持一个可选的第二个参数,即当前项的索引。语法格式为(item,index) in items,示例代码如下(itemindex变量名可随便取):

        <ul id='app'>
            <li v-for="(item,index) in list">{{index + 1}}个同学,姓名是:{{item.name}} </li>
        </ul>
        
        • 1
        • 2
        • 3
      • 【问】如何提高列表渲染指令的性能?(v-for在vue2.2+之后必须使用:key来提高渲染效率),参考列表渲染 — Vue.js

        Note

        • 当列表的数据变化时,默认情况下,vue会尽可能的复用已存在的DOM元素,从而提升渲染的性能。但这种默认的性能优化策略,会导致有状态的列表无法被正确更新。

        • 为了给vue一个提示,以便它能跟踪每个节点的身份,从而在保证有状态的列表被正确更新的前提下,提升渲染的性能。此时,需要为每项提供一个唯一的key属性:

          <ul id='app'>
              <li v-for="(user,i) in list" :key="user.id"> 姓名是:{{item.name}} </li>
          </ul>
          
          • 1
          • 2
          • 3
    • 【问】keep-alive的基本使用?

      Note

      • keep-alive是什么

        • 组件被缓存时,会自动触发组件的deactivated生命周期函数。

        • 组件被激活时,会自动触发组件的activated生命周期函数。

        • 当组件第一次被创建,会执行created生命周期函数,也会执行activated生命周期函数。之后组件再被激活,只会触发activated而不会触发created

      • keep-alive的基本使用以及基本属性:

        <keep-alive>
            <组件名></组件名>
        <keep-alive>
        
        • 1
        • 2
        • 3
        • include包含的组件(可以为字符串,数组,以及正则表达式,只有名称匹配的组件会被缓存)。

          // 只缓存组件name为a和b的组件
          <keep-alive include="a,b"> 
            <component />
          </keep-alive>
          
          • 1
          • 2
          • 3
          • 4
        • exclude排除的组件(可以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存)。

          // 组件name为c的组件不缓存(可以保留它的状态或避免重新渲染)
          <keep-alive exclude="c"> 
            <component />
          </keep-alive>
          
          // 如果同时使用include,exclude,那么exclude优先于include, 下面的例子只缓存a组件
          <keep-alive include="a,b" exclude="b"> 
            <component />
          </keep-alive>
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
        • max缓存组件的最大值(类型为字符或者数字,可以控制缓存组件的个数);

          // 如果缓存的组件超过了max设定的值5,那么将删除第一个缓存的组件
          <keep-alive exclude="c" max="5"> 
            <component />
          </keep-alive>
          
          • 1
          • 2
          • 3
          • 4
    • 【问】侦听器watch的作用及如何使用?(侦听器主要用于监听data中存储的数据对象是否发生变化;设置deep得到嵌套侦听器;设置immediate得到即时回调侦听器),参考侦听器 | Vue.js《Vue 入门教程》Vue 侦听器

      Note

      • watch侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。有两种创建方式:

        • 方法格式的侦听器:watch : { key : function(new,old){} }

        • 对象格式的侦听器:watch : {key : {handler(new,old){}, deep, immediate }支持创建嵌套侦听器和回调侦听器

      • 侦听器简单例子:

        • 侦听器 watch 实际是 vue 实例上的一个对象属性。当我们需要对 vue 实例上某个属性进行侦听时,我们以需要被侦听的属性名作为 watch 对象的键,以一个函数 function 作为该键的值

        • 函数 function 接收两个参数:侦听数据变化之后的值newValue;侦听数据变化之前的值oldValue

        var vm = new Vue({
          el: '#app',
          data() {
            return {
              count: 0
            }
          },
          watch: {
            count: function(newVal, oldVal) {
              // 具体处理逻辑
            },
          }
        })
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
      • 深层侦听器watch 默认是浅层的,仅在被赋新值时,才会触发回调函数。如果想侦听所有嵌套的变更需要将deep选项设置为true,得到使用深层侦听器;

        export default {
          watch: {
            question : {
              handler(newValue, oldValue) {
                // 注意:在嵌套的变更中,
                // 只要没有替换对象本身,
                // 那么这里的 `newValue` 和 `oldValue` 相同
              },
              deep: true
            }
          }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
      • 即时回调的侦听器watch 默认是懒执行的,仅当数据源变化时,才会执行回调。但在某些场景中,举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据,即在创建侦听器时,立即执行一遍回调。这里可以为侦听器设置 immediate: rue强制回调函数立即执行

        export default {
          // ...
          watch: {
            question: {
              handler(newQuestion) {
                // 在组件实例创建时会立即调用
              },
              // 强制立即执行回调
              immediate: true
            }
          }
          // ...
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
      • 侦听器综合案例(对字典、整型进行监听):参考Vue基础之侦听器详解

        const app = createApp({
          data() {
            return {
              a: 1,
              b: 2,
              c: {
                d: 4
              },
              e: 5,
              f: 6
            }
          },
          watch: {
            // 侦听顶级 property
            a(val, oldVal) {
              console.log(`new: ${val}, old: ${oldVal}`)
            },
            // 字符串方法名
            b: 'someMethod',
            // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
            c: {
              handler(val, oldVal) {
                console.log('c changed')
              },
              deep: true
            },
            // 侦听单个嵌套 property
            'c.d': function (val, oldVal) {
              // do something
            },
            // 该回调将会在侦听开始之后被立即调用
            e: {
              handler(val, oldVal) {
                console.log('e changed')
              },
              immediate: true
            },
            // 你可以传入回调数组,它们会被逐一调用
            f: [
              'handle1',
              function handle2(val, oldVal) {
                console.log('handle2 triggered')
              },
              {
                handler: function handle3(val, oldVal) {
                  console.log('handle3 triggered')
                }
                /* ... */
              }
            ]
          },
          methods: {
            someMethod() {
              console.log('b changed')
            },
            handle1() {
              console.log('handle 1 triggered')
            }
          }
        })
        const vm = app.mount('#app')
        vm.a = 3 // => new: 3, old: 1
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
        • 47
        • 48
        • 49
        • 50
        • 51
        • 52
        • 53
        • 54
        • 55
        • 56
        • 57
        • 58
        • 59
        • 60
        • 61
        • 62
    • 【问】计算属性computed是什么?(如果相关数据发生改变,computed会重新计算并返回结果,有点类似watch侦听的意思,应该也是采用观察者设计模式;默认走缓存),参考计算属性

      Note

      • 关于computed计算属性的理解:

        1、computed 和 data同级,计算属性写在computed中

        2、写起来像方法,用起来像属性

        3、计算属性虽然称为属性,但其本质是一个函数;

        4、虽然计算属性本质是一个函数,但是在页面中使用计算属性时,不要加()

        5、一定要有返回值

        6、可以使用data中的已知值;

        7、只要跟计算属性相关的数据发生了变化,计算属性就会重新计算,不相关的属性无论如何变化,都不会导致计算属性变化;

        8、计算属性名不能和data中的数据重名(因为要使用data中的数据)。

      • 使用例子:

        <template>
            {{reversedMsg}}
        </template>
        
        export default {
        
          data(){
            return{
                msg : ""
            }
          },
        
          computed: {
            reversedMsg(){
              return this.msg.split('').reverse().join('')
            }
          }
          // ...
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
    • 【问】computed与watch的区别和使用场景?,参考Computed 和 Watch 的区别

      Note

      • computed计算属性的作用:

        • 1)解决模板中放入过多的逻辑会让模板过重且难以维护的问题。例如两个数据的拼接或字体颜色的判断。

        • 2)它支持缓存,只有依赖的数据发生了变化,才会重新计算。例如模板中多次用到数据拼接可以用计算属性,只执行一次计算,除非数据发生变化。

        • 3)不支持异步,如果有异步操作,无法监听数据的变化。

        • 4)如果属性值是函数,默认使用get方法,函数的返回值就是属性的属性值。还有一个set方法,当数据变化时就会调用set方法

        • 5)computed的值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data声明过,或者父组件传递过来的props中的数据进行计算的。

      • watch侦听器的作用:

        • 1)它不支持缓存,数据变化时,它就会触发相应的操作。

        • 2)支持异步监听

        • 3)接受两个参数,第一个是最新的值,第二个是变化之前的值。

        • 4)监听data或者props传来的数据,发生变化时会触发相应操作。有两个参数:

          • immediate:立即触发回调函数。

          • deep:深度监听,发现数据内部的变化,在复杂数据类型中使用,例如数组中的对象发生变化。需要注意的是,deep无法监听到数组和对象内部的变化。

      • computedwatch的使用场景:

        • computed:是多对一,多个数据影响一个。当需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时都要重新计算(缓存多个值)。

        • watch:是一对多,一个数据发生变化,执行相应操作会影响多个数据。当需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许执行异步操作 ( 访问一个 API ),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

    三、组件化 & 模块化开发(组件化系统、全局和局部组件、父子组件通讯 - 传参 & 事件监听与回调、兄弟通讯-共享变量绑定多个事件、插槽的使用)

    Note:不使用VueCDN源也可以创建父子组件,并实现通信,参考不用vue-cli 创建vue组件

    • 【问】Vue组件化系统的基础概念
      Note

      • 组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。其实在HTML中,几乎任意类型的应用界面都可以抽象为一个组件树
        在这里插入图片描述

      • Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例

    • 【问】Vue中如何注册组件?(Vue.extend()创建组件,Vue.component()注册组件)

      Note

      • 组件的使用分成三个步骤:

        • 创建组件构造器;
        • 注册组件;
        • 使用组件;
      • 组件使用的简单案例(这里的组件是全局的组件):Vue.extend()这个方法 如果发现vue.extend不是方法,换其他cdn源试试,之前试了https://unpkg.com/vue@3/dist/vue.global.js没效果。

        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        </head>
        <body>
            <div id="app1">
                <my-cpn></my-cpn>
            </div>
        
            <div id="app2">
                <my-cpn></my-cpn>
            </div>
        </body>
        
        <script>
        //1.创建组件构造器
        const myComponent = Vue.extend({
            template:
                `

        组件标题

        我是组件中的一个段落内容

        `
        }); //2.注册组件,并且定义组件标签的名称 Vue.component('my-cpn',myComponent) let app = new Vue({ el:'#app1' }) let app1 = new Vue({ el:'#app2' }) </script>
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36

        效果如下:
        在这里插入图片描述

      • Vue.extend():调用Vue.extend()创建的是一个组件构造器

        • 通常在创建组件构造器时,传入template代表我们自定义组件的模板

        • 该模板就是在使用到组件的地方,要显示的HTML代码。

        • 事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。

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

        • 注册组件的标签名

        • 组件构造器

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

    • 【问】什么是全局组件和局部组件?(局部组件在Vue实例中通过components :{组件标签 : 组件名}注册,全局组件通过Vue.component()全局注册)

      Note

      • 当我们通过调用Vue.component()注册组件时,组件的注册是全局的,这意味着该组件可以在任意Vue示例下使用。 如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件

      • 全局组件看上一问代码;

      • 局部组件代码如下(效果为app2对应的div没有被渲染):

        <!-- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> -->
        
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        </head>
        <body>
            <div id="app1">
                <my-cpn></my-cpn>
            </div>
        
            <div id="app2">
                <my-cpn></my-cpn>
            </div>
        </body>
        
        <script>
        //1.创建组件构造器
        const myComponent = Vue.extend({
            template:
                `

        组件标题

        我是组件中的一个段落内容

        `
        }); let app = new Vue({ el:'#app1', components : { 'my-cpn' : myComponent } }) let app1 = new Vue({ el:'#app2' }) </script>
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38

        效果如下:my-cpn只在挂载了app1Vue实例上可见
        在这里插入图片描述

    • 【问】什么是父组件和子组件?(在父组件创建(Vue.extend())时通过components属性引入子组件;实例注册了父组件,父组件中注册了子组件为child-cpn,此时实例访问不到子组件child-cpn

      Note

      • 父组件和子组件:组件和组件之间存在层级关系,而其中一种非常重要的关系就是父子组件的关系。我们来看通过代码如何组成的这种层级关系(在父组件创建(Vue.extend())时通过components属性引入子组件):

        <!-- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> -->
        
        <!-- vue的template中只能有一个根节点 -->
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        </head>
        <body>
            <div id="app1">
                <parent-cpn></parent-cpn>
                <p>Hello world</p>
                <child-cpn></child-cpn>
            </div>
        
            <div id="app2">
                <child-cpn></child-cpn>
            </div>
        </body>
        
        <script>
        //1.创建组件构造器
        const childComponent = Vue.extend({
            template:
                `

        组件标题

        我是子组件

        `
        }); const parentComponent = Vue.extend({ template: `

        组件标题

        我是父组件

        `
        , components : { "child-cpn" : childComponent } }); let app = new Vue({ el:'#app1', components : { 'parent-cpn' : parentComponent } }) let app1 = new Vue({ el:'#app2', components : { "child-cpn" : childComponent } }) </script>
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
        • 47
        • 48
        • 49
        • 50
        • 51
        • 52
        • 53
        • 54
        • 55
        • 56

        效果如下:app1无法识别app2可以识别
        在这里插入图片描述

      • 父子组件错误用法:以子标签的形式在Vue实例中使用

        • 因为当子组件注册到父组件的components时,Vue会编译好父组件的模块

        • 该模板的内容已经决定了父组件将要渲染的HTML(相当于父组件中已经有了子组件中的内容了)

        • 是只能在父组件中被识别的。类似这种用法,是会被浏览器忽略的。

    • 【问】父组件和子组件之间如何通讯?(渲染父组件时会同时渲染子组件;父传子用props,子组件通过props定义要接收到参数;子传父用$emit:子组件定义事件,父组件定义方法)

      Note

      • 父传子用props:子组件通过props定义要接收到参数

        • 第一步:引入子组件。

        • 第二步:在数据源中定义要传入子组件的数据parentMsg

        • 第三步:在使用child组件时传入parentMsg

        • 第四步:在子组件中,要 props:['自定义属性名']来接收传过来的参数。

        父组件参考代码

        <template>
          <div>
            <h2>parent</h2>
            <!--3、传入parentMsg-->
            <child :visible="visible"></chld>
          </div>
        </template>
        <script>
        //1、引入子组件
        import child from './child.vue'
        export default {
            data() {
                return {
                    //2、定义要传入子组件的数据parentMsg
                    visible:'true'
                }
            },
            components:{
                child
            }
        }
        </script>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22

        子组件参考代码

        <template>
          <div>
            {{visible}}
          </div>
        </template>
        <script>
        export default {
            name:'child',
            props:['visible']//接收
        }
        </script>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11

        如果使用vuecdn源,将所有Vue实例(封装着template属性)写在一个html里,则可以在同一个html页面上实现父组件向子组件传值

        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        </head>
        <body>
            <div id="app1">
                <parent-cpn></parent-cpn>
            </div>
        
            <div id="app2">
                <child-cpn></child-cpn>
            </div>
        </body>
        
        <script>
        //1 创建子组件
        const childComponent = Vue.extend({
            name : 'child',
            props : ['visible'],
            template:
                `

        {{visible}}

        我是子组件

        `
        }); //2 创建父组件 const parentComponent = Vue.extend({ data() { return { //2、定义要传入子组件的数据parentMsg visible : '进入子组件' } }, template: `

        组件标题

        我是父组件

        >
        `
        , components : { "child-cpn" : childComponent } }); let app = new Vue({ el:'#app1', components : { 'parent-cpn' : parentComponent } }) let app1 = new Vue({ el:'#app2', components : { "child-cpn" : childComponent } }) </script>
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
        • 47
        • 48
        • 49
        • 50
        • 51
        • 52
        • 53
        • 54
        • 55
        • 56
        • 57
        • 58
        • 59
        • 60
        • 61
        • 62

        效果图如下:
        在这里插入图片描述

      • 子传父用$emit:子组件定义事件标识(sendmsg),通过this.$emit(‘sendmsg’,所需要传的值)向父组件发送该事件标识,父组件通过方法(getmsg)来监听指定事件标识中绑定的值(事件监听)。

        emit使用方法:this.$emit(‘自定义事件名’,所需要传的值)

        • 第一步:首先在子组件中定义一个事件,并且使用emit发送给父组件,在示例中子组件使用的click事件触发发送自定义事件(sendmsg)

        • 第二步:在父组件中需要定义方法(getmsg)接受自定义事件(sendmsg)

        • 第三步:在使用子组件时,

        【子组件】发送值代码:

        <template>
          <div>
            <button @click="childmsg">点我试试</button>
          </div>
        </template>
        <script>
        export default {
            name:'child',
            data() {
                return {
                     msg:"This is the first word from child"
                }
            },
            methods:{
                //点击按钮则向父组件传自定义事件sendmsg,childmsg
                childmsg(){
                    this.$emit('sendmsg',this.msg)
                }
            }
        }
        </script>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21

        【父组件】接收值代码:

        <template>
          <div>
            <child @sendmsg="getmsg"></child>
          </div>
        </template>
        <script>
        import child from './child.vue'
        export default {
            data() {
                return {
                }
            },
            components:{
                child
            },
            methods:{
                getmsg(val){
                    console.log(val)
                }
            }
        }
        </script>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22

        如果使用vuecdn源,将所有Vue实例(封装着template属性)写在一个html里,则可以在同一个html页面上实现子组件向父组件传值

        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        </head>
        <body>
            <div id="app1">
                <parent-cpn></parent-cpn>
            </div>
        
            <div id="app2">
                <child-cpn></child-cpn>
            </div>
        </body>
        
        <script>
        //1 创建子组件
        const childComponent = Vue.extend({
            name : 'child',
            props : ['visible'],
            template:
                `

        {{visible}}

        我是子组件

        `
        , methods : { sendMsg(){ this.$emit("getChildMsg","child出来了"); //"getChildMsg"是子组件向父组件暴露的参数方法标识 } } }); //2 创建父组件 const parentComponent = Vue.extend({ data() { return { //2、定义要传入子组件的数据parentMsg childMsg : "", visible : '进入子组件' } }, template: `

        组件标题

        我是父组件

        {{childMsg}}

        `
        , components : { "child-cpn" : childComponent }, methods : { getMsg(val){ this.childMsg = val; } } }); let app = new Vue({ el:'#app1', components : { 'parent-cpn' : parentComponent } }) let app1 = new Vue({ el:'#app2', components : { "child-cpn" : childComponent } }) </script>
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
        • 47
        • 48
        • 49
        • 50
        • 51
        • 52
        • 53
        • 54
        • 55
        • 56
        • 57
        • 58
        • 59
        • 60
        • 61
        • 62
        • 63
        • 64
        • 65
        • 66
        • 67
        • 68
        • 69
        • 70
        • 71
        • 72
        • 73
        • 74
        • 75

        实现效果如下:
        在这里插入图片描述

    • 【问】兄弟组件是什么?其之间如何进行数据共享?(可通过EventBus事件总线对象实现,即$emit发送数据(等价notify),$on监听数据(等价wait),基于事件驱动通信,参考Vue 兄弟组件之间的通信

      Note

      • Vue中实现兄弟组件的通讯一般为2种方式:

        1. 一种方法是让父组件允当两个子组件之间的中间件(中继)

        2. 另一种就是使用EventBus(事件总线),它允许两个子组件之间直接通讯,而不需要涉及父组件。

        由于中继方式比较简单,这里不做赘述,只讲EventBus方式。

      • EventBus事件总线方式:

        • 第一步:在兄弟组件同目录下创建eventBus.js,然后创建vue实例:

          import Vue from 'vue'
          export default new Vue()
          
          • 1
          • 2
        • 第二步:在【兄弟组件A】中,引入eventBus.js(定义为bus对象),接着定义数据msg,编写方法用于在监听到share事件后发送msg

          import bus from './eventBus.js'
          <button @click="sendMsg">
          export default{
           data(){
             return{
                     msg:'hello'  
               }  
           },
           methods:{
               sendMsg(){
                   bus.$emit('share',this.msg); 
              } 
            }
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
        • 第三步:在【兄弟组件B】中,引入eventBus.js,定义数据newMsg,编写方法用于接收msg和赋值给newMsg

          import bus from './eventBus.js'
          <button @click="sendMsg">
          export default{
          data(){
              return{
                  newMsg:[] 
              } 
          },
          created:{
            bus.$on('share',val=>{
              this.newMsg=val;  
            }) 
          }
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
        • 如果使用vuecdn源,将所有Vue实例(封装着template属性)写在一个html里,则可以在同一个html页面上实现子组件向父组件传值

           <!-- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> -->
        
           <!-- vue的template中只能有一个根节点 -->
           <html lang="en">
           <head>
               <meta charset="UTF-8">
               <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
           </head>
           <body>
               <div id="app1">
                   <bro1-cpn></bro1-cpn>
               </div>
           
               <div id="app2">
                   <bro2-cpn></bro2-cpn>
               </div>
           </body>
           
           <script>
           //1. 创建总线对象
           bus1 = new Vue();  //兄弟1向兄弟2传值 或者 兄弟2向兄弟1传值
           // bus2 = new Vue();  //兄弟2向兄弟1传值
           
           //事件监听
           bus1.$on('getMsg1', function (val) {
                   alert(val)
           })
           bus1.$on('getMsg2', function (val) {
                   alert(val)
           })
           
           //2. 创建兄弟组件1
           const brotherComp1 = Vue.extend({
               template:
                   `

        我是兄弟组件1

        `
        , methods : { sendMsg(){ bus1.$emit("getMsg1","兄弟2,我来了"); //"getChildMsg"是子组件向父组件暴露的参数方法标识 } } }); //3. 创建兄弟组件2 const brotherComp2 = Vue.extend({ template: `

        我是兄弟组件2

        `
        , methods : { sendMsg(){ bus1.$emit("getMsg2","兄弟1,我来了"); } } }); let app = new Vue({ el:'#app1', components : { 'bro1-cpn' : brotherComp1 } }) let app1 = new Vue({ el:'#app2', components : { "bro2-cpn" : brotherComp2 } }) </script>
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
        • 47
        • 48
        • 49
        • 50
        • 51
        • 52
        • 53
        • 54
        • 55
        • 56
        • 57
        • 58
        • 59
        • 60
        • 61
        • 62
        • 63
        • 64
        • 65
        • 66
        • 67
        • 68
        • 69
        • 70
        • 71
        • 72
        • 73
        • 74
        • 75

        实现效果如下:
        在这里插入图片描述

      • 父子组件通信 vs 兄弟组件通信

        • 这里声明事件share字符串, 兄弟组件之间通过Vue实例对象进行通信,通过obj.$emit向其他兄弟组件发送关于share事件的消息,其他兄弟组件通过obj.$on监听share事件的消息(即对象的waitnotify操作)
        • 和父子组件通信不同,兄弟组件可以通过一个共享Vue实例(bus),该实例可以绑定多个事件标识(share1share2)来实现通信,而父组件向子组件传参时,由于其自身嵌套关系
          • 父组件传值给子组件时相当于函数调用,父组件不用使用$emit,子组件不用使用$on
          • 子组件传值给父组件时相当于函数回调,父组件通过对指定的事件标识进行监听(不用使用$on),而子组件通过$emit进行回调,告知父组件已经处理好值了。
    • 【问】插槽是什么?插槽的用法(需要先理解父子组件之间如何通讯;默认插槽,具名插槽,作用域插槽(默认,具名,解构),动态插槽名),参考插槽 Slots | Vue.js

      Note

      • 在学习插槽之前需要先理解父子组件之间如何通讯
      • 总体思想是父组件在子组件的指定槽位中定制自己的模板(template),然后由子组件渲染模板内容(子组件中的slot起到占位符的作用)。
      • v-slot的基本用法:父组件可以通过子组件中的插槽显示自定义的内容

        • vue 官方规定:每一个 slot 插槽都要有一个name名称
          如果省略了 slotname则有一个默认名称 default
          默认情况下,使用组件时提供的内容会被填充到 namedefault 的插槽内。

        • 使用 v-slot:xxx, 其中 xxx为插槽 name 值,只能放在父组件标签内

        • v-slot是一个虚拟标签,只起到包裹性质的作用,不会被渲染为实质性的 html 元素,v-slot:xxx 可以简写为 #xxx

        • v-slot在父子组件中的使用:

          【父组件声明了子组件为Left,并使用具名插槽】
          <Left>
              <template v-slot:mySlot>
                  <p>这是在 Left 组件声明的p标签</p>
              </template>
          </Left>
          
          【子组件定义了mySlot插槽】
          <div style="color:#33e;background:#ee2">
              <slot name="mySlot"></slot>
          </div>
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11

          在这里插入图片描述

      • 默认插槽(default):

        • 当使用组件指定了插槽内容时,优先显示指定的内容
          当没有指定内容时,渲染 slot 标签内的默认内容

        • 简单演示:父组件显式提供的内容会取代子组件的默认内容

          //父组件声明子组件为SubmitButton
          <SubmitButton>Save</SubmitButton>
          
          //子组件默认内容
          <button type="submit">
            <slot>
              Submit <!-- 默认内容 -->
            </slot>
          </button>
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
      • 具名插槽(name):

        • 一个组件中包含多个插槽出口是很有用的,为了将内容置入组件的不同插槽中,要用带有name属性的插槽

        • 简单演示:没有提供 name 出口会隐式地命名为 “default”(默认插槽) 。

          //子组件定义多个插槽
          <div class="container">
            <header>
              <slot name="header"></slot>
            </header>
            <main>
              <slot></slot>
            </main>
            <footer>
              <slot name="footer"></slot>
            </footer>
          </div>
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12

          在父组件中使用 时,我们需要一种方式将多个插槽内容传入到各自目标插槽的出口。此时就需要用到具名插槽了

          //父组件将子组件声明为
          <BaseLayout>
            <template v-slot:header>
              <!-- header 插槽的内容放这里 -->
            </template>
          </BaseLayout>
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
        • 具名插槽传入内容时需要使用一个含 v-slot 指令的