1.创建vue项目
npm create vite
# or
yarn create vite
vite快捷使用
如果想要快速创建一个vue3项目,可以使用如下命令
yarn create vite vite-demo --template vue
yarn create vite vite-demo-ts --template vue-ts
2.vsocd插件说明
vue2中需要安装插件Vetur ,可以实现组件高亮。但是vue3的一些语法在vetur中报错。 vue3中需要安装插件Volar ,提供了更加强大的功能,插件和 Vetur 会出现冲突。 所以,使用功能vue3,需要禁用 vetur 插件,安装Volar 插件。 Volar 共需要安装两个插件,第二个提供 Volar TypeScript 支持。 3.# 组合式API
3. composition API vs options 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 的写法也完全兼容!!
3.1 setup函数
- setup 函数是一个新的组件选项, 作为组件中 compositionAPI 的起点
- 从生命周期角度来看, setup 会在 beforeCreate 钩子函数之前执行
- setup 中不能使用 this, this 指向 undefined
- 在模版中需要使用的数据和函数,需要在
setup 返回。
3.1 reactive 函数 通常是用来定义响应式 对象数据
前置说明:
- setup 需要有返回值, 只有返回的值才能在模板中使用
- 默认普通的数据, 不是响应式的
作用: 传入一个复杂数据类型,将复杂类型数据, 转换成响应式数据 (返回该对象的响应式代理Proxy)
setup() {
const obj = reactive({
name: "小明",
age: 18,
});
return { obj };
},
3.2 ref 函数
reactive 处理的数据, 必须是复杂类型, 如果是简单类型无法处理成响应式, 所以有 ref 函数! 作用: 对传入的数据(一般简单数据类型),包裹一层对象, 转换成响应式。
- ref 函数接收一个的值, 返回一个ref 响应式对象, 有唯一的属性 value
- 在 setup 函数中, 通过 ref 对象的 value 属性, 可以访问到值
- 在模板中, ref 属性会自动解套, 不需要额外的 .value
- ref函数也支持传入复杂类型,传入复杂类型,也会做响应式处理
ref 和 reactive 的最佳使用方式:
- 明确的对象,明确的属性,用 reactive,其他用 ref
- 从vue3.2之后,官方更推荐使用 ref
3.3 script setup 语法
script setup是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。相比于普通的 script 语法更加简洁 要使用这个语法,需要将 setup 添加到 <script> 代码块上:
<script setup lang="ts">
console.log('hello script setup')
console.log(this)
</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 可直接使用,顶层的绑定会自动暴露给模板。
3.4计算属性computed函数
computed函数调用时, 要接收一个处理函数, 处理函数中, 需要返回计算属性的值
- 简单写法,不带set的计算
- 复杂写法,带 set 的计算,传入对象
<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);
const nextAge = computed(() => {
return age.value + 1;
});
const nextAge2 = computed({
get() {
return age.value + 2;
},
set(val: number) {
age.value = val - 2;
},
});
</script>
3.5 侦听器watch函数
watch 侦听器, 接收三个参数
- 参数1: 监视的数据源
- 参数2: 回调函数
- 参数3: 额外的配置
const money = ref(100)
watch(money, (value, oldValue) => {
console.log(value)
})
const money = ref(100)
const count = ref(0)
watch([money, count], (value) => {
console.log(value)
})
const user = ref({
name: 'zs',
age: 18,
})
watch(
user,
(value) => {
console.log('user变化了', value)
},
{
deep: true,
immediate: true
}
)
const user = ref({
name: 'zs',
age: 18,
})
watch(
() => {
return user.value.name
},
(value) => {
console.log(value)
}
)
3.6 组件通讯-父传子
步骤:
- 父组件提供数据
- 父组件将数据传递给子组件
- 子组件通过
defineProps 进行接收 - 子组件渲染父组件传递的数据
父组件
<template>
<son :car="str" :user1="user"></son>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
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 接收数据,这个数据只能在模板中渲染 - 如果想要在
<script> 中也访问 props 属性,应该接收返回值。
3.7 组件通讯-子传父
步骤:
- 子组件通过
defineEmits 获取emit对象(因为没有this) - 子组件通过emit触发事件,并且传递数据
- 父组件提供方法
- 父组件通过自定义事件的方式给子组件注册事件
子组件
<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";
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>
3.8 组件通讯-依赖注入 - provide 和 inject
依赖注入, 可以非常方便的实现 跨层级的 组件通信
<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>
|