npm create vite
# or
yarn create vite
如果想要快速创建一个vue3项目,可以使用如下命令
yarn create vite vite-demo --template vue
yarn create vite vite-demo-ts --template vue-ts
vue2中需要安装插件Vetur
,可以实现组件高亮。但是vue3的一些语法在vetur中报错。
vue3中需要安装插件Volar
,提供了更加强大的功能,插件和 Vetur
会出现冲突。
所以,使用功能vue3,需要禁用 vetur
插件,安装Volar
插件。 Volar
共需要安装两个插件,第二个提供 Volar TypeScript
支持。
3.# 组合式API
vue2 采用的就是 optionsAPI
(1) 优点:易于学习和使用
, 每个代码有着明确的位置 (例如: 数据放 data 中, 方法放 methods中)
(2) 缺点: 相似的逻辑, 不容易复用, 在大项目中尤为明显
(3) 虽然 optionsAPI 可以通过mixins 提取相同的逻辑, 但是也并不是特别好维护
vue3 新增的就是 compositionAPI
(1) compositionAPI 是基于 逻辑功能 组织代码的, 一个功能 api 相关放到一起
(2) 即使项目大了, 功能多了, 也能快速定位功能相关的 api
(3) 大大的提升了 代码可读性
和 可维护性
vue3 推荐使用 composition API, 也保留了options API 即就算不用 composition API, 用 vue2 的写法也完全兼容!!
setup
返回。前置说明:
setup() {
const obj = reactive({
name: "小明",
age: 18,
});
return { obj };
},
reactive 处理的数据, 必须是复杂类型, 如果是简单类型无法处理成响应式, 所以有 ref 函数!
作用: 对传入的数据(一般简单数据类型),包裹一层对象, 转换成响应式。
ref 和 reactive 的最佳使用方式:
script setup是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。相比于普通的 script 语法更加简洁
要使用这个语法,需要将 setup
添加到 代码块上:
<script setup lang="ts">
console.log('hello script setup')
console.log(this) // undefined
</script>
顶层的绑定会自动暴露给模板,所以定义的变量,函数和import导入的内容都可以直接在模板中直接使用
<template>
<div>
<h3>根组件</h3>
<div>点击次数:{{ count }}</div>
<button @click="add">点击修改</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
const add = () => {
count.value++
}
</script>
script setup 优势:
ts
项目不需要再 defineComponent
包裹了。return
了, template
可直接使用,顶层的绑定会自动暴露给模板。computed函数调用时, 要接收一个处理函数, 处理函数中, 需要返回计算属性的值
<template>
<div>我今年的年纪 <input type="text" v-model.number="age" /></div>
<div>我明年的年龄 {{ nextAge }}</div>
<div>我后年的年龄 <input type="text" v-model.numbe="nextAge2" /></div>
</template>
<script setup lang="ts">
import { computed, ref } from "vue";
const age = ref(10);
// 不带set的计算属性
const nextAge = computed(() => {
return age.value + 1;
});
// 带set的计算属性
const nextAge2 = computed({
get() {
return age.value + 2;
},
set(val: number) {
age.value = val - 2;
},
});
</script>
watch 侦听器, 接收三个参数
// 监听单个ref
const money = ref(100)
watch(money, (value, oldValue) => {
console.log(value)
})
// 监听多个ref
const money = ref(100)
const count = ref(0)
watch([money, count], (value) => {
console.log(value)
})
// 监听ref复杂数据
const user = ref({
name: 'zs',
age: 18,
})
watch(
user,
(value) => {
console.log('user变化了', value)
},
{
// 深度监听,当ref的值是一个复杂数据类型,需要深度监听
deep: true,
immediate: true
}
)
// 监听对象的某个属性的变化
const user = ref({
name: 'zs',
age: 18,
})
watch(
() => {
return user.value.name
},
(value) => {
console.log(value)
}
)
步骤:
defineProps
进行接收父组件
<template>
<son :car="str" :user1="user"></son>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
// 在setup语法中,只需要导入子组件,就可以渲染,不需要components注册
import son from "./components/son.vue";
const user = ref(100);
const str = ref({
name: "ddd",
age: 18,
});
</script>
子组件
<template>
<div>父组件传过来的数据1:{{ user1 }}</div>
<div>父组件传过来的数据2:{{ car.name }}</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
interface Props {
car: object;
user1: number;
}
const props = defineProps<Props>();
console.log(props.user1);
</script>
注意:
defineProps
接收数据,这个数据只能在模板中渲染
中也访问 props
属性,应该接收返回值。步骤:
defineEmits
获取emit对象(因为没有this)子组件
<template>
<div>父组件传过来的数据1:{{ user1 }}</div>
<div>父组件传过来的数据2:{{ car.name }}</div>
<div @click="emit('xiu', 1000)">点击传个父组件</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
interface Props {
car: object;
user1: number;
}
const emit = defineEmits(["xiu"]);
const props = defineProps<Props>();
console.log(props.user1);
</script>
父组件
<template>
<son :car="str" :user1="user" @xiu="handleXiu"></son>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
// 在setup语法中,只需要导入子组件,就可以渲染,不需要components注册
import son from "./components/son.vue";
const user = ref(100);
const str = ref({
name: "ddd",
age: 18,
});
const handleXiu = (val: number) => {
console.log("子组件传过来的数据", val);
};
</script>
依赖注入, 可以非常方便的实现 跨层级的 组件通信
<script setup lang="ts">
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
const car = ref('小黄车')
provide('money', money)
provide('car', car)
</script>
<template>
<h1>根组件-{{ money }} --- {{ car }}</h1>
<hr />
<Son></Son>
</template>
子组件 (子孙后代, 都可以拿到这个数据)
<script setup lang="ts">
import { inject, Ref } from 'vue'
const money = inject<Ref<number>>('money')
const car = inject<Ref<string>>('car')
const changeMoney = (m: number) => {
if (money) {
money.value = money.value - m
}
}
</script>
<template>
<h5>Sun组件--{{ money }} --- {{ car }}</h5>
<button @click="changeMoney(10)">修改</button>
</template>
如果希望子传父, 可以 provide 传递一个方法
子组件
<script setup lang="ts">
import { inject, Ref } from 'vue'
const money = inject<Ref<number>>('money')
const car = inject<Ref<string>>('car')
const changeMoney = inject<(m: number) => void>('changeMoney')
</script>
<template>
<h5>Sun组件--{{ money }} --- {{ car }}</h5>
<button @click="changeMoney && changeMoney(10)">修改</button>
</template>
父组件
<script setup lang="ts">
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
const car = ref('小黄车')
provide('money', money)
provide('car', car)
const changeMoney = (m: number) => {
if (money) {
money.value = money.value - m
}
}
provide('changeMoney', changeMoney)
</script>
<template>
<h1>根组件-{{ money }} --- {{ car }}</h1>
<hr />
<Son></Son>
</template>