01- vue3的基本语法

546 阅读4分钟

一、setup简介

1. setup是什么?

组合式 API

setup() 钩子是在组件中使用组合式 API 的入口

二、Vue3的生命周期

  1. setup
  2. beforeCreate
option APIcomposition API setup()
created*
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmoutonBeforeUnmout
unmountedonUnmounted

三、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

封装封装复用逻辑

组合式函数 | Vue.js (vuejs.org)

1. 例子

  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 }
}
  1. 使用
<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>