一、setup简介
1. setup是什么?
组合式 API
setup()钩子是在组件中使用组合式 API 的入口
二、Vue3的生命周期
- setup
- beforeCreate
| option API | composition API setup() |
|---|---|
| created | * |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeUnmout | onBeforeUnmout |
| unmounted | onUnmounted |
三、toRefs
1. 作用
在使用ES6解构时不会失去相应式
toRefs在调用时只会为源对象上已经存在的属性创建 ref。
2. 例子
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2
})
// ...基于状态的操作逻辑
// 在返回时都转为 ref
return toRefs(state)
}
// 可以解构而不会失去响应性
const { foo, bar } = useFeatureX()
四、computed
1. 例子
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
五、watch
- immediate:在侦听器创建时立即触发回调。第一次调用时旧值是
undefined。- deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。
1. 例子
// 监听某一个属性
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// 深度监听
const state = reactive({ count: 0 })
watch(
() => state,
(newValue, oldValue) => {
// newValue === oldValue
},
{ deep: true }
)
// 当直接侦听一个响应式对象时 自动深度监听
const state = reactive({ count: 0 })
watch(state, () => {
/* 深层级变更状态所触发的回调 */
})
六、 watchEffect
1. 作用
响应式地追踪其依赖,
立即执行
2. 例子
副作用清除
const stop = watchEffect(async (onCleanup) => {
// cancel是 取消的逻辑
const { response, cancel } = doAsyncWork(id.value)
// 每次更改id重新请求
// 取消上一次 未完成的请求 `cancel` 会在 `id` 更改时调用
onCleanup(cancel)
data.value = await response
})
// 当不再需要此侦听器时:
stop()
七、路由
1. 例子
useRoute ===> this.$route
useRouter ===> this.$router
1. query方式
import { useRouter, useRoute } from 'vue-router'
export default {
setup() {
const router = useRouter()
const route = useRoute()
// 跳转
router.push({
path: '/search',
query: {
id: 'niahoa',
},
})
// 接收
route.query
},
}
2. params方式
import { useRouter, useRoute } from 'vue-router'
export default {
setup() {
const router = useRouter()
const route = useRoute()
// 跳转
router.push({
name: 'search',
params: {
id: 'niahoa',
},
})
// 接收
route.params
},
}
2. 创建
import { createRouter, createWebHitore } from 'vue-router';
const routes = [
{
path: '/',
name: 'Home',
component: ()=> import('../views/Home.vue')
},
]
const router = createRouter({
history: createWebHistory('/'),
routes,
})
export default router;
八、父子组件的传值
1. 父传子
1. 例子
// 父组件
<List :initMsg="'nihao'">
// 子组件
const props = defineProps({
initMsg:{
type: String,
default: '1111'
}
})
const msgSuper = ref(props.initMsg)
2. 子传父
1. 例子
// 子组件
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
const num = ref(200)
function buttonClick() {
emit('change', num)
}
// 父组件
<List @change="onChangeNumHandle">
const onChangeNumHandle = (n) =>{
console.log(n.value)
}
3. v-model
// 子组件
<!-- CustomInput.vue -->
<script setup>
import { computed } from 'vue'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const value = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
</script>
<template>
<input v-model="value" />
</template>
// 父组件
<CustomInput v-model="searchText" />
4. 兄弟组件的传值
1. 例子
// 1. 下载安装
cnpm install mitt -S
// 2. plugins/Bus.js
import mitt from 'mitt';
const emitter = mitt()
export default emitter;
// 3. 组件A
emitter.emit('fn',str);
// 4. 组件B
emitter.on('fn',e=>{
console.log(e)
})
5. Provide/Inject
// 先父组件
import { provide } from 'vue'
provide('changeNum', num)
// 孙子组件
import { inject } from 'vue'
const aNum = inject('changeNum')
九、插槽
1. 具名插槽
// BaseLayout.vue 创建
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
// 1. 使用
<BaseLayout>
<template v-slot:header>
<!-- header 插槽的内容放这里 -->
</template>
</BaseLayout>
// 2. 使用
<BaseLayout>
<template #header>
<h1>Here might be a page title</h1>
</template>
</BaseLayout>
2. 作用域插槽
1. 例子
<!-- <MyComponent> 的模板 -->
// 创建
<div>
<slot :text="greetingMessage" :count="1"></slot>
</div>
// 使用
<MyComponent v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</MyComponent>
3. 动态插槽
就是那个变量存具名插槽的名字
1. 例子
// 使用
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
<!-- 缩写为 -->
<template #[dynamicSlotName]>
...
</template>
</base-layout>
<script setup>
let dynamicSlotName = ref('header')
</script>
十、Teleport: 传送
<teleport to='body'>
内容
</teleport>
就会把 内容 传到body标签里面
十一、动态组件
1. 例子
1. markRaw
markRaw不让组件响应式
<template>
<component :is="currentComponent.compoent"></component>
</template>
<script setup>
import A from './A.vue'
import B from './B.vue'
import C from './C.vue'
let tabList = reactive([
{name:'A准备好面试题',component:markRaw(A)},
{name:'B准备好面试题',component:markRaw(B)},
{name:'C准备好面试题',component:markRaw(C)},
])
let currentComponent = reactive({
component:tabList[0].component
})
const changeHandle = (idx)=>{
currentComponent.component = tabList[idx].component
}
</script>
十二、异步组件
1. 基本用法
你可以使用这个异步的包装组件无缝地替换原始组
// 用法1
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
// ...从服务器获取组件
resolve(/* 获取到的组件 */)
})
})
// 用法2 配合import()
// ES模块动态导入import()
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)
2. 例子
<div ref="target">
<V v-if="showTarget"></V>
</div>
<script setup>
import { ref } from 'vue'
// 这个元素是不是可以在屏幕看到
import { useIntersectionObserver } from '@vueuse/core'
const V = defineAsyncComponent(()=>{
return import('../components/C.vue')
})
const target = ref(null)
const showTarget = ref(false)
const { stop } = useIntersectionObserver(
target,
([{ isIntersecting }], observerElement) => {
targetIsVisible.value = isIntersecting
},
)
</script>
3. 配合Suspense组件一起使用
主要作用可以有个加载中的提示
<div ref="target">
<Suspense v-if="showTarget">
<!-- 具有深层异步依赖的组件 -->
<V></V>
<!-- 在 #fallback 插槽中显示 “正在加载中” -->
<template #fallback>
Loading...
</template>
</Suspense>
</div>
<script setup>
import { ref } from 'vue'
// 这个元素是不是可以在屏幕看到
import { useIntersectionObserver } from '@vueuse/core'
const V = defineAsyncComponent(()=>{
return import('../components/C.vue')
})
const target = ref(null)
const showTarget = ref(false)
const { stop } = useIntersectionObserver(
target,
([{ isIntersecting }], observerElement) => {
targetIsVisible.value = isIntersecting
},
)
</script>
十三、组合式函数 composition AIP
封装封装复用逻辑
1. 例子
- 创建
// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'
// 按照惯例,组合式函数名以“use”开头
export function useMouse() {
// 被组合式函数封装和管理的状态
const x = ref(0)
const y = ref(0)
// 组合式函数可以随时更改其状态。
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
// 一个组合式函数也可以挂靠在所属组件的生命周期上
// 来启动和卸载副作用
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
// 通过返回值暴露所管理的状态
return { x, y }
}
- 使用
<script setup>
import { useMouse } from './mouse.js'
const { x, y } = useMouse()
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>
十四、paina
1. 安装
cnpm install pinia
2. 使用
// main.js
import { createPinia } from 'pinia';
import { createApp } from "vue";
createApp().use(createPinia())
// store/index.js
import { defineStore } from 'pinia'
export const useStore = defineStore('storeId', {
state: () => {
return {
num: 0,
name: '张三'
}
},
getters: {
getNum(){
return this.num +1000
}
},
actions: {
upNum(val){
this.num += val
}
}
})
// 组件中使用
import { useStore } from '../store/index.js'
import { storeToRefs } from 'pinia';
const store = useStore();
let { name, num, getNum, upNum } = storeToRefs(store)
const changeNum = ()=>{
store.upNum(100)
}
3. 模块化
// user.js
import { defineStore } from 'pinia'
export const useUser = defineStore('user', {
state: () => {
return {
age: 0,
name: '张三'
}
},
getters: {
getNum(){
return this.age +1000
}
},
actions: {
updateNum(val){
this.age += val
}
}
})
// shop.js
import { defineStore } from 'pinia'
export const useShop = defineStore('shop', {
state: () => {
return {
total: 20
}
},
getters: {
getTotal(){
return this.total +1000
}
},
actions: {
updateTotal(val){
this.total += val
}
}
})
4. 持久化存储
1. 安装
cnpm pinia-plugin-persist --save
// ./store/index.js
import { createPinia } from 'pinia'
// 引入持久化
import PiniaPluginPersist from 'pinia-plugin-persist'
const store = createPinia()
store.use(piniaPluginPersist)
export default store
// ./store/shop.js
import { defineStore } from 'pinia'
export const useShop = defineStore('shop', {
state: () => {
return {
total: 20
}
},
getters: {
getTotal(){
return this.total +1000
}
},
actions: {
updateTotal(val){
this.total += val
}
},
// 开启数据缓存
enabled:true,
strategies:[
{
key: 'my_shop',
storage: localStorage
}
]
})
十五、CSS绑定逻辑层数据
<script setup lang="ts">
import { ref } from "vue";
const height = ref('100px')
</script>
<style lang="less" scoped>
div {
width:100px;
height:v-bind('height')
}
</style>