Vue-组合式API
vite构建工具
搭建vue3项目
npm create vite
cd dir-vite
: 进入项目目录npm install
: 安装依赖npm run dev
: 启动项目
Local: http://localhost:5173/
Network: use --host to expose
Vue DevTools: Open http://localhost:5173/__devtools__/ as a separate window
Vue DevTools: Press Alt+Shift+D in App to toggle the Vue DevTools
启动过程
- 启动服务
当执行npm run dev
时, 使用了vite
工具启动了一个调试服务器(dev Server). 监听5173
端口.- 会将
vite-vue
作为网站的根目录
- 会将
index.html
作为网站的默认文件
- 当浏览器访问
localhost:5173
时, 调试服务器会将index.html
返回给浏览器
- 会将
- 加载
main.js
- 在
index.html
中, 加载了main.js
- 在
main.js
导入App组件
- 在
单文件组件
一个.vue
文件就是一个单文件组件, 包括
- template: 模板, 用于编写
HTML
- script: 逻辑, 用于编写
JS
- style: 样式, 用于编写
CSS
<template>
<!-- 组件的模板部分 -->
计数器:{{ count }}
<button @click="handleClick">点击+1</button>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function handleClick() {
count.value++
}
</script>
<style>
/* 组件的样式 */
</style>
组合式API
setup语法糖
在vue
项目中, 通常使用setup
语法简化书写
- 在setup语法中定义变量, 可以直接在模板中使用
- 在setup语法中定义函数, 可以直接在模板中使用
- 导入的组件对象, 可以直接在模板中使用
常用的组合式API
- ref
- computed
- watch
- onMounted
ref
响应性 API,用于创建一个响应式的数据源ref
对象, ref
对象具有一个指向内部值的 .value
属性。
import { ref } from 'vue'
// 声明变量
const count = ref(0);
// 使用变量, 需要通过.value属性来访问
console.log(count.value); // 输出 0
// 在 setup 函数中返回 ref
export default {
setup() {
const count = ref(0);
return { count };
}
};
<template>
<button @click="handleClick">显示/隐藏</button>
<div v-show="flag">这是一个div</div>
</template>
<script setup>
import { ref } from 'vue'
// 声明变量
const flag = ref(true)
// 声明函数
function handleClick() {
flag.value = !flag.value
}
</script>
computed
<template>
姓: <input type="text" v-model="firstname" /> <br />
名: <input type="text" v-model="lastname" /> <br />
全名: {{ fullname }}
</template>
<script setup>
import { computed, ref } from 'vue'
const firstname = ref('')
const lastname = ref('')
const fullname = computed(() => {
return firstname.value + lastname.value
})
</script>
创建可读写的计算属性:
const doubleCount = computed({
get: () => {
return count.value * 2;
},
set: (newValue) => {
count.value = newValue / 2;
}
});
watch
<template>
单价: 5 <br />
数量: <input type="text" v-model.number="num" /> <br />
总价: {{ total }}
</template>
<script setup>
import { ref, watch } from 'vue'
const num = ref(0)
const total = ref(0)
watch(num, () => {
total.value = num.value * 5
})
</script>
onMounted
生命周期钩子函数,用于在组件挂载(即元素被插入到 DOM 中)之后执行代码。这是执行副作用操作(如设置定时器、发起网络请求、直接操作 DOM 等)的理想时机。
<template>
<ul>
<li v-for="book in books" :key="book.id">{{ book.name }}</li>
</ul>
</template>
<script setup>
import { onMounted, ref } from 'vue'
const books = ref([])
onMounted(() => {
// 在onMounted中给books赋值
setTimeout(() => {
books.value = [
{ id: 1, name: 'vue' },
{ id: 2, name: 'gis' },
{ id: 3, name: 'js' },
]
}, 1000)
})
</script>
组件
简单使用
- 定义组件
在components
目录下, 创建组件文件TheCounter.vue
<template>
计数器:{{ count }}
<button @click="handleClick">点击+1</button>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
console.log(count)
function handleClick() {
count.value++
}
</script>
<style>
/* 组件的样式 */
</style>
- 导入组件
在App.vue
中, 通过import
语法导入组件对象
<script setup>
// 2. 导入组件对象
import TheCounter from './components/TheCounter.vue'
</script>
- 引用组件
在App.vue
的模板中, 通过组件名
引用组件
<template>
<!-- 3. 引用组件 -->
<TheCounter></TheCounter>
</template>
父传子
父组件向子组件传递数据
- 在父组件中, 借助自定义属性
- 在子组件中, 通过
defineProps
接受
defineProps
- 只能在
<script setup>
的顶层使用 type
:String
、Number
、Boolean
、Array
、Object
、Date
default
: 默认值required: true
,指定一个属性是必需的validator
,用于自定义验证函数
静态绑定
<template>
<TheBlog title="01-vue"></TheBlog>
<TheBlog title="02-gis"></TheBlog>
<TheBlog title="03-js"></TheBlog>
</template>
<script setup>
import TheBlog from './components/TheBlog.vue'
</script>
<template>
<div>{{ title }}</div>
</template>
<script setup>
const props = defineProps({
title: {
type: String,
required: true,
},
})
</script>
动态绑定
<template>
<TheBlog title="01-vue"></TheBlog>
<TheBlog title="02-gis"></TheBlog>
<!-- 将属性值和App组件中的一个状态动态绑定 -->
<TheBlog :title="t"></TheBlog>
</template>
<script setup>
import TheBlog from './components/TheBlog.vue'
import { ref } from 'vue'
const t = ref('03-标题')
</script>
子传父
子组件向父组件传递数据
- 在子组件中, 触发自定义事件
- 在父组件中, 监听自定义事件, 在回调函数中通过参数获取
自定义事件
const emit = defineEmits(['eventName1', 'eventName2']);
<script setup>
const emit = defineEmits({
eventName1: null, // 不进行类型检查
eventName2: (payload) => {
// 类型检查函数
if (typeof payload !== 'string') {
console.warn('eventName2 expects a string as the payload');
}
}
});
</script>
<template>
<button @click="updateValue(newvalue)">更新值</button>
</template>
<script setup>
import { ref } from 'vue';
const newvalue = ref('新值')
const emit = defineEmits(['childclick']);
function updateValue(newValue) {
emit('childclick', newValue);
}
</script>
<template>
<ChildComponent @childclick="handleChildClick($event)" />
<p>当前值: {{ modelValue }}</p>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './components/test.vue';
const modelValue = ref('初始值');
function handleChildClick(value) {
modelValue.value = value
}
</script>
实例: 根据id关闭指定组件
<template>
<div>
{{ title }}
<button @click="handleClick">关闭</button>
</div>
</template>
<script setup>
const props = defineProps({
title: {
type: String,
required: true,
},
id: {
type: Number,
required: true,
},
})
const emits = defineEmits(['close'])
function handleClick() {
emits('close', props.id)
}
</script>
<template>
<!-- v-if和v-for不能应用于同一个元素 -->
<TheBlog
v-for="blog in showBlogs"
:key="blog.id"
:title="blog.title"
:id="blog.id"
@close="handleClose"
></TheBlog>
</template>
<script setup>
import TheBlog from './components/test.vue'
import { ref, computed } from 'vue'
const blogs = ref([
{ id: 1, title: '01-vue', isShow: true },
{ id: 2, title: '02-gis', isShow: true },
{ id: 3, title: '03-mapbox', isShow: true },
])
const showBlogs = computed(() => {
return blogs.value.filter((item) => item.isShow)
})
function handleClose(id) {
console.log(id)
const blog = blogs.value.find((item) => item.id == id)
blog.isShow = false
}
</script>
provide和inject
- provide: 提供. 数据由祖先组件提供
- inject: 注入. 由后代组件获取数据
在app
应用实例上挂载
const app = createApp(App)
// 通过app定义全局的对象: $map
app.provide('$map', { map: 'world' })
在app
对应的子组件中, 可以直接引用
import { inject } from 'vue'
const map = inject('$map')
console.log(map)
插槽
- 在组件中预留一个
slot
插槽 - 在组件渲染时, 将内容
插入
到slot
中
普通插槽
在子组件定时时, 预留插槽
<template>
<!-- 预留一个slot插槽 -->
<slot></slot>
</template>
在渲染子组件时, 会使用组件内部的内容替换掉slot部分
<TheComputer>
<div>
<h3>名称: MacBookPro</h3>
<p>型号: MBP 14 M1</p>
</div>
</TheComputer>
具名插槽
在定义slot
插槽时, 设置name
属性
<template>
<!-- 具名插槽 -->
<slot name="cpu"></slot>
<slot name="memery"></slot>
</template>
在渲染时, 通过v-slot
指令指定插槽的名称
<template>
<TheComputer>
<template v-slot:cpu>
<!-- 这里的内容插入到名字叫cpu的插槽中 -->
CPU: M1 Pro
</template>
<template #memery> Memery: 16G </template>
</TheComputer>
</template>
作用域插槽
将子组件的属性及其值传递给父组件,以便在父组件的插槽内容中使用
- 默认插槽
<template>
<div>
<slot :userprop="user"></slot>
</div>
</template>
<script setup>
const user = reactive({
name: '张三',
age: 30})
</script>
<template>
<test>
<template v-slot:default="slotProps">
{{ slotProps.userprop.name }} - {{ slotProps.userprop.age }}
</template>
</test>
</template>
- 具名插槽
<template>
<div>
<slot name="body" msg="这是消息" :age="12"></slot>
</div>
</template>
<template>
<test>
<template #body="data">
这是插槽内部传递的数据: {{ data.msg }} age: {{ data.age }}
</template>
</test>
</template>