目录
认识Vue3
为什么需要学Vue3?
Vue3组合式API体验
Vue3更多的优势
使用create-vue搭建Vue3项目
认识 create-vue
使用create-vue创建项目
熟悉项目目录和关键文件
组合式API - setup选项
setup选项的写法和执行时机
setup选项中写代码的特点
组合式API - reactive和ref函数
reactive()
ref()
组合式API - computed
组合式API - watch
watch函数
基础使用 - 侦听单个数据
基础使用 - 侦听多个数据
immediate
deep
精确侦听对象的某个属性
组合式API - 生命周期函数
Vue3的生命周期API (选项式 VS 组合式)
生命周期函数基本使用
执行多次
组合式API - 父子通信
组合式API下的父传子
组合式API下的子传父
组合式API - 模版引用
模板引用的概念
如何使用(以获取dom为例 组件同理)
defineExpose()
组合式API - provide和inject
作用和场景
跨层传递普通数据
跨层传递响应式数据
跨层传递方法
认识Vue3
为什么需要学Vue3?
Vue3组合式API体验
<template>
<div>
<button @click="addCount">{{ count }}</button>
</div>
</template>
<!-- <script>
export default {
data() {
return {
count: 0,
};
},
methods: {
addCount() {
this.count++;
},
},
};
</script> -->
<script setup>
import { ref } from "vue";
const count = ref(0);
const addCount = () => count.value++;
</script>
Vue3更多的优势
使用create-vue搭建Vue3项目
认识 create-vue
create-vue是Vue官方新的脚手架工具,底层切换到了 vite(下一代前端工具链),为开发提供极速响应
使用create-vue创建项目
1. 前提环境条件
已安装 16.0 或更高版本的 Node.js
2. 创建一个Vue应用
npm init vue@latest
这一指令将会安装并执行 create-vue
熟悉项目目录和关键文件
关键文件:
1. vite.config.js - 项目的配置文件 基于vite的配置
2. package.json - 项目包文件 核心依赖项变成了 Vue3.x 和 vite 3. main.js - 入口文件 createApp函数创建应用实例
4. app.vue - 根组件 SFC单文件组件 script - template - style
变化一:脚本script和模板template顺序调整 变化二:模板template不再要求唯一根元素 变化三:脚本script添加setup标识支持组合式API
5. index.html - 单页入口 提供id为app的挂载点
入口文件
import './assets/main.css'
// vue2:new vue() 创建一个应用实例对象
import { createApp } from 'vue'
import App from './App.vue'
// 以app 作为参数 生成一个应用实例对象
// 挂载到一个id为 app的节点上
createApp(App).mount('#app')
组合式API - setup选项
setup选项的写法和执行时机
setup比beforeCreate之前触发
<script >
export default {
setup() {
console.log("setup");
},
beforeCreate() {
console.log("beforeCreate");
},
};
</script>
setup选项中写代码的特点
<script >
export default {
setup() {
console.log("setup", this);
const message = "this is massage";
const messageLog = () => {
console.log(message);
};
return {
message,
messageLog,
};
},
};
</script>
<script setup> 语法糖
<script setup >
console.log("setup", this);
const message = "this is massage";
const messageLog = () => {
console.log(message);
};
1. setup选项的执行时机? beforeCreate钩子之前 自动执行
2. setup写代码的特点是什么? 定义数据 + 函数 然后以对象方式return
3. <script setup>解决了什么问题? 经过语法糖的封装更简单的使用组合式API
4. setup中的this还指向组件实例吗? 指向undefined
组合式API - reactive和ref函数
reactive()
作用:接受对象类型数据的参数传入并返回一个响应式的对象
1. 从 vue 包中导入 reactive 函数
2. 在 <script setup> 中执行 reactive 函数并传入类型为对象的初始值,并使用变量接收返回值
<template>
<div>
<button @click="setCount">{{ state.count }}</button>
</div>
</template>
<script setup>
// 导入函数
import { reactive } from "vue";
// 执行函数 传入一个对象类型的参数 变量接受
const state = reactive({
count: 0,
});
const setCount = () => {
state.count++;
};
</script>
ref()
作用:接收简单类型或者对象类型的数据传入并返回一个响应式的对象
1. 从 vue 包中导入 ref 函数
2. 在 <script setup> 中执行 ref 函数并传入初始值,使用变量接收 ref 函数的返回值
<script setup>
// 导入函数
import { ref } from "vue";
// 执行函数 传入参数【 简单类型+对象类型 】 变量接受
const count = ref(0);
const setCount = () => {
//脚步区域修改ref产生的响应式对象数据,必须通过 .value属性
count.value++;
};
</script>
1. reactive和ref函数的共同作用是什么? 用函数调用的方式生成响应式数据
2. reactive vs ref ?
- 1. reactive不能处理简单类型的数据
- 2. ref参数类型支持更好但是必须通过.value访问修改 3. ref函数的内部实现依赖于reactive函数
3. 在实际工作中推荐使用哪个? 推荐使用ref函数,更加灵活,小兔鲜项目主用ref
组合式API - computed
计算属性基本思想和Vue2的完全一致,组合式API下的计算属性只是修改了写法
核心步骤:
1. 导入computed函数
2. 执行函数 在回调参数中return基于响应式数据做计算的值,用变量接收
<template>
<div>
<div>原始响应式数组:{{ list }}</div>
<div>计算属性数组:{{ computedList }}</div>
</div>
</template>
<script setup>
// 原始响应式数组
import { ref } from "vue";
const list = ref([1, 2, 3, 4, 5, 6, 7, 8]);
// 1.导入 computed
import { computed } from "vue";
// 2.执行函数 return 计算之后的值 变量接受
const computedList = computed(() => {
// 做计算 根据一个值计算得到一个新值
return list.value.filter((item) => item > 2);
});
setTimeout(() => {
list.value.push(9, 10);
}, 3000);
</script>
最佳实践
1. 计算属性中不应该有“副作用” 比如异步请求/修改dom2. 避免直接修改计算属性的值 计算属性应该是只读的
组合式API - watch
watch函数
作用: 侦听一个或者多个数据的变化,数据变化时执行回调函数 俩个额外参数:1. immediate(立即执行) 2. deep(深度侦听)
基础使用 - 侦听单个数据
1. 导入watch函数
2. 执行watch函数传入要侦听的响应式数据(ref对象)和回调函数
<template>
<div>
<button @click="setCount">{{ count }}</button>
</div>
</template>
<script setup>
// 原始响应式数组
import { ref, watch } from "vue";
const count = ref(0);
const setCount = () => {
count.value++;
};
// watch 监听单个数据 ref对象不需要加.value
watch(count, (newValue, oldValue) => {
console.log("count变化了" + newValue, oldValue);
});
</script>
基础使用 - 侦听多个数据
说明:同时侦听多个响应式数据的变化,不管哪个数据变化都需要执行回调
<template>
<div>
<button @click="setCount">{{ count }}</button>
<button @click="setName">{{ name }}</button>
</div>
</template>
<script setup>
// 原始响应式数组
import { ref, watch } from "vue";
const count = ref(0);
const name = ref("ZS");
const setCount = () => {
count.value++;
};
const setName = () => {
name.value = "ls";
};
// watch 监听多个数据
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log("count或者name变化了" + [newCount, newName], [oldCount, oldName]);
});
</script>
immediate
说明:在侦听器创建时立即触发回调, 响应式数据变化之后继续执行回调
<template>
<div>
<button @click="setCount">{{ count }}</button>
</div>
</template>
<script setup>
// 原始响应式数组
import { ref, watch } from "vue";
const count = ref(0);
const setCount = () => {
count.value++;
};
// watch 立即执行
watch(
count,
(newValue, oldValue) => {
console.log("count变化了" + newValue, oldValue);
},
{
immediate: true,
}
);
</script>
deep
默认机制:通过watch监听的ref对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep 选项
<template>
<div>
<button @click="setCount">{{ state.count }}</button>
</div>
</template>
<script setup>
// 原始响应式数组
import { ref, watch } from "vue";
const state = ref({ count: 1 });
const setCount = () => {
state.value.count++;
};
// watch 深度监听
watch(
state,
() => {
console.log("count变化了");
},
{
deep: true,
}
);
</script>
精确侦听对象的某个属性
需求:在不开启deep的前提下,侦听age的变化,只有age变化时才执行回调
<template>
<div>
<button @click="setCount">{{ state.count }}</button>
<button @click="setName">{{ state.name }}</button>
</div>
</template>
<script setup>
// 原始响应式数组
import { ref, watch } from "vue";
const state = ref({
name: "zs",
count: 0,
});
const setCount = () => {
state.value.count++;
};
const setName = () => {
state.value.name = "ls";
};
// watch 精确侦听对象的某个属性
watch(
() => state.value.count,
() => {
console.log("count变化了");
}
);
</script>
1. 作为watch函数的第一个参数,ref对象需要添加.value吗? 不需要,watch会自动读取
2. watch只能侦听单个数据吗? 单个或者多个
3. 不开启deep,直接修改嵌套属性能触发回调吗?不能,默认是浅层侦听4. 不开启deep,想在某个层次比较深的属性变化时执行回调怎么做? 可以把第一个参数写成函数的写法,返回要监听的具体属性
组合式API - 生命周期函数
Vue3的生命周期API (选项式 VS 组合式)
生命周期函数基本使用
1. 导入生命周期函数
2. 执行生命周期函数 传入回调
执行多次
生命周期函数是可以执行多次的,多次执行时传入的回调会在时机成熟时依次执行
<template>
<div></div>
</template>
<script setup>
// 引入函数
import { onMounted } from "vue";
onMounted(() => {
console.log("mounted1");
});
// 执行函数传入回调
onMounted(() => {
console.log("mounted2");
});
</script>
1. 组合式API中生命周期函数的格式是什么? on + 生命周期名字
2. 组合式API中可以使用onCreated吗? 没有这个钩子函数,直接写到setup中
3. 组合式API中组件卸载完毕时执行哪个函数? onUnmounted
组合式API - 父子通信
组合式API下的父传子
基本思想
1. 父组件中给子组件绑定属性
2. 子组件内部通过props选项接收
//父组件
<template>
<div>
<h2>父组件App</h2>
<!-- 1.绑定属性 -->
<son message="father message"></son>
</div>
</template>
<script setup>
// setup 语法糖下局部组件无需注册可以直接使用
import son from "./components/HelloWorld.vue";
</script>
//子组件
<script setup>
// defineProps 接收数据
const proprs = defineProps({
message: "String",
});
console.log(proprs);
</script>
<template>
<div>
<h3>子组件Son</h3>
<div>父组件App传递的值{{ message }}</div>
</div>
</template>
<style scoped>
</style>
组合式API下的子传父
基本思想
1. 父组件中给子组件标签通过@绑定事件
2. 子组件内部通过 $emit 方法触发事件
//父组件
<template>
<div>
<h2>父组件App</h2>
<!-- 1.绑定事件 -->
<son @getMessage="getMessage"></son>
</div>
</template>
<script setup>
// setup 语法糖下局部组件无需注册可以直接使用
import son from "./components/HelloWorld.vue";
const getMessage = (msg) => {
console.log(msg, "父组件");
};
</script>
//子组件
<script setup>
// 通过 defineEmits()=> emit(this.$emit)
const emit = defineEmits(["getMessage"]);
// 触发自定义事件传递给父组件
const sonMsg = () => {
emit("getMessage", "this is a son message");
};
</script>
<template>
<div>
<h3>子组件Son</h3>
<button @click="sonMsg">自定义事件</button>
</div>
</template>
<style scoped>
</style>
父传子
1. 父传子的过程中通过什么方式接收props?defineProps( { 属性名:类型 } )
2. setup语法糖中如何使用父组件传过来的数据?
const props = defineProps( { 属性名:类型 } )
子传父
1. 子传父的过程中通过什么方式得到emit方法?
defineEmits( [‘事件名称’] )
组合式API - 模版引用
模板引用的概念
通过ref标识获取真实的dom对象或者组件实例对象
如何使用(以获取dom为例 组件同理)
<template>
<div>
<!-- 通过一个ref标识 指定ref对象 -->
<h1 ref="refh1">我是dom标签</h1>
<son ref="sonref" />
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import son from "./components/HelloWorld.vue";
//调用 ref 函数 ==》ref对象
const refh1 = ref(null);
const sonref = ref(null);
//组件渲染完毕之后才能获取
onMounted(() => {
console.log(refh1.value);
console.log(sonref.value);
});
defineExpose()
默认情况下在<script setup>语法糖下组件内部的属性和方法是不开放给父组件访问的,可以通过defineExpose编译宏指定哪些属性和 方法允许访问
<script setup>
import { ref } from "vue";
const name = ref("test-name");
const setname = () => {
name.value = "test new name";
};
defineExpose({
name,
setname,
});
</script>
<template>
<div>
<h3>我是test组件</h3>
</div>
</template>
<style scoped>
</style>
1. 获取模板引用的时机是什么?
组件挂载完毕
2. defineExpose编译宏的作用是什么?
显式暴露组件内部的属性和方法
组合式API - provide和inject
作用和场景
顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信
跨层传递普通数据
1. 顶层组件通过provide函数提供数据
2. 底层组件通过inject函数获取数据
跨层传递响应式数据
在调用provide函数时,第二个参数设置为ref对象
跨层传递方法
顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据
//顶层组件
<template>
<div>
<h1>顶层组件</h1>
<son />
</div>
</template>
<script setup>
// 组件的关系是
// app--》son== 》componens
import son from "./components/HelloWorld.vue";
import { provide, ref } from "vue";
const count = ref(0);
// 顶层组件提供数据
provide("data-key", "顶层组件数据");
// 传递响应式数据
provide("data", count);
const setCount = () => {
console.log("1111");
count.value++;
};
setTimeout(() => {
count.value = 100;
}, 2000);
// 传递方法(谁的数据谁负责修改)
provide("setCountkey", setCount);
</script>
//底层组件
<template>
<div>
<h3>底层组件{{ appData }}{{ appData2 }}</h3>
<button @click="setCount">修改顶层组件的数据</button>
</div>
</template>
<script setup>
import { inject } from "vue";
// 接收数据
const appData = inject("data-key");
// 接收响应式数据
const appData2 = inject("data");
// 接收方法
const setCount = inject("setCountkey");
</script>
1. provide和inject的作用是什么? 跨层组件通信
2. 如何在传递的过程中保持数据响应式? 第二个参数传递ref对象
3. 底层组件想要通知顶层组件做修改,如何做? 传递方法,底层组件调用方法
4. 一颗组件树中只有一个顶层或底层组件吗? 相对概念,存在多个顶层和底层的关系