自学前端开发 - VUE 框架 (三) 常用的标签指令 、选项式API选项、组合式API函数

@[TOC](自学前端开发 - VUE 框架 (三) 常用的标签指令 、选项式API、组合式API)

vue 常用的标签指令

指令其实就是标签的一个属性,它由 v- 作为前缀,表明是一个由 vue 提供的特殊属性。它们将为渲染的 DOM 应用特殊的响应式行为。

v-html

v-html 指令的意思是,在当前组件实例上,将此元素的 innerHTML 与 rawHtml 属性保持同步。

<p>Using text interpolation: {
   
   { rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

span 的内容将会被替换为 rawHtml 属性的值,插值为纯 HTML——数据绑定将会被忽略。注意,不能使用 v-html 来拼接组合模板,因为 Vue 不是一个基于字符串的模板引擎。在使用 Vue 时,应当使用组件作为 UI 重用和组合的基本单元。

v-text

v-text 通过设置元素的 textContent 属性来工作,因此它将覆盖元素中所有现有的内容。相当于文本插值

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

v-bind

v-bind 指令会绑定一个或若干属性到元素对象上:

<div v-bind:id="dynamicId"></div>

v-bind 指令指示 Vue 将元素的 id 属性与组件的 dynamicId 属性保持一致。如果绑定的值是 null 或者 undefined,那么该属性将会从渲染的元素上移除。

也可以简写为

<div :id="dynamicId"></div>

对于 v-bind 指令绑定的属性,如果是布尔型属性,例如 disabled、hidden 等,可以这样使用:

<button :disabled="isButtonDisabled">Button</button>

isButtonDisabled 为真值或一个空字符串 (即 <button disabled="">) 时,元素会包含这个 disabled 属性。而当其为其他假值时属性将被忽略。

如果 v-bind 指令不带参数,则会绑定其包含的多个属性到元素对象上:

data() {
    
    
  return {
    
    
    objectOfAttrs: {
    
    
      id: 'container',
      class: 'wrapper'
    }
  }
}
<div v-bind="objectOfAttrs"></div>

可以使用的修饰符有:

  • .camel ——将短横线命名的属性转变为驼峰式命名。
  • .prop ——强制绑定为 DOM 成员。
  • .attr ——强制绑定为 DOM 属性。

参考示例:

<!-- 绑定 attribute -->
<img v-bind:src="imageSrc" />

<!-- 动态 attribute 名 -->
<button v-bind:[key]="value"></button>

<!-- 缩写 -->
<img :src="imageSrc" />

<!-- 缩写形式的动态 attribute 名 -->
<button :[key]="value"></button>

<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' + fileName" />

<!-- class 绑定 -->
<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]"></div>

<!-- style 绑定 -->
<div :style="{ fontSize: size + 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>

<!-- 绑定对象形式的 attribute -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>

<!-- prop 绑定。“prop” 必须在子组件中已声明。 -->
<MyComponent :prop="someThing" />

<!-- 传递子父组件共有的 prop -->
<MyComponent v-bind="$props" />

<!-- XLink -->
<svg><a :xlink:special="foo"></a></svg>

v-on

v-on 指令会监听一个 DOM 事件,参数就是需要监听的事件。当用于普通元素,只监听原生 DOM 事件。当用于自定义元素组件,则监听子组件触发的自定义事件

当监听原生 DOM 事件时,方法接收原生事件作为唯一参数。如果使用内联声明,声明可以访问一个特殊的 $event 变量:v-on:click="handle('ok', $event)"

<!-- 方法处理函数 -->
<button v-on:click="doThis"></button>

<!-- 动态事件 -->
<button v-on:[event]="doThis"></button>

<!-- 内联声明 -->
<button v-on:click="doThat('hello', $event)"></button>

<!-- 缩写 -->
<button @click="doThis"></button>

<!-- 使用缩写的动态事件 -->
<button @[event]="doThis"></button>

<!-- 停止传播 -->
<button @click.stop="doThis"></button>

<!-- 阻止默认事件 -->
<button @click.prevent="doThis"></button>

<!-- 不带表达式地阻止默认事件 -->
<form @submit.prevent></form>

<!-- 链式调用修饰符 -->
<button @click.stop.prevent="doThis"></button>

<!-- 按键用于 keyAlias 修饰符-->
<input @keyup.enter="onEnter" />

<!-- 点击事件将最多触发一次 -->
<button v-on:click.once="doThis"></button>

<!-- 对象语法 -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>

可以使用的修饰符:

  • .stop ——调用 event.stopPropagation()
  • .prevent ——调用 event.preventDefault()
  • .capture ——在捕获模式添加事件监听器。
  • .self ——只有事件从元素本身发出才触发处理函数。
  • .{keyAlias} ——只在某些按键下触发处理函数。
  • .once ——最多触发一次处理函数。
  • .left ——只在鼠标左键事件触发处理函数。
  • .right ——只在鼠标右键事件触发处理函数。
  • .middle ——只在鼠标中键事件触发处理函数。
  • .passive ——通过 { passive: true } 附加一个 DOM 事件。

v-model

v-model 指令会将表单元素的 value 属性双向绑定到数据模型中,例如当用户修改了元素对象的值,数据模型的值也同时被修改

<div id="app">
    <p>{
   
   { num }}</p>
    <input type="text" v-model="num">
</div>

<script>
    const {
      
      createApp} = Vue
    let vm = Vue.createApp({
      
      
        data() {
      
      
            return {
      
      
                num: 0
            }
        },
        mounted() {
      
      
            setInterval(() => {
      
      
                this.num++;
            }, 1000)
        }
    }).mount('#app')
</script>

可以使用修饰符

  • .lazy ——监听 change 事件而不是 input
  • .number ——将输入的合法符串转为数字
  • .trim ——移除输入内容两端空格

v-pre

跳过该元素及其所有子元素的编译,会被保留并按原样渲染。最常见的用例就是显示原始双大括号标签及内容。

<span v-pre>{
   
   { this will not be compiled }}</span>

v-once

仅渲染元素和组件一次,并跳过之后的更新。在随后的重新渲染,元素/组件及其所有子项将被当作静态内容并跳过渲染。这可以用来优化更新时的性能。

<!-- 单个元素 -->
<span v-once>This will never change: {
   
   {msg}}</span>
<!-- 带有子元素的元素 -->
<div v-once>
  <h1>comment</h1>
  <p>{
   
   {msg}}</p>
</div>
<!-- 组件 -->
<MyComponent v-once :comment="msg" />
<!-- `v-for` 指令 -->
<ul>
  <li v-for="i in list" v-once>{
   
   {i}}</li>
</ul>

v-memo

缓存一个模板的子树。在元素和组件上都可以使用。为了实现缓存,该指令需要传入一个固定长度的依赖值数组进行比较。如果数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过。举例来说:

<div v-memo="[valueA, valueB]">
  ...
</div>

当组件重新渲染,如果 valueA 和 valueB 都保持不变,这个 <div> 及其子项的所有更新都将被跳过。实际上,甚至虚拟 DOM 的 vnode 创建也将被跳过,因为缓存的子树副本可以被重新使用。

正确指定缓存数组很重要,否则应该生效的更新可能被跳过。v-memo 传入空依赖数组 (v-memo="[]") 将与 v-once 效果相同。

v-memo 与 v-for 一起使用

v-memo 仅用于性能至上场景中的微小优化,应该很少需要。最常见的情况可能是有助于渲染海量 v-for 列表 (长度超过 1000 的情况):

<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
  <p>ID: {
   
   { item.id }} - selected: {
   
   { item.id === selected }}</p>
  <p>...more child nodes</p>
</div>

当组件的 selected 状态改变,默认会重新创建大量的 vnode,尽管绝大部分都跟之前是一模一样的。v-memo 用在这里本质上是在说“只有当该项的被选中状态改变时才需要更新”。这使得每个选中状态没有变的项能完全重用之前的 vnode 并跳过差异比较。注意这里 memo 依赖数组中并不需要包含 item.id,因为 Vue 也会根据 item 的 :key 进行判断。需注意的是,当搭配 v-for 使用 v-memo,确保两者都绑定在同一个元素上。v-memo 不能用在 v-for 内部。

vue 常用的选项式API选项

状态选项

data

data 选项是 vue 默认的数据模型(model),它会接收一个函数返回的数据对象,并将其在模板中渲染出来。

<div id="app">
	<p>{
   
   { message }}</p>
</div>
<script>
	let vm = Vue.createApp({
      
      
		data() {
      
      
			return {
      
      
				message: 'Hello Vue!'
			}
		}
	}).mount('#app')
</script>

可以通过修改其数据来更新页面渲染。

vm.$data.message = 'Hello World!'

data 中声明的所有数据会被 vm 对象进行遍历,并赋值给 vm 对象作为其属性。所以也可以这样来修改数据:

vm.message = 'Hello World!'

computed

computed 选项(计算属性)是一个对象,对象中的属性则是一个 getter 方法,用来描述依赖响应式状态的复杂逻辑。computed 选项生命的属性(getter 方法)在应用实例化时,也会像 data 声明的属性一样成为应用的属性。

export default {
    
    
  data() {
    
    
    return {
    
    
      author: {
    
    
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  },
  computed: {
    
    
    // 一个计算属性的 getter
    publishedBooksMessage() {
    
    
      // `this` 指向当前组件实例
      return this.author.books.length > 0 ? 'Yes' : 'No'
    }
  }
}
<p>Has published books:</p>
<!-- 因为 computed 的成员是 getter 方法,所以所有成员可以像变量一样直接使用 -->
<span>{
   
   { publishedBooksMessage }}</span>

计算属性是只读的,如果尝试写则会收到一个运行时警告。如果确实需要进行写入操作,则需要同时提供 getter 和 setter 来创建

export default {
    
    
  data() {
    
    
    return {
    
    
      firstName: 'John',
      lastName: 'Doe'
    }
  },
  computed: {
    
    
    fullName: {
    
    
      // getter
      get() {
    
    
        return this.firstName + ' ' + this.lastName
      },
      // setter
      set(newValue) {
    
    
        // 注意:这里使用的是解构赋值语法
        [this.firstName, this.lastName] = newValue.split(' ')
      }
    }
  }
}

methods

methods 是方法选项,和 computed 组件类似的是,它也是一个对象,对象中的每个属性都是一个方法,可以在模板中被调用。

两者不同之处在于计算属性值会基于其响应式依赖被缓存,即计算属性仅会在其响应式依赖更新时才重新计算。而方法组件在每次调用时都会重新进行计算。

// 计算属性永远不会更新,因为 Date.now() 不是响应式依赖。所以应该使用方法组件。
computed: {
    
    
  now() {
    
    
    return Date.now()
  }
}

Vue 自动为 methods 中的方法绑定了永远指向组件实例的 this。这确保了方法在作为事件监听器或回调函数时始终保持正确的 this。你不应该在定义 methods 时使用箭头函数,因为箭头函数没有自己的 this 上下文。

watch

watch 选项用于声明在数据更改时调用的侦听回调。watch 选项期望接受一个方法或对象:如果是方法,则方法名为组件的示例属性名,方法参数包含旧值和新值,方法内容为回调函数。如果是对象,则其中的键是需要侦听的响应式组件实例属性 (例如,通过 data 或 computed 声明的属性),而值是相应的回调函数。该回调函数接受被侦听源的新值和旧值。

除了一个根级属性,键名也可以是一个简单的由点分隔的路径,例如 a.b.c。注意,这种用法不支持复杂表达式——仅支持由点分隔的路径。如果你需要侦听复杂的数据源,可以使用命令式的 $watch() API。

值也可以是一个方法名称的字符串 (通过 methods 声明),或包含额外选项的对象。当使用对象语法时,回调函数应被声明在 handler 中。额外的选项包含:

  • immediate:在侦听器创建时立即触发回调。第一次调用时,旧值将为 undefined。
  • deep:如果源是对象或数组,则强制深度遍历源,以便在深度变更时触发回调。
  • flush:调整回调的刷新时机,例如 ‘post’ 会在 DOM 更新后进行回调(默认在 DOM 更新前进行回调)
  • onTrack / onTrigger:调试侦听器的依赖关系。

声明侦听器回调时避免使用箭头函数,因为它们将无法通过 this 访问组件实例。

export default {
    
    
  data() {
    
    
    return {
    
    
      a: 1,
      b: 2,
      c: {
    
    
        d: 4
      },
      e: 5,
      f: 6
    }
  },
  watch: {
    
    
    // 侦听根级属性
    a(val, oldVal) {
    
    
      console.log(`new: ${
      
      val}, old: ${
      
      oldVal}`)
    },
    // 字符串方法名称
    b: 'someMethod',
    // 该回调将会在被侦听的对象的属性改变时调动,无论其被嵌套多深
    c: {
    
    
      handler(val, oldVal) {
    
    
        console.log('c changed')
      },
      deep: true
    },
    // 侦听单个嵌套属性:
    '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')
    }
  },
  created() {
    
    
    this.a = 3 // => new: 3, old: 1
  }
}

生命周期选项(钩子)

created

created 选项在组件实例处理完所有与状态相关的选项后调用。

当这个钩子被调用时,以下内容已经设置完成:响应式数据、计算属性、方法和侦听器。然而,此时挂载阶段还未开始,因此 $el 属性仍不可用。

mounted

mounted 选项接收一个函数,会在组件被挂载之后调用。

<div id='app'>
	<p>count = {
   
   { count }}</p>
<div>
<script>
	let vm = Vue.createApp({
      
      
		data() {
      
      return{
      
      
			count: 0
		}},
		mounted() {
      
      
			setInterval(()=>{
      
      
				this.count++;
				// 在 vm 外部,可以使用 vm.$data.count 改变绑定的数据
				// 也可以使用 vm.count 来改变绑定的数据
			},1000)
		}
	}).mount('#app')
</script>

updated

updated 选项接收一个函数,在组件因为一个响应式状态变更而更新其 DOM 树之后调用。父组件的更新钩子将在其子组件的更新钩子之后调用。这个钩子会在组件的任意 DOM 更新后被调用,这些更新可能是由不同的状态变更导致的。如果需要在某个特定的状态更改后访问更新后的 DOM,可以使用 nextTick() 作为替代。注:这个钩子在服务端渲染时不会被调用。

unmounted

unmounted 选项接收一个函数,在组件实例被卸载之后调用。一个组件在以下情况下被视为已卸载:

  • 其所有子组件都已经被卸载。
  • 所有相关的响应式作用 (渲染作用以及 setup() 时创建的计算属性和侦听器) 都已经停止。

可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接。这个钩子在服务端渲染时不会被调用。

vue 常用的组合式API函数

setup

setup() 钩子是在组件中使用组合式 API 的入口,通常只在以下情况下使用:

  • 需要在非单文件组件中使用组合式 API 时。
  • 需要在基于选项式 API 的组件中集成基于组合式 API 的代码时。
    其他情况下,都应优先使用 <script setup> 语法。

组合式API使用时也是基于应用实例,因此需要先创建一个应用实例。和选项式API不同的是,选项式API在创建应用实例时,传入的是若干选项组成的对象,而组合式API传入的,主要是 setup 函数对象。所有选项式API中各选项功能都写在 setup 函数中。

<div id="app">
	<p @click="increment">{
   
   { num }}</p>
</div>

<script>
    const {
      
      createApp, ref} = Vue;
    let app = createApp({
      
      
        setup() {
      
      
            // 响应式状态
            let num = ref(0);

            // 用来修改状态、触发更新的函数
            function increment() {
      
      
                num.value++
            }
            // 返回值会暴露给模板和其他的选项式 API 钩子
            return {
      
      num, increment}
        }
    }).mount('#app')
</script>

请注意在模板中访问从 setup 返回的 ref 时,它会自动浅层解包,因此你无须再在模板中为它写 .value。当通过 this 访问时也会同样如此解包。

ref

接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value。ref 对象是可更改的,也就是说可以为 .value 赋予新的值。它也是响应式的,即所有对 .value 的操作都将被追踪,并且写操作会触发与之相关的副作用。

如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。这也意味着如果对象中包含了嵌套的 ref,它们将被深层地解包。若要避免这种深层次的转换,可以使用 shallowRef() 来替代。

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

computed

接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象。

创建一个只读的计算属性 ref:

const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // 错误

创建一个可写的计算属性 ref:

const count = ref(1)
const plusOne = computed({
    
    
  get: () => count.value + 1,
  set: (val) => {
    
    
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

reactive

返回一个对象的响应式代理。响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。

值得注意的是,当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,可以使用 shallowReactive() 作替代。返回的对象以及其中嵌套的对象都会通过 ES Proxy 包裹,因此不等于源对象,建议只使用响应式代理,避免使用原始对象。

ref() 比起来, reactive 创建的响应式代理对象不用使用 .value 就能正确的使用其成员。所以对于基础数据使用 ref(),对于数组或对象使用 reactive() 来转化响应式状态。

watch

侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。watch(source,callback,options) 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。

第一个参数是侦听器的源。这个来源可以是以下几种:

  • 一个函数,返回一个值
  • 一个 ref
  • 一个响应式对象
  • …或是由以上类型的值组成的数组

第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。

第三个可选的参数是一个对象,支持以下这些选项:

  • immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined。
  • deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器。
  • flush:调整回调函数的刷新时机。参考回调的刷新时机及 watchEffect()。
  • onTrack / onTrigger:调试侦听器的依赖。参考调试侦听器。

watchEffect() 相比,watch() 使我们可以:

  • 懒执行副作用;
  • 更加明确是应该由哪个状态触发侦听器重新执行;
  • 可以访问所侦听状态的前一个值和当前值。

侦听一个 getter 函数:

const state = reactive({
    
     count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    
    
    /* ... */
  }
)

侦听一个 ref:

const count = ref(0)
watch(count, (count, prevCount) => {
    
    
  /* ... */
})

当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值:

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
    
    
  /* ... */
})

当使用 getter 函数作为源时,回调只在此函数的返回值变化时才会触发。如果你想让回调在深层级变更时也能触发,你需要使用 { deep: true } 强制侦听器进入深层级模式。在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象。

const state = reactive({
    
     count: 0 })
watch(
  () => state,
  (newValue, oldValue) => {
    
    
    // newValue === oldValue
  },
  {
    
     deep: true }
)

当直接侦听一个响应式对象时,侦听器会自动启用深层模式:

const state = reactive({
    
     count: 0 })
watch(state, () => {
    
    
  /* 深层级变更状态所触发的回调 */
})

猜你喜欢

转载自blog.csdn.net/runsong911/article/details/127845005