Vue3+Typescript(coderwhy)超详细学习笔记(三)Vue3开发基础语法(一)

27 篇文章 74 订阅
15 篇文章 15 订阅

(关注我可接受到更新推送)

一. 知识补充

1.1.(理解) methods中的this

在这里插入图片描述

1.1.1. 不能使用箭头函数

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

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

  • 不可以是window,因为window中我们无法获取到data返回对象中的数据;
  • 但是如果我们使用箭头函数,那么这个this就会是window了;

我们来看下面的代码:

  • 我将increment换成了箭头函数,那么它其中的this进行打印时就是window;
const App = {
  template: "#my-app",
  data() {
    return {
      counter: 0
    }
  },
  methods: {
    increment: () => {
      // this.counter++;
      console.log(this);
    },
    decrement() {
      this.counter--;
    }
  }
}

为什么是window呢?

  • 这里涉及到箭头函数使用this的查找规则,它会在自己的上层作用域中来查找this;
  • 最终刚好找到的是script作用域中的this,所以就是window;

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

  • 在coderwhy的公众号有另外一篇文章,专门详细的讲解了this的绑定规则;
  • https://mp.weixin.qq.com/s/hYm0JgBI25grNG_2sCRlTA;
  • 认真学习之后你绝对对this的绑定一清二楚;

this文章里面的小错误这里是引用

1.1.2. this到底指向什么

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

1.2.(了解) VSCode增加代码片段

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

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

具体的步骤如下:

  • 第一步,复制自己需要生成代码片段的代码;
  • 第二步,https://snippet-generator.app/在该网站中生成代码片段;
    生成代码片段
  • 第三步,在VSCode中生成代码片段;
    打开生成代码片段的配置
    选择HTML

生成代码片段

二. 模板语法

React的开发模式:

  • React使用的jsx,所以对应的代码都是编写的类似于js的一种语法;
  • 之后通过Babel将jsx编译成 React.createElement 函数调用;

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

  • 但是大多数情况下,使用基于HTML的模板语法;

  • 在模板中,允许开发者以声明式的方式将DOM绑定到底层组件实例的数据;

  • 在底层的实现中,Vue将模板编译成虚拟DOM渲染函数,这个我会在后续给大家讲到;

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

2.1. (掌握)插值语法

2.1.1. mustache语法

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

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

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

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

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

  <template id="my-app">
    <div>
      <!-- mustache基本使用 -->
      <h2>{{message}}</h2>
      <!-- JavaScript表达式 -->
      <h2>{{ counter * 2}}</h2>
      <h2>{{message.split(" ").reverse().join(" ")}}</h2>
      <!-- 调用一个methods中的函数 -->
      <h2>{{reverse(message)}}</h2>
    </div>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    // 创建App组件
    const App = {
      template: '#my-app',
      data() {
        return {
          message: "Hello World",
          counter: 10,
        }
      },
      methods: {
        reverse(msg) {
          return msg.split(" ").reverse().join(" ")
        }
      }
    }

    // 创建应用程序, 并且挂载
    Vue.createApp(App).mount('#app');
  </script>
</body>

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

<!-- 错误的写法 -->
<!-- 这是一个赋值语句, 不是表达式 -->
<h2>{{var name = "Hello"}}</h2>
<!-- 控制流的if语句也是不支持的, 可以使用三元运算符 -->
<h2>{{ if (true) { return message } }}</h2>

三元运算符是可以的:

<!-- 三元运算符 -->
<h2>{{ true ? message: counter }}</h2>

完整代码:


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

  <template id="my-app">
    <!-- 1.mustache的基本使用 -->
    <h2>{{message}} - {{message}}</h2>
    <!-- 2.是一个表达式 -->
    <h2>{{counter * 10}}</h2>
    <h2>{{ message.split(" ").reverse().join(" ") }}</h2>
    <!-- 3.也可以调用函数 -->
    <!-- 可以使用computed(计算属性) -->
    <h2>{{getReverseMessage()}}</h2>
    <!-- 4.三元运算符 -->
    <h2>{{ isShow ? "哈哈哈": "" }}</h2>
    <button @click="toggle">切换</button>

    <!-- 错误用法 -->
    <!-- var name = "abc" -> 赋值语句 -->
    <!-- <h2>{{var name = "abc"}}</h2>
    <h2>{{ if(isShow) {  return "哈哈哈" } }}</h2> -->
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          message: "Hello World",
          counter: 100,
          isShow: true
        }
      },
      methods: {
        getReverseMessage() {
          return this.message.split(" ").reverse().join(" ");
        },
        toggle() {
          this.isShow = !this.isShow;
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

2.1.2. v-once

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

  • 当数据发生变化时,元素或者组件以及其所有的子元素将视为静态内容并且跳过;
  • 该指令可以用于性能优化;
<h2 v-once>当前计数: {{counter}}</h2>
<button @click="increment">+1</button>

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

<div v-once>
  <h2>当前计数: {{counter}}</h2>
  <button @click="increment">+1</button>
</div>

完整代码:

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

  <template id="my-app">
      <!-- 重新渲染 -->
    <h2>{{counter}}</h2>
      <!-- 不会重新渲染 -->
    <div v-once>
      <h2>{{counter}}</h2>
      <h2>{{message}}</h2>
    </div>
    <button @click="increment">+1</button>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          counter: 100,
          message: "abc"
        }
      },
      methods: {
        increment() {
          this.counter++;
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

2.1.3. v-text

用于更新元素的 textContent:

<span v-text="msg"></span>
<!-- 等价于 -->
<span>{{msg}}</span>

2.1.4. v-html

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

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

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

  <template id="my-app">
    <div v-html='info'></div>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          info: `<span style='color: red; font-size: 30px'>哈哈哈</span>`
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>
</body>

2.1.5. v-pre

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

  • 跳过不需要编译的节点,加快编译的速度;
<div v-pre>{{message}}</div>

在这里插入图片描述

2.1.6. v-cloak

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

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

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

2.2. (掌握)v-bind

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

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

  • 缩写::
  • 预期:any (with argument) | Object (without argument)
  • 参数:attrOrProp (optional)
  • 修饰符:
    • .camel - 将 kebab-case attribute 名转换为 camelCase。
  • 用法:动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。

2.2.1. 绑定基本属性

v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值(这个学到组件时再介绍);
前面我们讲的Mustache等语法主要是将内容插入到 innerHTML 中。

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

  • 比如a元素的href属性、img元素的src属性;
 
  <div id="app"></div>

  <!-- vue2 template模板中只能有一个根元素 -->
  <!-- vue3 是允许template中有多个根元素 -->
  <template id="my-app">
    <!-- 1.v-bind的基本使用 -->
    <img v-bind:src="imgUrl" alt="">
    <a v-bind:href="link">百度一下</a>

    <!-- 2.v-bind提供一个语法糖 : -->
    <img :src="imgUrl" alt="">
    <img src="imgUrl" alt="">
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          imgUrl: "https://avatars.githubusercontent.com/u/10335230?s=60&v=4",
          link: "https://www.baidu.com"
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

2.2.2. 绑定class属性

对象语法

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

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

  <template id="my-app">
    <!-- 1.普通的绑定方式 -->
    <div :class="className">{{message}}</div>
    <!-- 2.对象绑定 -->
    <!-- 动态切换class是否加入: {类(变量): boolean(true/false)} -->
    <div class="why" :class="{nba: true, 'james': true}"></div>
    <!-- 案例练习 -->
    <div :class="{'active': isActive}">哈哈哈</div>
    <button @click="toggle">切换</button>
    <!-- 绑定对象 -->
    <div :class="classObj">哈哈哈</div>
    <!-- 从methods中获取 -->
    <div :class="getClassObj()">呵呵呵</div>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          message: "Hello World",
          className: "why",
          nba: 'kobe',
          isActive: false,
          classObj: {
            why: true,
            kobe: true,
            james: false
          }
        }
      },
      methods: {
        toggle() {
          this.isActive = !this.isActive;
        },
        getClassObj() {
          return {
            why: true,
            kobe: true,
            james: false
          }
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

数组语法:

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

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

  <template id="my-app">
    <!-- 1.直接传入一个数组 -->
    <div :class="['abc', title]">哈哈哈哈</div>
    <!-- 2.数组中也可以使用三元运算符或绑定变量 -->
    <div :class="['abc', title, isActive ? 'active': '']">哈哈哈哈</div>
    <!-- 3.数组中也可以使用(嵌套)对象语法 -->
    <div :class="['abc', title, {active: isActive}]">哈哈哈哈</div>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          message: "Hello World",
          title: "cba",
          isActive: true
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

2.2.3. 绑定style属性

对象语法:

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

  <template id="my-app">
    <!-- :style="{cssPropertyName: cssPropertyValue}" -->
    <div :style="{color: finalColor, 'font-size': '30px'}">哈哈哈哈</div>
    <div :style="{color: finalColor, fontSize: '30px'}">哈哈哈哈</div>
    <div :style="{color: finalColor, fontSize: finalFontSize + 'px'}">哈哈哈哈</div>

    <!-- 绑定一个data中的属性值, 并且是一个对象 -->
    <div :style="finalStyleObj">呵呵呵呵</div>
    <!-- 调用一个方法 -->
    <div :style="getFinalStyleObj()">呵呵呵呵</div>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          message: "Hello World",
          finalColor: 'red',
          finalFontSize: 50,
          finalStyleObj: {
            'font-size': '50px',
            fontWeight: 700,
            backgroundColor: 'red'
          }
        }
      },
      methods: {
        getFinalStyleObj() {
          return {
            'font-size': '50px',
            fontWeight: 700,
            backgroundColor: 'red'
          }
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

数组语法:

  • :style的数组语法可以将多个样式对象应用到同一个元素上;
<div id="app"></div>

  <template id="my-app">
    <div :style="[styleObj1, styleObj2]">{{message}}</div>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          message: "Hello World",
          size: 50,
          styleObj1: {
            color: 'red', 
            fontSize: '50px', 
            'background-color': 'blue'
          },
          styleObj2: {
            textDecoration: 'underline',
            fontWeight: 700
          } 
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

2.2.4. 动态绑定属性

在某些情况下,我们属性的名称可能也不是固定的:

  • 前端我们无论绑定src、href、class、style,属性名称都是固定的;
  • 如果属性名称不是固定的,我们可以使用 :[属性名]=“值” 的格式来定义;
  • 这种绑定的方式,我们称之为动态绑定属性;

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

  <template id="my-app">
    <!-- 属性的名称和值都是动态的 -->
    <div :[name]="value">哈哈哈</div>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          name: "cba",
          value: "kobe"
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

2.2.5. 绑定一个对象

如果我们希望将一个对象的所有属性,绑定到元素上的所有属性,应该怎么做呢?

  • 非常简单,我们可以直接使用 v-bind 绑定一个 对象;
  • info对象会被拆解成div的各个属性;
 
  <div id="app"></div>

  <template id="my-app">
    <div v-bind="info">哈哈哈哈</div>
    <div :="info">哈哈哈哈</div>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          info: {
            name: "why",
            age: 18,
            height: 1.88
          }
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

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 } 模式添加侦听器
  • 用法:绑定事件监听

案例演练:

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

  <template id="my-app">
    <div>{{message}}</div>
    <!-- 基本使用 -->
    <!-- 1.绑定函数 -->
    <button v-on:click="btnClick">按钮1</button>
    <button @click="btnClick">按钮2</button>
    <div v-on:mousemove="mouseMove">div的区域</div>

    <!-- 2.绑定对象 -->
    <button v-on="{click: btnClick, mousemove: mouseMove}">特殊按钮3</button>

    <!-- 3.内联语句 -->
    <!-- 默认会把event对象传入 -->
    <button @click="btn4Click">按钮4</button>
    <!-- 内联语句传入其他属性 -->
    <button @click="btn4Click($event, 'why')">按钮5</button>

    <!-- 4.修饰符 -->
    <div @click="divClick">
      <button @click.stop="btnClick">按钮6</button>
    </div>
    <input type="text" @keyup.enter="onEnter">

  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          message: "Hello World"
        }
      },
      methods: {
        btnClick() {
          console.log("按钮被点击了");
        },
        btn4Click(event) {
          console.log(event);
        },
        btn4Click(event, message) {
          console.log(event, message);
        },
        mouseMove() {
          console.log("鼠标移动");
        },
        divClick() {
          console.log("divClick");
        },
        onEnter(event) {
          console.log(event.target.value);
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

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类似;
<!-- vue3中, template不再要求必须只有一个根元素 -->
    <div id="app"></div>

  <template id="my-app">
    <input type="text" v-model="score">
    <h2 v-if="score > 90">优秀</h2>
    <h2 v-else-if="score > 60">良好</h2>
    <h2 v-else>不及格</h2>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          score: 95
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

v-if的渲染原理:

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

2.4.2. template元素

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

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

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

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

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

  <template id="my-app">
    <template v-if="isShowHa">
      <h2>哈哈哈哈</h2>
      <h2>哈哈哈哈</h2>
      <h2>哈哈哈哈</h2>
    </template>

    <template v-else>
      <h2>呵呵呵呵</h2>
      <h2>呵呵呵呵</h2>
      <h2>呵呵呵呵</h2>
    </template>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          isShowHa: true
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

2.4.3. v-show

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

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

  <template id="my-app">
    <h2 v-show="isShow">哈哈哈哈</h2>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          isShow: true
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

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是我们给每项元素起的一个别名,这个别名可以自定来定义;
 <template id="my-app">
    <h2>电影列表</h2>
    <ul>
      <li v-for="item in movies">{{item}}</li>
    </ul>
  </template>

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

  • 如果我们需要索引,可以使用格式:"(item, index) in 数组"
  • 注意上面的顺序:数组元素项item是在前面的,索引项index是在后面的;
 <template id="my-app">
    <h2>电影列表</h2>
    <ul>
      <li v-for="(item, index) in movies">{{index}}-{{item}}</li>
    </ul>
  </template>

2.5.2. (掌握)v-for支持类型

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

  • 一个参数:“value in object”;
  • 二个参数:"(value, key) in object";
  • 三个参数:"(value, key, index) in object";
  <template id="my-app">
    <h2>遍历对象</h2>
    <ul>
      <li v-for="(value, key, index) in info">
        {{index}} - {{key}} - {{value}}
      </li>
    </ul>
  </template>
  • v-for同时也支持数字的遍历:
    • 每一个item都是一个数字;

在这里插入图片描述
在这里插入图片描述

完整代码

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

  <template id="my-app">
    <h2>电影列表</h2>
    <ul>
      <!-- 遍历数组 -->
      <li v-for="(movie, index) in movies">{{index+1}}.{{movie}}</li>
    </ul>
    <h2>个人信息</h2>
    <ul>
      <!-- 遍历对象 -->
      <li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}</li>
    </ul>
    <h2>遍历数字</h2>
    <ul>
      <li v-for="(num, index) in 10">{{num}}-{{index}}</li>
    </ul>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          movies: [
            "星际穿越",
            "盗梦空间",
            "大话西游",
            "教父",
            "少年派"
          ],
          info: {
            name: "why",
            age: 18,
            height: 1.88
          }
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

2.5.3. template元素

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

  • 我们使用template来对多个元素进行包裹,而不是使用div来完成;
 <div id="app"></div>

  <template id="my-app">
    <ul>
      <template v-for="(value, key) in info">
        <li>{{key}}</li>
        <li>{{value}}</li>
        <hr>
      </template>
    </ul>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          info: {
            name: 'why',
            age: 18,
            height: 1.88
          }
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

2.5.4. 数组更新检测

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

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()
 
  <div id="app"></div>

  <template id="my-app">
    <h2>电影列表</h2>
    <ul>
      <li v-for="(movie, index) in movies">{{index+1}}.{{movie}}</li>
    </ul>
    <input type="text" v-model="newMovie">
    <button @click="addMovie">添加电影</button>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          newMovie: "",
          movies: [
            "星际穿越",
            "盗梦空间",
            "大话西游",
            "教父",
            "少年派"
          ]
        }
      },
      methods: {
        addMovie() {
          this.movies.push(this.newMovie);
          this.newMovie = "";

          // this.movies = this.movies.filter(item => item.length > 2);
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

替换数组的方法

上面的方法会直接修改原来的数组,但是某些方法不会替换原来的数组,而是会生成新的数组,比如 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的对象;
<div class="title" style="font-size: 30px; color: red;">哈哈哈</div>

在我们的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:
<div>
  <p>
    <i>哈哈哈哈</i>
    <i>哈哈哈哈</i>
  </p>
  <span>嘻嘻嘻嘻</span>
  <strong>呵呵呵呵</strong>
</div>

在这里插入图片描述
这个和我们的key有什么关系呢?

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

注:

  • DOM是树,node是树的节点
  • 为什么不渲染成真实DOM?为了跨平台,性能高

2.6.2. (理解)key作用和diff算法

diff=different

我们先来看一个例子:

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

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

  <template id="my-app">
    <ul>
      <li v-for="item in letters">{{item}}</li>
    </ul>
    <button @click="insertF">insert f</button>
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          letters: ['a', 'b', 'c', 'd']
        }
      },
      methods: {
        insertF() {
          this.letters.splice(2, 0, 'f');
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>

插入F的方案

  1. 数组更新时全部替换成新的
  2. a,b不变,把c换成f,把d换成c,在新增一个d元素
  3. 其他元素不变,直接在中间插入f,需要用到diff算法。(性能最高的方案,上面两种方案性能较低)

我们可以确定的是,这次更新对于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

patch->如果有n1,则进行更新操作
patch->为null,mount操作

在这里插入图片描述

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

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

第一步对比的过程
第二步的操作是从尾部开始进行遍历、比较:
第二步对比过程
第三步是如果旧节点遍历完毕,但是依然有新的节点,那么就新增节点:
新增节点
第四步是如果新的节点遍历完毕,但是依然有旧的节点,那么就移除旧节点:
移除旧节点
第五步是最特色的情况,中间还有很多未知的或者乱序的节点:
未知序列操作
所以我们可以发现,Vue在进行diff算法的时候,会尽量利用我们的key来进行优化操作:

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

在这里插入图片描述

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderYYY

请简单粗暴地爱我,一元一元的赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值