vue3核心面试题
vue3
vue3比vue2有什么优势?
性能更好
体积更小
更好的ts支持
更好的代码组织
更好的逻辑抽离
更多新功能
Composition API (vue3)和Options API(vue2)的生命周期变化
Composition API
//等于beforeCreate和created
setup() {
console.log('setup')
onBeforeMount(() => {
console.log('onBeforeMount')
})
onMounted(() => {
console.log('onMounted')
})
onBeforeUpdate(() => {
console.log('onBeforeUpdate')
})
onUpdated(() => {
console.log('onUpdated')
})
onBerforeUnmount(() => {
console.log('onBerforeUnmount')
})
onUnmounted(() => {
console.log('onUnmounted')
})
}
Options API
beforeDestroy改为beforeUnmount
destoyed改为Unmouted
其他沿用Vue2的生命周期
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
},
beforeMount() {
console.log('beforeMount')
},
mount() {
console.log('mount')
},
beforeUpdate() {
console.log('beforeUpdate')
},
Update() {
console.log('Update')
},
//beforeDestroy 改名
beforeUnmount() {
console.log('beforeUnmount')
}
//destroy 改名
unmounted() {
console.log('unmounted')
}
Composition API带来了什么?
更好的代码组织
更好的逻辑复用
更好的类型推导
Composition API 和Options API如何选择?
不建议共用,会引用混乱
小型项目、业务逻辑简单,用Options API
中小型项目、逻辑复杂,用Composition API
别误解Composition API
属于高阶技巧,不是必会技能
ref toRef 和toRefs
是什么
最佳使用方式
进阶,深入理解
ref
生成值类型和响应式数据
可用于模板和reactive
通过.value修改值
<template>
<div>{{ageRef}} {{state.value}}</div>
</template>
name: 'Ref'
setup() {
const ageRef = ref(20) //值类型,响应式
const nameRef = ref('张三')
const state = reactive({//响应式数据
name: nameRef
})
return {
ageRef,
state
}
}
<template>
<p ref= "elemRef">我是一行文字</p>
</template>
<script>
import {ref, onMounted} from 'vue'
export default {
name: 'RefTemplate',
setup() {
const elemRef = ref(null)
onMounted(() => {
console.log('ref',elemRef.value.innerHTML,elemRef.value)
})
}
}
</script>
toRef
针对一个响应式对象(reactive封装)的prop
创建一个ref,具有响应式
两者保持引用关系
一个对象想实现响应式就用reactive就行了,对象的一个属性要实现响应式就用toRef(如果对于普通对象产出结果不具备响应式)
<template>
<p>{{state.age}} {{ageRef}}
</template>
<script>
setup() {
const state = reactive({
age: 20,
name: '张三'
})
coonst ageRef = toRef(state,'age')
setTimeout(() => {
state.age = 25
},1500)
setTimeout(() => {
ageRef.value = 30 //.value 修改值
},3000)
return {
state,
ageRef
}
}
toRefs
将响应式对象(reactive封装)转换为普通对象
对象的每个prop都是对应的ref
两个保持引用关系
<template>
<p>{{age}} {{name}}
</template>
<script>
import {ref, toRef, toRefs, reactive} from 'vue'
setup() {
const state = reactive({
age: 20,
name: '双月'
})
const stateAsRefs = toRefs(state) //将响应式对象,变成普通对象
setTimeout(() => {
state.age = 25
}, 1500)
return stateAsRefs
}
合成函数返回响应式对象
function useFeatureX() {
const state = reactive({
x: 1,
y: 2
})
return toRefs(state)//返回普通对象的所有响应式属性
}
最佳使用方式
用reactive做对象的响应式,用ref做值类型响应式
setup中返回toRefs(state),或者toRef(state,'xxx')
ref的变量命名都用xxxRef
合成函数返回响应式对象,使用toRefs
进阶,深入理解
为何需要ref?
为何需要.value?
为何需要toRef toRefs
Proxy的响应式只针对对象类型,不针对值类型
返回值类型时,会丢失响应式
如在setup、computed、合成函数,都有可能返回值类型
Vue如不定义ref,用户将自造ref,反而会更乱
为何需要.value
ref是一个对象(不丢失响应式),value存储值
通过.value属性的get和set实现响应式
function computed(getter) {
const ref = {
value: null
}
setTimeout(() => {
ref.value = getter()
},1500)
retrun ref
}
用于模板、reactive时,不需要.value,其他情况都需要
为何需要toRef和toRefs
初衷:不丢失响应式的情况下,把对象数据分散/扩散
前提针对的是响应式对象(reactive封装的)非普通对象
注意:不创造响应式,而是延续响应式
emits属性
//父组件定义事件
<HelloWorld :msg="msg" @onSayHello="sayHello"/>
子组件传参
emits: ['onSayHello'],
setup(props,{emit}) {
emit('onSayHello','bbb')
}
多事件处理
<button @click="one($event),two($event)">
Sumit
</button>
Fragment去掉单一模板
//vue2.xx
<template>
<div class="blog-post">
<h3>{{title}}</h3>
<div v-html="content"></div>
</div>
</template>
//vue3
<template>
<h3>{{title}}</h3>
<div v-html="content"></div>
</template>
移除.sync
//2.xx
<MyComponent v-bind:title.sync="title"/>
//3.xx
<MyComponet v-model:title="title"/>
移除filter
{{message | capitalize}}
<div v-bind:id="rawId | formatId"></div>
Suspense具名插槽
<Suspense>
<template>
</Test1>
</template>
具名插槽
<template #fallback>
Loading
</template>
</Suspense>
Teleport弹窗
data中设置modalOpen:false
<button @click="modalOpen = true">
open
</button>
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
telePort弹窗(父元素是body)
<button @click="modalOpen = false">Close</button>
</div>
</div>
<teleport>
异步组件
//2.xxx
new Vue({
//..
components: {
'my-component': () => import('./my-async-component vue')}
})
//3.xx
import {createApp, defineAsyncComponent} from 'vue'
createAPP({
components: {
AsyncComponent: defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
}
})
Composition API 实现逻辑服用
抽离逻辑代码到一个函数
函数命名约定为useXxx格式
在setup中应用useXxx
import {ref, onMounted, onUnmounted} from 'vue'
function useMousePosition() {
const x = ref(0)
const y = ref(0)
function update(e) {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => {
const.log('useMousePosition mounted')
window.addEventListener('mouse',update)
})
onUnmounted(() => {
const.log('useMousePosition unMounted')
window.removeEventListener('mouse',update)//移除事件绑定,防止内存泄漏
})
return {
x,
y
}
}
<template>
<p>mouse position {{x}} {{y}}</p>
</template>
import {reactive} from 'vue'
import useMousePosition from './useMousePosition'
export default {
name: 'MousePosition',
setup() {
const {x, y} = useMousePosition()
return {
x,
y
}
}
}
<MousePosition v-if="flag"/>//v-if为false自动调用逻辑函数里面onUnmounted进行解绑
Object.defineProperty的缺点
深度监听需要一次性递归
无法监听新增属性/删除属性(Vue.set Vue.delete)
无法原生监听数组,需要特殊处理
Proxy基本使用
const data = {
name: 'zhangsan',
age: 20
}
const proxyData = new Proxy(data, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
console.log('get', key)
return result //返回结果
},
set(target, key, val, receiver) {
const result = Reflect.set(target, key, val receiver)
console.log('set', key,val)
return result //是否设置成功
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('deleteProperty', key)
return result //是否删除成功
}
})
Proxy是什么时候get就往下递归一层,不会一次性递归完
v-model用法
//子组件
<template>
<input :value="name" @input="$emit('update:name',$event.target.value)"//就是父组件传参数和方法,子组件返回参数
<input :value="age" @input="$emit('update:age',$event.target.value)"
</template>
export default {
name: 'UserInfo',
props: {
name: String,
age: String
}
}
<template>
<user-info
v-model:name="name"
v-model:age="age"
>
<user-info>
</template>
setup() {
const state = reactive({
name: '双月',
age:20
})
return toRefs(state)
}
watch和watchEffect的区别
两者都可监听data属性变化
watch需要明确需要监听哪个属性
watchEffect会根据其中的属性,自动监听其变化
watchEffect初始化会自动监听,为的就是收集监听属性
watch
setup() {
const numberRef = ref(100)
const state = reactive({
name: '双月'
age: 20
})
watch(numberRef, (newNumber, oldNumber) => {
console.log('ref watch',newNumber, oldNumber)
},
{
immediate: true//初始化之前就监听,可选
}
)
watch(
//第一个参数,确定要监听哪个属性
() => state.age,
//第二个参数,回调函数
(newAge, oldAge) => {
console.log('state watch', newAge,oldAge)
},
//第三个参数,配置项
{
immediate: true,
deep: true//深度监听
}
)
watchEffect(() => {
//初始化时,一定回收集一次,收集要监听的数据
console.log('state.name',state.name)//age没有收集到,即使变化也监听不到
})
setTimeout(() => {
state.age = 25
},1500)
setTimeout(() => {
state.age = '双鱼'
},3000)
}
setup中如何获取组件实例
Options API(使用this)和Composition API(使用getCurrentInstance)
data() {
return {
x: 1,
y: 2
}
},
setup() {
console.log('this1',this)//undefined
onMounted(() => {
console.log('this in onMunted',this)//undefined
})
const instance = getCurrentInstance()//Composition
console.log('instance',instance)
},
mounted() {
console.log('this2', this)//Options
console.log('y',this.y)
}
vue3为什么比vue2快
patchFlag
编译模板时,动态节点做标记
标记,分为不同的类型,如TEXT PROPS
diff算法时,可以区分静态节点,以及不同类型的动态节点
hositStatic
将静态结点的定义提升到父作用域,缓存起来
多个相邻的静态系欸但,会被合并起来
典型的拿空间换时间的优化策略
CacheHandler
缓存事件
SSR
静态节点直接输出,绕过了vdom
动态节点,还是需要动态渲染
tree shaking
编译时,根据不同情况的情况,引入不同的API,import引入减少
vite是什么
一个前端打包工具,Vue作者发起的项目
借助Vue的影响力,发展较快,和webpack竞争
优势:开发环境下无需打包、启动快
vite为何启动快
开发环境使用ES6 Module, 无需打包---------非常快
<script type="module">
import add from './src/add.js'
const res = add(10,20)
console.log('add res', res)
</script>
ESModule在浏览器中的应用
<p>基本演示</p>
<script type="module">
import add from './src/add.js'
const res = add(1, 2)
console.log('add res', res)
</script>
<script>
import {add, multi} from './src/math.js'
console.log('add res',add(10, 20))
console.log('multi res',multi(10, 20))
</script>
<body>
<p>外链</p>
<script type="module" src="./src/index.js"></script>
</body>
<p>远程引用</p>
<script type="module">
import {createStore} from 'https:xxxx'
</script>
<p>动态引入</p>
<script type="module">
document.getElementById('btn1').addEventListener('click',async () => {
const add = await import('./src/add.js')
const res = add.default(1, 2)
console.log(res)
})
document.getElementById('btn2').addEventListener('click',async () => {
const {add, multi} = await import('./src/math.js')
console.log(add(1, 2))
console.log(multi(1, 2))
}
</script>
Vue3和JSX
Vue3中基本应用
使用.js格式文件和defineComponent
引用自定义组件,传递属性
setup() {
const conRef = ref(200)
const render = () => {
return <p>dome1 {countRef.value}</p>
}
return render
}
JSX和template的区别
JSX本质就是JS代码,可以使用js的人和能力
template只能嵌入简单的js表达式,其他需要指令,如v-if
JSX已经成为ES规范,template还是Vue自家规范
JSX和template本质相同
都会被编译成js代码(render函数)
JSX和slot(体会JSX优越性)
script-setup使用方式更改
基本使用,<script>写在<template>前面易读性好
定义属性defineProps,定义事件defineEmits
defineExpose暴露数据给父组件
<script setup>
import {ref,reactive,toRefs} from 'vue'
import Child from './Child'
const countRef = ref(100)
function addCount() {
countRef.value++
}
const state = reactive({
name: '双月'
})
const {name} = toRefs(state)
</script>
<template>
<p @click="addCount">{{countRef}}</p>
<p>{{name}}</p>
<child></child>
</template>
script-setup定义属性和定义事件的方式改变
defineProps
defineEmits
<script setup>
import {defineProps, defineEmits} from 'vue'
//定义属性
const props = defineProps({
name: String,
age: Number
})
//定义事件
const emit = defineEmits(['change','delete'])
function deleteHandle() {
emit('delete' 'aaa')
}
</script>
<template>
<p>name:{{props.name}},age: {{props.age}}</p>
<button @click="$emit('change','bbb')">change</button>
<button @click="deleteHandler">delete</button>
</template>
使用
<script setup>
const { name } = toRefs(state)
function onChange(info) {
console.log('on change',info)
}
function onDelete(info) {
console.log('on delete',info)
}
</script>
<template>
<child-2 :name="name" :age="countRef" @change="onChange" @delete="onDelete">
</template>
defineExpose暴露数据给父组件
子组件
<script setup>
import { res, defineExpose} from 'vue'
const a = ref(101)
const b = 201
defineExpose{
a,
b
}
</script>
<template>
<p>Child3</p>
</template>
父组件
const child3Ref = ref(null)
onMounted(() => {
console.log(child3Ref.value)
console.log(child3Ref.value.a)
console.log(child3Ref.value.b)
})
<template>
<child-3 ref="childRef"></child-3>
</template>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报