Vue 2 基础

vue2

Vue 2 基础

Vue 是渐进式 JavaScript 框架,与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。因此既可以嵌入到已有项目中,优化其中一部分代码;也可以基于 Vue 三大套件开发管理大型项目。

Tip

参考:

实例化

最简单引入 Vue 的方法是使用 CDN

html
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

使用 new Vue({}) 的方式实例化一个 Vue 应用。其中接收的参数是一个选项对象,设置关于 Vue 实例的选项,例如 el 属性是指定挂载的 DOM 节点,data 属性是包含响应式的数据。

Tip

从组件化的角度看,Vue 实例也是一个根组件

html
<body>
  <div id="app"></div>
  <script>
    // 一般使用变量名 `vm`「存储」 Vue 实例,这是 ViewModel 的缩写
    const vm = new Vue({
      el: '#app',
      data: {
          name: 'Ben'
      }
    })
  </script>
</body>

实际上有多种渲染 template 的方式:

  • 创建应用时只有 el 选项:指定挂载的 DOM 节点,并以该节点内部原有的 HTML 内容作为模板 template
  • 创建应用时有 el 选项+ template 选项:将 Vue 应用挂载到指定的 DOM 节点,并用 template 内容取代掉 el 指定的 DOM 节点原有的内容
  • 创建应用时只有 template 选项:之后必须调用 vm.$mount(el) 方法将应用挂载到 el 指定的 DOM 节点,而且会取代掉 el 指定的 DOM 节点的内容

因此只要有 template 选项,Vue 应用就会取代掉挂载的 DOM 节点内部的内容​

Tip

Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 $

js
const vm = new Vue({
  el: '#app',
  data: {
    name: 'Ben'
  }
})

vm.$data === data // true
vm.$el === document.getElementById('app') // true

// $watch 设置一个侦听器
vm.$watch('name', function (newValue, oldValue) {
  // 这个回调将在 `vm.name` 改变后调用
})

视频教程中说到一个小 trick:

Play Video

如果将 Vue 「嵌入」到原有项目时,应该使用立即调用函数表达式 immediately-invoked function expressions,IIFE 包裹代码,可以模拟构建出块级作用域,代码立即执行并拥有了自己的私有变量,避免污染全局变量。另外在最前面加上 ; 是为了避免一些打包程序在合并代码的时候,错误地将立即执行函数识别为前一行代码中函数的参数。

Play Video

Play Video

Play Video

模板语法

将 Vue 应用挂载到 DOM 以后,该节点内部的 HTML 就会支持模板语法 Mustache,即使用双花括号包含的部分,在其中支持使用简单的 JavaScript 表达式

vue
<template>
  <p>{{ content }}</p>
</template>

过滤器

过滤器被用于一些常见的文本格式化。过滤器可以用在两个地方:

  • 双花括号插值
  • v-bind 表达式
vue
<template>
  <div>
    <!-- 在双花括号中 -->
    <p>{{ message | capitalize }}</p>

    <!-- 在 `v-bind` 中 -->
    <div v-bind:id="rawId | formatId"></div>
  </div>
</template>

过滤器应该被添加在 JavaScript 表达式的尾部,由 「管道」符号 指示 value | filterName,:IconCustom{name="mdi:youtube" iconClass="text-red-500"} 过滤器是 JavaScript 函数,因此可以接收参数,它总是接收管道号之前的 JavaScript 表达式的值作为第一个参数,主动传入的数据则作为第二个参数开始传递。

Play Video

全局注册

Play Video

js
Vue.filter('filterName', function () {...})

局部注册

也可以在一个组件的选项 filters 定义本地的过滤器

js
// ...
filters: {
  filterName: function() {...}
}

计算属性和侦听器

在选项 computed 中创建计算属性,在选项 watch 创建监听器,两者都是基于响应式数据的变化再执行操作的,但用处有不同。

computed

计算属性常常是为了将复杂和需要重用的资料处理逻辑,从模板中的函数表达式提出来。它类似 data 的属性一样,作为响应式数据在模板上直接使用。

计算属性的最大优势是对于响应式依赖有缓存。如果希望计算属性实时更新,那么它需要有响应式依赖(基于 data 或其他来源的响应式数据),并且返回一个值。

Play Video

计算属性除了可以使用函数的方式,:IconCustom{name="mdi:youtube" iconClass="text-red-500"} 还可以使用对象的方式,需要为对象设置 get(默认只有 getter)和 set 属性。

Play Video

  • getter 是基于 data property 响应式数据返回计算的值
  • setter 是为了响应用户直接为该计算属性赋值computed_property = value反过来设定其所依赖的响应数据(即更新 data property)
js
// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
js
// set computed property
// the setter will be invoked and vm.firstName and vm.lastName will be updated accordingly.
vm.fullName = 'John Doe';

watch

watch 也是依赖响应式数据的(每个 watch 都只能针对一个响应式数据,一般是 data property,相比较 computed 可以同时侦听多个响应式数据),但它一般是用于基于响应式数据的变化执行异步操作,可以设置中间状态。

js
// ...
watch: {
    a: function (val, oldVal) {
      console.log('new: %s, old: %s', val, oldVal)
    },
    // 可以侦听响应式对象中某个属性 watch vm.e.f's value: {g: 5}
    'e.f': function (val, oldVal) { /* ... */ }
    // 你可以传入回调数组,它们会被逐一调用
    e: [
      'handle1',
      function handle2 (val, oldVal) { /* ... */ },
      {
        handler: function handle3 (val, oldVal) { /* ... */ },
        /* ... */
      }
    ],
}
Tip

应该使用箭头函数来定义 watcher 函数,因为是箭头函数绑定了父级作用域的上下文,所以函数内的 this 将不会按照期望指向 Vue 实例。

Tip

如果所依赖的响应式数据是一个对象或数组,需要开启 deep 深度侦听选项;如果希望侦听器在实例初始化后立即执行其回调函数,可以开启 immediate 选项。

js
watch: {
  // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
  c: {
    handler: function (val, oldVal) { /* ... */ },
    deep: true
  },
  // 该回调将会在侦听开始之后被立即调用
  d: {
    handler: 'someMethod',
    immediate: true
  },
}

属性绑定

==在模板中使用指令 v-bind标签的属性与动态数据绑定,这样就实现了数据驱动画面(样式)。==

指令 v-bind:attrName="value" 可以简写为 :attrName="value",绑定的值可以是字符串、对象、数组等。

Tip

将数据绑定到属性时,:IconCustom{name="mdi:youtube" iconClass="text-red-500"} 由于 HTML 和 JavaScript 对于大小写和连字号的支持不同,留意相应关系。例如在 HTML 元素中设置内联样式可以使用 font-size,但在 JavaScript 表达式中应该使用 fontSize 或者用引号将其包裹 "font-size"

Play Video

对于属性 classstyle 这两个特殊的属性,:IconCustom{name="mdi:youtube" iconClass="text-red-500"} Vue 对这两个 attribute 进行特殊优化,可以将动态绑定的数据与该属性的静态数据进行合并(而其他 attribute 就是「后盖前」)。

Play Video

class 绑定

标签的属性 class 除了支持绑定字符串,还可以绑定对象和数组,以批量地设置多个 className,而且​​会与静态的数据==直接进行拼接合并==

  • 如果绑定值为对象,可同时设置多个 className,并通过对象属性的属性值的布尔类型 true/false 快速动态地切换标签的 class,但新增 newClassName 不方便
    html
    <div :class="classObj">
      <!-- content -->
    </div>
    
    js
    // ...
    classObj: {
      className1: true,
      className2: false
    }
    
  • 如果绑定值为数组,同时设置多个 className,可以方便地新增其他属性,只需要将 newClassName push 到数组就行,但切换/删除 className 不方便恢复
    html
    <div :class="classArr">
      <!-- content -->
    </div>
    
    js
    // ...
    classArr: ['className1', 'className2']
    

style 绑定

标签属性 style 用于添加内联样式,可以绑定对象或数组。

有一些细节需要注意:

  • 对于需要单位的样式,需要指定单位
  • 如果绑定的样式和静态的样式有冲突,会以==绑定的样式优先==
  • 数组允许是对象作为元素

Play Video

双向绑定

在模板的标签中使用指令 v-model 实现表单的值与动态数据双向绑定,实现表单 <input><textarea><select> 等标签的输入数据修改动态数据变量 data property,当然也可以通过修改相应的动态数据 data property 控制表单的值。

Tip

在文本区域 <textarea>{{text}}</textarea> 并不会生效,应用 v-model 来代替 <textarea v-model="message" placeholder="add multiple lines"></textarea>

v-model 的实现原理是为不同的表单元素监听相应的不同 property 属性,并抛出不同的事件(事件处理函数会修改表单所绑定的相应的响应式变量),实现双向绑定。如果需要客制化可以修改相应的事件处理函数。

Tip

Vue 已经对需要使用输入法(如中文、日文、韩文等) 的语言,v-model 不会在输入法组合文字过程中得到更新。如果你也想处理这个过程,请使用 input 事件。此外 input> 元素在中文输入法时按下 enter 选字,可能会误触发按下 enter 确认送出表单,如果要区分两种情况,可以监听 @compositionstart@compositionend 来判断输入法的开启与关闭。

Play Video

表单中 <input type="checkbox>" 可以绑定布尔类型的动态数据变量(单选时,推荐使用 type="radio" 更适合),也可以绑定数组类型的动态数据变量(而且数组的元素的顺序和勾选的顺序一致),由于 checkbox 可以是单选也可以是多选。

html
<!-- 单个复选框,绑定到布尔值 -->
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
html
<!-- 多个复选框,绑定到同一个数组 -->
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>

<script>
new Vue({
  el: '...',
  data: {
    checkedNames: []
  }
})
</script>

Play Video

如果 <input type="checkbox>" 绑定的是布尔类型的动态数据变量,可以通过 true-valuefalse-value 定制不同状态下的值,但是推荐使用 <input type="radio"> 并设定属性 value 来定制值,因为浏览器在提交表单时并不会包含未被选中的复选框(即 false-value 并不会提交),如果要确保提交表单时,这两个值中的一个能够被提交,应该使用单选按钮。

Play Video

元素 <select> 进行双向绑定后,动态数据变量是基于其子元素选中 option 的属性 value 的值进行改变,如果是支持多选就绑定到数组类型的动态数据变量

html
<div id="example-6">
  <select v-model="selected" multiple style="width: 50px;">
    <option value="dataA">A</option>
    <option value="dataB">B</option>
    <option value="dataC">C</option>
  </select>
</div>
<script>
new Vue({
  el: '#example-6',
  data: {
    selected: []
  }
})
</script>

Play Video

Vue 提供了一些修饰符,方便地对表单输入值进行设置:

  • 表单的值默认是字符串类型,如果希望绑定的动态数据变量保存的是数值类型,可以使用表单修饰符 v-model.number
  • 表单修饰符 v-model.trim 自动过滤用户输入的首尾空白字符
  • 表单修饰符 v-model.lazychange 事件之后(失去焦点后)进行数据同步(默认是在 input 事件触发后)

在组件上使用 v-model

在使用自定义的输入型组件时,也可以使用 v-model 实现双向绑定,但是组件内部需要进行相应的处理:

  • 在外部(引用该组件时,在组件的标签上)通过指令 v-model 双向绑定需要同步的变量,它的数据作为 prop 传递到组件内(因此记得在组件内部需要在 props 选项里声明 value 作为 prop),作为子组件内部表单元素的 value 属性的值
  • 在组件内使用指令 v-bind 将表单元素的 value 属性绑定到以上声明的 prop 变量上,即 v-bind:value="value"(这样就可以将外部传进来的值作为表单的值,实现与外部数据的「同步」)
  • 在组件内使用指令 v-on 监听表单元素 input 输入事件,并通过抛出相同的事件 $emit('input', $event.target.value) 向外传递相应的数据,然后外部通过指令 v-model 绑定的变量就会基于抛出的数据进行改变 (即数据的修改的操作还是在父层完成)
js
// component
Vue.component('base-checkbox', {
  props: ['value'],
  template: `
    <input
      type="text"
      v-bind:value="value"
      v-on:change="$emit('input', $event.target.value)"
    >
  `
})
html
<base-input v-model="inputText"></base-input>

Play Video

Tip

视频中的第 1 个组件,子组件向外抛出的是自定义事件,因为目的是为待办事项列表添加列表项,这和单纯抛出 changeinput 事件不同,因此需要在父级监听该自定义事件;如果只是想数据实现「双向同步」,使用 v-model 即可。

条件渲染

指令 v-ifv-show 都可以实现条件渲染。

在模板标签中使用指令 v-if 控制 DOM 的渲染,只有条件指令的值为 true 时才渲染元素,否则 DOM 被「拔出」

还可以使用 v-ifv-else-ifv-else 设置同级并列元素的条件渲染,这三个指令的元素需要连续且同级。如果通过条件切换多块类似的元素,可能会因为重用造成一些 Bug,这样可以为元素加上设置不同的 key 属性值,以便 Vue 区分它们。:IconCustom{name="mdi:youtube" iconClass="text-red-500"} 但是并不推荐将业务逻辑直接写在 HTML 里,应该在 JS 里面根据条件直接生成相应的数据。

Play Video

在模板标签中使用指令 v-show 也是控制 DOM 的渲染,只有条件指令的值为 true 时才显示元素,否则 DOM 被隐藏

Tip

v-ifv-show 都是控制 DOM 在页面上的显示或隐藏,但原理和用处有很大不同:

  • v-if 是基于条件控制 DOM 是否会进行渲染来显示或隐藏元素;而 v-show 是先对 DOM 进行渲染,再根据条件来改变 CSS 属性 display 的值,如果要隐藏就设置为 display: none。因此如果需要频繁地进行元素显示隐藏的切换,应该使用 v-show 效能更高。
  • ​指令 v-if 可以与 <template> 结合使用, <template> 可以作为容器,包装需要条件渲染的 DOM,这样就可以控制一堆 DOM 的渲染,同时不会在页面上新增不必要的 tag;但是指令 v-show 无法使用 <template> 上,由于 v-show 需要元素先渲染,再在该元素调整 CSS 的 display 属性,因此它只能用在会实际出现在页面的标签 Play Video

列表渲染

在模板标签中使用指令 v-for 可以将响应式数据中的数组或对象渲染为一系列的 DOM 元素,称为列表渲染,常用形式是 v-for item in arrv-for item of arr

  • 可以遍历 arr 数组、obj 对象、number(从 1 开始)数字
  • 但是遍历 objList (类数组的对象)时候,无法保证每次遍历 item 的先后次序

其中很重要的一点是==需要为生成的元素设置 key 属性==,一般绑定的是与该元素相关的唯一标识(不要直接用 index),以便提高 DOM 更新的效率。

Play Video

Tip

由于对象和数组都是引用数据类型,所以修改它们后常常会出现无法正确地触发响应式变化的问题,:IconCustom{name="mdi:youtube" iconClass="text-red-500"} Vue 特意针对数组常用方法(push()pop()shift()unshift()splice()sort()reverse())做出优化,如果通过这些方法修改数组,Vue 可以侦听到,相关依赖会做出响应式变化;而==直接通过下标来修改数组的值,往往无法触发视图进行响应式地更新==

Play Video

v-ifv-for 推荐一起使用,因为指令 v-for 优先级更高,所以无论该元素 v-if 的条件如何都会先执行列表渲染,再通过 v-if 判断已经渲染的 DOM 是否要「拔除」,这样会浪费 Vue 的性能。v-for 应该和 v-show 配合使用。

Play Video

事件处理

在模板的标签中使用指令 v-on:eventName 监听事件,可以使用简写形式 @eventName,并触发回调函数。

html
<div id="example-1">
  <!-- 触发事件后直接运行一些 JavaScript 代码 -->
  <button v-on:click="counter += 1">Add 1</button>
  <!-- 触发事件后调用处理函数 `greet` 是在下面定义的方法名 -->
  <button v-on:click="greet">Greet</button>
  <!-- 可以为处理函数传递参数 -->
  <button v-on:click="say('what')">Say what</button>
</div>
Tip

处理函数默认将 DOM 事件 event 作为传入的参数;如果需要同时向回调函数传递参数和原始的 DOM 事件,:IconCustom{name="mdi:youtube" iconClass="text-red-500"} 可以再传递特殊的变量 $event,相应地事件处理函数需要设置形参进行接收

html
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>
js
// ...
methods: {
  warn: function (message, event) {
    // 现在我们可以访问原生事件对象
    if (event) {
      event.preventDefault()
    }
    alert(message)
  }
}

Play Video

Vue 提供了多种事件修饰符,用于更方便地对事件的行为进行设置,可以链式调用多个

  • .stop 阻止事件继续传播
  • .prevent 阻止事件的默认行为,例如表单提交后跳转到指定网址
  • .self事件在该元素自身触发时才执行事件处理函数
  • .once 只对事件进行一次响应后失效
  • .capture 使用事件捕获模式
  • .passive 针对 scroll 事件进行优化,提高性能。但不要把 .passive.prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。

对于 UI 用户界面交互事件是最常见的,如鼠标事件、按键事件等,Vue 提供了鼠标和按键修饰符

  • 按键修饰符:.enter.tab.delete(捕获「删除」和「退格」键)、.esc.space.up.down.left.right.ctrl.alt.shift.meta(在 macOS 上是 command 键,在 Windows 上是 win 键)
  • 鼠标按钮修饰符:.left.right.middle
  • .exact 由精确指定的键组合才触发事件
Tip

单纯监听 ctrl 键放开的操作,需要监听 ctrlkeycode,即 keyup.17

视频教程说到一个小 trick:可以监听 click.right.prevent 鼠标右键点击事件,并阻止弹出菜单的默认行为,这样就可以客制化网页的右键菜单

Play Video

Tip

使用指令 v-on 设置的事件监听会在 ViewModel 销毁后事件处理器都会自动被删除,但是使用自己使用 addEventlistener 等方法设置的事件监听,:IconCustom{name="mdi:youtube" iconClass="text-red-500"} 记得需要在 beforeDestroy() 生命周期函数中手动解绑,以便释放效能。

Play Video

自定义指令

指令一般是为了更方便地 对普通 DOM 元素进行操作,Vue 提供多种内置指令,如 v-ifv-bindv-onv-model 方便在模板中使用,也允许注册自定义指令。

Play Video

全局注册

通过 Vue 原型 prototype 上的方法 Vue.directive 注册一个全局可用的自定义指令,第一个参数 directiveName 是指令的后缀名,第二个参数就是定义指令的行为,:IconCustom{name="mdi:youtube" iconClass="text-red-500"} Vue 为自定义指令提供了 5 种钩子函数,可以在所绑定的元素的不同阶段进行调用。然后可以在 Vue 实例中的任何元素中使用该自定义的指令 <tag v-directiveName="value">

js
Vue.directive('directiveName', {
  // 提供 5 种钩子函数备选,在所绑定的元素的不同阶段进行调用
  bind: function(el) {},
  inserted: function (el) {},
  update: function (el) {},
  componentUpdated: function (el) {},
  unbind: function (el) {},
})
Tip

很多时候可能只想使用 bindupdate 钩子函数,而且想触发相同的行为,可以使用简写形式 Vue.directive('directiveName', function (el, binding) {...})

Play Video

指令的钩子函数会传入以下参数,以便操作所绑定的 DOM 元素和传递数据:

  • el 是指令所绑定的元素,可以用来直接操作 DOM
  • binding 是一个对象,包含以下 property:
    • name 指令名,不包括 v- 前缀
    • value 指令的绑定值,例如 v-directiveName="1 + 1" 中,绑定值为 2
    • oldValue 指令绑定的前一个值,仅在 updatecomponentUpdated 钩子函数中可用。无论值是否改变都可用
    • expression 字符串形式的指令表达式,如 v-directiveName="1 + 1" 中,表达式为 1 + 1
    • arg 传给指令的(可选)参数,如 v-directiveName:argument 中,参数为 argument
    • modifiers 一个包含修饰符的对象,如 v-directiveName.foo.bar 中,修饰符对象为 { foo: true, bar: true }
    • def 指令使用的钩子函数
  • vnode Vue 编译生成的虚拟节点
  • oldVnode 上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用
Warning

使用指令时,如果要同时使用参数 arg 和修饰符 modifier,应该按照先后次序使用(因为 arg 只有一个;而 modifier 可以串联多个,应该放在最后) v-directiveName:argument.modifier

Play Video

Tip

动态指令参数:指令的参数是指 v-directiveName:paramparam 部分,可以使用将参数设定为动态,通过 v-mydirective:[argument]="value" 的形式让指令支持动态参数,即允许使用者传递各种参数(而不进行预先限制),这使得自定义指令可以在应用中被灵活使用。

局部注册

在组件的选项 directives 中注册局部指令,也是提供 5 中钩子函数。然后可以在组件的模板中任何元素上使用自定义的指令 <tag v-directiveName="value">

js
// ...
directives: {
  directiveName: {
    // 设定钩子函数
    inserted: function (el) {}
  }
}

Copyright © 2024 Ben

Theme BlogiNote

Icons from Icônes