vue3的一些知识点

都知道Vue升级Vue3 好,到底好在哪里呢?【性能更好、体积更小、更好ts支持、更好逻辑抽离】
说Vue3性能比Vue2好、体积更小(打包大小减少 41%,初次渲染快 55%,更新快 133%,内存使用减少 54%)到底性能怎么好呢?
下面将揭开上面两个问题神秘面纱。

一、响应式原理*

响应式是Vue3的一大重要升级, 也是Vue3性能好的一大方面,对比学习一下就会发现Vue3的响应式原理为什么好。

1、defineProperty模拟实现响应式
//  初始化数据
const data = {
    name:  '张三',
    info: {
        age:  '30',
        address: '深圳'
    },
   years: [20101, 2015, 2021]
}
let arrProto = null //  处理数组时候用的
// 监听数据
observer(data)
//  监听对象属性
function observer(target) {
   if (typeof target !== 'object' || target === null)  { // 不是对象或数组
        return target
    }
    // 如果是数组, 因为defineProperty不能坚听数组的变化,这也是其一大缺点
    if (Array.isArray(target)) {
        target.__proto__ = arrProto
    }
    // 重新定义各个属性(for in 也可以遍历数组)
    for (let key in traget) {
        defineReactive(target, key, target[key])
    }
}
function defineReactive(target, key, value) {
   // 深度监听 , 最开始一上来不管用没用到数据 全部变成响应式
  observer(value)
  // 核心 API
  Object.defineProperty(target, key, {
        get()  {
            return value
        },
       set(newValue)  {
           if (newValue !== value) {
              // 设置新值
              value = newValue
            // 深度监听  如果新赋的值 又得变成响应式
             observer(newValue)
              //   触发更行
              updateView()
          }
      }
  })
}
// 触发更新
function updateView () {
    console.log('视图更新')
}
// 重新定义数组原型
const oldArrayProperty = Array.prototype
// 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
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)
    }
})
1、proxy模拟实现响应式
// 初始化数据
const data = {
    name:  '张三',
    info: {
        age:  '30',
        address: '深圳'
    },
   years: [20101, 2015, 2021]
}
const proxyData = observe(data) // 返回响应式数据
// 创建响应式
function observe (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)
            // 深度监听
            // 性能如何提升的?用的时候才把其变成响应式
            return observe(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 observeData = new Proxy(target,proxyConf)
  return observeData
}
总结: 1、defineProperty 深度监听, 需要一次性递归到底, 性能不好
      2、不能监听原生数组变化, 需要重新定义数组的原型
      3、不能监听对象新增或删除属性---所以才有Vue.set 和 Vue.delete
但Vue3完美解决上面的问题【Proxy是在get的时候 才会变成响应式】

二、生命周期【Options API 和 Composition API】*

vue 生命周期.png

三、理解ref、toRef、toRefs*

1、ref: 生成值类型的响应式数据、可用于模板和reactive、通过.value修改值

export default {
      setup () {
          const ageRef = ref(20) // 值类型、响应式
          const nameRef = ref('terry')
         // 建议命名规范都通过xxRef结尾 --- 习惯问题
          const state = reactive({
              name: nameRef 
          })
        // 通过.value修改值
          setTimeout(() => {
              ageRef .value= 30
             nameRef .value = 'Terry'
          })
        // 用于template
        const elemRef = ref(null)
        onMounted(() => {
            console.log('ref template', elemRef .value.innerHTML,   elemRef.value)
        })
        retrun {
              ageRef,
              nameRef,
              elemRef 
          }
     }
}

2、toRef: 针对一个响应式对象(reactive)的prop、创建一个ref,具有响应式、两者保持引用关系。

export default {
        setup () {
           const state = reactive({    //   针对响应式对象的,  如果用于普通对象, 产出结果不具备响应式
                age: 20,
                name: 'terry'
           })
           const nameRef = toRef(state,  name)  // 响应式对象(reactive)的prop
          //   两者保存引用
          setTimeout(() => {
              state.name= 'terry a'
          })
          setTimeout(() => {
              nameRef.value= 'terry b'
          })
          // 上面两个都是双向改变的
           return {
                state,
                nameRef
            }
        }
}

3、toRefs:将响应式对象(reactive封装)转换为普通对象、对象的每个prop都是对应的ref、两者保持引用关系。

 export default {
       setup () {
          const state = reactive({ 
               age: 20,
               name: 'terry'
          })
         const stateAsRefs = toRefs(state) //  将响应式对象(reactive封装)转换为普通对象 但是每个prop都是对应的ref, 两者保持引用关系
         setTimeout(() => {
             state .name= 'terry a'
         })
         return stateAsRefs
         //  如果返回下面的
         return {
               // state,  // 在模版里就得state.age
              // ...state, //  解构出来 则失去响应式了
           }
       }
}

最佳使用方式

1、用reactive做对象的响应式, 用ref做值类型的响应式
2、setup中返回toRefs(state),或者toRef(state, 'xxx')
3、ref的变量命名建议都用xxRef对象时, 使用toRef
4、合成函数返回响应式

深入理解上述api

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

四、emits 【组件事件现在需要在 emits选项中声明】

// 父组件
<template>
   <HelloWorld  @onSayHello  />
</template>
// 子组件
export default {
    emits: ['onSayHello'],
    setup(props, {emit}) {
          emit('onSayHello',  'vue3') // 其父组件触发的事件
    }
}

五、多事件处理器

<template>
   <!-- 这两个 one() 和 two() 将执行按钮点击事件 -->
   <button @click="one($event), two($event)">
     Submit
   </button>
</template>
export default {
    methods: {
         one(event) {
           // 第一个事件处理器逻辑...
        },
        two(event) {
          // 第二个事件处理器逻辑...
        }
    }
}

六、fragment

1.png

七、移除.sync

// 2.x
<ChildComponent :title.sync="pageTitle" />
// 3.x
<ChildComponent v-model:title="pageTitle" />

八、异步组件

// vue2.x
components: {
    'my-component': () => import('./my-cpmponent.vue')
}
// vue 3.x
components: {
    'my-component': defineAsyncComponent(() => {
        import('./my-cpmponent.vue')
    })
}

九、移除filter

十、Teleport 【对应react的portal】

<!-- data中设置modalOpen:false -->
<button @click="modalOpne = true">
      open full screen modal!!
</button> 
<teleport to="body">
    <div v-if="modalOpen">
        <div>
            telePort弹窗 (父元素body)
        </div>
    </div>
</teleport>

十一、Suspense

3.png

十二、Compositon API --- reactvie响应式对象

十三、Compositon API --- ref相关的见上面

十四、Compositon API --- readonly

十五、Compositon API --- watch 与 watchEffect

1、两者都可监听data属性变化
2、watch需要明确监听哪个属性
3、watchEffect会根据其中的属性, 自动监听其变化

*watchEffect默认初始化执行一次 【要收集要监听的数据】

十六、Compositon API --- setup

在setup和其他composition API中没有this
可以通过 getCurrentInstance()获取当前实例
如果使用options API 可照常使用this

const instance = getCurrentInstance()

十七、Compositon API --- 生命周期钩子函数

十八、Compositon API --- 实现逻辑复用

1、抽离逻辑代码到一个函数
2、函数命名约定为useXxxx 【react hook 也是】
3、在setup中引用useXxxx函数

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270