vue3知识点汇总

vue3比vue2有什么优势

  • 性能更好
  • 体积更小
  • 更好的ts支持
  • 更好的代码组织(不同业务逻辑的代码在不同区域)
  • 更好的逻辑抽离(因为不同逻辑的代码区域不混乱因此更好的逻辑抽离)
  • 更多新功能

描述vue3生命周期

Options API生命周期

  • beforeDestroy改为beforeUnmount
  • destroyed改为unmouted
  • 其他沿用vue2的生命周期

Composition API生命周期

  • 各生命周期调用时,要先从vue中引入,写在setup中
  • setup等于beforeCreate和created
  • setup里的生命周期写法:onBeforeMount(()=>{})
    • onBeforeMount
    • onMounted
    • onBeforeUpdate
    • OnUpdated
    • onBeforeUnmount
    • onUmounted

如何看待Composition API和Options API

Composition API带来了什么

  • 更好的代码组织(抽离逻辑到函数,统一setup中调用再返回出去)
  • 更好的逻辑复用
  • 更好的类型推导( 更符合js语法,不使用this.fn()语法糖调用methods里的方法)

Composition API和Options API如何选择

  • 不建议共用,会引起混乱
  • 小型项目、业务逻辑简单,用Options API
  • 中大型项目,逻辑复杂,用Composition API

别误解Composition API,应该按需使用

  • Composition API属于高阶技巧,不是基础必会
  • Composition API是为解决复杂业务逻辑而设计的
  • Composition API就像Hooks在React中的地位

如何理解ref、toRef和toRefs

是什么

ref
<p>{{ageRef}}-{{state.name}}</p>
<!-- 可以用于模板 -->
<p ref="elemRef"></p>
// 先从vue中引入
import {ref,reactive, onMounted} = from 'vue'
setup(){
    const ageRef =  ref(20)
    const nameRef = ref('小洪')
    const elemRef = ref(null)
    const state = reactive({
     name:nameRef
    })
    onMounted(()=>{
     console.log('ref template',elemRef.value.innerHTml)
    })
    return {
     state,
     ageRef,
     eleRef
   }
}
  • ref生成值类型的响应式数据,reactive创建引用类型的响应式数据,ref的本质是通过reactive创建的,Ref(10)=>Reactive({value:10})
  • 可用于模板和reactive
  • 通过.value修改值
    • 除了在模版或者在reactive可以直接用,其他地方修改要用ageRef.value的方式修改
toRef
<p>{{ageRef}}-{{state.age}}-{{state.name}}</p>
const state = reactive({
    age: 20,
    name: '小洪'
})
const ageRef = roRef(state, 'age')
setTimeout(() => {
    state.age = 25
},1500)
setTimeout(() => {
    ageRef.value = 30
})
  • 针对一个响应式对象(reactive封装)的prop
  • 创建一个ref,具有响应式
    • toRef 如果用于普通对象(非响应式对象,如上例子不用reactive包裹),产出的结果不具备响应式,即1.5s后模版内容不会发生变化
    • 一个对象要实现响应式的时候用reactive即可
      一个对象里的某个属性要实现响应式用toRef
  • 两者保持引用关系
toRefs
  • 将响应式对象(reactive封装)转换为普通对象
    • 如果不用toRefs转换,响应式对象里的属性直接解构return到模版将失去响应式
  • 对象的每个prop都是对应的ref
  • 两者保持引用关系
最佳使用方式
  • 用reactive做对象的响应式,用ref做值类型响应式
  • setup中返回toRefs(state)或toRef(state,‘xxx’)
  • ref的变量名都用xxxRef
  • 合成函数返回响应式对象,使用toRefs,方便使用解构
function useFeatureX() {
    const state =  reactive({
        age: 20,
        name: '小洪'
    })
    // 返回数据为ref
    return toRefs(state)
}
export default {
    name: 'WhyRef'
    setup(){
        // 可以在不失去响应式的情况下破坏结构
        const {age , name} =  useFeatureX()
    }
}

进阶、深入理解

为什么需要ref
  • 返回值类型,会丢失响应式,proxy只对对象起作用
  • 如在setup、computed、合成函数,都有可能返回值类型
  • vue如不定义ref,用户将自造ref,反而混乱
为何ref需要value属性
  • ref是一个对象(不丢失响应式),value存储值
  • 通过.value属性的get和set实现响应式
  • 用于模版,reactive时,不需要.value,其他情况都需要
为什么需要toRef和toRefs
  • 初衷:不丢失响应式的情况下,把对象进行分解 /扩散,即结构
  • 前提:针对的是响应式对象(reactive封装的),非普通对象
  • 注意:不创造响应式,而是延续响应式

vue3升级了哪些重要功能

createApp

vue2

const app =new Vue({/*选项*/})
Vue.use(/*…*/)
Vue.mixin(/*…*/)

vue3

const app = Vue.CreateApp({/*选项*/})
app.use(/*…*/)
app.mixin(/*…*/)

emits属性

<!-- 父组件 -->
<HelloWorld :msg="msg" @onSayHello="sayHello"></HelloWorld>
<!-- 子组件 -->
export default {
    name: 'HelloWorld'
    props: {
        msg: String
    },
    emits:['onSayHello']
    setup(props, { emit }){
        emit('onSayHello','aaa')
    }
}
  • 父组件引入子组件,传入事件时,子组件要用emits:[‘onSayHello’]接收,事件要用on开头
  • setup的第二个参数接收{emits}
  • 通过emit(‘onSayHello’,’aaa’)调用

生命周期

多事件

  • 在methods里定义one two两个函数
    • 如@click=“one( e v e n t ) , t w o ( event),two( event),two(event)”

Frament

vue2 Template里只能有一个节点

vue3 可以有多个节点

移除.sync

vue2

v-bind:title.sync="title"

vue3改为

v-model:title="title"

异步组件的写法

vue2

new Vue({
    components: {
        'my-component': () => import('./my-component.vue')
    }
})

vue3

import { createApp, defineAsyncComponent } from 'vue'
createApp({
    AsyncComponent: defineAsyncComponent(() => {
        import('./my-component.vue')
    })
})

移除filter

<!-- 以下filter在vue3中不可用了 -->
<!-- 在双花括号中 --> 
{{ message | capitalize }} 
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

Teleport

把组件放到外面

<button @click="modalOpen = true">
    open modal button
</button>

<teleport to="body">
    <div v-if="modalOpen" class="modal">
        <div>
            teleport dialog
            <button @click="modalOpen = false">close</button>
        </div>
    </div>
</teleport>

Suspense

相当于封装了一个插槽给我们用

<Suspense>
    <template>
        <!-- 是一个异步组件 -->
        <Test1/>
    </template>
    <!-- #fallback 就是一个具名插槽。
         即 Suspense组件内部,有两个slot,
         其中一个具名为fallback -->
    <template #fallback>
        loading...
    </template>
</Suspense>

Composition API

  • reactive
  • ref相关
  • readonly
  • watch和watchEffect
  • setup
  • 生命周期钩子函数

Composition API如何实现代码逻辑复用

  • 抽离逻辑代码到一个函数
    • 比如说抽离获取当前鼠标坐标函数
  • 函数命名约定为useXxxx格式(React Hooks 也是)
  • 在setup中引用useXxxx函数

vue3如何实现响应式

回顾Vue2.x的Object.defineProperty

// 触发更新视图
function updateView() {
    console.log('视图更新')
}
// 重新定义数组原型
const oldArrayProperty = Array.prototype
// 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
    arrProto[methodName] = function () {
        updateView() // 触发视图更新
        oldArrayProperty[methodName].call(this, ...arguments)
        // Array.prototype.push.call(this, ...arguments)
    }
})
// 重新定义属性,监听起来
function defineReactive(target, key, value) {
    // 深度监听
    observer(value)
    // 核心 API
    Object.defineProperty(target, key, {
        get() {
            return value
        },
        set(newValue) {
            if (newValue !== value) {
                // 深度监听
                observer(newValue)
                // 设置新值
                // 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
                value = newValue
                // 触发更新视图
                updateView()
            }
        }
    })
}
// 监听对象属性
function observer(target) {
    if (typeof target !== 'object' || target === null) {
        // 不是对象或数组
        return target
    }
    // 污染全局的 Array 原型
    // Array.prototype.push = function () {
    //     updateView()
    //     ...
    // }
    if (Array.isArray(target)) {
        target.__proto__ = arrProto
    }
    // 重新定义各个属性(for in 也可以遍历数组)
    for (let key in target) {
        defineReactive(target, key, target[key])
    }
}
// 准备数据
const data = {
    name: 'zhangsan',
    age: 20,
    info: {
        address: '北京' // 需要深度监听
    },
    nums: [10, 20, 30]
}
// 监听数据
observer(data)
// 测试
// data.name = 'lisi'
// data.age = 21
// // console.log('age', data.age)
// data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set
// delete data.name // 删除属性,监听不到 —— 所有已 Vue.delete
// data.info.address = '上海' // 深度监听
data.nums.push(4) // 监听数组

缺点:

  • 深度监听需要一次性递归,如果data比较长的话会造成卡住
  • 无法监听新增属性/删除属性(需要用Vue.set Vue.delete)
  • 无法原生监听数组,需要特殊处理

Proxy基本使用

// const data = {
//     name: 'zhangsan',
//     age: 20,
// }
const data = ['a', 'b', 'c']
const proxyData = new Proxy(data, {
    get(target, key, receiver) {
        // 只处理本身(非原型的)属性
        const ownKeys = Reflect.ownKeys(target)
        if (ownKeys.includes(key)) {
            console.log('get', key) // 监听
        }
        const result = Reflect.get(target, key, receiver)
        return result // 返回结果
    },
    set(target, key, val, receiver) {
        // 重复的数据,不处理
        if (val === target[key]) {
            return true
        }
        const result = Reflect.set(target, key, val, receiver)
        console.log('set', key, val)
        // console.log('result', result) // true
        return result // 是否设置成功
    }, 
    deleteProperty(target, key) {
        const result = Reflect.deleteProperty(target, key)
        console.log('delete property', key)
        // console.log('result', result) // true
        return result // 是否删除成功
    }

})

Reflect作用

  • 和Proxy能力一一对应
  • 规范化、标准化、函数式 如:Reflect.has(obj,‘a’)比 js的 ‘a’ in obj好用
  • 替代掉Object上的工具函数 如Reflect.ownKeys(obj)替代Object.getOwnPropertyNames(obj)

Vue3如何用proxy实现响应式

// 创建响应式
function reactive(target = {}) {
    if (typeof target !== 'object' || target == null) {
        // 不是对象或数组,则返回
        return target
    }
    // 代理配置
    const proxyConf = {
        get(target, key, receiver) {
            // 只处理本身(非原型的)属性
            const ownKeys = Reflect.ownKeys(target)
            if (ownKeys.includes(key)) {
                console.log('get', key) // 监听
            }
            const result = Reflect.get(target, key, receiver)
            // 深度监听
            // 性能如何提升的?什么时候用get到那一层再递归到那一层级,因此性能提升 
            return reactive(result)
        },
        set(target, key, val, receiver) {
            // 重复的数据,不处理
            if (val === target[key]) {
                return true
            }
            const ownKeys = Reflect.ownKeys(target)
            if (ownKeys.includes(key)) {
                console.log('已有的 key', key)
            } else {
                console.log('新增的 key', key)
            }
            const result = Reflect.set(target, key, val, receiver)
            console.log('set', key, val)
            // console.log('result', result) // true
            return result // 是否设置成功
        },
        deleteProperty(target, key) {
            const result = Reflect.deleteProperty(target, key)
            console.log('delete property', key)
            // console.log('result', result) // true
            return result // 是否删除成功
        }
    }
    // 生成代理对象
    const observed = new Proxy(target, proxyConf)
    return observed
}
// 测试数据
const data = {
    name: 'zhangsan',
    age: 20,
    info: {
        city: 'beijing',
        a: {
            b: {
                c: {
                    d: {
                        e: 100
                    }
                }
            }
        }
    }
}
const proxyData = reactive(data)

Proxy实现响应式优点

  • 深度监听,性能更好
  • 可监听 新增/删除 属性
  • 可监听数组变化
    总结:
  • Proxy能规避Object.defineProperty的问题
  • Proxy无法兼容所有浏览器,无法ployfill(webpack知识)

watch和watchEffect的区别是什么

  • 两者都可监听data属性变化
  • watch需要明确监听哪个属性
watch(numberRef, (newNumber, oldNumber) => {
    console.log('ref watch', newNumber, oldNumber)
}
// , { // 配置项
//     immediate: true // 初始化之前就监听,可选参数
//     deep: true // 是否深度监听,可选参数
// }
)
  • watchEffect会根据其中的属性,自动监听其变化
watchEffect(() => {
        // 初始化时,一定会执行一次(收集要监听的数据)
        console.log('hello watchEffect')
    })
    watchEffect(() => {
        console.log('state.name', state.name)
    })
    watchEffect(() => {
        console.log('state.age', state.age)
    })
    watchEffect(() => {
        console.log('state.age', state.age)
        console.log('state.name', state.name)
})

setup中如何获取组件实例

  • 在setup和其他Composition API中没有this
  • 可通过getCurrentInstance获取当前实例,代替this
  • 若使用Options API可照常使用this
const instance = getCurrentInstance
console.log('instance', instance)

vue3为何比vue2快

Proxy响应式

PatchFlag

  • 编译模板时,动态节点做标记
  • 标记,分为不同的类型,如TEXT PROPS
  • diff算法,可以区分静态节点,以及不同类型的动态节点

hoistStatic

  • 将静态节点的定义,提升到父作用域,缓存起来
  • 多个相邻的静态节点,会被合并起来
  • 典型的拿空间换时间的优化策略

cacheHandler

  • 缓存事件

SSR优化

  • 静态节点直接输出,绕过了vdom
  • 动态节点,还是需要动态渲染

tree-shaking

  • 编译时,根据不同的情况,引入不同的API

vite是什么

是什么

  • 一个前端打包工具,vue作者发起的项目
  • 借助vue的影响力,发展较快,和webpack竞争
  • 优势:开发环境下无需打包、启动快

为何启动快

  • 开发环境使用ES6 Module,无需打包——非常快
  • 生产环境使用rollup,并不会快很多

Composition API和React Hooks的对比

  • 前者setup只会被调用一次,而后者函数会被多次调用(本质)
  • 前者无需useMemo useCallback,因为setup只调用一次(表象)
  • 前者无需顾虑调用顺序,而后者需要保证hooks的顺序一致(表象)
  • 前者reactive+ref比后者useState,更难理解
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值