[TOC]

核心语法

# 1.OptionsAPI(选项式)和 CompositionAPI(组合式)

选项式 API:data,methods,name 等 Vue2 的基础写法

弊端:数据方法计算属性分别在不同的 api 中不便于维护和复用组合式 API?

2.setup

原来数据是写在 data 里面的现在这样的写法不是响应式

而且 setup 会比 beforeCreate 要响应的更早, setup return 也可以是一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
return ()=>'hellow'
export default {
name: 'person',
setup() {
let name = '张三'//此时的name不是响应式的
let age = 20/此时的age不是响应式的
let tel = '11111111'/此时的tel不是响应式的
function showTel() {
alert(tel)
}
return {
name,
age,
tel,
showTel
}
}

setup 与 data,methods 同时存在

data 能不能拿到 setup 中的值呢?可以拿到!

setup 是生命周期里面最早的

setup 能拿到 data 里面的值么?不能

# setup 语法糖

1
2
3
4
5
6
7
<script setup>


相当于在setup函数里面写,而且不用自己return


<script>

但是呢语法糖里不能写组件名称相关的,这时候需要用到一个插件

1
npm i vite-plugin-vue-setup-extend

在 vite.config.ts 中配置即可

1
2
3
4
5
6
7
import VueSetup from 'vite-plugin-vue-setup-extend'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
VueSetup()
],

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script lang="ts">
export default {
name: 'person',
}
</script>
<script setup lang="ts">
let name = '张三'
let age = 20
let tel = '11111'
let a = 101
function showTel() {
alert(tel)
}
</script>

不想写两个标签可以直接在 script 标签上使用 name 属性自定义,如果与文件名同名不用插件也可以

# ref 响应式数据

基本类型的响应式数据

引入

1
2
//引入ref
import { ref } from 'vue'

想让什么数据是响应式的就用 ref 包住

1
2
3
4
5
let name = ref('张三')
let age = ref(20)
let tel = '11111'
let a = 101
console.log(1, name, 2, age, 3, tel)

看看区别

而且插值上会自动.value 不用写,但是修改值的时候需要使用.value 修改

1
2
3
4
5
6
7
}
function changeName() {
name.value = '大哥'
}
function addAge() {
age.value++
}

# reactive 只能定义 object 的响应式数据()

1
2
3
4
5
6
7
8
9
10
11
import { reactive } from 'vue'
//基本类型的响应式数据
let name = ref('张三')
let age = ref(20)
//对象类型的响应式数据hobby

let hobby = reactive({
name: '章子怡',
age: 45,
sex: '男'
})

ref 可以定义基本类型的响应式数据,也可以定义对象类型的响应式数据

ref 的底层还是 reactive

区别:

1.ref 创建的变量必须使用.value 可以使用 volar 自动添加.value

2.reactive 重新定义一个对象 hobby 的时候,这个对象就失去了响应式属性

hobby={name:'',age:''.......},

可以使用 Object.assign (a,b) a 和 b 的都会对比不一样就新建,一样就替换

1
2
3
4
5
6
7
8
9
10
let assign = { name: 'ls', age: 12, sex: '5' }
let tel = '11111'
let a = 101
console.log(hobby)
function changeC() {
hobby={ name: 'ls', age: 12, sex: '5'}//页面不更新hobby用reactive包裹
Object.assign(hobby, assign)//这样页面才会更新hobby用reactive包裹
hobby.value={ name: 'ls', age: 12, sex: '5'}//页面更新hobby用ref包裹
console.log(hobby)
}

# toRefs 解构

确保结构出来的对象都是响应式对象

通过把 reactive 中每一组键值提出来成一个新的对象

解构后页面也可以直接使用 name

1
2
3
4
5
6
7
8
let person = reactive({ name: '章子怡', age: 45 })
let { name, age } = toRefs(person)
function changeName() {
name.value += '冯刚'
}
function changeAge() {
age.value += 1
}

# 计算属性 computed

只读的计算属性,fullName 不能修改

1
2
3
4
let fullName = computed(() => {
const name = na.value.slice(0, 1).toUpperCase() + na.value.slice(1) + me.value
return name
})

v-model 修改后发现是只读,也就是没有 set , 怎么改成可读可写呢?写 get,set 函数

1
2
3
4
5
6
7
8
let fullName = computed(() => {
get(val){
return name+'zha'
}
set(val){
name.value=val
}
})

# watch 侦听

vue3 中 watch 可以监视的数据只有四种

# 1.ref 定义的数据

基本类型

1
2
3
4
5
6
watch(na, (newV, oldV) => {
console.log(newV, oldV)
})
watch(me, (newV, oldV) => {
console.log(newV, oldV)
})

对象类型

1
2
3
4
5
6
7
8
watch(
hud,
(newV, oldV) => {
//监视对象类型的数据但是对象里面的数据修改监听需要添加属性deep:true
console.log(newV, oldV)
},
{ deep: true,immediate:true }
)

总结:如果是修改的整个对象那就有新旧值之分,修改的是对象里面的某个属性那新的和旧的都是一样的

# 2.reactive 定义的对象

如果监视的是 reactive 类型的对象数据已经隐式创建了深层监听,而且关不掉

因为修改姓名和 age 都是在原对象上操作原对象并没发生改变所以 newval 和 oldval 是一样的

1
2
3
4
5
6
7
8
9
10
watch(obj, (n, o) => {
console.log('obj', n, o)
})
let obj = reactive({
a: {
b: {
c: 100
}
}
})

修改整个对象后才分新旧值

1
2
3
function changePerson() {
Object.assign(hud, { name: '终南山', age: 68 })
}

# 3.getter 函数(能返回一个值的函数)只想让监视对象中的某个属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let person = reactive({
name: '张宝果',
age: 18,
car: {
c1: '奔奔',
b1: '江淮',
a1: '解放'
}
})
watch(
() => person.name,//getter函数一个有返回值的函数
(n, o) => {
console.log('person', n, o)
}
)

监视对象里面的对象的时候可以直接写对象但是修改对象整个的时候不会被监听到

1
2
3
watch(person.car, (n, o) => {
console.log('person', n, o)
})

加上函数后就会被监听到,但是里面的属性就监听不到了

1
2
3
4
5
6
watch(
() => person.car,
(n, o) => {
console.log('person', n, o)
}
)

总:1. 监视对象中基本类型的数据的时候需要写成函数式

2. 监视对象中对象类型的数据时,不需要监听整个对象变化时可以直接写 ,需要监听整个对象不监听对象里面的变化时可以写成函数式,既需要监听整个对象变化,又要监听对象中属性的变化是时不仅要写成函数式,还要加 deep:true;

推荐使用函数式

4. 一个包含上述内容的数组

监视对象中指定的多个值

1
2
3
4
5
6
7
8
9
//监视对象中的多个函数
watch(
[() => person.car.a1, () => person.name],

(n, o) => {
console.log('person', n, o)
},
{ deep: true }
)

# WatchEffect

用到谁就监视谁,加载自动执行一次

1
2
3
4
5
6
7
8
9
10
11
// watch([sum, count], (val) => {
// let [n, o] = val
// if (n > 200) {
// console.log(n, o)
// }
// })
watchEffect(() => {
if (sum.value >= 60) {
console.log('水温过高')
}
})

# 标签的 Ref 属性

在 html 标签上就是获取 DOM 元素

ref获取节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div class="person">
<button class="btn" @click="aler"></button>
<h2 ref="cl">123</h2>
</div>
</template>

<script setup lang="ts" name="person">
//引入ref
import { ref } from 'vue'
let cl = ref()
function aler() {
console.log(cl.value)
cl.value.innerText = '128'
}
</script>

# 组件上的 ref 就是获取组件实例

打印时获取到的发现获取不到身上的值,还需要一个属性 defineExpose 来允许被访问

1
defineExpose({ cl1, cl2, cl3 })//加载最底部

# TS 接口,泛型,自定义类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定义一个接口
export interface PersonInter {
id: string,
name: string,
age: number
}
//泛型
export type Persons = Array<PersonInter>
// 自定义类型
export type PersonList = PersonInter[]
--------------------------------------
import { type PersonInter, type Persons, type PersonList } from '../types/index'
// let person: PersonInter = { id: 'ssdsdadsadas', name: '132', age: 68 }
let personsList: PersonList = [{ id: 'ssdsdadsadas', name: '132', age: 68 }, { id: 'ssdsdadsadas', name: '132', age: 68 }, { id: 'ssdsdadsadas', name: '132', age: 68 }]

# Props 传值

props 传值就子组件接收发生改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { [defineProps],withDefault } from 'vue'
..只接收值
defineProps(['list'])
//限制接受数据的类型
defineProps<{ list: PersonList }>()
//传不传都可以
defineProps<{ list?: PersonList }>()
//默认值,withDefault
withDefault (defineProps<{ list?: PersonList }>(),{
list:()=>[{id:'sssss',name:'tsc',age:20}]
})


[]代表可以省略

# Vue3 生命周期

创建

beforeCreate,Created 被 vue3 里面的 setup 替代了

挂载

1
2
3
4
5
6
7
8
import {onBeforeMount}
cl(创建)
onBeforeMount(()=>{
cl(挂载前)
})
onMounted(()=>{
cl(挂载完)
})

更新

1
2
3
4
5
6
7
8
9
onBeforeUpdate,onUpdated


onBeforeUpdate(()=>{
cl(更新前)
})
onUpdated(()=>{
cl(更新完)
})

卸载(vue2 销毁)

1
2
3
4
5
6
7
8
9
10
11
12
onBeforeUnMount(vue2beforeDestory),onUnmounted(vue2Destoryed)

onBeforeUnMount(()=>{
cl(卸载前)
})

onUnMounted(()=>{
cl(卸载完毕)
})



父子执行顺序,修改顺序

# Hook s

把数据和方法分类整理成一个模块

里面可以写所有在组件中用到的方法属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { ref } from 'vue'

export default function () {
let sum = ref(0)
function add() {
sum.value += 1
}
return { add, sum }
}


-------------------------------------------------------------------------------------------
import useSum from '../Hooks/useSum'
import useDog from '../Hooks/useDog'
let { dogList, getNewdog } = useDog()
let { sum, add } = useSum()
console.log(useDog())

# Router

一般由程序员亲自写标签的为一般组件

一般组件一般放在 components 里面

靠路由规则渲染的是路由组件

路由组件一般放在 pages 或 views 文件夹里面

路由工作模式
history :

不带 #更美观但是缺点是项目上线需要服务端(houduan)配合处理路径问题否则会找不到 404

vue2:mode:history

vue3history:createWebHistory()

hash:hisrory:createWebHashHistory () 带有 #不美观不利于 seo

to 的写法
1. 字符串

1
router-link to="/home"

2. 对象

1
router-link :to="{path:'/about'}"

# 命名路由

1
2
3
4
5
{    name:'zhuye'
path: '/home',
component: Home,
},
<router-link :to="{name:'/zhuye'}" active-class="zhuzhuxia">首页</router-link>

# 嵌套路由

# query

传值(基础方法)

父 :发送 子:接受

传值(对象)

1
2
3
4
5
6
7
8
9
10
11
<router-link
:to="{
path: '/news/content',或者用name:content
query: {
id: item.id,
title: item.title,
content: item.content
}
}"
>{{ item.title }}
</router-link>

# params(接受的地方 query 换成 params 就可以了)

匹配路由规则:content? 传不传都不会报错

1
2
3
path: '/news',
component: News,
children: [{ name: 'content', path: 'content/:id/:title/:content?', component: content

传参(基本)

1
2
3
4
<router-link
:to="`/news/content/${item.id}/${item.title}/${item.content}`"
>{{ item.title }}
</router-link>

传参(简单)

1
2
3
4
5
6
7
<router-link
:to="{
name: 'content',
params: { id: item.id, title: item.title, content: item.content }
}"
>{{ item.title }}
</router-link>

解构的话需要引入 ref 或者 reactive

# 路由 props 配置

# props:true 开启路由传参(params)

1
children: [{ name: 'content', path: 'content/:id/:title/:content', component: content, props: true }

接收

1
defineProps(['id', 'title', 'content'])

# props(query)

路由配置

1
2
3
4
5
6
7
8
9
10
children: [
{
name: 'content',
path: 'content/:id/:title/:content',
component: content,
// props: true
props(route){
return route.query
}
}]

接收

1
defineProps(['id', 'title', 'content'])

拓展:对象(传固定的参数,不建议)

1
2
3
4
props:{
a:1,
c:2
}

在 router-link 上加 replace 就是这个模式了,路由记录会直接覆盖上一条路由记录,而默认 push 会一直插入到最前面记录不会覆盖

到上面的位置后

继续点 push 的路由

# 编程式路由导航 (脱离 router-link 实现路由跳转)

引入

1
import { useRouter } from 'vue-router'

使用(传值和正常的 router-link 里面的传值格式一样)

1
2
3
4
5
6
7
8
9
10
11
et router = useRouter()
interface NewsInter {
id: string
title: string
content: string
}
function jump(item: NewsInter) {
router.replace({
name: 'content',
params: { id: item.id, title: item.title, content: item.content }
})

# 路由重定向 redirect

1
2
3
4
{
path: '/',
redirect: '/home'
}

# Pinia 集中式状态管理工具

vue2(vuex)

引入

1
2
3
npm i pinia

import {createPinia} from 'pinia'

注册

1
2
3
const pinia=createPinia()

app.use(pinia)

创建 store 文件夹,把不同组件的数据创建不同的模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//导入插件
import { defineStore } from 'pinia'

//const后的名字用来接受数据,用于向外暴露,talk是pinia库的名字自己起
state函数里用来存放数据
export const useListStore = defineStore('talk', {
state() {
return {
talkList: [
{ id: '23132', content: '你今天有点怪啊,怪恶心的' },
{ id: '27732', content: '草莓,蓝莓,蔓越莓,遇见你我最倒霉' },
{ id: '5654654', content: '给你留了一块地,风水宝地' }]
}
},
})

页面直接引入解构即可

1
2
import { useListStore } from '../store/bilins'
const { talkList } = useListStore()

交互(set)修改里面的值

1. 直接拿到值就修改

1
countStore.sum = s.value + o.value

2. 用于批量修改数据

1
countStore.$patch({ sum: s.value - o.value })

3. 使用 actions 修改数据

在 count 的 store 里面定义 action 可以对数据进行约束

1
2
3
4
5
6
7
8
9
actions: {
increment(a, b) {

if(b!=0){
//this指向是当前的store(useCountStore)就是暴露出去的那个仓库
this.sum = a / b
}
}
},

在组件中修改,直接调用方法名传参过来

1
countStore.increment(s.value, o.value)

# storeToRefs

因为这样写模板太臃肿

所以简化

1
2
let countStore = useCountStore()
let { sum } = countStore

发现简化后不能修改

发现他把所有的属性都变成响应式的了,—_-

幸好 pinia 封装了一个好用的可以识别哪些是需要响应式属性哪些不需要的 storeToRefs,

就不打开对比了,一眼就看出来了 - 真不错

storeToRefs 只会处理数据不会处理方法

1
2
3
4
5
//让数据变成响应式数据不然只能展示不能修改数据后展示到页面
import { storeToRefs } from 'pinia'
let countStore = useCountStore()
//让数据变成响应式数据不然只能展示不能修改数据后展示到页面
let { sum } = storeToRefs(countStore)

# getters 对数据进行进一步的计算

可以写成函数式,这里的 state 形参和 this 的效果是一样的不想用函数式,也可以写成箭头函数更加简洁

1
2
3
4
5
6
7
8
9
10
11
12
//存储数据的地方
state() {
return {
sum: 5
}
},
getters: {
//形参就是state
maxSum(state) {
return state.sum * 100
}
}

也可以写成箭头函数,

1
maxSum: state => state.sum * 100

接收的话直接在接收的时候解构函数就可以了

1
let { sum,maxSum } = storeToRefs(countStore)

# $subscribe

mutate 本次修改的信息

state 就是库里面的 state

1
2
3
countStore.$subscribe((mutate, state) => {
console.log(mutate, state)
})

可以让页面刷新不丢失数据

1
2
3
4
countStore.$subscribe((mutate, state) => {
console.log(mutate, state === countStore.$state)
localStorage.setItem('sum', JSON.stringify(sum.value))
})

配置参数

1
2
3
4
5
6
//存储数据的地方
state() {
return {
sum: JSON.parse(localStorage.getItem('sum') || '0')
}
},

刷新不丢失数据

# 选项式写法

组合式写法

我真的崩溃,这个 ref 设置的响应式对象一定要使用.value 取值赋值,找了半天以为是 ts 类型的错误,向外暴露用 {} 暴露

1
2
3
4
5
6
7
8
9
10
11
12
import { ref } from 'vue'
export const useCountStore = defineStore('count', () => {

//存储数据的地方
let sum = ref(JSON.parse(localStorage.getItem('sum') || '0'))
function increment(a: number, b: number) {
sum.value = a / b
}
const maxSum = () => sum.value * 100

return { sum, increment, maxSum }
})

组件通信 Props

# Props 传值

props 传值就子 **** 组件接收发生改变(父传子)

1
defineProps([''])

# 子传父

1
2
3
传一个方法给儿子形参就是接受的值

<子组件 :sendAtm='父组件定义的方法名'/>

子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
defineProps(['sendAtm'])

let atm=ref(100)



btn @click='sendAtm(atm)'

或者方法二

const props=defineProps(['sendAtm'])
function send(atm){
props.sendAtm(atm)
}

# 自定义事件

父组件声明接收子组件的自定义事件

命名多个单词推荐使用 new-Toys 命名,或者小驼峰

1
2
3
4
5
6
7
8
<h1>好大二{{ son }}</h1>
<Count @enter="toys" />

let son = ref('')
function toys(value: string) {
son.value = value
console.log('父组件接收到值', value)
}

子组件接收来自父组件的自定义事件并且设置触发按钮

1
2
3
4
5
6
7
8
9
10
11
let toy = ref('奥特曼')
const emit = defineEmits(['enter'])





<button @click="emit('enter', toy)">发送数据给父组件</button>
或者触发事件funtion onEnte(){
emit('enter',toy.value)
}

# Mitt 任意组件通信 (约等于 pubsub,$bus)

下载插件

1
npm i mitt

创建 tools 文件夹,并创建 tools/emitter.ts 引入 mitt

1
2
3
import mitt from 'mitt'

const emmitter=mitt()

定义方法 on ()

1
2
3
4
5
6
emmitter.on('test', () => {
console.log('8848');
})
emmitter.on('test2', () => {
console.log('8849');
})

使用 emit ()

1
2
3
4
import emmitter from './tools/emitter'
import { ref, onMounted } from 'vue'
onMounted(() => {
emmitter.emit('test')

单个解绑 off ()

1
emmitt.off('test')

全部解绑 clear ()

1
emmit.all.clear()

传值

发送组件里面

1
2
3
emmitter.emit('send-atm', toy)

触发事件名,发送的数据

接收 emmitter.on (触发事件名,回调函数(穿过来的值))

1
2
3
4
emmitter.on('send-atm', (value: any) => {
console.log(123)
son.value = value
})

组件卸载时在事件定义的组件上要卸载解绑该事件

1
2
3
onUnmounted(()=>{
emmitter.off('send-atm')
})

# v-model 组件传值、父子双向数据绑定

绑定原理

1
2
3
4
input  v-model='username'

//:value数据到页面的数据绑定。每当input数据发生变化时触发@input事件把值重新赋值给username
input :value='username' @input='username=$event.target.value'

1
2
3
4
5
<son v-model='username'/>

原理就是

<son :modelValue='username' 把数据送过去组件上就多了一个是事件 @update:modelValue='username=$event'

但是子组件上需要事件去触发更改

1
2
3
4
5
defineProps(['modelValue'])

//接收传过来的事件,也可以传多个

const emit=defineEmits(['update:modelValue''update:mima'])

给子组件里面的 input 绑定触发事件,emit ('update:modelValue', 要传的值)

1
input :value='modelValue' @input='emit('update:modelValue',$event.target.value)'

TIp: 在原生 DOM 上 $event 就是 DOM 事件对象,在组件标签上就是触发事件时传过来的值

也可以在 v-model: 自定义名称 ='username' 事件触发(update: 自定义名称,要传的值)

# 组件通信 $attrs(祖孙)

现在有这么个关系的组件

第一父传子

1
2
3
4
5
6
7
8
9
10
father




son :a='1' :b='2' v-bind='{c:1,d:2,e:5}' :grandFather='grandFather'

function grandFather(val){
cl(val)
}

子传孙子:自己没有用的都给了孙

1
2
3
4
5
6
7
8
9
son


son里没有用一个参数,如果传的是abc 这里defineProps用了a$attrs里就只剩下bc了


grandson v-bind='$attrs'


在 grandson 里面接收值

1
defineProps(['a','b','c','d','e','grandFather'])

触发事件传值过去

1
2
3
4
5
6
7
8
9
10
11
<template>
<div>
<h6>{{ a }}{{ b }} {{ c }}</h6>

<button @click="grandFather(1)">传给father</button>
</div>
</template>

<script lang="ts" setup name="GrandSon">
defineProps(['a', 'b', 'c', 'grandFather'])
</script>

# refs 和 parent

refs 统一收集有 ref 的组件,可以处理对象通过 defineExpose 的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
<div class="count">
<h1>好大二{{ toy }}</h1>
<Count ref="c1" />
<Bilins ref="c2" />
<button @click="grandFather($refs)">refs</button>
</div>
</template>

<script lang="ts" setup name="App">
import Count from './components/Count.vue'
import Bilins from './components/Bilins.vue'
import { ref, onMounted } from 'vue'
const c1 = ref()
const c2 = ref()
// onMounted(() => {
// console.log(c1.value, c2.value)
// })
let toy = ref('奥特曼')
function grandFather(value: object) {
console.log(value)
}
</script>
<style scoped>
.count {
color: rgb(20, 109, 187);
}
</style>

# parent

在父组件向外暴露可以给子组件使用的参数

1
defineExpose({'name'})

子组件操作父组件的值

1
2
3
4
<button @click="dejsck($parent)">操作</button>
function dejsck(val: any) {
val.toy = 'GGBon'
}

ref 包裹的对象里面并不用.value, 只有最顶层的对象需要使用.value

1
2
3
4
5
6
7
const obj=ref({
a:ref(5)
n:ref(5)
k:ref(5)
})

obj.value.n

# provide**&**inject (祖 - 后代通信)

发送

1
2
3
// provide('toy', toy)
// provide('toy1', toy1)
// provide('toy2', toy2)

接收

1
2
3
let toy = inject('toy', '默认值')
let toy1 = inject('toy1', { a: 'morenzhi' })
let toy2 = inject('toy2', 1)

方法发送

1
2
3
4
5
provide('changeToy', { toy2, changeToy })

function changeToy(val: string) {
toy2.value += val
}

传值修改

1
2
let { toy2, changeToy } = inject('changeToy')
<button @click="changeToy('ro')">changeToy</button>

设置默认值

1
2
3
4
5
6
let { toy2, changeToy } = inject('changeToy', {
toy2: '动画片',
changeToy: (n: any) => {
console.log(n)
}
})

# 插槽 slot

默认插槽:在子组件中用 slot 标签占位,父组件中子组件中间内容区域的内容会被添加到 slot 指定位置

# 具名插槽 带有名字的插槽

使用的时候用模板标签 template 包裹在上面匹配插槽的名字就可以

1
2
3
4
5
6
7
8
9
10
11
12

<Count>
<template v-slot:bottom>
<div>下</div>
</template>
<template v-slot:top>
<div>上</div>
</template>
<template v-slot:middle>
<div>中</div>
</template>
</Count>

1
2
3
4

<slot name="top"></slot>
<slot name="middle"></slot>
<slot name="bottom"></slot>

简便写法 v-slot 可以简写成#

1
2
3
4
5
6

<Count>
<template #bottom>
<div></div>
</template>
</Count>

# 作用域插槽 :子组件维护数据,父组件决定展示结构

在封装属性时为预留的插槽自定义属性值可以在父组件中用到传过来的值,就是作用域插槽

子组件中有一个数组对象 carList 但是要求用三种不同方式展示

默认作用域插槽和具名作用域插槽

1
2
3
4
5
6
7
8
9
10
11
12
<Count>
<template #middle="obj">
<div v-for="item in obj">
{{ item }}
</div>
</template>
</Count>
<hr />
<Count v-slot="obj">
<h1>默认作用域插槽</h1>
{{ obj }}
</Count>

其他 API
shallowRef 与 shallowReactive
shallowRef 只处理第一层的响应式就是第一个点之前的数据(person.value.name), 只能处理到 person.value 这一层的响应式

shallowReactive 也是只处理第一层的响应式也就是第二个点之前的对象都能处理

值得注意的是如果其他深层次的对象在触发响应式对象之前触发的,在响应式对象触发时也会跟着更新到页面展示上

readonly 和 shallowReadonly(保护原始数据)
readonly 把一个属性的所有层次都变成只读的,

shallowReadonly 只把一个属性第一层变成只读的

toRaw 和 markRaw
toRaw 把一个响应式对象变成一个响应式对象的原始对象(用于向 vue 外的库出传输数据等)

markRaw 标记一个对象让他永远都不会变成响应式的

customRef 自定义 ref
一般情况下会把自定义 ref 封装成一个 hooks

track () 跟踪 // 告诉 vuemsg 很重要,要持续关注,发生变化就去更新

trigger () 触发 // 通知 vue 数据变化了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//定义默认值
let carList = 'nihao'
//使用自定义ref定义响应式数据
//为了不让看起来像计算属性所以还有两个参数
//track()跟踪,trigger()触发
let msg = customRef((track, trigger) => {
return {
get() {
track() //告诉vuemsg很重要,要持续关注,发生变化就去更新
return carList
},
set(value) {
carList = value
trigger()//通知vue数据变化了
}
}
})

hooks

1
2
3
4
5
import useMsgRef from '../useCustomRef'
//定义默认值
let carList = 'nihao'
//转换成响应式对象
let { msg } = useMsgRef(carList)

Teleport 给它罩着的东西换个 father
本来 b 标签是 a 标签的父容器,但是 a 标签是一个固定定位的窗口,当 b 标签需要加一些改变当前文档流的属性值时会影响到 a 的位置,这时候可以使用 teleport 标签的 to='body' 属性值指定被包裹内容的父级元素

Suspense(实验性功能)
等待异步组件渲染一些额外的内容

当异步任务没有写在方法里面使用 await 的时候,没办法写 async 的时候可以请求到数据,但是子组件受到异步任务的影响导致子组件被堵塞没有显示在页面上

使用 Supense 标签包裹即可解决

1
import {Suspense} from 'vue'

需要使用 template 模板标签包裹,并且组件内容放在默认插槽,默认显示内容放在 fallback 插槽

# 全局 API 转移到应用对象

# Vue2 和 Vue3

# 1. 组件注册
Vue.component('hellow',hellow)注册全局组件app.component('hellow',hellow)
# 2. 全局配置对象,原型链

Vue2

1
2
3
4
5
6
const vm = new Vue({
render: (h) => h(App)
// beforeCreate() {
// Vue.prototype.$bus = this //安装全局事件总线
// }
}).$mount('#app')

Vue3

1
2
3
4
5
6
7
引入main.ts
declare module 'vue'{
interface componentCustomProperties{
x:number,//自定义属性
$http:type of axios //挂载全局axios
}
}

# 3. 注册全局指令,一般都封装在 Hooks

Vue2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//注册全局自定义指令
Vue.directive('color', {
bind (el, binding) {
el.style.color = binding.value
},
update (el, binding) {
el.style.color = binding.value
}
})
//简写形式
Vue.directive('color', function (el, binding) {
el.style.color = binding.value
})
局部
directives: {
Capital: {
inserted(el) {
//被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
console.log(el, '-----inserted')
},
bind(el, binding, vnode, oldVnode) {
//只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
console.log(el, binding, vnode, oldVnode, '-----bind')
},
update(el, binding, vnode, oldVnode) {
console.log(el, binding, vnode, oldVnode, '-----update')
}
}

vue3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.directive('beauty',(ele)=>{
ele.style.color='red' //用来设置样式等标签属性
...........
})
局部
const focus = {
mounted: (el) => el.focus()
}
export default {
directives: {
// 在模板中启用 v-focus
focus
}
}

# 4. 卸载组件

Vue2

1
app.unmount()

# 5. 安装插件
Vue.use安装插件app.use

# Vue3 总结与 vue2 不一样的地方

尽在 vue3 迁移指南 Vue 3 迁移指南

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Xiao Yang Guo 微信支付

微信支付

Xiao Yang Guo 支付宝

支付宝

Xiao Yang Guo 贝宝

贝宝