vue3代码结构对比vue2的区别
1.template不需要根元素
2.使用createApp这个API来创建vue实例
3.可以在script标签上写setup,开启setup语法糖
4.template和script交换位置
vue3的优点:
1.更低的维护成本 1.组合式API 2.发更好的TypeScript支持
2.更高的运行效率 1.新的diff算法 2.模板编译优化 3.更高效的组件初始化
3.更小的体积 1.良好的TreeShaking 2.按需引入
4.使用proxy来实现数据响应式
vue3的生命周期
setup => onBeforeMount => onMounted => onBeforeUpdate => onUpdated => onBeforeUnmount => onUnmounted
简单来说,就是前面加个on,需要注意的是,vue3将beforeCreate和created合并为了setup,它执行于beforeCreat之前,在vue3中,distory改为unmount
使用生命周期钩子:
<script setup>
// 导入
import {onMounted} from 'vue'
// vue3的钩子可以写多个,因为是回调函数
onMounted(() => {
函数语句
})
</script>
1.setup
1.1 基本使用
setup(){}可以理解为钩子函数,是vue2中beforecreate和created合并而来的,它可以return出在其中定义的数据,这些数据可以在template中使用,但不是响应式的
<script>
export default {
setup() {
const message = 'nihao'
const say = () => {
console.log(message)
}
return {
message,
say
}
},
}
</script>
<template>
<div @click="say">
{{message}}
</div>
</template>
1.2 语法糖 <script setup />
在script标签上写setup属性,开启setup语法糖,script中的代码将自动识别并添加到setup钩子中,并自动return,导入的组件自动注册
但需要注意,在setup中this.指向undefined
以下代码效果与上面完全相同
<script setup>
const message = '你好'
const say = () => {
console.log(message)
}
</script>
<template>
<div @click="say">
{{message}}
</div>
</template>
2.reactive
2.1 导入
2.1 定义,并使用,参数必须是对象
<script setup>
// 导入并注册
import {reactive} from 'vue'
// 定义响应式数据,注意,reactive的参数必须是一个对象,定义一个变量接收reactive函数的返回值
const state = reactive({
count: 0
})
// 使用reactive的返回值调用内部数据并修改
const sum = () => {
state.count++
}
</script>
<template>
<div @click="sum">
{{state.count}}
</div>
</template>
3.ref
3.1 导入
3.2 定义,并使用,注意,在模板中不需要带value,在js中则必须要带
<script setup>
// 导入并注册
import {ref} from 'vue'
// 定义
const age = ref(17)
const happyBthDay = () => {
console.log('happy happy happy');
//需要用.value来获取ref的值
age.value++
}
const message = ref({
say: 'nihao'
})
const say = () => {
message.value.sing = 'la la xi la so~'
//对象的属性的取值方法
console.log(message.value.sing)
}
</script>
<template>
<header>
<button @click="happyBthDay">
{{age}}
</button>
<button @click="say">
{{message.say}}
</button>
</header>
</template>
3.3 对比 ref 和 reactive
a.reactive不能处理简单类型的数据
b.ref参数类型支持更好,但是必须通过.value做访问修改
c.ref本质上还是调用了reactive实现的
可以看源码cdn.bootcdn.net/ajax/libs/v…
内部实现是这样的:
function ref -> createRef -> new RefImpl -> constructor -> toReactive -> reactive
- 计算属性
4.1 导入
4.2 计算属性的定义
4.3 使用
<script setup>
// 导入
import {ref, computed} from 'vue'
// 定义一个用于测试的数组
const arr = ref([1,2,3,4,5,6,7])
// 定义一个计算属性,将数组中大于2的元素筛选出来
const newArr = computed(() => {
return arr.value.filter(item => {
return item > 2
})
})
// 添加一个数组的元素,测试计算属性能否响应式更新
const add = () => {
arr.value.push(10)
}
</script>
<template>
<header>
<div>
{{arr}}
<button @click="add">补充一个元素 10</button>
</div>
<div>
{{newArr}}
</div>
</header>
</template>
4.4 传值
<script setup>
// 导入
import {ref, computed} from 'vue'
// 定义用于测试的数组
const arr = ref([1,2,3,4,5,6,7])
const arr1 = ref([a,b,c,d,e,f,g])
// 定义一个计算属性,根据参数返回一个对应字母表中的字母
// 本质是计算属性返回了一个 返回结果的方法
const str = computed(() => (item) => {
return arr1[item-1]
})
</script>
<template>
<ul>
<li v-for="item in arr">{{str(item)}}</li>
</ul>
</template>
- watch
5.1 导入watch
5.2 使用watch侦听响应式的数据
5.3 侦听多个值变化,将第一个参数改为数组,将需要侦听的数据都丢进去即可
5.4 开启立即执行侦听器,可以写第三个参数,这个参数是个对象,里面有im属性,设置为true即可
5.5 开启深度监听,watch默认只监听浅层,开启深度监听后,可以监听到深层数据,但它会遍历递归监听,导致性能的损耗
5.6 在不开启deep的情况下,对对象中某个值进行精准监听,需要将第一个参数改为函数,并返回要监听的属性
<script setup>
// 1. 导入
import { ref, watch } from "vue";
// 2. 定义一个基本数据
const count = ref(17)
// 3. 定义让该数据变化的方法
const add = () => {
count.value++
}
// 4. 定义一个监听器, 当count发生变化时,打印新值和旧值
// 第一个参数是目标变量,不需要加value,第二个参数是回调,写监听到之后的处理函数
watch(count, (newVal, oldVal) => {
console.log('newVal: ' , newVal);
console.log('oldVal: ' , oldVal);
})
// 5. 定义第二个测试数据和测试方法
const age = ref(18)
const addAge = () => {
age.value++
}
// 6. 侦听多个变量
watch([count, age], (newVal, oldVal) => {
console.log('某个数据变化了');
console.log('newVal: ' , newVal);
console.log('oldVal: ' , oldVal);
})
// 7. 立即执行侦听器
watch(count, (newVal, oldVal) => {
console.log('newVal: ' , newVal);
console.log('oldVal: ' , oldVal);
}, {
immediate: true
})
// 8. 准备深度监听的测试数据和方法
const obj = ref({
a: 123
})
const changeA = () => {
obj.value.a = 456
}
// 9. 开启深度侦听
watch(obj, (newVal, oldVal) => {
console.log('newVal: ' , newVal);
console.log('oldVal: ' , oldVal);
}, {
deep: true
})
// 10. 精准监听
watch(() => obj.a, (newVal, oldVal) => {
console.log('newVal: ' , newVal);
console.log('oldVal: ' , oldVal);
})
</script>
<template>
<header>
<div>
{{count}}
<button @click="add">让count自增</button>
</div>
<div>
{{age}}
<button @click="addAge">让age自增</button>
</div>
<div>
{{obj}}
<button @click="changeA">改变obj中属性a的值</button>
</div>
</header>
</template>
注:在侦听pinia的ref数据时不需要加value!!!通宵半宿的教训啊!!!!!!!!
vue3的父子通信
一. 父传子
1.父组件中基本无变化
2.子组件接收需要用编译器宏 defineProps
3.如果需要在script标签内调用defineProps中的值,需要先用变量接收编译器宏的返回值
父组件:
<script setup>
import SonCom from '@/components/son-com.vue'
import { ref } from 'vue'
// 定义测试数据
const list = ref([1, 2, 3, 4])
const obj = ref({
name: 'nengdie',
habby: ['lashit', 'sleep']
})
// 定义一个定时器,3秒后操作数据,看看子组件是否成功接收,并响应式更新
setTimeout(() => {
list.value.push(10)
}, 3000)
</script>
<template>
<header>
<SonCom :obj="obj" :list="list" message="nihao"></SonCom>
</header>
</template>
子组件:
<template>
<div>{{message}}</div>
<div>{{list}}</div>
<div>{{obj}}</div>
</template>
<script setup>
// 用编译器宏接收父组件传来的值,在模板中可直接使用
// 在代码中调用需要先用变量接收这个编译器宏的返回值
const props = defineProps({
message: String,
list: Array,
obj: Object
})
// 使用 编译器宏的返回值.值的变量名 来调用
console.log(props.message);
</script>
二.子传父
1.在子组件中使用defineEmits注册需要上传的事件
2.使用emit('事件名', 值)来向父组件发出事件
3.字父组件定义该事件的处理函数,和vue2相同
父组件:
<script setup>
// 1. 导入
import SonCom2 from '@/components/son-com2.vue'
import { ref } from 'vue';
// 2. 定义测试数据,以及接收处理函数
const message = ref('nihao')
const getMessage = (msg) => {
console.log(msg);
message.value = msg
}
</script>
<template>
<header>
<!-- 使用自定义事件接收 -->
<SonCom2 @getMessage="getMessage"></SonCom2>
{{message}}
</header>
</template>
子组件:
<template>
<div>
<button @click="take">take message</button>
</div>
</template>
<script setup>
// 使用defineEmits声明自定义事件,并使用一个变量接收其返回值
const emit = defineEmits(['getMessage'])
// 定义传值的事件,点击按钮执行
const take = () => {
emit('getMessage', '你好')
}
</script>
三.获取原生DOM或组件引用,并利用引用获取子组件的数据
1.利用defineExpose({数据1, 数据2,...})暴露子组件的数据,此时外界就可以通过ref获取的引用访问这个数据
子组件中:
<template>
<div></div>
</template>
<script setup>
import { ref } from "vue";
// 定义数据和方法以供父组件调用
const message = ref('nihao')
const changeMsg = () => {
message.value = '你好'
}
// 对外暴露需要使用的数据和方法
defineExpose({message, changeMsg})
</script>
2.利用 ref 获取原生DOM或组件引用
3.利用引用拿值,或调用子组件的方法
父组件中:
<script setup>
// 1. 导入
import SonCom3 from '@/components/SonCom3.vue'
import { onMounted, ref } from 'vue';
2.定义一个空的ref数据,用以接收DOM或组件引用
const refCom3 = ref(null)
3. 在 onMounted 钩子中,通过获取已经挂载的组件引用调用子组件的数据和方法
onMounted(() => {
// 打印子组件数据
console.log(refCom3.value.message)
// 调用子组件方法
refCom3.value.changeMsg()
// 打印新数据
console.log(refCom3.value.message)
})
</script>
<template>
<header>
<!-- 通过ref属性获取引用 -->
<SonCom3 ref="refCom3"></SonCom3>
</header>
</template>
四.使用provide和inject
- 祖先传后代
1.1.顶层通过 provide 提供数据
1.2.底层通过 inject 接收数据
2.后代修改祖先的数据
2.1祖先传一个修改数据的方法
2.2将该方法通过 provide 传到底层
2.3底层使用 inject 接收并调用
结构: 祖先组件 => 子组件 => 孙组件
顶层组件/祖先组件
<script setup>
// 1. 导入
import MySon from './components/MySon.vue';
import { provide, ref } from 'vue';
// 2. 定义测试数据
const message = ref('nihao')
// 3. 定义修改数据的方法
const changeMessage = () => {
message.value = '你好'
}
// 4. 通过provide提供数据
provide('message', message)
provide('i18n', changeMessage)
</script>
<template>
<div>
<MySon></MySon>
</div>
</template>
底层组件/孙组件
<template>
<div>{{message}}</div>
<button @click="change">change</button>
</template>
<script setup>
// 导入
import { inject } from "vue";
// 通过inject接收数据
const message = inject('message')
//接受改变祖先组件数据的方法并通过按钮调用
const i18n = inject('i18n')
const change = () => {
i18n()
}
</script>