从前慢-Vue3

Vue3

在这里插入图片描述

1 认识Vue3

1) 了解相关信息
Vue.js 3.0 "One Piece" 正式版在今年9月份发布
2年多开发, 100+位贡献者, 2600+次提交, 600+次PR
Vue3支持vue2的大多数特性
更好的支持Typescript

2) 性能提升:
打包大小减少41%
初次渲染快55%, 更新渲染快133%
内存减少54%
使用Proxy代替defineProperty实现数据响应式
重写虚拟DOM的实现和Tree-Shaking
3) 新增特性
Composition (组合) API

setup
	ref 和 reactive
	computed 和 watch
	新的生命周期函数
	provide与inject
	
新组件
	Fragment - 文档碎片
	Teleport - 瞬移组件的位置
	Suspense - 异步加载组件的loading界面
	
其它API更新
	全局API的修改
	将原来的全局API转移到应用对象
	模板语法变化

2 创建vue3项目

2.1 使用 vue-cli 创建

安装
npm install -g @vue/cli
创建项目
vue create my-project

2.2 使用 vite 创建

安装vue-cli到最新版本 (必须高于4.5.0)
npm init vite-app   <project-name>
cd  <project-name>
npm install 
npm run dev

3 Composition API的使用

Options API 选项api
Options API的优点是容易学习和使用,代码有明确的书写位置
Options API的缺点就是相似逻辑不容易复用,在大项目中尤为明显。
Options API可以通过mixins提取相同的逻辑,但是容易发生命名
冲突且来源不清晰

Composition API 组合API
Composition API是根据逻辑功能来组织代码的,一个功能所
有的api放到一起
即便项目很大,功能很多,都能够快速的定位到该功能所有的API
Composition API提高了代码可读性和可维护性
Vue3.0中推荐使用composition API,也保留了options API。

3.1 setup

Setup函数是一个新的组件选项,作为组件中composition
API的起点
从生命周期钩子的角度来看,setup会在beforeCreate
钩子函数之前执行。Setup中不能使用this,this指向undefined
新的option, 所有的组合API函数都在此使用, 只在初始化时
执行一次
函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用
<template>
  <div class="app"></div>
</template>

<script>
export default {
     
     
  setup() {
     
     
    // composition api的入口
    // 不能访问this
    console.log('setup执行了')
    console.log(this)
  },
  beforeCreate() {
     
     
    console.log('beforeCreate')
  }
}
</script>

<style></style>

3.1.1 setup细节
setup执行的时机

在beforeCreate之前执行(一次), 此时组件对象还没有创建
this是undefined, 不能通过this来访问
data/computed/methods / props
其实所有的composition API相关回调函数中也都不可以

setup的返回值

一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使
用此对象中的所有属性/方法
返回对象中的属性会与data函数返回对象的属性合并成为
组件对象的属性
返回对象中的方法会与methods中的方法合并成功组件
对象的方法

如果有重名, setup优先
注意:
一般不要混合使用: methods中可以访问setup提供的属性
和方法, 但在setup方法中不能访问data和methods
setup不能是一个async函数: 因为返回值不再是return的对象, 
而是promise, 模板看不到return对象中的属性数据


setup的参数

setup(props, context) / setup(props, {attrs, slots, emit})
props: 包含props配置声明且传入了的所有属性的对象
attrs: 包含没有在props配置中声明的属性的对象, 
相当于 this.$attrs
slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
emit: 用来分发自定义事件的函数, 相当于 this.$emit
<template>
  <h2>App</h2>
  <p>msg: {
  
  {msg}}</p>
  <button @click="fn('--')">更新</button>

  <child :msg="msg" msg2="cba" @fn="fn"/>
</template>

<script lang="ts">
import {
     
     
  reactive,
  ref,
} from 'vue'
import child from './child.vue'

export default {
     
     

  components: {
     
     
    child
  },

  setup () {
     
     
    const msg = ref('abc')

    function fn (content: string) {
     
     
      msg.value += content
    }
    return {
     
     
      msg,
      fn
    }
  }
}
</script>
<template>
  <div>
    <h3>{
  
  {n}}</h3>
    <h3>{
  
  {m}}</h3>

    <h3>msg: {
  
  {msg}}</h3>
    <h3>msg2: {
  
  {$attrs.msg2}}</h3>

    <slot name="xxx"></slot>

    <button @click="update">更新</button>
  </div>
</template>

<script lang="ts">

import {
     
     
  ref,
  defineComponent
} from 'vue'

export default defineComponent({
     
     
  name: 'child',

  props: ['msg'],

  emits: ['fn'], // 可选的, 声明了更利于程序员阅读, 且可以对分发的事件数据进行校验

  data () {
     
     
    console.log('data', this)
    return {
     
     
      // n: 1
    }
  },

  beforeCreate () {
     
     
    console.log('beforeCreate', this)
  },

  methods: {
     
     
    // update () {
     
     
    //   this.n++
    //   this.m++
    // }
  },

  // setup (props, context) {
     
     
  setup (props, {
      
      attrs, emit, slots}) {
     
     

    console.log('setup', this)
    console.log(props.msg, attrs.msg2, slots, emit)

    const m = ref(2)
    const n = ref(3)

    function update () {
     
     
      // console.log('--', this)
      // this.n += 2 
      // this.m += 2

      m.value += 2
      n.value += 2

      // 分发自定义事件
      emit('fn', '++')
    }

    return {
     
     
      m,
      n,
      update,
    }
  },
})
</script>

3.2 ref

ref函数接受一个简单类型的值,返回一个可改变的
ref对象。返回的对象有唯一的属性 value
在setup函数中,通过ref对象的value属性可以访问到值
在模板中,ref属性会自动解套,不需要额外的.value
如果ref接受的是一个对象,会自动调用reactive
<template>
  <h2>{
  
  {count}}</h2>
  <hr>
  <button @click="update">更新</button>
</template>

<script>
import {
     
     
  ref
} from 'vue'
export default {
     
     

  /* 在Vue3中依然可以使用data和methods配置, 但建议使用其新语法实现 */
  // data () {
     
     
  //   return {
     
     
  //     count: 0
  //   }
  // },
  // methods: {
     
     
  //   update () {
     
     
  //     this.count++
  //   }
  // }

  /* 使用vue3的composition API */
  setup () {
     
     

    // 定义响应式数据 ref对象
    const count = ref(1)
    console.log(count)

    // 更新响应式数据的函数
    function update () {
     
     
      // alert('update')
      count.value = count.value + 1
    }

    return {
     
     
      count,
      update
    }
  }
}
</script>
3.2.1 模板refs
为了获得对模板内元素或组件实例的引用,我们可
以像往常一样在 setup() 中声明一个 ref 并返回它
<template>
  <div class="app">
    <h1 ref="hRef">钩子函数----123</h1>

    <Demo ref="dRef"></Demo>
  </div>
</template>

<script>
import Demo from './Demo.vue'
import {
     
      ref, provide, onMounted } from 'vue'
export default {
     
     
  components: {
     
     
    Demo
  },
  setup() {
     
     
    // 创建了一个空的ref
    const hRef = ref(null)
    const dRef = ref(null)

    onMounted(() => {
     
     
      console.log(hRef.value.innerHTML)
      console.log(dRef.value)
    })

    return {
     
     
      hRef,
      dRef
    }
  }
}
</script>

<style></style>

3.3 reactive

Reactive函数接受一个普通对象,返回该对象的响应式代理。
<template>
  <div class="app">
    <div>{
  
  { car.brand }}----{
  
  { car.price }}</div>
    <button @click="car.brand = '奔驰'">修改</button>
  </div>
</template>

<script>
import {
     
      reactive } from 'vue'
export default {
     
     
  setup() {
     
     
    // 1. setup需要返回值, setup中返回的值才能在模板中使用
    // 2. reactive中传入一个普通对象,返回一个代理对象
    // 3. 普通对象没有响应式,需要reactive
    const car = reactive({
     
     
      brand: '宝马',
      price: 100
    })

    return {
     
     
      car
    }
  }
}
</script>

<style></style>

3.4 Vue2.0和vue3.0响应式原理对比

3.4.1 Vue2.0响应式原理
Vue2.0中
响应式数据
核心:
对象: 使用ES5中的Object.defineProperty方法实现
对对象的已有属性值的读取和修改进行劫持(监视/拦截)
数组: 通过重写数组更新数组一系列更新元素的方法来实
现元素修改的劫持
缺点
对象直接新添加的属性或删除已有属性, 界面不会自动更新
直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {}

解决方案
Vue2.0提供Vue.set方法用于动态给对象添加属性
Vue2.0提供Vue.delete方法用于动态删除对象的属性
重写vue中数组的方法,用于监测数组的变更
Object.defineproperty
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      // vue会做一个数据劫持,,,监视数据的变化,一旦数据变化了,更新DOM
      const data = {
     
     
        name: 'zs',
        age: 18
      }

      for (let k in data) {
     
     
        let temp = data[k]
        Object.defineProperty(data, k, {
     
     
          get() {
     
     
            console.log(`我劫持了${ 
       k}的获取`)
            return temp
          },
          set(value) {
     
     
            console.log(`我劫持了${ 
       k}的设置,值${ 
       value}`)
            temp = value
          }
        })
      }

      // Object.defineProperty有缺点
      // 1. 无法监视到新增的属性的变更和删除
      // 2. 无法劫持到数组的下标和长度
    </script>
  </body>
</html>

3.4.2 Vue3.0响应式原理
核心:
通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 
包括属性值的读写, 属性的添加, 属性的删除等...
通过 Reflect(反射): 动态对被代理对象的相应属性进行特
定的操作

优点
可以检测到代理对象属性的动态添加和删除
可以监测到数组的下标和length属性的变更

缺点
ES6的proxy语法对于低版本浏览器不支持,IE11
Vue3.0会针对于IE11出一个特殊的版本用于支持ie11
es6的proxy语法.html
new Proxy(data, {
   
   
	// 拦截读取属性值
    get (target, prop) {
   
   
    	return Reflect.get(target, prop)
    },
    // 拦截设置属性值或添加新属性
    set (target, prop, value) {
   
   
    	return Reflect.set(target, prop, value)
    },
    // 拦截删除属性
    deleteProperty (target, prop) {
   
   
    	return Reflect.deleteProperty(target, prop)
    }
})

proxy.name = 'tom'   
vue3中响应式原理
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Proxy 与 Reflect</title>
</head>
<body>
  <script>
    
    const user = {
     
     
      name: "John",
      age: 12
    };

    /* 
    proxyUser是代理对象, user是被代理对象
    后面所有的操作都是通过代理对象来操作被代理对象内部属性
    */
    const proxyUser = new Proxy(user, {
     
     

      get(target, prop) {
     
     
        console.log('劫持get()', prop)
        return Reflect.get(target, prop)
      },

      set(target, prop, val) {
     
     
        console.log('劫持set()', prop, val)
        return Reflect.set(target, prop, val); // (2)
      },

      deleteProperty (target, prop) {
     
     
        console.log('劫持delete属性', prop)
        return Reflect.deleteProperty(target, prop)
      }
    });
    // 读取属性值
    console.log(proxyUser===user)
    console.log(proxyUser.name, proxyUser.age)
    // 设置属性值
    proxyUser.name = 'bob'
    proxyUser.age = 13
    console.log(user)
    // 添加属性
    proxyUser.sex = '男'
    console.log(user)
    // 删除属性
    delete proxyUser.sex
    console.log(user)
  </script>
</body>
</html>

3.5 reactive与ref-细节

是Vue3的 composition API中2个最重要的响应式API
ref用来处理基本类型数据, reactive用来处理对象
(递归深度响应式)
如果用ref对象/数组, 内部会自动将对象/数组转换
为reactive的代理对象
ref内部: 通过给value属性添加getter/setter来实
现对数据的劫持
reactive内部: 通过使用Proxy来实现对对象内部所有
数据的劫持, 并通过Reflect操作对象内部数据
ref的数据操作: 在js中要.value, 在模板中不需
要(内部解析模板时会自动添加.value)
<template>
  <h2>App</h2>
  <p>m1: {
  
  {m1}}</p>
  <p>m2: {
  
  {m2}}</p>
  <p>m3: {
  
  {m3}}</p>
  <button @click="update">更新</button>
</template>

<script lang="ts">
import {
     
     
  reactive,
  ref
} from 'vue'

export default {
     
     

  setup () {
     
     
    const m1 = ref('abc')
    const m2 = reactive({
     
     x: 1, y: {
     
     z: 'abc'}})

    // 使用ref处理对象  ==> 对象会被自动reactive为proxy对象
    const m3 = ref({
     
     a1: 2, a2: {
     
     a3: 'abc'}})
    console.log(m1, m2, m3)
    console.log(m3.value.a2) // 也是一个proxy对象

    function update() {
     
     
      m1.value += '--'
      m2.x += 1
      m2.y.z += '++'

      m3.value = {
     
     a1: 3, a2: {
     
     a3: 'abc---'}}
      m3.value.a2.a3 += '==' // reactive对对象进行了深度数据劫持
      console.log(m3.value.a2)
    }

    return {
     
     
      m1,
      m2,
      m3,
      update
    }
  }
}
</script>

3.6 计算属性与监视

computed函数:

与computed配置功能一致
只有getter
有getter和setter
watch函数

与watch配置功能一致
监视指定的一个或多个响应式数据, 一旦数据变化, 
就自动执行监视回调
默认初始时不执行回调, 但可以通过配置immediate为true, 
来指定初始时立即执行第一次
通过配置deep为true, 来指定深度监视

watchEffect函数
不用直接指定要监视的数据, 回调函数中使用的哪些响应式
数据就监视哪些响应式数据
默认初始时就会执行第一次, 从而可以收集需要监视的数据
监视数据发生变化时回调
<template>
  <h2>App</h2>
  fistName: <input v-model="user.firstName"/><br>
  lastName: <input v-model="user.lastName"/><br>
  fullName1: <input v-model="fullName1"/>
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

unique_perfect

您的支持是我创造的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值