(10)Vue3核心语法大全


本系列教程目录Vue3+Element Plus全套学习笔记-目录大纲


第1章 Vue3核心语法

1.1 响应式

Vue 3 的响应式系统是其核心特性之一,它允许开发者以声明式的方式构建用户界面。如果数据不是响应式数据,当数据的值发生改变时,页面上的数据是不会发生改变的。

Vue 3 引入了两种主要的响应式 API:refreactive。下面将详细介绍这两种 API 的用法、区别以及在修改对象属性和修改整个对象时的不同表现。

1.1.1 ref

ref 用于定义基本类型数据和对象类型数据。使用 ref 创建的变量必须通过 .value 属性访问和修改其值。

语法:

let xxx=ref(初始值)

ref()函数将返回一个RefImpl的实例对象,简称ref对象或ref,ref对象中的value属性是响应式的。在JS中操作数据需要使用 "xxx.value"的形式,但在模板中不需要用. value,直接使用即可。

Tips:推荐使用 const(常量) 接收ref对象。代表代理对象不可变,但是内部值变化会被追踪。

观察如下案例:

<template>
  <p>个人信息:</p>

  <p>年龄:{{ age }}</p>

  <p>余额:{{ money }}</p>

  <button @click="moneyChange">点击余额加100</button>

  <hr>
  <button @click="ageChange">点击年龄加1</button>

</template>

<script setup>
import {ref} from "vue";

// 数据
let info = "大家好,我叫小灰"
let money = 10000;
let age = ref(25);

// 方法
function moneyChange() {
  money += 100;
  console.log('money: ', money);
}

function ageChange() {
  age.value += 1;
  console.log('age: ', age.value);
}
</script>

效果如下:

另外,ref也可以创建对象类型的响应式数据。

<template>
  <p>个人信息: {{ info.name }}</p>

  <p>小灰的年龄是:{{ info.age }} 岁</p>

  <p>小灰的身高是:{{ info.height }} cm</p>

  <button @click="ageChange">点击年龄加1</button>

  <button @click="heightChange">点击身高加1</button>

  <br />

  <button @click="nameChange">改变名称</button>

  <input type="text" v-model="name" />
</template>

<script setup>
import { ref } from "vue";

let name = "";

// 数据
let info = ref({
  name: "小灰",
  age: 20,
  height: 165,
});
let Constellation = ref([
  { id: 1, star: "双子座" },
  { id: 2, star: "狮子座" },
  { id: 3, star: "巨蟹座" },
]);

// 方法
function ageChange() {
  info.value.age += 1;
}
function heightChange() {
  info.value.height += 1;
}
function nameChange() {
  info.value.name = name;
}
</script>

推荐使用 const(常量) 接收ref对象。代表代理对象不可变,但是内部值变化会被追踪。

观察如下案例:

<template>
  <p>
    <!-- 绑定的是link.value,而不是link -->
    链接:<a :href="link">{{ link }}</a>

  </p>

  <button @click="linkChange">改变链接</button>

</template>

<script setup>
import {ref} from "vue";

let link = ref("http://www.jd.com");

// 最好使用const来声明变量,避免变量被修改
// const link = ref("http://www.jd.com");

function linkChange() {
  // 改变了link指向的引用,原来的link.value并不会改变
  link = ref("http://www.taobao.com")

  /*
   link.value的值为http://www.taobao.com
   但页面上显示的仍然是http://www.jd.com(页面上绑定的仍是之前的link)
   */
  console.log(link.value);
}

</script>

1.1.2 reactive

reactive 不能将基本类型的数据变为响应式,只适用于引用类型的数据(对象)。

reactive响应式数据由于是 proxy 代理的对象数据,可以直接获取到数据,不必添加 .value,即不论是在模板中还是JS中,不需要加.value即可访问或修改reactive响应式数据的值。

使用示例:

<template>
  <p>个人信息: {{ info.name }}</p>

  <p>年龄是:{{ info.age }} 岁</p>

  <p>身高是:{{ info.height }} cm</p>

  <button @click="ageChange">点击年龄加1</button>

  <button @click="heightChange">点击身高加1</button>

</template>

<script setup>
import {reactive} from "vue";

// 数据
let info = reactive({
  name: "小灰",
  age: 20,
  height: 165,
});

// 方法
function ageChange() {
  // 不用加value
  info.age += 1;
  console.log("age: ", info.age);
}

function heightChange() {
  // 不用加value
  info.height += 1;
  console.log("height: " + info.height);
}
</script>

需要注意,当reactive响应式数据重新分配一个新对象时, 将会失去响应式(ref并不会s);

使用示例:

<template>
  <p>个人信息: {{ info.name }}</p>

  <p>年龄是:{{ info.age }} 岁</p>

  <p>身高是:{{ info.height }} cm</p>

  <button @click="ageChange">点击年龄加1</button>

  <button @click="heightChange">点击身高加1</button>

  <hr>
  <button @click="infoChange">重置</button>

</template>

<script setup>
import {reactive} from "vue";

// 数据
let info = reactive({
  name: "小灰",
  age: 20,
  height: 185,
});

// 方法
function ageChange() {
  // 不用加value
  info.age += 1;
  console.log("age: ", info.age);
}

function heightChange() {
  // 不用加value
  info.height += 1;
  console.log("height: " + info.height);
}

function infoChange() {
  // 重置所有数据后,将失去响应式
  info = {
    name: "小蓝",
    age: 25,
    height: 155,
  };
}
</script>

1.1.3 响应式总结

  • ref:返回一个 RefImpl 对象,通过 .value 访问或修改值。为基本数据类型string, number, boolean 等)提供响应式能力,也可包装对象/数组(内部自动调用 reactive)。应用场景:
    • 当需要包装基本数据类型(如计数器、开关状态)。
    • 当需要灵活的类型支持(如联合类型 string | number)。
    • 当需要将值作为组合式函数的返回值(方便其他组件使用)。
  • reactive():直接操作对象的属性,无需 .value。为对象或数组创建深层次的响应式代理。应用场景:
    • 当处理复杂对象或嵌套结构(如表单数据、配置对象)。
    • 当需要直接操作属性(避免频繁使用 .value)。
    • 当需要与 watchwatchEffect 配合监听整个对象的变化。

综合对比如下:

特性ref()reactive()
数据类型基本类型 + 对象/数组(自动转换)仅对象/数组
访问方式.value(模板中自动解包)直接访问属性
响应式层级浅层(包装值本身)深层(递归代理所有嵌套属性)
类型支持更灵活(支持联合类型、泛型)需明确对象结构
解构响应式解构会丢失响应式(需用 toRefs解构会丢失响应式(需用 toRefs

1.2 Vue属性

1.2.1 监听属性 - watch

watch 的作用是用于监测响应式属性的变化,并在属性发生改变时执行特定的操作,它是 Vue 中的一种响应式机制,允许你在数据发生变化时做出相应的响应,执行自定义的逻辑。

Tips:需要注意,watch监听的是响应式属性的变化。

使用示例:

<template>
  <p>年龄: {{age}}</p>

  <p>{{info}}</p>

  <hr>
  <button @click="age++">年龄+1</button>

</template>

<script setup>
// 导入ref和watch
import {ref, watch} from "vue";

let age = ref(15);
let info = ref('未成年');
/*
参数1: 需要监听的变量
参数2: 监听函数,参数1是新值,参数2是旧值
 */
watch(age, (newVal, oldVal) => {

  console.log("newVal:", newVal, "oldVal:", oldVal);

  if(age.value >= 18){
    info.value = "成年";
  }

})
</script>

1.2.2 监听属性 - watchEffect

watchEffect 也是一个帧听器,只不过不像watch那样明确监听某一个响应数据,watchEffecx 是隐式的监听所有的响应数据。不需要具体到某个属性,一旦运行就会立即监听,组件卸载的时候会停止监听。

在使用 watchEffect 时我们只需要传递一个回调函数给它,在这个回调函数当中,它会自动监听响应数据,当回调函数里面的响应数据发生变化,回调函数就会立即执行。

使用示例:

<template>
  <p>年龄: {{ age }}</p>

  <p>{{ info }}</p>

  <hr>
  <button @click="age++">年龄+1</button>

</template>

<script setup>
// 导入ref和watch
import {ref, watch, watchEffect} from "vue";

let age = ref(15);
let info = ref('未成年');

// 监听所有的响应式数据变化
watchEffect(() => {

  // 由于
  console.log('age:', age.value);
  console.log('info:', info.value);

  if (age.value >= 18) {
    info.value = '成年';
  } else {
    info.value = '未成年';
  }
})

</script>

上述代码中,当我们第一次进入页面时,info和age响应数据从无到有,这个时候就会触发一次 watchEffect 的回调函数。

1.2.3 计算属性 - computed

computed计算属性用于计算某些响应式数据的结果集,在使用computed计算属性时我们需要传递一个函数给computed,该函数必须有返回值,返回值就是计算属性的值。在计算属性函数中会监听所使用到的响应式数据,当响应式数据发送变化后计算属性的函数会自动触发。

Tips:计算属性本身也是一种响应式数据。

使用示例:

<template>
  <h3>商品单价: {{ goods.price }}</h3>

  <h3>购买数量: {{ goods.count }}</h3>

  <h3>商品单价: {{ totalPrice }}</h3>

  <hr>
  <button @click="goods.count++">购买数量+1</button>

  <button @click="goods.price+=10">商品单价+10</button>

</template>

<script setup>
// 导入computed和reactive
import {computed, reactive} from "vue";

// 定义响应式商品数据
let goods = reactive({
  price: 25.00,
  count: 10
})

/*
 定义计算属性totalPrice,当商品单价或购买数量发生变化时,totalPrice会自动更新
 另外,price和count属性从无到有,也会触发一次计算属性的函数
 */

let totalPrice = computed(() => {
  console.log("商品单价", goods.price, "购买数量", goods.count, "总价", goods.price * goods.count)
  return goods.price * goods.count;
})

</script>

1.3 组件传值

在Vue中整个组件的入口为App.vue组件,在该组件中我们可以导入很多其他的组件,这些被导入到App.vue中的组件都是App.vue的子组件。在实际开发中,我们可能会有组件的嵌套。例如我们在App.vue中编写如下代码:

App.vue:
<template>
  <A />
  <D />
</template>

-------------------
A.vue:
<template>
  <B />
</template>

-------------------
B.vue:
<template>
  <C />
</template>

A是B的父组件,B是C的父组件,A与D是兄弟组件。这样就形成很多的父子组件,这些组件之间经常需要进行值的传递,分为父组件给子组件传递以及子组件给父组件传递。

vue中的父子组件传值遵守单向数据流原则。所谓单向数据流原则,简单的说就是父组件的数据可以传递给子组件,子组件也可以正常获取并使用由父组件传过来的数据;但是,子组件中不能直接修改父组件传过来的数据,必须要向父组件传递一个事件来父组件需要修改数据,即通过子组件的操作,在父组件中修改数据;这就是单项数据流。

1.3.1 父子传值 - Props

定义父组件Parent.vue:

<script setup>
// 1 引入子组件
import Child from './Child.vue'
import {ref} from "vue";

const money = ref(100)
</script>

<template>
  <div id="Outer">
    <h2>我是Parent</h2>

    <button @click="money+=10">加钱</button>

    <!--使用子组件,通过属性向子组件传值,向子组件传递的参数对于子组件来说是只读的,不能修改-->
    <Child title="Hello Child" :money="money"></Child>

  </div>

</template>

<style>
  #Outer{
    padding: 30px;
    background-color: #ccc;
  }
</style>

定义子组件Child.vue:

<script setup>
// 接受父组件传递过来的title和money属性
let parent= defineProps(['title','money']);

function buy(){
  parent.money -= 10;
  console.log(parent.money);
}
</script>

<template>
  <div id="inner">
    <h2>我是Child</h2>

    <button @click="buy()">买东西</button>

    <h3>父组件的title --- {{ parent.title }}</h3>

    <h3>父组件的money --- {{ parent.money }}</h3>

  </div>

</template>


<style>
#inner{
  padding: 30px;
  background-color: #99FF99;
}
</style>

1.3.2 子父传值 - Emit

子组件无法直接修改父组件传递过来的值,但是可以通过“事件感知”的方式通知父组件,将修改的值传递给父组件,然后由父组件自身来修改值,最后再传回子组件完成数据的修改。

父组件Parent.vue:

<script setup>
// 1 引入子组件
import Child from './Child.vue'
import {ref} from "vue";

const money = ref(100)

// 当子组件的money值发生变化时,触发父组件的childBuy事件
function childBuy(){
    money.value -= 10
}
</script>

<template>
  <div id="Outer">
    <h2>我是Parent</h2>

    <button @click="money+=10">加钱</button>

    <!--
    使用子组件,通过属性向子组件传值,向子组件传递的参数对于子组件来说是只读的,不能修改
    @buy: 子组件的事件,子组件通过调用父组件的buy方法来修改父组件的money值
    -->
    <Child title="Hello Child" :money="money" @buy="childBuy"></Child>

  </div>

</template>

<style>
  #Outer{
    padding: 20px;
    background-color: #ccc;
  }
</style>

子组件Child.vue:

<script setup>

// 接受父组件传递过来的title和money属性
let parent= defineProps(['title','money']);

// 定义一个事件,通知父组件修改money属性
let moneyChangeEmits = defineEmits(['moneyChange'])

function buy(){

  // 不能直接修改父组件的money属性,需要通过emit事件通知父组件修改money属性
  // parent.money -= 10;
  // console.log(parent.money);

  // 通知父组件修改money属性(以函数的方式), 并传递参数-10给父组件
  moneyChangeEmits('buy', -10);
}
</script>

<template>
  <div id="inner">
    <h2>我是Child</h2>

    <button @click="buy()">买东西</button>

    <h3>父组件的title --- {{ parent.title }}</h3>

    <h3>父组件的money --- {{ parent.money }}</h3>

  </div>

</template>


<style>
#inner{
  padding: 10px;
  background-color: #99FF99;
}
</style>

1.4 插槽 - Slots

Vue 的 插槽(Slots) 是一种组件间内容分发的机制,允许父组件向子组件传递模板片段(HTML 结构或其他组件),实现更灵活的组件复用和组合。

插槽 slot 是写在子组件的代码中,供父组件使用的占位符进行填充。子组件中使用插槽 slot 后,父组件可以在这个占位符中填充内容,包括数据、html代码、组件等,也就是说,当子组件的某部分内容是根父组件填充的不同而变化的,那我们就可以使用插槽slot,具体填充什么,由父组件而定。

插槽 slot 主要分为三大类:默认插槽、具名插槽和作用域插槽。

1.4.1 默认插槽

  • 子组件:用 <slot> 标签定义占位符,未提供内容时显示默认值。
  • 父组件:在子组件标签内部直接传递内容。

子组件:

<!-- 子组件 Child.vue -->
<template>
  <div>
    <slot>默认内容(当父组件不传内容时显示)</slot>

  </div>

</template>

父组件:

<script setup>
import Child from './Child.vue'
</script>

<template>
  <!-- 父组件 -->
  <Child>
    <h3>这是父组件插入的内容</h3>  <!-- 替换默认插槽 -->
  </Child>

</template>

1.4.2 具名插槽

  • 子组件:通过 name 属性定义多个插槽。
  • 父组件:用 v-slot:name#name 指令指定内容分发的目标插槽。

子组件:

<!-- 子组件 Layout.vue -->
<template>
  <div>
    <header>
      <slot name="header"></slot>

    </header>

    <main>
      <slot></slot>

    </main>  <!-- 默认插槽 -->
    <footer>
      <slot name="footer"></slot>

    </footer>

  </div>

</template>

父组件:

<script setup>
import Child from './Child.vue'
</script>

<template>
  <Child>
    <template #header>  <!-- 简写语法(v-slot:header) -->
      <h3>这是标题</h3>

    </template>

    <h2>这是主内容(自动放入默认插槽)</h2>

    <template v-slot:footer>
      <h3>这是页脚</h3>

    </template>

  </Child>

</template>

1.4.3 作用域插槽

  • 子组件:通过 <slot> 绑定数据(类似 props),允许父组件访问子组件内部状态。
  • 父组件:用 v-slot:name="props" 接收数据。

作用域插槽实际上是父组件访问子组件的一种形式,此时若子组件的值发送变化则父组件的值也会发送变化。因此作用域插槽可以当做子组件为父组件传递参数的一种方式。

子组件:

<!-- 子组件 Child.vue -->
<script setup>
import {reactive, ref} from "vue";

let user = reactive({
    name: "小灰",
    age: 20,
    sex: "男"
});
</script>

<template>
  <div>
    <h3>我是子组件</h3>

    <!--  传递数据给父组件  -->
    <slot :message="user"></slot>

    <hr>
    <!-- 修改用户数据之后父组件也会跟着变化   -->
    <button @click="user.age++">修改用户数据</button>

  </div>

</template>

父组件:

<script setup>
import Child from './Child.vue'

</script>

<template>
  <!-- 父组件 -->
  <h2>我是父组件</h2>

  <Child>
    <template #default="{message}">
      <h3>子组件传递的值:{{message.name}}</h3>

      <h3>子组件传递的值:{{message.age}}</h3>

      <h3>子组件传递的值:{{message.sex}}</h3>

    </template>

  </Child>

</template>

1.4 setup

setup 是 Vue 3 新增的语法糖,可以让我们使用更简洁的代码来编写组件。它在编译时会将代码转换为使用 setup 函数的形式,省略了传统 Vue 组件中的 data、methods 等属性的定义。

1.4.1 Options 与 Composition

Options API被称为选项式API,是Vue2的API设计风格。Composition API被称为组合式API,是Vue3的API设计风格。

1)Options API

Options API将数据、方法、生命周期等分散到不同的选项中,适合简单组件。复杂组件中,同一功能的逻辑可能分散在 datamethodsmounted 等不同选项中,导致代码碎片化。

代码示例:

<script>
export default {
  // 数据
  data() {
    // 在Options API中,data 返回的对象自动成为响应式,通过 this 访问属性。
    return { count: 0 };
  },
  // 方法
  methods: {
    increment() {
      this.count++;
    }
  },
  // 计算属性
  computed: {
    doubleCount() {
      return this.count * 2;
    }
  },
  // 生命周期钩子
  mounted() {
    console.log("Component mounted");
  }
};
</script>

<template>
  <div>
    <h3>{{count}}</h3>

    <button @click="increment()">增加</button>

  </div>

</template>

Tips:在Options API中,data 返回的对象自动成为响应式,通过 this 访问属性。

2)Composition API

Composition API是Vue 3引入的,基于函数式编程,允许将逻辑按功能组织,提高复用性。

代码示例:

<script>
import {ref} from "vue";

export default {

  setup() {
    // setup定义的属性默认不是响应式的,需要手动设置为响应式的
    let count = ref(0);
    const increment = () => {
      count.value++;
      // setup中不可以访问this(undefined)
      console.log("this: " + this);
    };

    // 导出
    return {count, increment};
  },
  // 生命周期方法
  mounted() {
    console.log("Component mounted");
  }
}
</script>

<template>
  <div>
    <h3>{{ count }}</h3>

    <button @click="increment()">增加</button>

  </div>

</template>

Tips:在setup()中定义的数据,方法都需要 return 返回出去,不然会报错。

1.4.2 Composition API的特殊用法

Composition API是Vue3推荐的语法,但Composition API与Options API仍存在一些特殊的小问题,虽然这些小问题不影响我们的开发,但是我们还是了解一下比较好。

(1)Composition API与不能访问到Options API中的数据,但Options API可以访问到Composition API中的数据;

示例代码:

<script>
export default {
  data() {
    return { optionsCount: 0 };
  },
  methods: {
    optionsMethod() {
      // 访问setup中的数据
      console.log('setupCount:',this.setupCount++)
    }
  },
  setup() {
    let setupCount = 0;
    const setupMethod = () => {
      // 访问options中的数据
      console.log('optionsCount:',optionsCount++)
    };
    return {
      setupCount,
      setupMethod
    };
  }
};
</script>

<template>
  <div>
    <button @click="optionsMethod">访问setup</button>

    <button @click="setupMethod">访问options</button>

  </div>

</template>

效果如下:

(2)Composition API 中的返回值若是一个函数,那么该函数的返回值会被填充到页面渲染。

示例代码:

<script>
export default {
  setup() {
    return ()=> {
      return "<h1>This is Demo03</h1>"
    }
  }
};
</script>

<template>
    <div>This is Demo03</div>

</template>

效果如下:

(3)setup 语法糖写法,可以让我们将setup函数中的代码独立出来,这个写法也是我们之前一直使用的写法。

示例代码:

<!--不要忘了在script标签中引入setup语法-->
<script setup>
import { ref, computed, onMounted } from "vue";

// count是非响应式数据
// let count = 0;
// 响应式数据
let count = ref(0);
// 方法
const increment = () => count.value++;
// 计算属性
let doubleCount = computed(() => count.value * 2);
// 生命周期
onMounted(() => {
  console.log("Component mounted");
});
</script>

<template>
  <div>
    <h3>{{count}}</h3>

    <button @click="increment()">增加</button>

  </div>

</template>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

緑水長流*z

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

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

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

打赏作者

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

抵扣说明:

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

余额充值