Vue3学习笔记

1. Vue2与Vue3的区别

1.1 v-ifv-for优先级

2.x版本中 v-for > v-if

3.x版本中 v-for < v-if

1.2 v-for中的ref数组

2.x版本中 会自动把ref填充内容

3.x版本中 需要手动添加

<!--Vue3.x-->
<ur>
    <li v-for='item in 5' :key='item' :ref="setItemRef">{{ item }}</li>
</ul>

<script>
    methods: {
        setItemRef(el){
            this.arr.push(el)
        }
    }
</script>
1.3 $children

2.x版本中 访问前实例的子组件

3.x版本中 在3.x中,$children 已被移除,且不再支持。可以用以下方式所代替:

设置:<HelloWorld msg=''Hello world! ref='hw/>

访问:this.$refs.hw

2. setup

2.1 是什么?

组合式API

2.2 来解决生么问题?

使用(data、computed、methods、watch)组件选项来组织逻辑通用都很有效。然而当我们的组件开始变得更大时,逻辑关注点的列表也会增长。尤其对于那些一开始没有编写这些组件的人来说,这会导致组件难以阅读和理解。

2.3 相应区别

2.x版本中:Object.defineProperty()

3.x版本中:Proxy

Object.defineProperty()中存在的问题

1、不能监听数组的变化

2、必须遍历对象的每一属性

Proxy不需要遍历

2.4 渲染函数
  • ref:定义简单类型的数据 eg:String、Number、Boolean

  • reactive:定义复杂类型的数据 eg:对象、数组

2.5 setup语法糖插件

unplugin-auto-import

解决场景:在组件中开发无需每次都引入import {ref, reative, …}

下载:npm i unplugin-auto-import -D

vite.config.js中配置

import AutoImport from 'unplugin-auto-import/vite'

export default defineConfig({
    plugins: [
        AutoImport({
            imports: ['vue', 'vue-router'] // 自动导入vue和vue-router相关函数
        })
    ]
})
2.6 toRefs

如果需要解构 prop,可以在 setup 函数中使用 toRefs 函数来完成此操作:

<script setup>
    import { reactive, toRefs } from 'vue'

    let obj = reactive({
        name: '张三',
        age: 20
    })
    let target = toRefs(obj)
    target.name.value = '李四'
    console.log(`姓名:${target.name},年龄:${target.age}`) //控制台  姓名:李四,年龄:20
</script>
2.7 computed

接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。

const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // 错误

或者,接受一个具有 get 和 set 函数的对象,用来创建可写的 ref 对象。

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
let obj = reactive({
    name: '张三',
    age: '20',
    sex: computed(() => {
        return '男'
    })
})
2.8 watch

2.x版本中,watch的使用:

watch: {
    obj: {
        handler(newVal, oldVal) {
            console.log(newVal, oldVal)
        },
        immediate: true // 当刷新页面时会立即执行一次handler函数
        deep: true // 深度侦听
    }
}

3.x版本中,watch的使用:

  • 监控一个数据
let msg = ref('Hello vue!')

watch(msg, (newVal, oldVal) => {
    console.log(newVal, oldVal)
},{
    immediate: true,
})

return {
    msg
}
  • 监控多个数据(一起监控)
let msg = ref('Hello vue!')
let str= ref('Hello world!')
let obj = reactive({
    name: '张三',
    hobby: ['篮球','游泳','射击']
})

watch([msg, str, ()=>obj.hobby], (newVal, oldVal) => {
    console.log(newVal, oldVal)
},{
    immediate: true,
})

return {
    msg,str
}
  • 监听对象中的对象
let obj = reactive({
    name: '张三',
    hobby: ['篮球','游泳','射击']
})

watch(()=>obj.hobby, (newVal, oldVal) => {
    console.log(newVal, oldVal)
})

watchEffect立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

let msg = ref('Hello vue!')

watchEffect(() => {
    console.log(msg.value)
})
2.9 路由
  • Vue2.x与Vue3.x的区别
Vue2.xVue3.x
this.$routeuseRoute
this.$routeruseRouter
  • 导航守卫

  • 动态路由

2.10 组件
2.10.1 父传子

父组件:

<template>
    <List :msg='msg' />
</template>

<script setup>
    import List from '@/components/List'
    let msg = ref('父传子')
</script>

子组件:(defineProps自定义属性)

<template>
    <div>这是子组件 ===> {{ msg }}</div>
</template>

<script setup>
    defineProps({
        msg:{
            type: String,
            default: ''
        }
    })
</script>
2.10.2 子传父

子组件:

<template>
    <div>这是子组件 ===> {{ num }}</div>
</template>

<script setup>
let num = ref(200)
const emit = defineEmits(['getData'])
    const backData = () => {
    emits('getData', num)
}
</script>

父组件:

<template>
    <List @getData='getData' />
</template>

<script setup>
    import List from '@/components/List'

    const getData = val => {
        console.log(val)
    }
</script>
2.10.3 父子组件双向数据

父组件:

<template>
    <List v-model:num="num" />
</template>

<script setup>
    import List from '@/components/List'

    const getData = val => {
        console.log(val)
    }
</script>

子组件:

<template>
    <div>这是子组件 ===> {{ num }}</div>
    <button @click='btn'>按钮</button>
</template>

<script setup>
const props = defineProps({
    num:{
        type: Number,
        default: 0
    }
})
const emit = defineEmits(['update:num'])
const btn = ()=>{
    emit('update:num', 200)
}
</script>
2.10.4 兄弟传值

父组件:

<template>
    <A @fn='changeFn'/>
    <B/>
</template>

<script>
import 'A' from '@/components/A'
import 'B' from '@/components/B'

let str = ref('')
let changeFn = val => {
    str.value = val.value
}
</script>

兄弟组件A(发送数据)

<template>
    <h1>组件A</h1>
    <button @click='btn'>按钮</button>
</template>

<script setup>
let str = ref('组件A中的数据')
const emit = defineEmits(['fn'])
const btn = () => {
    emit('fn', str)
}
</script>

兄弟组件B(接收数据)

<template>
    <h1>组件B ===> {{ str }}</h1>
    <button @click='btn'>按钮</button>
</template>
<script setup>
const props = defineProps({
    str: {
    type: String,
    default: '123'
    }
})
</script>

兄弟组件使用mitt传值

  • 下载mitt

    npm i mitt -S

  • 配置文件(Bus.js)

    import mitt from 'mitt'
    
    const emmiter = mitt()
    
    export default emmiter
    
  • 兄弟组件A(发送数据)

    <template>
        <h1>A组件</h1>
        <button @click='btn'>按钮</button>
    </template>
    
    <script setup>
    import emitter from '@/utils/Bus.js'
    
    let str = ref('A组件传数据')
    const btn = () => {
        emitter.emit('fn', str)
    }
    </script>
    
  • 兄弟组件B(接收数据)

    <template>
        <h1>B组件 ===> {{ msg }}</h1>
    </template>
    
    <script setup>
    import emitter from '@/utils/Bus.js'
    
    let msg = ref('')
    onBeforeMount(() => {
        emitter.on('fn', e => {
            msg.value = e.value
        })
    })
    </script>
    

3. 生命周期

3.1 选项式API(与Vue2.x用法基本类似)
3.2 setup组合式API
选项式 APIHook inside setup
beforeCreateNot needed*
createdNot needed*
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered
activatedonActivated
deactivatedonDeactivated

4. 插槽

4.1 匿名插槽

父组件:

<template>
    <A>
        <template>这是xxxx数据</template>
        <template>这是yyyy数据</template>
    </A>
</template>

子组件:

<template>
    <header>
        <div>头部</div>
        <slot></slot>
    </header>
    <footer>
        <div>底部</div>
        <slot></slot>
    </footer>
</template>
4.2 具名插槽

父组件:

<template>
    <A>
        <!--<template v-slot:xxx>这是xxxx数据</template>
        <template v-slot:yyy>这是yyyy数据</template>-->
        <!--简写-->
        <template #xxx>这是xxxx数据</template>
        <template #yyy>这是yyyy数据</template>
    </A>
</template>

子组件:

<template>
    <header>
        <div>头部</div>
        <slot name='xxx'></slot>
    </header>
    <footer>
        <div>底部</div>
        <slot name='yyy'></slot>
    </footer>
</template>
4.3 作用域插槽

父组件:

<template>
    <A>
        <!--<template v-slot='{data}'>-->
        <template #default='{data}'>
            {{ data.name }} --->{{ data.age }}
        </template>
    </A>
</template>

子组件:

<template>
   <div v-for='item in list' :key='item.id'>
       <slot :data='item'></slot>
   </div>     
</template>

<script setup>
let list = ref({
    {id: 1, name: '张三', age: 20},
    {id: 2, name: '李四', age: 25},
    {id: 3, name: '王五', age: 20},
    {id: 4, name: '赵六', age: 18}
})
</script>
4.4 动态插槽

通过数据进行切换

<template>
   <template #[hw]>Hello world!</template>
</template>

<script setup>
    let hw = ref('hw')
</script>

5. Teleport 【传送】

  • 传送给id选择器

    <teleport to='#container' />

  • 传递给class选择器

    <teleport to='.class' />

  • 传递给标签选择器

    <teleport to='body' />

    传送之前,必须要声明出对应的选择器

6. 动态组件

<!--失活的组件将会被缓存!-->
<keep-alive>
    <component :is="currentTabComponent"></component>
</keep-alive>

7. 异步组件

7.1 使用场景1

组件按需引入:当用户访问到了组件再去加载该组件

下载vueuse插件npm i @vueuse/core -S

<template>
    <div ref='target'>
        <C v-if='targetIsVisible'></C>
    </div>
</template>
<script setup>
import { useIntersectionObserver } from '@vueuse/core'

const C = defineAsyncComponent(() =>
  import('@/components/C.vue')
)

const target = ref(null)
const targetIsVisible = ref(false)

const { stop } = useIntersectionObserver(
    target,
    ([{ isIntersecting }] => {
        if( isIntersecting ) {
            targetIsVisible.value = isIntersecting
        }
    })
)
</script>
7.2 使用场景2

异步组件在默认情况下是可挂起的。这意味着如果它在父链中有一个 <Suspense>,它将被视为该 <Suspense> 的异步依赖。在这种情况下,加载状态将由 <Suspense> 控制,组件自身的加载、错误、延迟和超时选项都将被忽略。通过在其选项中指定 suspensible: false,异步组件可以退出 Suspense 控制,并始终控制自己的加载状态

<template>
    <Suspense>
        <template #default><A/></template>
        <template #fallback>加载中</template>
    </Suspense>
</template>
<script setup>
const A = defineAsyncComponent(() =>
  import('@/components/A.vue')
)
</script>
7.3 打包时,分包处理

npm run build打包完成后,异步组件有单独的js文件,是从主体js包中分离出来的。

8. Mixin

是什么?来分发Vue组件中的可复用功能

8.1 setup写法

mixin.js

import { ref } from 'vue'

export default function() {
    let num = ref(1)
    let fav = ref(false)

    let favBtn = () => {
        num.value += 1
        fav.value = true
        setTimeout(() => {
           fav.value = false
        },2000)
    }
    return {num, fav, favBtn}
}

组件A:

<template>
    <div>
        <h1>A组件</h1>
        {{ num }}
        <button @click='favBtn'>{{ fav ? '收藏中...' : '收藏' }}</button>
    </div>
</template>

<script setup>
import mixin from '@/mixin/mixin.js'

let {num, fav, favBtn} = mixin()
</script>

组件B:

<template>
    <div>
        <h1>B组件</h1>
        {{ num }}
        <button @click='favBtn'>{{ fav ? '收藏中...' : '收藏' }}</button>
    </div>
</template>

<script setup>
import mixin from '@/mixin/mixin.js'

let {num, fav, favBtn} = mixin()
</script>
8.2 选项式API写法

mixin.js

export const fav = {
    data() {
       return {
           num: 1
        } 
    },
    methods: {
        favBtn(params) {
            this.num += params
        }
    }
}

A组件:

<template>
    <div>
        <h1>A组件</h1>
        {{ num }}
        <button @click='favBtn(2)'>按钮</button>
    </div>
</template>

<script>
import mixin from '@/mixin/mixin.js'

export const fav = {
    data() {
       return {
           str: 'Hello vue!'
        } 
    },
    mixin: [fav]
}
</script>

B组件:

<template>
    <div>
        <h1>B组件</h1>
        {{ num }}
        <button @click='favBtn(1)'>按钮</button>
    </div>
</template>

<script>
import mixin from '@/mixin/mixin.js'

export const fav = {
    data() {
       return {
           str: 'Hello vue!'
        } 
    },
    mixin: [fav]
}
</script>

9. provide和Inject 依赖、注入

通常,当我们需要从父组件向子组件传递数据时,我们使用 props。想象一下这样的结构:有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递下去,可能会很麻烦。

对于这种情况,我们可以使用一对 provide 和 inject。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。

Provide/inject scheme

provide:

<script setup>
    provide('changeNum', num)
</script>

inject:

<template>
    <h1>{{num}}</h1>
</template>
<script setup>
    const num = inject('changeNum')
</script>

10. Vuex

11. pinia

pinia与Vuex的区别
  1. 支持选项式api和组合式api写法
  2. pinia没有mutations,只有:state、getters、actions
  3. pinia分模块不需要modules(之前vuex分模块需要modules)
  4. TypeScript支持很好
  5. 自动化代码拆分
  6. pinia体积更小(性能更好)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lucky赵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值