1. Vue2与Vue3的区别
1.1 v-if
与v-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.x | Vue3.x |
---|---|
this.$route | useRoute |
this.$router | useRouter |
-
导航守卫
-
动态路由
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
选项式 API | Hook inside setup |
---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
activated | onActivated |
deactivated | onDeactivated |
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:
<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的区别
- 支持选项式api和组合式api写法
- pinia没有mutations,只有:state、getters、actions
- pinia分模块不需要modules(之前vuex分模块需要modules)
- TypeScript支持很好
- 自动化代码拆分
- pinia体积更小(性能更好)