这是我参与「第四届青训营 」笔记创作活动的第2天
vue3 语法
vue3 语法有了新的变化相较于vue2
从编写方式,以及到api 变动都有所所变化
从选项式api 过渡到了组合式api,编写业务代码的逻辑从杂乱无章,到,从上往下,有了质一样的飞跃
选项式api
过渡vue3 之前,我们需要了解下vite
vite
vite是开发前端项目的脚手架,其具有快速启动,强大的热重载特点 之前使用vue都是使用webpack,webpack 的功能也挺强的的,但是随着项目的扩大,依赖增加,每次项目的启动时间在5-10s 之间,非常缓慢,而vite,启动速度极快,初始项目大概只需要400ms 左右就能启动,所以,我非常推荐使用vite 来进行开发
在命令行输入
npm create vite@latest
就可以快速创建一个vue 项目,接着就可以愉快的开发了
template标签 变动
现在 template 标签下 可以写多个根标签,不在是只能写一个根标签
<template>
<div>dsad</div>
<div>dsad</div>
</template>
setup 语法
setup 是vue3 新增语法糖,简化 js
<script setup lang="ts">
</script>
使用了setup 后,当前组件页面内,使用外部组件无需使用 export default components 属性声明,在setup 内的变量会全部自动导出,并且在setup 的变量会全部导出到vue 对象种,也就是我们之前使用的data 属性,因此,我们只需要关注我们的业务逻辑,就行了,并不需要,再使用this 关键词引用data 属性
vue2 写法
<template>
<hello-world msg="dsads"></hello-world>
</template>
<script lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
export default {
components:{
HelloWorld
}
}
</script>
vue3 写法
<template>
<hello-world msg="dsads"></hello-world>
</template>
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
</script>
ref
ref 是vue3 声明响应式数据的方式 由于vue3 不推荐使用 export default 方式,因此改为以下方式
<template>
<hello-world :msg="num"></hello-world>
<button @click="change">change</button>
</template>
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
import {ref} from "vue";
// 使用 ref 绑定数据
let num = ref<string>("111")
const change =()=>{
num.value= "dsadsa"
}
</script>
ref 适用了简单数据类型,并且如果要更改ref 绑定的数据,需要 num.value= "dsadsa"方式,才能动态更改
ref 也可以用于复杂类型,源码中进行了判断,如果是复杂数据类型会转换成 reactive 类型
reactive
也是声明响应式数据的方式
<template>
<button @click="change">change</button>
<div>{{list}}</div>
</template>
<script setup lang="ts">
import {ref,reactive} from "vue";
let list = reactive<number[]>([1,2,34,5]);
const change =()=>{
list = [2]
console.log(list)
}
console.log(list)
</script>
修改响应式数据为数组时,可以直接修改,不需要再调用value 属性
但是当修改值为数组时,会破坏响应数据,从而变成普通数据
解决方法1
<template>
<button @click="change">change</button>
<div>{{list}}</div>
</template>
<script setup lang="ts">
import {ref,reactive} from "vue";
type o ={
list:number[]
}
let list = reactive<o>({
list:[1,2,34,5]
});
const change =()=>{
list.list = [2]
console.log(list)
}
console.log(list)
</script>
通过定义一个临时类型对象来赋值
解决方法2
<template>
<button @click="change">change</button>
<div>{{list}}</div>
</template>
<script setup lang="ts">
import {ref,reactive} from "vue";
let list = reactive<number[]>([1,2,34,5]);
const change =()=>{
let a = [2]
list.push(...a)
console.log(list)
}
console.log(list)
</script>
# 响应性语法糖 $ref
使用ref 本质上也是ref 注意:响应性语法糖目前是一个实验性功能,默认是禁用的,需要显式选择使用
需要在vite 的配置文件中
// vite.config.js
export default {
plugins: [
vue({
reactivityTransform: true
})
]
}
如果使用了ref 并不需要引入,它是一个编译时的宏命令 更多的内容可以查阅文档 响应性语法糖 | Vue.js (vuejs.org)
computed
计算属性,当函数内部使用到的外部变量发生变动时,就会除非计算属性方法
totalPrice = computed(() => {
let $total = 0
shopList.forEach(e => {
$total += (e.price * e.total)
})
return $total;
})
计算属性可以在一些动态计算的变量中实现,例如,购物车,当用户修改购物车的商品或者数量时就会动态计算总价格等等
watch
监听变量
<template>
<input type="text" v-model="obj">
</template>
<script setup lang="ts">
import {ref, reactive, watch} from "vue";
let obj= ref(111)
watch(obj,(newVal,oldVal)=>{
console.log("新值:===",newVal);
console.log("旧值:===",oldVal);
})
</script>
这样当我们修改obj 时,就会出发监听函数,
但是当我们监听复杂类型时,ref 绑定的数据,是不会监听深层次的值的,
例如
let obj= ref({
name:'zs'
})
vue 并不会帮我们监听name的值,
需要开启深层次监听 deep:true
<template>
<input type="text" v-model="obj.name">
</template>
<script setup lang="ts">
import {ref, reactive, watch} from "vue";
let obj= ref({
name:"zs"
})
watch(obj,(newVal,oldVal)=>{
console.log("新值:===",newVal);
console.log("旧值:===",oldVal);
},{deep:true})
</script>
如果使用reactive 来绑定,则不需要开启deep:true
注意,如果监听对象的话,会导致旧值与新值相同,这不是bug,这是因为vue 绑定的是数据的引用地址原因
监听对象中的某个值
<template>
<input type="text" v-model="obj.name">
</template>
<script setup lang="ts">
import {ref, reactive, watch} from "vue";
let obj= ref({
name:"zs"
})
// 使用监听函数,返回需要监听的值
watch(()=>obj.value.name,(newVal,oldVal)=>{
console.log("新值:===",newVal);
console.log("旧值:===",oldVal);
})
</script>
生命周期函数
<script setup lang="ts">
import {onMounted} from "vue";
// 类比vue2 中的 mounted() 函数
onMounted(()=>{
})
</script>
所有的生命周期函数都发生了变化,都在原始的api方法上添加了onXXXX
defineProps
props 也是开发中常用的内容之一,如果配合ts 使用,效果会非常好
props 属性,父组件向子组件传值使用了 defineProps
<!--父组件-->
<template>
<div class="layout">
<Menu :name="name"></Menu>
<div class="layout-right">
<Header></Header>
<Content></Content>
</div>
</div>
</template>
<script setup lang="ts">
import Content from './Content/index.vue'
import Header from './Header/index.vue'
import Menu from './Menu/index.vue'
import {reactive, ref} from 'vue'
let name = ref<string>("zs")
</script>
<!--子组件-->
<template>
<div class="menu">
Menu
<div>
{{name}}
</div>
</div>
</template>
<script setup lang="ts">
type Props={
name:string
}
// 使用 defineProps 泛型来声明props 的类型
defineProps<Props>()
</script>
<!--子组件-->
<template>
<div class="menu">
Menu
<div>
{{name}}
</div>
</div>
</template>
<script setup lang="ts">
type Props={
name:string
data?:number[] // 加问号,表示可以不传递data props值,但是此时没有默认值
}
// 使用 withDefaults来规定默认值,如果类型是对象,则使用函数返回值形式
withDefaults(defineProps<Props>(),{
data:()=>[1233,54,768]
})
</script>
defineEmits
事件派发,可以有子组件触发事件,传递给父组件,也可以从子组件传值给父组件
<template>
<div class="menu">
Menu
<div>
{{name}}
</div>
<div>
<button @click="onClick">派发</button>
</div>
</div>
</template>
<script setup lang="ts">
import {reactive} from "vue";
// defineEmits 是一个函数,接收一个数组,数组中就是传递的事件名称,返回值是 派发对象
const emits = defineEmits(['on-click'])
let list = reactive([1,23,5])
const onClick = ()=>{
// 触发子组件事件时,给父组件传递一个事件名称,并且传值给父组件
emits('on-click',list)
}
</script>
<template>
<div class="layout">
<!-- 父组件需要使用 @事件名称 来绑定事件 -->
<Menu :name="name" @on-click="getData"></Menu>
<div class="layout-right">
<Header></Header>
<Content></Content>
</div>
</div>
</template>
<script setup lang="ts">
import Content from './Content/index.vue'
import Header from './Header/index.vue'
import Menu from './Menu/index.vue'
import {reactive, ref} from 'vue'
let name = ref<string>("zs")
// 子组件派发的事件,参数为子组件传递的参数
const getData = (list:number[])=>{
console.log(list)
}
</script>
样式穿透
在使用ui库时,可能需要修改原本组件的样式,则,可以使用
:deep(.ui-sdsa){
color:red
}