Vue.js基础

Ⅰ、通过 CDN 使用 Vue💥

你可以借助 script 标签直接通过 CDN 来使用 Vue:

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

这里我们使用了 unpkg,但你也可以使用任何提供 npm 包服务的 CDN,例如 jsdelivrcdnjs。当然,你也可以下载此文件并自行提供服务。


第一个实例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>VUE 3.0</title>
    <script src="js/vue.global.min.js"></script>
</head>
<body>

<div id="hello-vue" class="demo">
    {{ message }}
</div>

</body>
<script>
    const HelloVueApp = {
        data() {
            return {
                message: 'Hello Vue!!'
            }
        }
    }
    Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
</html>

Ⅱ、Vue3 起步 💥

Vue3 中的应用是通过使用 createApp 函数来创建的,语法格式如下:

const app = Vue.createApp({ /* 选项 */ })

传递给 createApp 的选项用于配置根组件。

在使用 mount() 挂载应用时,该组件被用作渲染的起点。

Vue.createApp(HelloVueApp).mount('#hello-vue')

createApp 的参数是根组件(HelloVueApp),在挂载应用时,该组件是渲染的起点。

一个应用需要被挂载到一个 DOM 元素中,以上代码使用 mount(’#hello-vue’) 将 Vue 应用 HelloVueApp 挂载到 <"div id=“hello-vue”> 中 。


HTML 页面中有一个 div 元素:

<div id="hello-vue" class="demo">
  {{ message }}
</div>
  • mount(’#hello-vue’) 将 Vue 应用 HelloVueApp 挂载到 <"div id=“hello-vue”> 中。

  • {{ }} 用于输出 对象属性 和 函数返回值。

  • {{ message }} 对应应用中 message 的值。

data 选项

data 选项是一个函数


Vue 在创建新组件实例的过程中调用此函数。它应该返回一个对象,然后 Vue 会通过响应性系统将其包裹起来,并以 $data 的形式存储在组件实例中。

<div id="hello-vue" class="demo">
</div>

const app = Vue.createApp({
  data() {
    return { count: 4 }
  }
})

const vm = app.mount('#hello-vue')

document.write(vm.$data.count) // => 4
document.write("<br>")
document.write(vm.count)       // => 4
document.write("<br>")
// 修改 vm.count 的值也会更新 $data.count
vm.count = 5
document.write(vm.$data.count) // => 5
document.write("<br>")
// 反之亦然
vm.$data.count = 6
document.write(vm.count) // => 6

以上实例属性仅在实例首次创建时被添加,所以你需要确保它们都在 data 函数返回的对象中。


方法

我们可以在组件中添加方法,使用 methods 选项,该选项包含了所需方法的对象。

以下实例我们添加了 methods 选项,选项中包含了 increment() 方法:

<div id="hello-vue" class="demo">
</div>
const app=Vue.createApp({
        data(){
            return{count:4}
        },
        methods:{
            increment(){
                this.count++;
            }
        }
    })
    const vm=app.mount("#hello-vue");

    document.write(vm.count); //=>4
    document.write("<br>");
    vm.increment();
    document.write(vm.count); //=>5

Ⅲ、模板语法 💥

Vue 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。

插值 --文本

数据绑定最常见的形式就是使用 {{…}}(双大括号)的文本插值:

<div id="app">
  <p>{{ message }}</p>
</div>
  • {{…}} 标签的内容将会被替代为对应组件实例中 message 属性的值,如果 message 属性的值发生了改变,{{…}} 标签内容也会更新。

使用 v-once 指令执行一次性地插值,当数据改变时,插值处的内容不会更新。

<span v-once>这个将不会改变: {{ message }}</span>

Html

使用 v-html 指令用于输出 html 代码:

<div id="example1" class="demo">
    <p>使用双括号文本插值:{{rawHtml}}</p>
    <p>使用v-html指令:<span v-html="rawHtml"></span></p>
</div>
    const app=Vue.createApp({
        data(){
            return{
               rawHtml:'<span style="color: red">我是红色字体!</span>'
            }
        }

    })
    const vm=app.mount("#example1");

属性 V-bind

HTML 属性中的值应使用 v-bind 指令。

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

对于布尔属性,常规值为 true 或 false,如果属性值为 null 或 undefined,则该属性不会显示出来。

<button v-bind:disabled="isButtonDisabled">按钮</button>

以下实例判断 use 的值,如果为 true 使用 class1 类的样式,否则不使用该类:


    <script src="js/vue.global.min.js"></script>
    <style>
        .class1{
            background-color: black;
            color: white;
        }
    </style>

<div id="app" class="demo">
    <label for="r1">修改颜色为黑底白字</label>
    <input type="checkbox" v-model="use" id="r1">
    
    <br><br>
    
    <div v-bind:class="{'class1':use}">
        v-bind:class 指令
    </div>
</div>

<script>
    const app=Vue.createApp({
        data(){
            return{
                use:false
            }
        }
    })
    const vm=app.mount("#app");    
</script>

在这里插入图片描述


动态绑定多个值

如果你有像这样的一个包含多个 attribute 的 JavaScript 对象:

data() {
  return {
    objectOfAttrs: {
      id: 'container',
      class: 'wrapper'
    }
  }
}

通过不带参数的 v-bind,你可以将它们绑定到单个元素上:

<div v-bind="objectOfAttrs"></div>

表达式

Vue.js 都提供了完全的 JavaScript 表达式支持。

 <style>
        #list-1{
            background-color: red;
        }
</style>
<div id="app" class="demo">
    {{5+5}} <br>
    {{ok?'YES':'NO'}}<br>
    {{message.split('').reverse().join('')}}<br>
    <div v-bind:id="'list-'+id">Maodi教程</div>
</div>

</body>
<script>

    const app=Vue.createApp({
        data(){
            return{
               ok:true,
                message:'RUNOOB!!',
                id:1
            }
        }

    })
    const vm=app.mount("#app");
</script>

在这里插入图片描述


指令 V-

指令是带有 v- 前缀的特殊属性。
指令用于在表达式的值改变时,将某些行为应用到 DOM 上。如下例子:

<div id="app" class="demo">
    <span v-if="seen">现在你能看的到我</span> <br>
    <button @click="btn">显示隐藏</button>
</div>

    const app=Vue.createApp({
        data(){
            return{
                seen:true
            }
        },
        methods:{
            btn(){
                this.seen=!this.seen
            }
        }

    })
    const vm=app.mount("#app");

这里, v-if 指令将根据表达式 seen 的值( true 或 false )来决定是否插入 p 元素。


参数

参数在指令后以冒号指明。例如, v-bind 指令被用来响应地更新 HTML 属性:

<div id="app" class="demo">
    <p><a v-bind:href="url">{{message}}</a></p>
</div>

<script>
    const app=Vue.createApp({
        data(){
            return{
                url:"https://www.baidu.com",
                message:'百度一下'
            }
        }


    })
    const vm=app.mount("#app");
</script>

在这里插入图片描述

在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定。

另一个例子是 v-on 指令,它用于监听 DOM 事件:

<!-- 完整语法 -->
<a v-on:click="doSomething"> ... </a>

<!-- 缩写 -->
<a @click="doSomething"> ... </a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>

在这里参数是监听的事件名。


修饰符

修饰符是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()

<form v-on:submit.prevent="onSubmit"></form>

用户输入 v-model

在 input 输入框中我们可以使用 v-model 指令来实现双向数据绑定

<div id="app" class="demo">
    <p>{{message}}</p>
    <input v-model="message">
</div>

<script>
    const app=Vue.createApp({
        data(){
            return{
                message:'百度一下'
            }
        }
    })
    const vm=app.mount("#app");
</script>

v-model 指令用来在 input、select、textarea、checkbox、radio 等表单控件元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值。

  • 按钮的事件我们可以使用 v-on 监听事件,并对用户的输入进行响应。

以下实例在用户点击按钮后对字符串进行反转操作:

<div id="app" class="demo">
    <p>{{message}}</p>
    <input v-model="message">
    <button @click="btn">反转字符串</button>
</div>

<script>
    const app=Vue.createApp({
        data(){
            return{
                message:''
            }
        },
        methods:{
            btn(){
                this.message=this.message.split('').reverse().join('')
            }
        }
    })
    const vm=app.mount("#app");

</script>

Ⅳ、计算属性👀

VUE 计算属性

计算属性关键词: computed

计算属性在处理一些复杂逻辑时是很有用的。

可以看下以下反转字符串的例子:

<div id="app">
  {{ message.split('').reverse().join('') }}
</div>

如果变的很复杂起来,也不容易看懂理解。

✅使用了计算属性的实例:

<div id="app">
     原始字符串:{{message}} <br>
     反转后字符串:{{reversedMessage}}
</div>

<script>
    const app=Vue.createApp({
       data(){
           return{
                message:"MAODIAINAO!!"
           }
       },
        computed:{
            reversedMessage:function (){
                return this.message.split('').reverse().join('')
            }
        }
    });
    const mount = app.mount("#app");
    
</script>

声明了一个计算属性 reversedMessage
提供的函数将用作属性 vm.reversedMessage 的 getter 。
vm.reversedMessage 依赖于 vm.message,在 vm.message 发生改变时,vm.reversedMessage 也会更新。


computed vs methods

  • 我们可以使用 methods 来替代 computed,效果上两个都是一样的
  • 但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值
  • 而使用 methods ,在重新渲染的时候,函数总会重新调用执行。
<div id="app">
  <p>原始字符串: {{ message }}</p>
  <p>使用 computed 计算后反转字符串: {{ reversedMessage }}</p>
  <p>使用 methods 计算后反转字符串: {{ reversedMessage2() }}</p>
</div>

<script>
const app = {
  data() {
    return {
      message: 'RUNOOB!!'
    }
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  },
  methods: {
    reversedMessage2: function () {
      return this.message.split('').reverse().join('')
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

可以说使用 computed 性能会更好,但是如果你不希望缓存,你可以使用 methods 属性。


可写计算属性 computed setter 👌

计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建:

<script>
    const app = Vue.createApp({
        data() {
            return {
                firstName:'John',
                lastName:'Doe'
            }
        },
        computed: {
            fullName:{
                get(){
                    return this.firstName + ' ' + this.lastName
                },
                set(newValue){
                    // 注意:我们这里使用的是解构赋值语法
                    [this.firstName,this.lastName] = newValue.split(' ');
                }
            }
        }

    });
    const mount = app.mount("#app");

    document.write("原来"+mount.fullName);// 原来John Doe
    document.write("<br>");

    mount.fullName="阮棠 RT";
    document.write("姓:"+mount.lastName); //RT
    document.write("<br>");
    document.write("名:"+mount.firstName);//阮棠
    document.write("<br>");
   document.write("更改后:"+mount.fullName)//更改后:阮棠 RT


</script>

现在当你再运行 this.fullName = '肢窝 胳 ' 时,setter 会被调用而 this.firstName 和 this.lastName 会随之更新。

在这里插入图片描述


Ⅴ、Class 与 Style 绑定

API
在这里插入图片描述


绑定 HTML class

在这里插入图片描述

<div :class="{ active: isActive }"></div>

在这里插入图片描述

data() {
  return {
    isActive: true,
    hasError: false
  }
}

配合以下模板:

<div
  class="static"
  :class="{ active: isActive, 'text-danger': hasError }"
></div>

渲染的结果会是:

<div class="static active"></div>

在这里插入图片描述

✅绑定的对象并不一定需要写成内联字面量的形式,也可以直接绑定一个对象:

data() {
  return {
    classObject: {
      active: true,
      'text-danger': false
    }
  }
}
<div :class="classObject"></div>

这也会渲染出相同的结果。我们也可以绑定一个返回对象的计算属性。这是一个常见且很有用的技巧:

		data() {
            return {
                isActive: true,
                error: null,
            }
        },
        computed:{
            
            classObject(){
                return{
                    active:this.isActive && !this.error,
                    'danger':!this.error
                }

            }
        }
<div :class="classObject"></div>
		.active{
            border-radius: 10px;
            background-color: burlywood;
            width: 100px;
            height: 100px;
        }
        .danger{
            color: white;
        }

运行结果:
在这里插入图片描述


# 绑定数组

我们可以给 :class 绑定一个数组来渲染多个 CSS class:

data() {
  return {
    activeClass: 'active',
    errorClass: 'danger'
  }
}
<div :class="[activeClass, errorClass]"></div>

渲染的结果是:

<div class="active danger"></div>

在这里插入图片描述

✅如果你也想在数组中有条件地渲染某个 class,你可以使用三元表达式:

<div :class="[isActive ? activeClass : '', errorClass]"></div>

errorClass 会一直存在,但 activeClass 只会在 isActive 为真时才存在。

✅然而,这可能在有多个依赖条件的 class 时会有些冗长。因此也可以在数组中嵌套对象:

<div :class="[{ active: isActive }, errorClass]"></div>

绑定内联样式

绑定对象

:style 支持绑定 JavaScript 对象值,对应的是 HTML 元素的 style 属性:

data() {
  return {
    activeColor: 'red',
    fontSize: 30
  }
}
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

尽管推荐使用 camelCase,但 :style 也支持 kebab-cased 形式的 CSS 属性 key (对应其 CSS 中的实际名称),例如:

<div :style="{ 'font-size': fontSize + 'px' }"></div>

✅直接绑定一个样式对象通常是一个好主意,这样可以使模板更加简洁:

data() {
  return {
    styleObject: {
      color: 'red',
      fontSize: '13px'
    }
  }
}
<div :style="styleObject"></div>

同样的,如果样式对象需要更复杂的逻辑,也可以使用返回样式对象的计算属性。


绑定数组

我们还可以给 :style 绑定一个包含多个样式对象的数组。这些对象会被合并后渲染到同一元素上:

<div :style="[baseStyles, overridingStyles]"></div>

自动前缀

当你在 :style 中使用了需要浏览器特殊前缀的 CSS 属性时,Vue 会自动为他们加上相应的前缀。Vue 是在运行时检查该属性是否支持在当前浏览器中使用。如果浏览器不支持某个属性,那么将尝试加上各个浏览器特殊前缀,以找到哪一个是被支持的。


样式多值

你可以对一个样式属性提供多个 (不同前缀的) 值,举例来说:

<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

数组仅会渲染浏览器支持的最后一个值。在这个示例中,在支持不需要特别前缀的浏览器中都会渲染为 display: flex


Ⅵ、条件渲染 💦

  • v-if
  • v-else
  • v-else-if
  • v-show
    在这里插入图片描述

在这里插入图片描述


Ⅶ、列表渲染

# v-for

我们可以使用 v-for 指令基于一个数组来渲染一个列表。v-for 指令的值需要使用 item in items 形式的特殊语法,其中 items 是源数据的数组,而 item 是迭代项的别名

data() {
  return {
    items: [{ message: 'Foo' }, { message: 'Bar' }]
  }
}
<li v-for="item in items">
  {{ item.message }}
</li>

✅在 v-for 块中可以完整地访问父作用域内的属性和变量。v-for 也支持使用可选的第二个参数表示当前项的位置索引。

data() {
  return {
    parentMessage: 'Parent',
    items: [{ message: 'Foo' }, { message: 'Bar' }]
  }
}
<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>

在这里插入图片描述
v-for 变量的作用域和下面的 JavaScript 代码很类似:

const parentMessage = 'Parent'
const items = [
  /* ... */
]

items.forEach((item, index) => {
  // 可以访问外层的 `parentMessage`
  // 而 `item` 和 `index` 只在这个作用域可用
  console.log(parentMessage, item.message, index)
})

注意 v-for 是如何对应 forEach 回调的函数签名的。实际上,你也可以在定义 v-for 的变量别名时使用解构,和解构函数参数类似:

<li v-for="{ message } in items">
  {{ message }}
</li>

<!-- 有 index 索引时 -->
<li v-for="({ message }, index) in items">
  {{ message }} {{ index }}
</li>

✅你也可以使用 of 作为分隔符来替代 in,这更接近 JavaScript 的迭代器语法:

<div v-for="item of items"></div>

# v-for 与对象

你也可以使用 v-for 来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用 Object.keys() 的返回值来决定。

data() {
  return {
    myObject: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
}
<ul>
  <li v-for="value of myObject">
    {{ value }}
  </li>
</ul>

✅ 可以通过提供第二个参数表示属性名 (例如 key):

<li v-for="(value, key) in myObject">
  {{ key }}: {{ value }}
</li>

✅ 第三个参数表示位置索引:

<li v-for="(value, key, index) in myObject">
  {{ index }}. {{ key }}: {{ value }}
</li>

以上结果:
在这里插入图片描述


# 在 v-for 里使用范围值

v-for 可以直接接受一个整数值。在这种用例中,会将该模板基于 1…n 的取值范围重复多次。

<span v-for="n in 10">{{ n }}</span>

注意此处 n 的初值是从 1 开始而非 0


# v-forv-if

在这里插入图片描述
当它们同时存在于一个节点上时,v-ifv-for 的优先级更高。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名:

<!--
 这会抛出一个错误,因为属性 todo 此时
 没有在该实例上定义
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo.name }}
</li>

在外新包装一层 <template> 再在其上使用 v-for 可以解决这个问题 (这也更加明显易读):

<template v-for="todo in todos">
  <li v-if="!todo.isComplete">
    {{ todo.name }}
  </li>
</template>

# 通过 key 管理状态

Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。

默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况

为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的 key attribute:

<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>

当你使用 <template v-for> 时,key 应该被放置在这个 <template> 容器上:

<template v-for="todo in todos" :key="todo.name">
  <li>{{ todo.name }}</li>
</template>

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

# 组件上使用 v-for

我们可以直接在组件上使用 v-for,和在一般的元素上使用没有区别 (别忘记提供一个 key):

<MyComponent v-for="item in items" :key="item.id" />

但是,这不会自动将任何数据传递给组件,因为组件有自己独立的作用域。为了将迭代后的数据传递到组件中,我们还需要传递 props

<MyComponent
  v-for="(item, index) in items"
  :item="item"
  :index="index"
  :key="item.id"
/>

不自动将 item 注入组件的原因是,这会使组件与 v-for 的工作方式紧密耦合。明确其数据的来源可以使组件在其他情况下重用。

这里是一个简单的 Todo List 的例子,展示了如何通过 v-for 来渲染一个组件列表,并向每个实例中传入不同的数据。


数组变化侦测


在这里插入图片描述

在这里插入图片描述

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


展示过滤或排序后的结果

有时,我们希望显示数组经过过滤或排序后的内容,而不实际变更或重置原始数据。在这种情况下,你可以创建返回已过滤或已排序数组的计算属性。

✅举例来说:

 		data() {
            return {
                numbers:[1,2,2,3,4,5,6,7,8,9,10,10]
            }
        },
        computed:{
            eventNUmber(){
                return this.numbers.filter((item,index,arr)=>{
                    return arr.indexOf(item,0)===index; //去重
                }).filter(n=>n%2==0)
            }
        }
<li v-for="n in evenNumbers">{{ n }}</li>

✅ 在计算属性不可行的情况下 (例如在多层嵌套的 v-for 循环中),你可以使用以下方法:

data() {
  return {
    sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
  }
},
methods: {
  even(numbers) {
    return numbers.filter(number => number % 2 === 0)
  }
}
<ul v-for="numbers in sets">
  <li v-for="n in even(numbers)">{{ n }}</li>
</ul>

在计算属性中使用 reverse()sort() 的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:

- return numbers.reverse()
+ return [...numbers].reverse()

Ⅶ、事件处理 💥

在这里插入图片描述

# 内联事件处理器

✅ 内联事件处理器通常用于简单场景,例如:

data() {
  return {
    count: 0
  }
}
<button @click="count++">Add 1</button>
<p>Count is: {{ count }}</p>

# 方法事件处理器

随着事件处理器的逻辑变得愈发复杂,内联代码方式变得不够灵活。因此 v-on 也可以接受一个方法名或对某个方法的调用。

✅ 举例来说:

data() {
  return {
    name: 'Vue.js'
  }
},
methods: {
  greet(event) {
    // 方法中的 `this` 指向当前活跃的组件实例
    alert(`Hello ${this.name}!`)
    // `event` 是 DOM 原生事件
    if (event) {
      alert(event.target.tagName)
    }
  }
}
<!-- `greet` 是上面定义过的方法名 -->
<button @click="greet">Greet</button>

方法事件处理器会自动接收原生 DOM 事件并触发执行。在上面的例子中,我们能够通过被触发事件的 event.target.tagName 访问到该 DOM 元素。


# 在内联处理器中调用方法

除了直接绑定方法名,你还可以在内联事件处理器中调用方法。这允许我们向方法传入自定义参数以代替原生事件:

methods: {
  say(message) {
    alert(message)
  }
}
<button @click="say('hello')">Say hello</button>
<button @click="say('bye')">Say bye</button>

# 在内联事件处理器中访问事件参数

有时我们需要在内联事件处理器中访问原生 DOM 事件。你可以向该处理器方法传入一个特殊的 $event 变量,或者使用内联箭头函数:

<!-- 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>

<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
  Submit
</button>
methods: {
  warn(message, event) {
    // 这里可以访问 DOM 原生事件
    if (event) {
      event.preventDefault()
    }
    alert(message)
  }
}

# 事件修饰符


在处理事件时调用 event.preventDefault()event.stopPropagation() 是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注于数据逻辑而不用去处理 DOM 事件的细节会更好。

为解决这一问题,Vue 为 v-on 提供了事件修饰符。修饰符是用 . 表示的指令后缀,包含以下这些:

在这里插入图片描述

<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>

<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>

<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>

<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>

<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>

在这里插入图片描述

  • .capture.once.passive 修饰符与原生 addEventListener 事件相对应:
<!-- 添加事件监听器时,使用 `capture` 捕获模式 -->
<!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 -->
<div @click.capture="doThis">...</div>

<!-- 点击事件最多被触发一次 -->
<a @click.once="doThis"></a>

<!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 -->
<!-- 以防其中包含 `event.preventDefault()` -->
<div @scroll.passive="onScroll">...</div>

.passive 修饰符一般用于触摸事件的监听器,可以用来改善移动端设备的滚屏性能。
在这里插入图片描述


# 按键修饰符

在监听键盘事件时,我们经常需要检查特定的按键。Vue 允许在 v-on@ 监听按键事件时添加按键修饰符。

<!-- 仅在 `key` 为 `Enter` 时调用 `submit` -->
<input @keyup.enter="submit" />

你可以直接使用 KeyboardEvent.key 暴露的按键名称作为修饰符,但需要转为 kebab-case 形式。

<input @keyup.page-down="onPageDown" />

在上面的例子中,仅会在 $event.key'PageDown' 时调用事件处理。


按键别名

Vue 为一些常用的按键提供了别名:
在这里插入图片描述


系统按键修饰符

在这里插入图片描述

✅ 举例来说:

<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />

<!-- Ctrl + 点击 -->
<div @click.ctrl="doSomething">Do something</div>

在这里插入图片描述

.exact 修饰符

.exact 修饰符允许控制触发一个事件所需的确定组合的系统按键修饰符。

<!-- 当按下 Ctrl 时,即使同时按下 Alt 或 Shift 也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 仅当按下 Ctrl 且未按任何其他键时才会触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 仅当没有按下任何系统按键时触发 -->
<button @click.exact="onClick">A</button>

鼠标按键修饰符

在这里插入图片描述

# 事件修饰符 测试✅✅✅✅✅✅


✅prevent:阻止默认事件(常用)

 <a href="https://www.baidu.com" @click.prevent="showInfo">百度一下你就知道</a>
methods:{
       showInfo(){
           alert("点我提示信息 不做任何操作")
        }
  }

a标签的默认有跳转到href的行为,我们把默认行为禁用后,就不会跳转页面

补充:

如果不使用Vue指令,那么怎么屏蔽事件的默认行为呢?
在这里插入图片描述


✅stop:阻止事件冒泡(常用)

<div class="demo" @click="showInfo">
        <button @click.stop="btnInfo">nihao1</button>
       <!-- 修饰符可以连续写 -->
       <!-- <a href="http://www.atguigu.com" @click.prevent.stop="showInfo">点我提示信息</a> -->
 </div>
	 methods:{
            showInfo(){
                alert("点我提示信息 不做任何操作")
            },
            btnInfo(){
                alert("点击了Button")
            }
        }

运行结果:
在这里插入图片描述

🚫 如果不阻止事件冒泡:

<div class="demo" @click="showInfo">
        <button @click="btnInfo">nihao1</button>
</div>

在这里插入图片描述
✅ 如果不使用Vue指令,那么怎么阻止事件冒泡呢?

在这里插入图片描述


✅once :事件只触发一次(常用)

<button @click.once="showInfo">点我提示信息</button>
 methods:{
        showInfo(){
           alert("点我提示信息 不做任何操作")
      	}
 }

运行结果:

在这里插入图片描述


✅capture:使用事件的捕获模式

		<!-- 使用事件的捕获模式 -->
		<div class="box1" @click.capture="showMsg(1)">
			div1
			<div class="box2" @click="showMsg(2)">
				div2
			</div>
		</div>
  • js:
    在这里插入图片描述
    运行结果:

在这里插入图片描述

🚫 关闭事件捕获:

在这里插入图片描述

运行结果:

在这里插入图片描述

分析原因: ❤✔💦

在这里插入图片描述


✅self:只有event.target是当前操作的元素时才触发事件

 <!-- 只有event.target是当前操作的元素时才触发事件; -->
    <div class="demo1" @click.self="showInfo">
        <button @click="showInfo">点我提示信息</button>
    </div>

js 💦

 methods:{
            showInfo(){
                alert("点我提示信息 不做任何操作")
            }

        }

因为event.target不是当前操作的元素,所有没有触发事件,这个在一定程度上面也可以阻止冒泡

注意

  • 只会弹出一次alert()
  • 如果不加 .self 则会执行两次

#按键修饰符

一般用来 input 来形容


Ⅷ、表单输入绑定

在前端处理表单时,我们常常需要将表单输入框的内容同步给 JavaScript 中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦:

<input
  :value="text"
  @input="event => text = event.target.value">
{{text}}

v-model 指令帮我们简化了这一步骤:

<input v-model="text">

另外,v-model 还可以用于各种不同类型的输入,<textarea>、<select> 元素。它会根据所使用的元素自动使用对应的 DOM 属性和事件组合:

  • 文本类型的 <input><textarea> 元素会绑定 value property 并侦听 input 事件;
  • <input type="checkbox"><input type="radio"> 会绑定 checked property 并侦听 change 事件;
  • <select> 会绑定 value property 并侦听 change 事件。
    在这里插入图片描述

基本用法


文本

<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="edit me" />

在这里插入图片描述

在这里插入图片描述


多行文本

	<span>Multiline message is:</span>
	<p style="">{{message}}</p>
	<textarea v-model="message" placeholder="add multiple lines"></textarea>

在这里插入图片描述

注意<textarea> 中是不支持插值表达式的。请使用 v-model 来替代:

<!-- 错误 -->
<textarea>{{ text }}</textarea>

<!-- 正确 -->
<textarea v-model="text"></textarea>

复选框

单一的复选框,绑定布尔类型值:

<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>

在这里插入图片描述

✅ 我们也可以将多个复选框绑定到同一个数组或集合的值:

	 data() {
            return {
                checkedNames:[]
            }
       }
<div>Checked names: {{ checkedNames }}</div>

<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>

在这里插入图片描述


单选按钮

	<div>Picked:{{ picked }}</div>
    <input type="radio" id="one" value="One" v-model="picked" checked> <!--默认被选中-->
    <label for="one">One</label>
    <input type="radio" id="two" value="Two" v-model="picked">
    <label for="Two">Two</label>
	 data() {
            return {
               picked:'One'
            }
        },

在这里插入图片描述


选择器

✅ 单个选择器的示例如下:

<div>Selected:{{ Selected }}</div>

    <select v-model="Selected">
        <option disabled value="" >Please select one</option>
        <option>A</option>
        <option>B</option>
        <option>C</option>
        <option>D</option>
    </select>

在这里插入图片描述
在这里插入图片描述
✅ 多选 (值绑定到一个数组):

<div>Selected:{{ Selected }}</div>

    <select v-model="Selected" multiple>  <!--multiple 倍数 n.数量多的-->
        <option>A</option>
        <option>B</option>
        <option>C</option>
        <option>D</option>
    </select>

✅ 选择器的选项可以使用 v-for 动态渲染:

	 data() {
            return {
                selected:'A', //初始 选择器 为 A
                options:[
                    {text:'One',value:'A'},
                    {text:'Two',value:'B'},
                    {text:'Three',value:'C'}
                ]
            }
        },
 <select v-model="selected">
        <option v-for="option of options" :value="option.value"> <!--value 值 为 A-->
            {{option.text}} <!--信息为 One-->
        </option>
    </select>

    <div>Selected: {{ selected }}</div> <!--已设置 初始值 A-->

值绑定 💦

对于单选按钮,复选框和选择器选项,v-model 绑定的值通常是静态的字符串 (或者对复选框是布尔值):

<!-- `picked` 在被选择时是字符串 "a" -->
<input type="radio" v-model="picked" value="a" />

<!-- `toggle` 只会为 true 或 false -->
<input type="checkbox" v-model="toggle" />

<!-- `selected` 在第一项被选中时为字符串 "abc" -->
<select v-model="selected">
  <option value="abc">ABC</option>
</select>

但有时我们可能希望将该值绑定到当前组件实例上的动态数据。这可以通过使用 v-bind 来实现。此外,使用 v-bind 还使我们可以将选项值绑定为非字符串的数据类型。

复选框

<input
  type="checkbox"
  v-model="toggle"
  true-value="yes"
  false-value="no" />
{{toggle}}

true-valuefalse-value 是 Vue 特有的 attributes,仅支持和 v-model 配套使用。这里 toggle 属性的值会在选中时被设为 'yes',取消选择时设为 'no'。你同样可以通过 v-bind 将其绑定为其他动态值:

<input
  type="checkbox"
  v-model="toggle"
  :true-value="dynamicTrueValue"
  :false-value="dynamicFalseValue"  checked/>  
	data() {
            return {
                toggle:'已被选中',
                dynamicTrueValue:'已被选中',  //动态的设置 选中和未选中的值
                dynamicFalseValue:'取消选中'
            }
        },

在这里插入图片描述


单选按钮

<input type="radio" v-model="pick" :value="first" checked/>
<input type="radio" v-model="pick" :value="second" />
 return {
        pick:"男",
        first:"男",
        last:"女"
 }

pick 会在第一个按钮选中时被设为 first,在第二个按钮选中时被设为 second


选择器选项

<select v-model="selected">
  <!-- 内联对象字面量 -->
  <option :value="{ number: 123 }">123</option>
</select>
{{selected.number}} <!--获取当前 option.number 的值-->

v-model 同样也支持非字符串类型的值绑定!在上面这个例子中,当某个选项被选中,selected 会被设为该对象字面量值 { number: 123 }


修饰符 💦

# .lazy

  • 默认情况下,v-model 会在每次 input 事件后更新数据
  • .lazy 会转变为在 change 事件中同步。
  • 也就是在失去焦点 或者 按下回车键时才 更新
<!-- 在 "change" 失去焦点事件后 更新 -->
<input v-model.lazy="msg" />

# .number

如果你想让用户输入自动转换为数字,你可以在 v-model 后添加 .number 修饰符来管理输入:

<input v-model.number="age" />

如果该值无法被 parseFloat() 处理,那么将返回原始值。

number 修饰符会在输入框有 type="number" 时自动启用。


# .trim

如果你想要默认自动去除用户输入内容中两端的空格 ,你可以在 v-model 后添加 .trim 修饰符:

<input v-model.trim="msg" />

❤🧡💛

过滤器


 <!--日期格式化-->
    <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.7/dayjs.min.js"></script>

在这里插入图片描述

自定义指令 💨💦


1. 介绍

除了 Vue 内置的一系列指令 (比如 v-modelv-show) 之外,Vue 还允许你注册自定义的指令

下面我们注册一个全局指令 v-focus, 该指令的功能是在页面加载时也就是 当一个 input 元素被 Vue 插入到 DOM 中后 ,元素获得焦点:

<div id="app">
    <p>页面载入时,input 元素自动获取焦点:</p>
    <input v-focus>
</div>
 
<script>
const app = Vue.createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
  // 当被绑定的元素挂载到 DOM 中时……
  mounted(el) {
    // 聚焦元素
    el.focus()
  }
})
app.mount('#app')
</script>

✅ 我们也可以在实例使用 directives 选项来注册局部指令,这样指令只能在这个实例中使用:

<div id="app">
    <p>页面载入时,input 元素自动获取焦点:</p>
    <input v-focus>
</div>
 
<script>
const app = {
   data() {
      return {
      }
   },
   directives: {
      focus: {
         // 指令的定义
         mounted(el) {
            el.focus()
         }
      }
   }
}
 
Vue.createApp(app).mount('#app')

在这里插入图片描述

2. 指令钩子

指令定义函数提供了几个钩子函数(可选):

  • created : 在绑定元素的属性或事件监听器被应用之前调用。

  • beforeMount : 指令第一次绑定到元素并且在挂载父组件之前调用。。

  • mounted : 在绑定元素的父组件被挂载后调用。。

  • beforeUpdate: 在更新包含组件的 VNode 之前调用。。

  • updated: 在包含组件的 VNode 及其子组件的 VNode 更新后调用。

  • beforeUnmount: 当指令与在绑定元素父组件卸载之前时,只调用一次。

  • unmounted: 当指令与元素解除绑定且父组件已卸载时,只调用一次。

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
	

全局指令:

	// 注册 (功能指令)
	app.directive('my-directive', () => {
	  // 这将被作为 `mounted` 和 `updated` 调用
	})
	
	// 配置对象
	app.directive('指令名',配置对象)

3. 钩子参数

在这里插入图片描述

4. 简化形式

对于自定义指令来说,一个很常见的情况是仅仅需要在 mountedupdated 上实现相同的行为,除此之外并不需要其他钩子。这种情况下我们可以直接用一个函数来定义指令,如下所示:

<div v-color="color"></div>
app.directive('color', (el, binding) => {
  // 这会在 `mounted` 和 `updated` 时都调用
  el.style.color = binding.value
})

5. 对象字面量

如果你的指令需要多个值,你可以向它传递一个 JavaScript 对象字面量。别忘了,指令也可以接收任何合法的 JavaScript 表达式。

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
app.directive('demo', (el, binding) => {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text) // => "hello!"
})

在这里插入图片描述

Ⅹ、生命周期


ⅩⅠ、侦听器

计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。

在选项式 API 中,我们可以使用 watch 选项在每次响应式属性发生变化时触发一个函数。

参考

测试:

✅ 你问我答

	 <p>
       Ask a yes/no question:
       <input v-model="question" />
    </p>
        <p>{{ answer }}</p>
	data(){
            return{
                question:'',
                answer: 'Questions usually contain a question mark. ;-)'
            }
        },
        watch:{
            question(newQuestion, oldQuestion) {
                if (newQuestion.indexOf('?') > -1) {
                    if(oldQuestion=="你是谁"){
                        this.answer="我是阮棠...."
                    }else if(oldQuestion=="你多大了"){
                        this.answer="21岁"
                    }
                }
            }
        }

在这里插入图片描述


深度监听

深度监视:

  • (1). Vue中的watch默认不监测对象内部值的改变(一层)
  • (2). 配置deep:true可以监测对象内部值改变(多层)。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>3天气案例_深度监视</title>
  <script src="/js/vue.global.min.js"></script>
  <style>

  </style>
</head>
<body>
  <!--
  深度监视:
    (1).Vue中的watch默认不监测对象内部值的改变(一层)
    (2).配置deep:true可以监测对象内部值改变(多层)。
  备注:
    (1).vue自身可以监测对象内部值的改变,但vue提供的watch默认不可以!
    (2).使用watch时根据数期的具体结构,决定是否采用深度监视。

  -->
<div id="app">
  <h2>今天天气很{{info}}</h2>
  <button @click="isHot =!isHot">切换</button>
  <hr>
  <h3>a的值是:{{numbers.a}}</h3>
  <button @click="numbers.a++">点我让a++</button>
  <hr>
  <h3>b的值是:{{numbers.b}}</h3>
  <button @click="numbers.b++">点我让b++</button>
</div>

</body>

<script>
  const app = Vue.createApp({
    data() {
      return {
        isHot:true,
        numbers:{
          a:1,
          b:1
        }
      }
    },
    computed:{
      info(){
       return  this.isHot?'炎热':'凉爽'
      }
    },
    watch:{
      isHot:{
        handler(newVal,oldVal){
          console.log("isHost被修改了",newVal,oldVal)
        }
      },
      //监听多级结构中某个属性的变化
     /*'numbers.a':{
          handler() {
            console.log("a被改变了")
          }
      }*/

      //监听多级结构中所有属性的变化
      numbers:{
        deep:true,
        handler(){
          console.log("number被改变了")
        }
      }

    }


  });
  const vm = app.mount("#app");

</script>
</html>

侦听器写法

	 return {
        isHot:true,
      }
      
	 watch:{
      //正常的写法
      isHot:{
        handler(newVal,oldVal){
          console.log("isHost被修改了",newVal,oldVal)
        }
      },
      //简写
      isHot(newVal,oldVal){
          console.log("isHost被修改了",newVal,oldVal)
      }
    }

//-----------------------------------------------------

	//正常的写法
  vm.$watch('isHot',{
    handler(newVal,oldVal){
      console.log("isHost被修改了",newVal,oldVal)
    }
  })

  //简写
  vm.$watch('isHot',function (newVal,oldVal){
      console.log("isHost被修改了",newVal,oldVal)
  })

即时回调的侦听器

watch 默认是懒执行的:

  • 仅当数据源变化时,才会执行回调。
  • 但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。
  • 举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。

我们可以用一个对象来声明侦听器,这个对象有 handler 方法和 immediate: true 选项,这样便能强制回调函数立即执行:

export default {
  // ...
  watch: {
    question: {
      handler(newQuestion) {
        // 在组件实例创建时会立即调用
      },
      // 强制立即执行回调
      immediate: true
    }
  }
  // ...
}

总结

  • 正常的写法 可以监听多级结构中所有属性的变化

    • deep:true
    • immediate:true

computedwatch之何的区别:

  1. computed能完成的功能,watch都可以完成
  2. watch能完成的功能,computed不一定能完成,例如: watch可以进行异步操作。

两个重要的小原则:

  • 所被 Vue 管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
  • 所有不被 Vue 所管理的函数( 定时器的回调函数、ajax的回调函数等, Promise的回调函数),最好写成箭头函数,
  • this的指向才是vm 或 组件实例对象。

ⅩⅡ、组件

组件(Component)是 Vue.js 最强大的功能之一。

组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:

在这里插入图片描述
这和我们嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。Vue 同样也能很好地配合原生 Web Component。如果你想知道 Vue 组件与原生 Web Components 之间的关系,可以阅读此章节


每个 Vue 应用都是通过用 createApp 函数创建的,传递给 createApp 的选项用于配置根组件。当我们挂载应用时,该组件被用作渲染的起点。

一个应用需要被挂载到一个 DOM 元素中。

✅ 以下实例我们将 Vue 应用挂载到 <div id="app"></div>,应该传入 #app

const RootComponent = { /* 选项 */ }
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')

全局组件

✅ 注册一个全局组件语法格式如下:

const app = Vue.createApp({...})

app.component('my-component-name', {
  /* ... */
})

my-component-name 为组件名,/* ... */ 部分为配置选项。注册后,我们可以使用以下方式来调用组件:

<my-component-name></my-component-name>

✅ 全局组件实例

  • 注册一个简单的全局组件 runoob,并使用它:
<div id="app">
    <runoob></runoob>
</div>
 
<script>
// 创建一个Vue 应用
const app = Vue.createApp({})
 
// 定义一个名为 runoob 的新全局组件
app.component('runoob', {
    template: '<h1>自定义组件!</h1>'
})
 
app.mount('#app')
</script>

局部组件

  • 全局注册往往是不够理想的。

比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。

在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:

const ComponentA = {
  /* ... */
}
const ComponentB = {
  /* ... */
}
const ComponentC = {
  /* ... */
}

✅ 然后在 components选项中定义你想要使用的组件:

const app = Vue.createApp({
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

对于 components 对象中的每个属性来说,其属性名就是自定义元素的名字(component-a、component-b),其属性值就是这个组件的选项对象(ComponentA、ComponentB)。

✅ 局部组件实例

var com1={
        data(){
            return{
                message:'Vue-Component'
            }
        },
        template:'<h1>我是自定义组件{{message}}</h1>'
    }

    const app = Vue.createApp({
        components:{
            'compon': com1
        }
    })

组件的复用

你可以将组件进行任意次数的复用:

<div id="app">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

Prop

prop子组件 用来接受父组件传递过来的数据的一个自定义属性

父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 “prop”:

<div id="app">
  <site-name title="Google"></site-name>
  <site-name title="Runoob"></site-name>
  <site-name title="Taobao"></site-name>
</div>
 
<script>
const app = Vue.createApp({})
 
app.component('site-name', {
  props: ['title'],
  template: `<h4>{{ title }}</h4>`
})
 
app.mount('#app')
</script>

一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。


动态 Prop

类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件:


<div id="app">
    <site-info v-for="site in sites" :id="site.id" :title="site.title"></site-info>
</div>

 const Site = {
        data() {
            return {
                sites: [
                    {id: 1, title: "Goole"},
                    {id: 2, title: "Runoob"},
                    {id: 3, title: "Taobao"}
                ]
            }
        }
    }

    const app = Vue.createApp(Site)
    app.component('site-info', {
        props: ['id', 'title'],
        template: `<h4>{{ id }} - {{ title }}</h4>`
    })

    const vm = app.mount("#app");
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

軟糖

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值