目标 了解vue3 现状,以及他的优点,展望他的未来
vue3现状:
vue3优点:
vue3展望:
总结 为什么要学习vue3?
目标:了解vite是什么,使用vite创建vue项目,用来学习vue3
vite是什么?
他是一个更加轻量(热更新速度块,打包构建速度快)的vue项目脚手架工具
它相对于vue-cli它默认安装的插件非常少 随着开发过程以来增多,需要自己额外配置
所以:在单纯学习vue3语法会使用它,后面做项目可以继续使
用vue-cli
vite 基本使用:
总结:vite 是什么?
下一代前端开发与构建工具
使用vite创建项目 学习vue3 语法 使用vue-cli 创建项目正式开发
目标:掌握如何创建vue3 应用实例
基本步骤:
import { createApp } from "vue";
import App from "./App.vue";
const app = createApp(App);
app.mount("#app");
总结:如何创建vue应用实例?
通过createApp创建应用实例 -> 拓展功能将来都是在app上进行
目标:理解什么是选项API写法 什么是组合API写法
vue2.x 中的选项API 是在各个选项中实现一个共同逻辑,代码是分散的,项目过大时,不好维护,无论是阅读性还是可维护性都比较差
vue3.x 中组合式API 的每个逻辑是集中在一起的,便于后期的维护,书写起来,也不用对照前后的代码,去书写一个功能,阅读性,可维护性更高
什么是选项API写法:Option API
咱们再vue2.x 项目中使用的就是选项API 写法
优点:易于学习和使用 写代码的位置已经约定好
缺点:代码组织性比较差,相似的逻辑代码不便于复用,逻辑复杂代码多了就不好阅读
补充:虽然提供mixins用来封装逻辑,但是出现数据函数覆盖的概率比较大,不好维护,容易出现bug,变量的隐式声明。
什么是组合API写法:Componsition API
vue3.x 项目中 将会使用组合API 写法
优点:功能逻辑复杂繁多的情况下,各个功能逻辑代码组织在一起,便于阅读和维护
缺点:需要有良好的代码组织能力和拆分逻辑的能力
补充:为了能让大家更好的过度到vue 3.0 ,也会支持选项API
目标:掌握setup函数的基本使用
使用细节:
vue2 生命周期钩子 | vue3生命周期钩子 | |
---|---|---|
beforeCreate | setup | |
创建实例 | created | |
beforeMount | onBeforeMount | |
挂载DOM | mounted | onMounted |
beforeUpdate | onBeforeUpdate | |
组件更新 | updated | onUpdated |
beforeDestroy | onbeforeUnmount | |
组件销毁 | destroyed | onUnmounted |
在vue2.x 中 钩子函数选项只可以写一个
在vue3.0 中 钩子函数可以定义多个 用于实现不同的逻辑
目标:掌握使用reactive函数定义响应式数据
定义响应式数据:
reactive 是什么?
他是一个函数
有什么用?
它可以定义一个复杂数据类型 成为响应式数据
怎么用?
const obj = reactive({
name: "小王",
age: 18,
});
为什么要用?
因为vue3.0 生命的普通变量并不是响应式的
使用场景?
当需要定义一些复杂的响应式数据的时候 需要用reactive 进行转换
优缺点?
目标:掌握使用toRef函数转换响应式对象中某个属性为单独响应式数据, 并且值是关联的。
toRef 是什么?
他是一个函数
有什么用?
它可以转换 响应式对象 中 某个 属性为单独响应式数据,并且 值是关联的
怎么用?
// 定义响应式对象
let obj = reactive({
name: "张三",
age: 18,
});
let name = toRef(obj, "name");
// 抽离响应式数据 (抽离不代表解除关联)
// 这个时候可以将对象的某个属性 转为 一个响应式对象 注意是对象
let name = toRef(obj, "name");
// 修改数据 value是存放值的地方
name.value = 5555;
为什么要用?
当我们定义了一个响应式对象 ,我们恰好在页面中只想使用它的一个属性,这个时候我们通过解构赋值,得到的属性并不是响应式的,如果想要该属性为响应式,需要我们通过toRef转换为响应式的,并且关联原有的数据。
注意:这个时候直接通过ref转为响应式数据也是可以的,但是会失去与原有数据的关联性,他将是一个全新的响应式数据,与原有对象无关!
使用场景?
有一个响应式对象数据,但是模板中 只需要使用其中一项数据
注意:从响应式数据对象中结构的数据,不再是响应式数据了
目标:掌握使用toRefs函数定义转换响应式中所有属性为响应式数据 通常用于结构 | 展开reactive定义的对象
toRefs 是什么?
他是一个函数
有什么用?
它可以 转换 响应式对象 中所有属性为单独响应式数据 对象成为普通对象
怎么用?
// 定义响应式数据
let obj1 = reactive({
name1: "reactive name",
age1: 18,
});
// 调用refs 函数 将对象的每一项都转为响应式数据
// 此时 obj1 和 obj3 是有关联的
let obj3 = toRefs(obj1); // 接受的参数为reactive对象
// 修改的时候需要注意
// 可以直接修改 obj1
obj.name1 = '修改啦'
// 也可以修改包装之后处理的对象 但是 因为是响应式对象 他的值存在value中
obj3.name1.value = "修改啦";
// 导出
return{
// 记得导出转换之后的对象
...obj3
// 模板中可以直接使用属性 而不用通过对象. 的方式进行调用
}
// 注意 如果 toRefs 接受的是一个普通对象
// 也可以将这个对象的所有属性转为响应式的 不过 此时原有数据并不是响应式的,通过toRefs 处理之后的数据是响应式的
为什么要用?
使用场景?
剥离响应式对象 想使用响应式对象中的多个或者所有属性作为响应式数据
目标:掌握使用ref函数定义响应式数据,一般用于简单类型数据
ref 是什么?
他是一个函数
有什么用?
通常用于 简单数据类型定义为响应式数据
怎么用?
//ref() 通常接受一个普通数据类型
// 返回值为一个响应式数据
//修改的时候 :
//一定要通过 .value 对响应式数据进行修改
//在模板中是用ref申明的响应式数据 可以省略.value
const num = ref(5)
num.value = 10
// ref 也可以接受一个 复杂数据类型 其内部会调用reactive函数
const ref = ref({
name:"666"
})
// 当对未来要定义的响应式数据 类型是未知的
const data = ref(null)
为什么要用?
原生vue 不支持响应式数据 我们如果需要使用响应式数据 必须对原有数据进行转化
使用场景?
// 1.当你明确知道 需要的一个响应式数据是 对象 那么就是用reactive即可
// 2.其他情况下使用ref
目标:掌握使用computed 函数定义计算属性
什么是computed?
计算属性是用来计算数据的函数
有啥用呢 ?
computed 是用来定义计算属性的 计算属性不可以修改
是一个函数 在vue2.x 是一个选项
怎么用呢?
// 用法1 - 普通用法 传函数
const age = ref(18);
const newAge = computed(() => {
// 该函数的返回值 就是计算属性的值
return age.value + 2;
});
return {
age,
newAge,
};
注意:
1. vue3计算属性是可以多次调用的
2. 计算属性接收一个回调函数 return的值 就是计算属性的值
3. 计算属性是可以进行缓存的 当依赖的响应式数据未发生改变的时候 ,计算属性会返回上次计算的结果.
4. 计算属性默认是只读的,要想实现双向数据绑定,可以传一个对象进去
// 用法2 - 高级用法 传对象
const newAge = computed({
// get 函数 获取计算属性的值
get() {
return age.value + 2;
},
// set 函数 当你给计算属性设置值的时候触发 监听计算属性值得改变
set(value) {
age.value = value - 2;
},
});
// 这样就实现了双向数据绑定的用法
使用场景?
1. 当你需要以来一个现有的响应式数据,根据一定逻辑得到一个新的数据
优缺点?
目标:掌握使用watch函数定义侦听器
定义计算属性:
watch 函数,用来定义侦听器的
// vue3 监听复杂数据类型 对象的时候 默认开启深度监听
// 但是 任然无法监听到oldvalue
监听ref 定义的响应式数据
const count = ref(0);
const add = () => {
count.value++;
};
// 当需要监听数据的变化 需要使用ref
// 1. 监听一个ref数据
// 1.1 第一个参数 需要监听的目标
// 1.2 第二个参数 改变后的回调函数
watch(count, (newVal, oldVal) => {
console.log(newVal, oldVal);
});
监听多个响应式数据变化
// 监听多个响应式数据 数组形式
watch([count, obj], () => {
console.log("count或者obj改变啦");
});
监听reactive定义的响应式数据
// 直接监听
const obj = reactive({
name: "la",
age: 18,
info: {
msg: "lalal 我是里层的对象",
},
});
const mdName = () => {
obj.name = 888;
};
const mdMsg = () => {
obj.info.msg = 888;
};
watch(obj, (newVal, oldVal) => {
console.log("obj数据改变了~");
});
监听reactive定义的响应式数据 某一个属性 默认开启深度监听
// 监听 reactive 中的某个属性,要用回调函数返回值形式
watch(
() => obj.name,
() => {
console.log("监听obj.name 的变化");
}
);
深度监听 监听reactive 定义的响应式对象的对象属性
要监听对象的某个属性的时候 如果该属性不是对象的话,是默认开启深度监听的
如果需要坚挺的这个属性又是一个对象的时候 这个时候 是无法监听到 这个对象属性的 需要开启深度监听
const obj = reactive({
name: "la",
age: 18,
info: {
msg: "lalal 我是里层的对象",
},
});
watch(
() => obj.info,
() => {
console.log("obj.info.msg 改变啦");
},
{
deep: true,
}
);
目标 :掌握使用ref属性绑定DOM或组件
获取DOM 或组件实例 可以使用ref属性 写法与vue2.0需区分开
获取单个DOM 或 组件
// 1. 定义以一个空的响应式数据 ref定义的
// 2. setup 中返回该数据 想获取那个DOM 在该元素上使用ref属性绑定该数据即可
const box = ref(null);
console.log(box);
return {
box,
};
<template>
<div ref="box">我是box</div>
</template>
获取v-for 遍历的DOM 或组件
// 2 获取v-for 遍历的元素
// 2.1 定义一个空数组 接收所有的li
// 2.2 定义一个函数 往空数组中pushDOM
const list = [];
const setDom = (el) => {
console.log("setDOM");
list.push(el);
console.log(list);
};
<template>
<ul>
<li v-for="i in 4" :key="i" :ref="setDom">{{ i }}</li>
</ul>
</template>
总结:
目标:掌握使用props 选项和emits选项完成父子通讯
父传子
// 父组件
setup() {
const count = ref(0);
return {
count,
};
},
// 子组件
// 和vue2.x 一样 正常接收数据
props: {
count: {
type: Number,
default: 0,
},
},
// 如果想要在setup函数中使用数据 可以直接传参 props
setup(props) {
console.log(props);
},
子传父
// 子组件
我是子组件
{{ count }}
// 第一个参数为props 第二个参数为context
// context 中有emit方法
setup(props, context) {
// 获取父组件传来的值
console.log(props);
// 向父组件传值
const changeMoney = () => {
// 消费50 元
// 通知父组件 money需要变成50
context.emit("change-money", 50);
};
return {
changeMoney,
};
},
// 父组件
我是父组件里
{{ count }}
setup() {
const count = ref(100);
const updateMoney = (money) => {
count.value -= 50;
};
return {
count,
updateMoney,
};
},
拓展:
在vue2.x 的时候 .sync 除去v-model的情况下实现双向数据绑定的另外一种方式
简写:
在vue 3.0 的时候 使用v-model:money=“money” 即可
// 父组件
// 子组件
const changeMoney = () => {
// 消费50 元
// 通知父组件 money需要变成50
context.emit("update:count", 50);
};
总结:
父传子:在setup 中使用props 数据,接受的第一个参数就是
子传父:触发自定义时间的时候 emit 来自setup的第二个参数 context对象
在vue2.x 中 v-model 和 .sync 已经和并成 v-model 指令
目标:掌握使用provide函数和inject函数完成后代组件通讯
使用场景: 有一个父组件 有很多后代组件 都需要共享父组件的数据
//父组件
setup() {
const money = ref(1000);
const changeMoney = (val) => {
console.log("changeMoney");
money.value -= 50;
};
// 将数据提供给后代组件 provide
provide("money", money);
// 将修改数据的函数给后代组件
provide("changeMoney", changeMoney);
return {
money,
};
},
// 后代组件接收数据
setup() {
const money = inject("money");
const changeMoney = inject("changeMoney");
// 孙组件 消费50 通知父组件App 进行修改
// 不能自己修改数据 遵循单项数据流 在哪定义在那修改
const fn = () => {
changeMoney(50);
};
return {
money,
fn,
};
},
总结:
目标 掌握vue3 的 v-model 的语法糖原理
在 vue 2.0 中 v-model 语法糖 简写
在vue 3 中v-model 语法糖有所调整
<Son :modelValue="msg" @update:modelValue="msg=$event"/>
//父组件
<!-- <Son :modelValue="count" @update:modelValue="count = $event"> </Son>-->
<!-- 简写-->
<Son v-model="count"></Son>
// 子组件
setup(props, { emit }) {
const fn = () => {
console.log("fn");
// 改变数据
emit("update:modelValue", 100);
};
return { fn };
},
目标: 掌握mixins 语法的基本使用 Vue2.x 封装逻辑的方式 Vue3.0 建议使用组合式API
官方解释:
- 混入(mixins)提供了一种非常灵活的方式,来分发Vue组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入时,所有混入对象的选项将被"混合"进入组件本身的选项
理解全局混入:所有组件混入了这些逻辑代码
// vue 2
Vue.mixin({ 选项 })
// vue 3
app.mixin({
选项
})
总结:在vue2.0 中一些可复用逻辑可以用mixins来封装,但是需要考虑逻辑代码冲突的问题。 vue3.0 的组合API很好的解决了这个问题 ,就不再推荐使用mixins了