一、新生命周期setup
1.html化的setup
新增setup属性(比created更早执行,同时setup中this不会指向实例),更贴近html写法,这个方法onBeforeMounted之前被调用,同时vue3在生命周期中也删除了this指向,所有的方法必须自己在vue实例中调用
<script setup>
import { ref } from 'vue';
let data = ref(123)
</script>
// 此时data的值就是123,通过data.value调用访问数据
2.生命周期的延续与变化
vue3删除了create生命周期,其他生命周期方法前面写on进行访问 , 例如:onMounted、onUpdated
使用生命周期必须在vue实例中引用,销毁实例变为onUnmointed,与vue2的destroy作用一致
<script setup>
import { onMounted, onUnmounted } from 'vue';
// 所有生命周期用法均为回调函数
onMounted(() => {
console.log('创建了');
})
// 销毁实例变为onUnmounted,与vue2的destroy作用一致
onUnmounted(() => {
console.log('销毁了');
})
</script>
二、vue3的数据、方法绑定
1.使用ref定义数据
ref在vue3中将数据转换成响应式数据结构的一种
vue3中没有了data() {} 那么就没办法劫持数据做到响应式数据,所以使用ref将数据变成响应式数据
<template>
<div>
<div>{{ num }}</div>
<button @click="addNum">num+1</button>
</div>
</template>
<script setup>
import { ref } from "vue";
let num = ref(1);
let addNum = () => {
//注意,使用ref变成响应式数据的时候,必须通过.value才可以拿到定义的数据
num.value = num.value + 1;
console.log("我执行了,现在的num是" + num.value);
console.log(num);
};
</script>
可以看到,数据变成响应式,同时num变成了对象,这样只要数据改变,数据就会被劫持,视图就会改变,需要注意拿取ref的数据必须加上value
2.使用ref获取DOM元素
<div ref="box">{{ num }}</div>
let box =ref();
console.log(box.value)
3.使用reactive定义数据
使用ref,每次都需要.value显得很麻烦,所以采用reactive将整个对象变成响应式数据
<template>
<div>
<div ref="box">{{ data.name }}</div>
<button @click="setName">修改名字</button>
</div>
</template>
<script setup>
import {reactive} from 'vue';
let data = reactive({
name: '张三',
age: 13,
sex: '男'
});
const setName = () => {
data.name = '李四';
data.age = 18;
data.sex = '女'
}
</script>
template中使用reactive数据会觉得很麻烦可以使用toRefs将reactive数据解构成响应式数据
const state = reactive({
name: '张三',
age: 13
})
const { name, age } = toRefs(state)
如果reactive定义的是数组那么不能给数据直接赋值,否则会失去响应式
const arr = reactive([])
const getList = async () => {
const res = await list()
arr.push(...res.data)
}
4.方法绑定
<button @click="setFn"></button>
<script setup>
// 一下两种方法都可以定义函数,按照自己开发习惯定义一种即可
/*1.*/
const setFun = () => {
console.log('方法函数操作')
}
/*2.*/
function setFun() {
console.log('方法函数操作')
}
</script>
5.计算属性computed
计算属性的用法与vue2中产生了一定的差别
<div class="box">
<!-- 在上方调用即可,结果为169 -->
{{add}}
</div>
<script setup>
import { computed, ref } from 'vue';
const num1 = ref(13);
const num2 = ref(13);
// 设置变量接收
let add = computed(() => {
return num1.value * num2.value;
})
</script>
6.watch监听器
watch监听器接收两个或者三个匿名函数。第一个是监听的数值,第二个是处理监听的函数,第三个是可选配置
(1)单属性监听
<input type="text" v-model="user" />
<script setup>
import { watch, ref } from 'vue';
const user = ref();
watch(
() => user.value,
(newVal, oldVal) => {
console.log('新值:', newVal, '旧值:', oldVal);
},
// 可选项
{
deep: true, // 是否开启深监听
immediate: true // 开启自动立即执行一次
}
)
</script>
(2)多属性监听
多属性监听有两种,一种是有几个就写几个watch,还有就是监听数据为一个数组
<input type="text" v-model="user" />
<input type="password" v-model="password" />
<script setup>
import { watch, ref } from 'vue';
let user = ref();
let password = ref();
// 同时监听这两个值,只要任意一个值发生变化,就会触发监听方法
watch(
() => [user.value, password.vlaue],
([newUser, newPassword], [oldUser, oldPassword]) => {
console.log('新值:', newUser, newPassword);
console.log('旧值:', oldUserm oldPasssword);
}
)
</script>
三、路由
1.路由跳转
在vue2中使用this.$router.push等进行跳转,在vue3中没有this,这就需要借助 userRouter
import { useRouter } from 'vue-router'
const router = useRouter();
const jumpNewPage = () => {
// 注意: 不要将router定义在方法中,这样他就不是响应式数据,会报错
router.push({path: '/'})
}
2.路由传参
路由跳转传参有两种方式:
- query传参,前面跳转加path路径即可,刷新也存在
- params传参,采用路由中跳转组件的name进行跳转,否则拿不到params
<script setup>
import { useRouter } from 'vue-router';
const router = useRouter();
const jumpNewPage = () => {
// 有两种中传参方式,query和params,两者写法不同
// query更像是get传参,是显性传参,前面跳转加path路径即可,刷新也存在
router.push({path: '/', query: {name: '首页'}})
// params更像post,是隐性传参,跳转时要注意,不适用path跳转,而是采用路由中跳转组件的name进行跳转,否则拿不到params
router.push({name: 'Home', params: {name: '首页'}})
}
</script>
3.路由接收参数
元素采用query跳转,使用query接收参数
元素采用params跳转,使用params接收参数
<script setup>
// 引入useRoute,获取是route
import {useRoute} from 'vue-router'
import {onMounted} from 'vue'
const route = useRoute()
onMounted(()=>{
console.log(route.params);//结果为{name:'首页'}
console.log(route.query);//结果为{name:'首页'}
})
</script>
四、vuex的使用
1.创建文件
// vuex
import { createStore } from 'vuex';
export default createStore({
// 定义state状态
state: {
num: 1
},
// 改变state状态的方法
mutations: {
setNum(state, newNum) {
state.num = newNum;
}
},
// 进行异步操作
actions: {
// 异步递增num
addAsync(store) {
setTimeout(()=>{
console.log(store.state.num += 10);
}, 1000)
}
},
// 划分模块
modules: {
}
})
2.使用store
import { useStore } from 'vuex';
import { onMounted } from 'vue';
// vuex实例
const store new useStore();
onMounted(() => {
// 调用state 中的key
store.state.num += 1;
console.log(store.state.num); // 输出结果: 2
// 调用mutation的方法
store.commit('setNum', 10);
console.log(store,state.num); // 输出结果: 10
// 调用action里的addAsync方法
store.dispatch('addAsync') // 输出结果: 20
// 如果vuex进行了模块化,那么state数据前面要加模块名
store.module.state.num
store.comm
})
五、组件
1.组件引用
组件在props里直接引入就可以在template模板中使用,无需在components中注册
<template>
<div class="box">
<!-- 子组件引用 -->
<v-child></v-child>
</div>
</template>
<script setup>
// 只需引入无需注册注册
import vChild from '@/components/child.vue'
// 后续脚手架提供了自动引入注册,但是需要满足命名规范才可以
</script>
2.defineProps父子传参
- 父组件传递给子组件数据。子组件需要使用defineProps进行接收
- vue3 props接收必须要规定数据类型,如果父元素数据类型出错,那么会报错
- defineProps在后续更新中将不需要从vue中解构,可以直接使用
// 父组件
<template>
<div class="box">
// 子组件引用
<v-child msg='父传子数据'></v-child>
</div>
</template>
// 子组件
<template>
<div class="child">
我是子组件
</div>
</template>
<script setup>
// defineProps在后续更新中将不需要从vue中解构,可直接使用
import { defineProps } from 'vue';
// 在接收时候也得注意,vue3 props接收必须要规定数据类型,如果父元素数据类型出错,那么会报错
const props = defineProps({msg: String});
console.log(props); // Proxy {msg: '父传子数据'}
</script>
3.defineEmits用法
(1)defineEmits子传父普通用法
// 子组件
<template>
<div class="child">
我是子组件
</div>
</template>
<script setup>
import { defineEmits, onMounted } from 'vue';
const emit = defineEmits();
onMounted(() => {
emit('getChildMsg', '我是子组件传递给父组件的数据');
})
</script>
// 父组件
<template>
<div class="box">
<!-- 接收子组件的方法 -->
<v-child @getChildMsg="getMsg"></v-child>
</div>
</template>
<script setup>
import vChild from '@/components/child.vue';
const getMsg = (e) => {
console.log(e); // e是传递的参数, e: '我是子组件传递给父组件的数据'
}
</script>
(2)defineEmits的单向数据更新
/* 这是传递单向数据的组件 */
<Children v-model="dialogValue"/> // 此时使用双向数据绑定v-model的形式传递数据
/* 这是接收单向数据的组件 */
<el-dialog
:model-value="dialogValue" // 使用model-value是单向数据绑定,v-model是双向数据绑定
/>
const emit = defineEmits(['update:modelValue', 'initUserList'])
const handelValue = () => {
emit('update:modelValue', false) // 在此必须是update:modelValue的单向数据绑定形式
}
// 普通触发emit
const handleEmit = () => {
emit('initUserList')
}