vue3学习笔记

文章目录

  • 一、常用Composition API(组合式API)
    • 1、setup
        • (1)setup是什么
        • (2)setup执行的时机
        • (3)setup返回值
        • (4)setup参数props 和 context
        • (5)注意点
    • 2、ref函数
    • 3、reactive函数
    • 4、ref函数对比reactive函数
    • 5、监听
        • (1)watch
        • (2)watchEffect
        • (3)watch 和 watchEffect 区别
    • 6、计算属性computed
        • (1)定义
        • (2)computed的响应式依赖(缓存)
        • (3)vue3使用
        • (4)应用场景
    • 7、toRefs与toRef
    • 8、provide与inject
    • 9、shallowReactive 与 shallowRef (不常用)
        • (1)shallowReactive:
        • (2)shallowRef
    • 10、readonly与shallowReadonly (不常用)
    • 11、toRaw与markRaw (不常用)
    • 12、响应式数据的判断 (不常用)
  • 二、生命周期钩子
  • 三、组合式api 父子方法互相调用
        • (1)子组件调用父组件方法
        • (2)父组件调用子组件方法
  • 四、Vue3到底做了哪些优化
        • (1)Vue2存在的问题
        • (2)Vue3的更小
        • (3)Vue3的更快
            • 1、diff算法优化
            • 2、静态提升
            • 3、事件监听缓存
            • 4、SSR优化
        • (4)Vue3更友好
            • 1、 Composition API
            • 2、 proxy
        • (5)总结:Vue3对于Vue2有什么更新

一、常用Composition API(组合式API)

1、setup

(1)setup是什么

  • Vue3.0中一个新的配置项,值为一个函数
  • setup是所有Composition API(组合API)
  • 组件中所有用到的:数据、方法等等,均要配置在setup中

(2)setup执行的时机

  • Vue3.0中一个新的配置项,值为一个函数
  • setup是所有Composition API(组合API)
  • 组件中所有用到的:数据、方法等等,均要配置在setup中

(3)setup返回值

  • 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(常用)
  • 若返回一个渲染函数:则可以自定义渲染内容。
import {h} from 'vue'
//向下兼容,可以写入vue2中的data配置项
module default {
	name: 'App',
	setup(){
		//数据
		let name = '张三',
		let age = 18,
		//方法
		function sayHello(){
			console.log(name)
		},

		//f返回一个对象(常用)
		return {
			name,
			age,
			sayHello
		}
		
		//返回一个函数(渲染函数)
		//return () => {return h('h1','学习')} 
		return () => h('h1','学习')
	}

}

(4)setup参数props 和 context

  • 参数props
//father
<template>
  <HelloWorld :message="str" />
</template>

<script>
import { ref } from 'vue'
import HelloWorld from './components/HelloWorld.vue'
export default {
  name: 'App',
  components: {
    HelloWorld
  },
  setup() {
    const str = ref('helloword')
    return {
      str
    }
  }

}
</script>


//children
<script>
export default {
  //必须在这里先定义props
  props: {
    message: {
      type: String,
      default: 'nihao'
    }
  },
  setup(props) {
    console.log(props.message) // 'helloword'  
  }
}
</script>

//语法糖写法
<script setup>
const props = defineProps({
  foo: {
    type: String,
    default: '1212312312312312312'
  }
})
console.log(props.message) // 'helloword'  

}
</script>
  • 参数context
    context :上下文,包括 attrs 、 emit 、slots
    ① attrs :在此部分,接收在父组件传递过来的,并且没有在props中声明的参数参数。
    ② emit:子组件对父组件发送事件
    ③ slots:和vue2中的插槽使用类似

(5)注意点

  • 尽量不要与Vue2.x配置混用
    • Vue2.x配置(data,methods,computed…)中可以访问setup中的属性,方法
    • 但在setup中不能访问到Vue2.x配置(data,methods,computed…)
    • 如有重名,setup优先
  • setup不能是一个async函数,因为返回值不再是return的对象,而是Promise,模板看不到return对象中的属性

2、ref函数

  • 作用:定义一个响应式的数据
  • 语法: const xxx = ref(initValue)
    • 创建一个包含响应式数据引用对象
    • JS中操作数据:xxx.value
    • 模板中读取数据:不需要.value,直接:{{xxx}}
  • 备注:
    • 接收的数据可以是:基本类型、也可以是对象类型
    • 基本类型的数据:响应式依然靠的是Object.defineProperty()的get和set完成的
    • 对象类型的数据: 内部”求助“了Vue3.0中的一个新的函数——reactive函数

3、reactive函数

  • 作用:定义一个对象类型的响应式数据(基本数据类型别用它,用ref函数)
  • 语法:const 代理一个对象 = reactive(被代理的对象) 接收一个对象(或数组),返回一个代理器对象(proxy对象)
  • reactive定义的响应式数据是“深层次的”
  • 内部基于ES6的Proxy实现,通过代理对象内部的数据都是响应式的
<script setup>
import {reactive} from "vue"
let obj=reactive(
    {
      name:"aaa",
      age:22, 
      family:[{relation: 'father',age:52}, { relation: 'mother', age: 55 }],
    }
    )
  let fn=()=>{
    let fn=()=>{
    //直接用点语法,因为reactive使用的是Proxy代理了整个对象
     obj.age++
     obj.family[0].age++
     obj.family[1].age++
  }

4、ref函数对比reactive函数

  • 从定义数据角度对比:
    • ref用来定义: 基本数据类型
    • reactive用来定义: 对象(或数组)类型数据
    • 备注: ref也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive转为代理对象
  • 从原理角度对比:
    • ref通过Object.defineProperty()的get和set来实现响应式(数据劫持)
    • reactive通过Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据
  • 从使用角度对比:
    • ref定义数据:操作数据需要 .value ,读取数据时模板中直接读取不需要 .value
    • reactive 定义的数据: 操作数据和读取数据均不需要 .value

5、监听

(1)watch

监视reactive定义的响应式数据的时候:oldValue无法获取到正确的值,强制开启了深度监视(deep配置无效)

<template>
  <h1>当前求和为: {{sum}}</h1>
  <button @click="sum++">点我+1</button>
  <hr>
  <h1>当前信息为: {{msg}}</h1>
  <button @click="msg+='!' ">修改信息</button>
  <hr>
  <h2>姓名: {{person.name}}</h2>
  <h2>年龄: {{person.age}}</h2>
  <button @click="person.name += '~' ">修改姓名</button> 
  <button @click="person.age++">增长年龄</button>
</template>

<script>
    //使用setup的注意事项
    import { watch,ref,reactive } from 'vue'

    export default {
        name: 'test5',
        props: ['msg'],
        emits:['hello'],
        setup(){
            let sum  = ref(0)
            let msg = ref('你好啊')
            let person = reactive({
                name: '张三',
                age: 18,
                job:{
                    salary: '15k'
                },
            })
            //情况一:监视ref所定义的一个响应式数据
             watch(sum, (newValue,oldValue)=>{
                 console.log('新的值',newValue);
                 console.log('旧的值',oldValue);
             })

            //情况二:监视ref所定义的多个响应式数据
            watch([sum,msg], (newValue,oldValue)=>{
                console.log('新的值',newValue); //['sum的newValue', 'msg的newValue']
                console.log('旧的值',oldValue); //['sum的oldValue', 'msg的oldValue']
            },{immediate: true,deep:true}) //这里vue3的deep是有点小问题的,可以不用deep,(隐式强制deep)

            //情况三:监视reactive定义的所有响应式数据,
            //1.此处无法获取正确的oldValue(newValue与oldValue是一致值),且目前无法解决
            //2.强制开启了深度监视(deep配置无效)
            /**
            * 备注:
            * 1. 当你监听一个响应式对象的时候,这里的newVal和oldVal是一样的,因为他们是同一个对象【引用地址一样】,
            *    即使里面的属性值会发生变化,但主体对象引用地址不变。这不是一个bug。要想不一样除非这里把对象都换了
            * 
            * 2. 当你监听一个响应式对象的时候,vue3会隐式的创建一个深层监听,即对象里只要有变化就会被调用。
            *    这也解释了你说的deep配置无效,这里是强制的。
            */
            watch(person, (newValue,oldValue)=>{
                console.log('新的值',newValue); 
                console.log('旧的值',oldValue);
            })

            //情况四:监视reactive对象中某一个属性的值,
            //注意: 这里监视某一个属性的时候可以监听到oldValue
            watch(()=>person.name, (newValue,oldValue)=>{
                console.log('新的值',newValue);  
                console.log('旧的值',oldValue);
            })

            //情况五:监视reactive对象中某一些属性的值
            watch([()=>person.name,()=>person.age], (newValue,oldValue)=>{
                console.log('新的值',newValue);  
                console.log('旧的值',oldValue);
            })

            //特殊情况: 监视reactive响应式数据中深层次的对象,此时deep的配置奏效了
            //注意: 这里监视某一个属性的时候可以监听到oldValue ????
            watch(()=>person.job, (newValue,oldValue)=>{
                console.log('新的值',newValue);  
                console.log('旧的值',oldValue);
            },{deep:true}) //此时deep有用

            return {
                sum,
                msg,
                person,
            }
        },
        
    }
</script>

(2)watchEffect

备注:

  • 不需要指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
  • 页面初始化的时候会执行一次函数,自动收集依赖
  • watchEffect有点像computed:
    • 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值
    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
<template>
  <h2>工资: {{person.job.salary}}</h2>
  <button @click="person.job.salary++">增长工资</button>
</template>

<script>
    import { ref,reactive,watchEffect } from 'vue'
    export default {
        name: 'test5',
        props: ['msg'],
        emits:['hello'],
        setup(){
            let sum  = ref(0)
            let msg = ref('你好啊')
            let person = reactive({
                name: '张三',
                age: 18,
                job:{
                    salary: 15
                },
            })
           //用处: 如果是比较复杂的业务,发票报销等,那就不许需要去监听其他依赖,只要发生变化,立马重新回调
            //注重逻辑过程,你发生改变了我就重新执行回调,不用就不执行,只执行一次
           watchEffect(() => {
              //这里面你用到了谁就监视谁,里面就发生回调
              console.log(person.job.salary, ' 111111');
           })
           
            return {
                sum,
                msg,
                person,
            }
        },
        
    }
</script>

(3)watch 和 watchEffect 区别

  • watchEffect不需要制定监听的属性,自动收集依赖,只要在回调中引用到了响应式属性,只要这些属性发生改变,回调就会执行。watch只能监听指定的属性,做出回调函数的执行,同时可以监听多个
  • watch可以获取到新值和旧值,watchEffect拿不到旧值
  • watchEffect在组件初始化的时候就会自动执行一次用来收集依赖,watch不需要一开始就指定了

6、计算属性computed

(1)定义

computed是vue的计算属性,是根据依赖关系进行缓存的计算,只有在它的相关依赖发生改变时才会进行更新

(2)computed的响应式依赖(缓存)

  • computed的每一个计算属性都会被缓存起来,只要计算属性所依赖的属性发生变化,计算属性就会重新执行,视图也会更新。下面代码中,计算属性fullName,它依赖了firstName和lastName这两个属性,只要它们其中一个属性变化,fullName就会重新执行。
  • computed计算属性会被缓存,在下面代码中使用了两次fullName,但在控制台只输出了一次 “这是fullName”。
<template>
  <div>
    <div>
      姓:<input type="text" v-model="firstName" />
    </div>
    <div>
      名:<input type="text" v-model="lastName" />
    </div>
    <!-- 调用两次fullName -->
    <div>姓名:{{ fullName }}</div>
    <div>姓名:{{ fullName }}</div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      firstName: "张",
      lastName: "三",
    };
  },
  computed: {
    fullName() {
        console.log("这是fullName");
        return this.firstName + this.lastName;
    }
  }
};
</script>

(3)vue3使用

import { ref, reactive, computed } from 'vue'
export default {
  name: 'HelloWorld',
  setup() {
    let msg = ref('你好啊')
    
    let person = reactive({
      name: '张三',
      job: {
        salary: 15
      },
      reverseMsg: computed(() => {
        return msg.value.split('').reverse().join('')
      })
    })
    
    let strings = computed(() => {
       //返回一个带有value属性的对象
      return msg.value.split('').reverse().join('')
    })
    
    console.log(strings.value, person.reverseMsg)
    
    return {
      msg,
      person,
    }
  },

}
</script>

(4)应用场景

  • 当一个数据受多个数据影响时,可以使用computed
  • 本组件计算
  • 计算props的值
  • 计算vuex的state或者getters值的变化

7、toRefs与toRef

  • 作用:创建一个ref对象,其value值指向另一个对象中的某个属性值
  • 语法:const name = toRef(person, ‘name’)
  • 应用:要将响应式对象中的某个属性单独提供给外部使用
  • 扩展: toRefs与toRef功能一致,但是可以批量创建多个ref对象,语法: toRefs(person)

<template>
  <h2>姓名: {{name2}}</h2>
  <h2>年龄: {{age}}</h2>
  <button @click="name += '~' ">修改姓名</button> 
  <button @click="age++">增长年龄</button>
</template>

<script>
    //使用setup的注意事项
    import { reactive, toRef, toRefs } from 'vue'

    export default {
        setup(){
            let person = reactive({
                name: '张三',
                age: 18,
                job:{
                    salary: '15k'
                },
            })
            //toRef
            const name2 = toRef(person,'name') //第一个参数是对象,第二个参数是键名
            console.log('toRef转变的是',name2); //ref定义的对象

            //toRefs,批量处理对象的所有属性
            //const x  = toRefs(person)
            //console.log('toRefs转变的是',x); //是一个对象
            return {
                person,
                name2,
                ...toRefs(person)
            }
        },
        
    }
</script>

8、provide与inject

  • 作用:实现祖孙组件间的通信
  • 语法:父组件有一个provide选项提供数据,子组件有一个inject选项来开始使用这些数据
  • 具体写法:
<script setup>
// 父组件
import { provide } from 'vue';
import ChildVue from './components/Child.vue';

let car = reactive({
  name: '奔驰',
  price: '40w'
})
provide('car',car) //给自己的后代组件传递数据
</script>
<script setup>
// 子组件/孙组件
import { inject } from 'vue';
et car = inject('car') //拿到父组件的数据
const {name, price} = car
</script>

9、shallowReactive 与 shallowRef (不常用)

  • shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
  • shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
  • 什么时候使用?
    • 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
    • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
  • 代码演示

(1)shallowReactive:

<template>
  <div>
    <h1>姓名:{{name}}</h1>  //响应式发生变化
    <h2>年龄:{{age}}</h2> //响应式发生变化
    <h3>喜欢的水果:{{likeFood.fruits.apple}}</h3> //普通对象不会发生变化
    <button @click="name += '~'">修改姓名</button>
    <button @click="age++">修改年龄</button>
    <button @click="likeFood.fruits.apple += '!'">修改水果</button>
  </div>
</template>
 
<script>
import {reactive, toRefs, shallowReactive} from 'vue'
export default {
  name: "App",
  setup() {
    // 定义了一段数据
    let person = shallowReactive({    // 只将第一层数据做了响应式处理 
      name: '张三',
      age: 18,
      likeFood: {
        fruits:{
          apple: '苹果'               // 深层次的数据将会是一个普通的对象
        }
      }
    })
    // 将数据返回出去
    return {
      ...toRefs(person)
    }
  }
};
</script>

(2)shallowRef

// 我们调用了shallowRef方法传递了基本数据类型 我们可以看到 当前属性是有响应式的
<div>
    <h1>姓名:{{ sum}}</h1> // 响应式数据变化
    <button @click="sum++">点击+</button>
</div>
  setup() {
    // 定义了一段数据
    let sum = shallowRef(0);
    // 将数据返回出去
    return {
      sum,
    };
  },
//但是我们现在传递一个对象进去 我们可以看到 传递的对象将变成一个普通对象 不在具有响应式功能了
<div>
    <h1>姓名:{{ sum.n}}</h1> // 数据不会发生变化
    <button @click="sum.n++">点击+</button>
</div>
  setup() {
    // 定义了一段数据
    let sum = shallowRef({      // sum将不在是一个响应式对象 
      n: 0
    });
    // 将数据返回出去
    return {
      sum,
    };
  },

10、readonly与shallowReadonly (不常用)

  • readonly:让一个响应式的数据变成只读的(深只读)
  • shallowReadonly: 让一个响应式数据变成只读的(浅只读)
<template>
  <h4>当前求和为:{{ sum }}</h4>
  <button @click="sum++">点我++</button>
  <hr>
  <h2>姓名:{{ name }}</h2>
  <h2>年龄:{{ age }}</h2>
  <h2>薪资:{{ job.j1.salary }}K</h2>
  <button @click="name += '~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
</template>

<script>
//shallowReadonly
import { ref, reactive, toRefs, readonly, shallowReadonly } from 'vue'
export default {
  setup() {
    //数据
    let sum = ref(0)
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })
    person = readonly(person) //全部都不能修改
    person = shallowReadonly(person) //salary可以修改
    sum = readonly(sum) //不能修改
    sum = shallowReadonly(sum) //不能修改

    //返回一个对象(常用)
    return {
      sum,
      ...toRefs(person)
    }
  }
}
</script>

11、toRaw与markRaw (不常用)

  • toRaw
    • 作用:将一个由reactive生成的响应式对象转换为普通对象
    • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
  • markRaw
    • 作用:标记一个对象,使其永远不会再成为响应式对象
    • 使用场景:
      • 有些值不应被设置成响应式的,例如复杂的第三方类库等
      • 当渲染具有不可变数据的大列表时候,跳过响应式转换可以提高性能
import {reactive,toRaw,markRaw} from 'vue'
setup(){
	let person = reactive({
		name: '张三',
	})
	function showRawPerson(){
		const p = toRaw(person)
		p.age++
		console.log(p)
	}
	function addCar(){
		let car = {name: '奔驰'}
		person.car = markRaw(car) //一旦这么做时候,他就永远不能当成响应式数据去做了
	}
}

vue3学习笔记_第1张图片

12、响应式数据的判断 (不常用)

  • isRef:检查一个值是否为ref对象
  • isReactivce:检查一个对象是否是由reactive创建的响应式代理
  • isReadonly:检查一个对象是否由readonly创建的只读代理
  • isProxy:检查一个对象是否由reactive或者readonly方法创建的代理
<script>
import { ref, reactive, toRefs, readonly, isRef, isReactive, isReadonly, isProxy } from 'vue'
export default {
  name: 'App',
  setup() {
    let car = reactive({ name: '奔驰', price: '40W' })
    let sum = ref(0)
    let car2 = readonly(car)

    console.log(isRef(sum)) //true
    console.log(isReactive(car)) //true
    console.log(isReadonly(car2)) //true
    console.log(isProxy(car)) //true
    console.log(isProxy(sum)) //false

    return { ...toRefs(car) }
  }
}
</script>

二、生命周期钩子

onMounted()
onUpdated()
onUnmounted()
onBeforeMount()
onBeforeUpdate()
onBeforeUnmount()
onActivated()
onDeactivated()
onServerPrefetch()

三、组合式api 父子方法互相调用

(1)子组件调用父组件方法

父组件通过ref定义子组件实例,通过调用实例获得子组件的数据和方法

<!-- 父组件 app.vue -->
<template>
  <div class="itxst">
    <!-- 使用 ref  指令关联子组件 -->
    <child ref="childComp"/>
    <button @click="onTry">点击试一试</button>
  </div>
</template>
<script setup>
import { reactive, ref } from "vue";
import child from "./child.vue";
//定义子组件实例,名称要和上面的ref相同
const childComp = ref(null);
 
//访问demo组件的方法或对象
const onTry = () => {
  //获取到子组件的 title 数据 
  let msg = childComp.value.state.title;
  //调用子组件的 play方法
  childComp.value.play();
};
</script>

子组件通过defineExpose暴露对象和方法

<!--子组件名称  child.vue -->
<template>
  <div class="itxst">
    {{ state.title }}
  </div>
</template>
<script setup>
import { ref, reactive } from "vue";
//定义一个变量
const state = reactive({
  title: "www.itxst.com",
});
//定义一个方法
const play = () => {
  state.title = "你调用了子组件的方法";
};
 
//暴露state和play方法
defineExpose({
  state,
  play,
});
</script>

(2)父组件调用子组件方法

父组件

<template>
	<Child @sayHello="handle"></Child>
</template>
 
<script lang="ts" setup>
	import Child from '../../components/child.vue';
 
	const handle = () => {
		console.log('子组件调用了父组件的方法')
	}
</script>

子组件

<template>
	<view>我是子组件</view>
	<button @click="say">调用父组件的方法</button>
</template>
 
<script lang="ts" setup>
	const emit = defineEmits(["sayHello"])
 
	const say = () => {
		emit('sayHello')
	}
</script>

四、Vue3到底做了哪些优化

(1)Vue2存在的问题

  • 随着功能的增长,复杂组件的代码变得越来越难以维护;
  • 缺少一种比较“干净”的在多个组件之间提取和复用逻辑的机制;
  • 类型推断不够友好;
  • bundle的时间过久。

(2)Vue3的更小

  • Vue3移除一些不常用的API,引入tree-shaking,可以将无用模块“剪辑”,仅打包需要的,使打包的整体体积变小了。简单来说,treeshaking就是找出使用的代码。
  • Vue2中,无论使用什么功能,最终都会出现在生产代码中,主要原因是Vue实例在项目是单例的,捆绑程序无法检测到该对象的哪些属性在代码中被使用到。
import Vue from 'vue'
 
Vue.nextTick(() => {})

Vue3中引入tree shaking特性,将全局API进行分块,如果不使用某些功能,就不会出现在打包文件中。

import { nextTick, observable } from 'vue'
 
nextTick(() => {})

(3)Vue3的更快

在更快方面,Vue3主要做出的优化有:diff算法优化、静态提升、事件监听缓存、SSR优化。

1、diff算法优化

diff算法详情
vue3在diff算法中相比于vue2增加了静态标记,作用是为会发生变化的地方添加一个flag标记,下次发生变化的时候直接找该地方进行比较。比如下述例子:

<template>
    <div id="content">
        <p class="text">静态文本</p>
        <p class="text">静态文本</p>
        <p class="text">{{ message }}</p>
        <p class="text">静态文本</p>
        ...
        <p class="text">静态文本</p>
    </div>
</template>

这里的组件内部只有一个动态节点,剩余一堆都是静态节点,所以这里很多diff和遍历其实都是不需要的,会造成性能浪费。此时会给{{message}}标签标记一个flag=1,其他的静态文本会标记flag=-1,代表永远都不会进行diff操作。这样比较的时候可以直接选择flag=1的节点进行比较。

2、静态提升

Vue3对于不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用,这样就可以免去了重复的创建节点。

3、事件监听缓存

默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化,但是因为是同一个函数,所以没必要去追踪它的变化,想办法将它直接缓存起来复用就会提升性能。因此要打开事件监听缓存,这样静态标记就不存在了,这部分内容也就不会进行比较了。

4、SSR优化

当静态内容达到一定量级的时候,会用createStaticVNode方法在客户端去生成一个static node,这些静态node,会直接innerHtml,就不需要创建对象,然后根据对象渲染。

(4)Vue3更友好

vue3的更友好体现在,兼顾vue2的options API的同时还推出了composition API,大大增加了代码的逻辑组织和代码的复用能力,同时优化了vue2中数据劫持,vue2中通过Object.defineProperty,这个API并不能检测对象属性的添加和删除,而Vue3通过proxy解决了这个问题。

1、 Composition API
  • Vue2中,代码是Options API风格的,也就是通过填充(option)data、methods、computed等属性来完成一个Vue组件。这种风格始得Vue容易上手,但是Options API不够灵活,组件间很难优雅的公用代码,并且Vue2的书写方式与JS的开发原则相悖,比如methods中的this竟然指向实例而不指向methods所在的对象。
  • 在Vue3中,舍弃了Options API,开始使用Composition API。组件根据逻辑功能来组织的,一个功能所定义的所有 API 会放在一起(更加的高内聚,低耦合)。
2、 proxy

Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象现有属性,并返回此对象。但是,如果存在深层的嵌套对象关系,需要深层次的进行监听,造成了性能的极大问题。
Proxy的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作,这就完全可以代理所有属性,包括新增属性和删除属性,并且Proxy可以监听数组的变化。

(5)总结:Vue3对于Vue2有什么更新

  • Vue2在使用过程中,随着项目越来越复杂,项目代码维护起来也越来越困难,主要原因是Vue2使用的是Options API,这种方式把同一个功能的数据、方法、请求都分开写在data、methods等Options中。并且组件之间相同功能的复用比较也比较困难,同时响应式也没有那么灵活,因此,Vue3做出了如下的更新:
  • 用Composition API代理Options API,正如刚刚所说,Options API 将相同的功能的不同部分都分开写,不仅不利于阅读和维护,也和原生JS的思想相悖,缺乏灵活性,Vue3采用的Composition API按照功能将代码分割开,这样方便维护,也方便复用。
  • 采用Proxy代理Object.defineProperty,Vue2通过defineProperty的get、set和发布订阅来完成响应式,但是defineProperty的get、set并不能监控深层的对象与数组的变化,需要手动调用set来增加、删除响应式属性,还是造成了一些麻烦。Vue3采用Proxy监控整个对象,无论多深的属性都可以被监控到。
  • Vue3增加了tree shaking,能在打包的时候只打包用到的组件,可以让运行速度更快和打包文件更小。
  • Vue3还改变了虚拟DOM的diff策略,在Vue2中,diff策略不会区别节点是静态节点还是动态节点,而对比过多的静态节点会造成资源的浪费。因此- - Vue3给每一个节点都打上了标签,如果标签不为-1,则证明是动态节点,在比较的时候也只需要比较动态节点,使diff算法的效率更高。
  • Vue3还增加了静态提升和事件监听缓存,将不需要重复创建的节点和方法单独提出、进行缓存,避免重复创建和加载。
    Vue3还做了SSR优化。如果增加的静态内容过多,就会直接使用innerHTML的方法插入,而不会一个一个的创建的节点。

你可能感兴趣的:(学习,javascript,vue.js)