【前端-VUE+TS】Vue3基础语法(一)

前情提要:

 Vue3开发基础语法(一):

1.1. methods中的this

1.1.1. 不能使用箭头函数

我们在methods中要使用data返回对象中的数据,那么这个this是必须有值的,并且应该可以通过this获取到data返回对象中的数据。

那么我们这个this能不能是window呢?

我们来看下面的代码:

const App = {
  template: "#my-app",
  data() {
    return {
      counter: 0
    }
  },
  methods: {
    increment: () => {
      // this.counter++;
      console.log(this);
    },
    decrement() {
      this.counter--;
    }
  }
}

为什么是window呢?

this到底是如何查找和绑定的呢?

1.1.2. this到底指向什么

事实上Vue的源码当中就是对methods中的所有函数进行了遍历,并且通过bind绑定了this:

vue3源码的this绑定过程

1.2. VSCode增加代码片段

我们在前面练习Vue的过程中,有些代码片段是需要经常写的,我们在VSCode中我们可以生成一个代码片段,方便我们快速生成。

VSCode中的代码片段有固定的格式,所以我们一般会借助于一个在线工具来完成。

具体的步骤如下:

生成代码片段

打开生成代码片段的配置

选择HTML

生成代码片段

模板语法:

React的开发模式:

Vue也支持jsx的开发模式(后续有时间也会讲到):

所以,对于学习Vue来说,学习模板语法是非常重要的。 

2.1. 插值语法

2.1.1. mustache语法

如果我们希望把数据显示到模板(template)中,使用最多的语法是 “Mustache”语法 (双大括号) 的文本插值:

{{message}}

并且我们前端提到过,data返回的对象是有添加到Vue的响应式系统中,当data中的数据发生改变时,对应的内容也会发生更新。

当然,Mustache中不仅仅可以是data中的属性,也可以是一个JavaScript的表达式:

  
      
            

{{message}}

            

{{ counter * 2}}

      

{{message.split(" ").reverse().join(" ")}}

            

{{reverse(message)}}

           

但是下面的代码是错误的:

{{var name = "Hello"}}

{{ if (true) { return message } }}

三元运算符是可以的:

2.1.2. v-once

用于指定元素或者组件只渲染一次:

当前计数: {{counter}}+1

如果是子节点,也是只会渲染一次:

  

当前计数: {{counter}}

  +1

2.1.3. v-text

用于更新元素的 textContent:

{{msg}}

2.1.4. v-html

默认情况下,如果我们展示的内容本身是 html 的,那么vue并不会对其进行特殊的解析。

如果我们希望这个内容被Vue可以解析出来,那么可以使用 v-html 来展示:

  
      
      

 2.1.5. v-pre

v-pre用于跳过元素和它的子元素的编译过程,显示原始的Mustache标签:

{{message}}

显示效果

2.1.6. v-cloak

这个指令保持在元素上直到关联组件实例结束编译。

和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到组件实例准备完毕。

[v-cloak] {
  display: none;
}
  {{ message }}

 不会显示,直到编译结束。

2.2. v-bind

前端讲的一系列指令,主要是和内容相关的,元素除了内容之外还会有各种各样的属性。

绑定属性我们使用v-bind:

2.2.1. 绑定基本属性

前面我们讲的Mustache等语法主要是将内容插入到 innerHTML 中。

很多时候,元素的属性也是动态的:

   

2.2.2. 绑定class属性

对象语法

  • 我们可以传给 :class (v-bind:class 的简写) 一个对象,以动态地切换 class。
      
              {{message}}
                
            哈哈哈
        切换        哈哈哈
            呵呵呵
          

    数组语法:

    • 我们可以把一个数组传给 :class,以应用一个 class 列表;
         

       2.2.3. 绑定style属性

      对象语法:

      • :style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。
        • CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名
             

          数组语法:

          • :style 的数组语法可以将多个样式对象应用到同一个元素上;
               

             2.2.4. 动态绑定属性

            属性的名称和值都是动态的

             

             2.2.5. 绑定一个对象

            info对象会被拆解成div的各个属性;

            2.3. v-on

            前面我们绑定了元素的内容和属性,在前端开发中另外一个非常重要的特性就是交互。

            在前端开发中,我们需要经常和用户进行各种各样的交互:

            • 这个时候,我们就必须监听用户发生的时间,比如点击、拖拽、键盘事件等等
            • 在Vue中如何监听事件呢?使用v-on指令

              v-on的使用:

              • 缩写:@
              • 预期:Function | Inline Statement | Object
              • 参数:event
              • 修饰符:
                • .stop - 调用 event.stopPropagation()。
                • .prevent - 调用 event.preventDefault()。
                • .capture - 添加事件侦听器时使用 capture 模式。
                • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
                • .{keyAlias} - 仅当事件是从特定键触发时才触发回调。
                • .once - 只触发一次回调。
                • .left - 只当点击鼠标左键时触发。
                • .right - 只当点击鼠标右键时触发。
                • .middle - 只当点击鼠标中键时触发。
                • .passive - { passive: true } 模式添加侦听器
                  • 用法:绑定事件监听

                    案例演练:

                       

                    2.4. 条件渲染

                    在某些情况下,我们需要根据当前的条件决定某些元素或组件是否渲染,这个时候我们就需要进行条件判断了。

                    Vue提供了下面的指令来进行条件判断:

                    • v-if
                    • v-else
                    • v-else-if
                    • v-show

                      下面我们来对它们进行学习。

                       2.4.1. v-if、v-else、v-else-if

                      v-if、v-else、v-else-if用于根据条件来渲染某一块的内容:

                      • 这些内容只有在条件为true时,才会被渲染出来;
                      • 这三个指令与JavaScript的条件语句if、else、else if类似;

                        v-if的渲染原理:

                        • v-if是惰性的;
                        • 当条件为false时,其判断的内容完全不会被渲染或者会被销毁掉;
                        • 当条件为true时,才会真正渲染条件块中的内容;

                          2.4.2. template元素

                          因为v-if是一个指令,所以必须将其添加到一个元素上:

                          • 但是如果我们希望切换的是多个元素呢?
                          • 此时我们渲染div,但是我们并不希望div这种元素被渲染;
                          • 这个时候,我们可以选择使用template;

                            template元素可以当做不可见的包裹元素,并且在v-if上使用,但是最终template不会被渲染出来:

                            • 有点类似于小程序中的block
                               

                              2.4.3. v-show

                              v-show和v-if的用法看起来是一致的:

                               

                               2.4.4. v-show和v-if区别

                              首先,在用法上的区别:

                              • v-show是不支持template;
                              • v-show不可以和v-else一起使用;

                                其次,本质的区别:

                                • v-show元素无论是否需要显示到浏览器上,它的DOM实际都是有渲染的,只是通过CSS的display属性来进行切换;
                                • v-if当条件为false时,其对应的原生压根不会被渲染到DOM中;

                                  开发中如何进行选择呢?

                                  • 如果我们的原生需要在显示和隐藏之间频繁的切换,那么使用v-show;
                                  • 如果不会频繁的发生切换,那么使用v-if;

                                    2.5. 列表渲染

                                    在真实开发中,我们往往会从服务器拿到一组数据,并且需要对其进行渲染。

                                    • 这个时候我们可以使用v-for来完成;
                                    • v-for类似于JavaScript的for循环,可以用于遍历一组数据;

                                      2.5.1. v-for基本使用

                                      v-for的基本格式是 "item in 数组":

                                      • 数组通常是来自data或者prop,也可以是其他方式;
                                      • item是我们给每项元素起的一个别名,这个别名可以自定来定义;
                                         

                                        我们知道,在遍历一个数组的时候会经常需要拿到数组的索引:

                                        • 如果我们需要索引,可以使用格式:"(item, index) in 数组";
                                        • 注意上面的顺序:数组元素项item是在前面的,索引项index是在后面的;
                                           

                                          2.5.2. v-for支持类型

                                          v-for也支持遍历对象,并且支持有一二三个参数:

                                          • 一个参数:"value in object";
                                          • 二个参数:"(value, key) in object";
                                          • 三个参数:"(value, key, index) in object";
                                             

                                            2.5.3. template元素

                                            类似于v-if,你可以使用 template 元素来循环渲染一段包含多个元素的内容:

                                            • 我们使用template来对多个元素进行包裹,而不是使用div来完成;
                                                 

                                              2.5.4. 数组更新检测

                                              Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:

                                              • push()
                                              • pop()
                                              • shift()
                                              • unshift()
                                              • splice()
                                              • sort()
                                              • reverse()

                                                替换数组的方法

                                                上面的方法会直接修改原来的数组,但是某些方法不会替换原来的数组,而是会生成新的数组,比如 filter()、concat() 和 slice()。

                                                const nums = [10, 21, 34, 6];
                                                const newNums = nums.filter(num => num % 2 === 0);
                                                console.log(newNums);

                                                2.6. key和diff算法

                                                2.6.1. 认识VNode和VDOM

                                                在使用v-for进行列表渲染时,我们通常会给元素或者组件绑定一个key属性。

                                                这个key属性有什么作用呢?我们先来看一下官方的解释:

                                                • key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes;
                                                • 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法;
                                                • 而使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素;

                                                  官方的解释对于初学者来说并不好理解,比如下面的问题:

                                                  • 什么是新旧nodes,什么是VNode?
                                                  • 没有key的时候,如何尝试修改和复用的?
                                                  • 有key的时候,如何基于key重新排列的?

                                                    我们先来解释一下VNode的概念:

                                                    • 因为目前我们还没有比较完整的学习组件的概念,所以目前我们先理解HTML元素创建出来的VNode;
                                                    • VNode的全称是Virtual Node,也就是虚拟节点;
                                                    • 事实上,无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode;
                                                    • VNode的本质是一个JavaScript的对象;
                                                      哈哈哈

                                                      在我们的Vue中会被转化创建出一个VNode对象:

                                                      const vnode = {
                                                      	  type: 'div',
                                                      	  props: { 
                                                      	    'class': 'title',
                                                      	    style: {
                                                      	      'font-size': '30px',
                                                      	      color: 'red'
                                                      	    }
                                                      	  },
                                                      	  children: '哈哈哈'
                                                      	}
                                                      

                                                      Vue内部在拿到vnode对象后,会对vnode进行处理,渲染成真实的DOM。

                                                      • 这一部分我会在后面专门和大家阅读createApp函数中讲到;

                                                        DOM渲染过程

                                                        如果我们不只是一个简单的div,而是有一大堆的元素,那么它们应该会形成一个VNode Tree:

                                                         

                                                        哈哈哈哈 哈哈哈哈

                                                        嘻嘻嘻嘻 呵呵呵呵

                                                        这个和我们的key有什么关系呢?

                                                        • 接下来我们就进行具体的分析;

                                                          2.6.2. key作用和diff算法

                                                          我们先来看一个例子:

                                                          • 这个例子是当我点击按钮时会在中间插入一个f;
                                                               

                                                            我们可以确定的是,这次更新对于ul和button是不需要进行更新,需要更新的是我们li的列表:

                                                            • 在Vue中,对于相同父元素的子元素节点并不会重新渲染整个列表;
                                                            • 因为对于列表中 a、b、c、d它们都是没有变化的;
                                                            • 在操作真实DOM的时候,我们只需要在中间插入一个f的li即可;

                                                              那么Vue中对于列表的更新究竟是如何操作的呢?

                                                              • Vue事实上会对于有key和没有key会调用两个不同的方法;
                                                              • 有key,那么就使用 patchKeyedChildren方法;
                                                              • 没有key,那么就使用 patchUnkeyedChildren方法;

                                                                有key和没有key的操作

                                                                2.6.3. 没有key执行操作

                                                                没有key对应的源代码如下:

                                                                patchUnkeyedChildren

                                                                它的过程画图就是如下的操作:

                                                                patchUnkeyedChildren画图

                                                                我们会发现上面的diff算法效率并不高:

                                                                • c和d来说它们事实上并不需要有任何的改动;
                                                                • 但是因为我们的c被f所使用了,所有后续所有的内容都要一次进行改动,并且最后进行新增;

                                                                  2.6.4. 有key执行操作

                                                                  如果有key,那么会执行什么样的操作呢?

                                                                  patchKeyedChildren

                                                                  第一步的操作是从头开始进行遍历、比较:

                                                                  • a和b是一致的会继续进行比较;
                                                                  • c和f因为key不一致,所以就会break跳出循环;

                                                                    第一步对比的过程

                                                                    第二步的操作是从尾部开始进行遍历、比较:

                                                                    第二步对比过程

                                                                    第三步是如果旧节点遍历完毕,但是依然有新的节点,那么就新增节点:

                                                                    新增节点

                                                                    第四步是如果新的节点遍历完毕,但是依然有旧的节点,那么就移除旧节点:

                                                                    移除旧节点

                                                                    第五步是最特色的情况,中间还有很多未知的或者乱序的节点:

                                                                    未知序列操作

                                                                    所以我们可以发现,Vue在进行diff算法的时候,会尽量利用我们的key来进行优化操作:

                                                                    • 在没有key的时候我们的效率是非常低效的;
                                                                    • 在进行插入或者重置顺序的时候,保持相同的key可以让diff算法更加的高效;