vue3对比vue2

您所在的位置:网站首页 h2的读法 vue3对比vue2

vue3对比vue2

2023-06-13 12:30| 来源: 网络整理| 查看: 265

使用vite创建vue项目 什么是vite? 新一代前端构建工具 优势如下: 开发环境中,无须打包操作,可快速的冷启动清亮快速的热重载真正的按需编译,不在等待整个应用编译完成 ## 创建工程 npm init vite-app ## 进入工程目录 cd ## 安装依赖 npm install ## 运行 npm run dev 注意,vite初始化后需要你手动安装一下node_modules vue3中的根标签可以不止一个

vue2就不行

setup vue3中一个新的配置项,值为一个函数setup是所有Composition API(组合式API)表演的舞台组件中所用到的:数据,方法等等,均要配置在setupsetup函数的两种返回值 若返回一个对象,则对象中的属性,方法在模板中均可以直接使用若返回一个渲染函数,则可以自定义熏染内容 注意点: 尽量不要与vue2.0配置混用vue2.0配置(data,methos,computed…)中可以访问到setup中的属性,方法但在setpup中不能访问到vue2配置(data,methods,computed)如果有重名,setup优先 setup不能是一个async函数,因为返回值不再是return对象,而是promise,模板看不到return对象中的属性 vue3向下兼容

vue3中也可以按照vue2的方式来写,vue3是向下兼容的

index页面 name: {{ name }} age: {{ age }} test1 export default { name: "Index", // 测试只是测试函数,并不考虑响应式 setup () { let name = "张三" let age = 18 // 方法 function sayHello () { } // 返回对象 return { name, age, sayHello } } }

这里面的returb也可以这样写:

index页面 name: {{ name }} age: {{ age }} test1 import { h } from "vue" export default { name: "Index", // 测试只是测试函数,并不考虑响应式 setup () { let name = "张三" let age = 18 // 方法 function sayHello () { } // 返回渲染函数 // 此时你页面中写什么都不重要了,直接渲染下面这里写的东西 return () => { return h('h1', '白马') } } }

而此时,页面中的内容只剩下return h('h1', '白马')这里指定渲染的内容了

关于你可以在vue3中按照vue2那样写 点击测试 export default { name: "test2", data () { return { name2:'ls' } }, created () { }, methods: { test1 () { console.log(this.name) console.log(this.name2) } }, setup () { const name = 'zs' return { name } } }

像是上面的代码,可以访问到setup中的变量

注意,setup不可以使用async修饰

因为如果被async修饰了,那么它的返回值就不再是对象了,而是一个被promise包裹的对象,而你的模板不认识这个返回值

ref

当初在使用vue2的时候,使用ref是为了给元素打标识之类的作用 在vue3中ref是个函数,原来的没有废掉

非响应式数据:

像是下面一样,定义了非响应式的数据:

一个人的信息: 姓名:{{ name }} 年龄:{{ age }} 修改数据 export default { setup () { let age = 24 let name = 'zs' const changeInfo = () => { name = '李四' age = 50 console.log(name,age) } return { age, name, changeInfo } } }

这里的按钮点击了,在控制台输出的值会更改,但是页面中的值并没有发生改变,因为此时他不是响应式数据

虽然这里不是响应式数据了,但是也没有一些多余的getter,setter,当你想要使用响应式数据的时候,可以进行按需引入ref来使用

例如下面的:

一个人的信息: 姓名:{{ name }} 年龄:{{ age }} 爱好:{{ hobby }} 修改数据 修改爱好 import { ref } from 'vue' export default { setup () { let age = 24 let name = 'zs' let hobby = ref('唱跳rap,篮球') const changeInfo = () => { name = '李四' age = 50 console.log(name, age) } const changeHobby = () => { hobby.value = '学习' } return { age, name, changeInfo, hobby, changeHobby } } }

此时如果你直接输出一下hobby这个响应式对象,可以发现它是:

{ "__v_isShallow": false, "dep": { "w": 0, "n": 0 }, "__v_isRef": true, "_rawValue": "学习", "_value": "学习" }

这样的 而这里的RefImpl的意思是引用的实现

标准的称呼为引用实现对象,或是引用对象,简称ref对象

在vue3的模板中,解析html部分的{{}}内容时,它会自动.value

ref中的对象会使用proxy来包装

例如下面的代码:

setup () { let job = ref({ salary: 30, work: 'black man' }) const getObj = () => { console.log(job.value) } getObj() }

这里输出的对象是: ![[Pasted image 20230603141346.png]] 这里的对象包装方式不同了

像是refimpl的实例对象,是通过getter,setter实现响应式的

ref处理对象的时候,会将里面的对象使用proxy来包装

对象类型的数据,内部会求助vue3中的一个新函数:reactive

reactive函数

作用:定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数) 语法:const 代理对象 = reactive(源对象)接收一个对象(或数据),返回一个代理对象(proxy对象) reactive定义的响应式数据是深层次的 内部基于es6的proxy实现,通过代理对象操作源对象内部数据进行操作

reactive函数处理不了基本类型

用reactive处理对象要比ref简单,像是下面这个:

setup () { let job = ref({ salary: 30, work: 'black man' }) let company=reactive({ name:'武汉轻工大学', year:4, numbers:9000 }) const getObj = () => { console.log(job.value) console.log("company:") console.log(company) console.log(company.name) } getObj() }

直接.属性名即可,不用在.value之后再.属性名

vue3中的响应式原理

vue2的响应式: 实现原理:

对象类型: 通过Object.defineProperty对属性的读取,修改进行拦截(数据劫持)数组类型:通过重写更新数组的一系列方法来实现拦截(对数组的变更方法进行了包裹) Object.defineProperty(data,'count',{ get(){}, set(){} }) 存在问题: 新增属性,删除属性,界面不会更新 像是下面的代码在vue2环境下跑: 测试vue2中为对象新增/删除属性是否能被监听到 name:{{ person.name }} age:{{ person.age }} gender:{{ person.gender }} school:{{ person.school }} sclary:{{ person.sclary }} 删除school属性 添加sclary属性 export default { name: "Index", data () { return { person: { name: '椎间孔', age: 22, gender: '大三', school: '武汉轻工大学' } } }, methods: { delAttr () { delete this.person.school console.log('删除之后的对象:') console.log(this.person) }, addAttr () { this.person.sclary = 3000 console.log('添加之后的对象:') console.log(this.person) } } }

依次点击删除和添加: ![[Pasted image 20230603170440.png]] 对象是发生了改变,但是页面中没有改变

监听不到,那么你可以使用this.$set来设置: this.$set(this.person,'sclary','3000')

那么此时添加属性,页面上也会有响应的

实现同样的目的也可以使用vue.set: import Vue from 'vue' // 使用它之前要引入 Vue.set(this.person, 'sclary', '3000') 实现响应式的对象删除属性: Vue.set(this.person, 'sclary', '3000') // 下面这个: this.$delete(this.person, 'school') // 或者: Vue.delete(this.person,'school') 对象中的数组实现响应式: import Vue from 'vue' export default { name: "Index", data () { return { person: { name: '椎间孔', age: 22, gender: '大三', school: '武汉轻工大学', hobby: ['唱', '跳', 'tap', '篮球'] } } }, methods: { change () { // 下面两种方式都可以 // this.$set(this.person.hobby, 0, '逛街') // Vue.set(this.person.hobby, 0, '洗黑钱') // 当然你也可以使用数组的方法来修改: this.person.hobby.splice(0,1,'洗钱') } } }

vue3的响应式: 实现原理:

通过proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写,属性的添加,属性的删除等通过Reflect(反射):对被代理对象的属性进行操作MDN文档中描述的Proxy与Reflect:

实现一个proxy的响应式对象:

const person = { name: 'zs', age: 18 } const p = new Proxy(person, { get (target, propName) { console.log('有人读取了') return target[propName] }, set (target, propName, value) { console.log(`将${ propName }修改为${ value }`) return target[propName] = value } })

在控制台尝试使用: ![[Pasted image 20230604000442.png]]

![[Pasted image 20230604000624.png]]

但是像上面的代码,捕获到了他的更新和读取,并没有捕获到它的删除和新增属性 其实proxy也可以监听删除:

const person = { name: 'zs', age: 18 } const p = new Proxy(person, { get (target, propName) { console.log('有人读取了') return target[propName] }, set (target, propName, value) { console.log(`将${ propName }修改为${ value }`) target[propName] = value }, deleteProperty (target, p) { console.log(`删除属性${p}`) delete target[p] } })

![[Pasted image 20230604002915.png]] 要注意,delete target[p]这里的删除是有返回值的,其实把这里的删除返回即可:

deleteProperty (target, p) { console.log(`删除属性${p}`) return delete target[p] }

关于proxy的增加属性:

get是有人读取某个属性时会被调用set有人修改属性,或追加属性时会被调用deleteProperty删除属性时调用

虽然proxy可以监听到增删改查,但是vue3不是这样做的 ![[Pasted image 20230604003421.png]]

使用两种方式实现对象的映射 let obj = { a: 1, b: 2 } /* console.log('使用Object.defineProperties来实现映射:') Object.defineProperties(obj, 'c', { get () { return 3 } }) Object.defineProperties(obj, 'c', { get () { return 4 } })*/ console.log('使用reflect来实现映射') const x1 = Reflect.defineProperty(obj, 'c', { get () { return 3 } }) console.log(x1) const x2 = Reflect.defineProperty(obj, 'c', { get () { return 4 } }) console.log(x2)

第一种方式使用Object.defineProperties重复覆盖属性会报错,此时可以使用try,catrch来捕获,但是为了程序的壮行这样是否有些麻烦了 所以可以使用Reflect.defineProperty来进行映射,它重复覆盖并不会出错,而且在每一次覆盖是都会有一个为boolean的返回值

vue3中响应式对象的雏形: let person = { name: 'zs', age: 24, gender: '大三', school: '武汉轻工大学' } const p = new Proxy(person, { get (target, propName) { console.log(`有人获取了${ propName }`) return Reflect.get(target, propName) }, set (target, propName, value) { console.log(`有人修改了${ propName }`) Reflect.set(target, propName, value) }, deleteProperty (target, propName) { console.log(`有人删除了p身上的${ propName }`) return Reflect.deleteProperty(target, propName) }}) reactive对比ref

从定义数据角度对比: ref用来定义:基本类型数据 reactive用来定义:对象(或数组)类型数据 备注:ref也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive转为代理对象

从原理角度对比: ref通过Object.defineProperty()的get与set来实现响应式(数据劫持) reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据

从使用角度对比: ref定义的数据:操作数据需要.value,读取数据时,模板中直接读取不需要.value reactive定义的数据:操作数据与读取数据:均不需要.value

关于vue2中的props

如果你像是在下面的子组件中不写props: 父组件:

import Vue from 'vue' import Chindren from '@/components/chindren.vue' export default { name: "Index", components: { Chindren }, } }

子组件:

export default { name: "chindren", mounted () { console.log(this) } }

注意,此时在子组件的props中是没有定义的 ,但是在页面的控制台可以看到输出的this: ![[Pasted image 20230605234406.png]] 在vm的$attrs上挂载了传入的值

到这里你可能想着,不定义props,直接传过来多方便, 但是,如果不定义props的话,你是无法限制传入的类型

关于vue2的slot

在子组件中不去定义slot: 父组件:

这是一个slot的内容

子组件:

export default { name: "chindren", mounted () { console.log(this) } }

控制台中: ![[Pasted image 20230605234843.png]]

slot是存在的 使用多个插槽: 父组件:

张三 gender:5

子组件:

export default { name: "chindren", mounted () { console.log(this) } } setup的两个注意点:

setup的执行时机: 在beforeCreate之前执行一次,this是undefined

setup的参数 props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性 context:上下文对象

attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于this. a t t r s s l o t s : 收到的插槽内容 , 相当于 t h i s . attrs slots:收到的插槽内容,相当于this. attrsslots:收到的插槽内容,相当于this.slota emit:分发自定义事件的函数,相当于this.$emit

例如在下面的例子中,父组件向子组件传值,但是子组件并没有定义props: 父组件:

你好 import Demo from './demo.vue' export default { name: "setup", components: { Demo } }

子组件:

子组件的内容 export default { name: "demo", setup (props) { console.log('setup props:') console.log(props) } }

控制台输出: ![[Pasted image 20230611142633.png]] 如果此时在子组件中定义了props:

子组件的内容 export default { name: "demo", props:['msg','index'], setup (props) { console.log('setup props:') console.log(props) } }

此时setup中接收的第一个参数输出: ![[Pasted image 20230611143321.png]] 注意,此时它也是响应式对象

那么关于传入的其他形参: 父组件:

你好 这是第一个slot 自定义插槽 自定义插槽的第二个插槽 import Demo from './demo.vue' export default { name: "setup", components: { Demo }, setup () { const hello = () => { console.log('hello world') } return { hello } } }

子组件:

子组件的内容 测试触发子组件事件 export default { name: "demo", props: ['msg', 'index'], emits: ['hello'], setup (props, context) { console.log('setup props:') console.log(props) console.log('context.attrs:') // vue2中的$attrs console.log(context.attrs) console.log('context.emit:') // 插槽 console.log('context.slots:') console.log(context.slots) const log = () => { console.log('触发了子组件的click') } return { log } }, }

在控制台中查看:

![[Pasted image 20230611144611.png]]

计算属性 性: 名: 名字: import { computed, ref } from 'vue' const lastName = ref('') const firstName = ref('') // 简写,没有考虑计算属性被修改的情况 /*const fullName = computed(() => { return lastName.value + ' ' + firstName.value})*/ // 完整写法 const fullName = computed({ get () { return lastName.value + ' ' + firstName.value }, set (val) { console.log(val) }}) .computed { display: flex; flex-direction: row; .inputLim { display: flex; flex-direction: row; } }

![[Pasted image 20230611162743.png]]

watch属性

一个简单的使用:

sum:{{ sum }} add 当前的信息为:{{ msg }} msg改变 姓名:{{ person.name }} 年龄:{{ person.age }} 学校:{{ person.school }} 薪资:{{ person.job.salary }} 注意在vue3中只要对象使用active包裹,那么不管多深,都是可以监听到的 修改名字 修改年龄 修改学校 薪资增加 import { reactive, ref, watch } from 'vue' let sum = ref(0) let msg = ref("你好") let person = reactive({ name: 'zs', age: 24, school: '武汉', job: { salary: '3000' } }) // 监视ref所定义的数据 watch(sum, (newVal) => { console.log('sum发生改变') }) // 监视ref所定义的多个数据 watch([sum, msg], (newVal, oldVal) => { console.log(newVal, oldVal) }) // 配置一上来就监听 watch(sum, (newVal) => { console.log(newVal) }, { immediate: true }) // 监听reactive 定义的数据, // 如果监听使用reactive 定义的数据,那么在获取oldVal上会有问题,无法正确地获取oldVal,目前无法解决 // 但如果你使用ref来定义一个对象,它走的还是reactive的逻辑,也是行不通的 watch(person, (newVal, oldVal) => { console.log("newVal:") console.log(newVal) console.log("oldVal:") console.log(oldVal) }) vue3中无法正确监听到对象中watch函数中的oldVal

像是上面代码中,监听persojn的修改中,修改学校字段,此时在控制台查看: ![[Pasted image 20230612232234.png]] 注意,不论你是用ref还是reactive包裹对象,他都是无法监听到oldVal的,这是目前vue3中的问题 当你使用ref包裹对象时,它会自动使用reactive来包裹对象的

监听多个 let sum = ref(0) let msg = ref("你好") watch([sum, msg], (newVal, oldVal) => { console.log(newVal, oldVal) })

在vue2中watch为配置对象,只能调用一次,而vue3中 ,watch为函数,可以调用多次

监听中的配置项

一上来就监听一次:

watch(sum, (newVal) => { console.log(newVal) }, { immediate: true }) vue3中可以监听嵌套很深的对象

强制开启了deep,你关闭了也没用

监听对象中的某一属性 let person = reactive({ name: 'zs', age: 24, school: '武汉', job: { salary: '3000' } }) watch(() => person.name, (newVal, oldVal) => { console.log('值发生改变了') console.log(newVal) })

![[Pasted image 20230612235713.png]] 此时你修改其它字段是不会被监听到



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3