2024总结的vue3的面试题

一、vue2和vue3的区别

答案:

1、数据绑定原理不同

vue2:vue2的数据绑定是利用ES5的一个API:Object.definePropert() 对数据进行劫持,结合发布订阅模式的方式来实现的。

vue3:vue3中使用了ES6的Proxy API对数据代理。相比vue2.x,使用proxy的优势如下:

defineProperty只能监听某个属性,不能对全对象监听

可以省去for in,闭包等内容来提升效率(直接绑定整个对象即可)

可以监听数组,不用再去单独的对数组做特异性操作vue3.x可以检测到数组内部数据的变化。

2、是否支持碎片

vue2:vue2不支持碎片。

vue3:vue3支持碎片(Fragments),就是说可以拥有多个根节点。

3、API类型不同

vue2:vue2使用选项类型api,选项型api在代码里分割了不同的属性:data,computed,methods等。

vue3:vue3使用组合式api,新的合成型api能让我们使用方法来分割,相比于旧的api使用属性来分组,这样代码会更加简便和整洁。

4、定义数据变量和方法不同

vue2:vue2是把数据放入data中,在vue2中定义数据变量是data(){},创建的方法要在methods:{}中。

vue3:,vue3就需要使用一个新的setup()方法,此方法在组件初始化构造的时候触发。使用以下三个步骤来建立反应性数据:

从vue引入reactive;

使用reactive() 方法来声明数据为响应性数据;

使用setup()方法来返回我们的响应性数据,从而template可以获取这些响应性数据。

5、生命周期钩子函数不同

vue2:vue2中的生命周期:

beforeCreate 组件创建之前
​
created 组件创建之后
​
beforeMount 组价挂载到页面之前执行
​
mounted 组件挂载到页面之后执行
​
beforeUpdate 组件更新之前
​
updated 组件更新之后

vue3:vue3中的生命周期:

setup 开始创建组件前
​
onBeforeMount 组价挂载到页面之前执行
​
onMounted 组件挂载到页面之后执行
​
onBeforeUpdate 组件更新之前
​
onUpdated 组件更新之后

而且vue3.x 生命周期在调用前需要先进行引入。除了这些钩子函数外,vue3.x还增加了onRenderTracked 和onRenderTriggered函数。

6、父子传参不同

vue2:父传子,用props,子传父用事件 Emitting Events。在vue2中,会调用this$emit然后传入事件名和对象。

vue3:父传子,用props,子传父用事件 Emitting Events。在vue3中的setup()中的第二个参数content对象中就有emit,那么我们只要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了。

7、指令与插槽不同

vue2:vue2中使用slot可以直接使用slot;v-for与v-if在vue2中优先级高的是v-for指令,而且不建议一起使用。

vue3:vue3中必须使用v-slot的形式;vue3中v-for与v-if,只会把当前v-if当做v-for中的一个判断语句,不会相互冲突;vue3中移除keyCode作为v-on的修饰符,当然也不支持config.keyCodes;vue3中移除v-on.native修饰符;vue3中移除过滤器filter。

8、main.js文件不同

vue2:vue2中我们可以使用pototype(原型)的形式去进行操作,引入的是构造函数。

vue3:vue3中需要使用结构的形式进行操作,引入的是工厂函数;vue3中app组件中可以没有根标签。

9、Vue3带来了什么

更快的渲染速度:Vue3使用了Proxy代理对象,可以更快地跟踪数据变化,从而提高渲染速度。

更小的体积:Vue3的体积比Vue2更小,同时也支持按需加载,减少了页面加载时间。

更好的TypeScript支持:Vue3对TypeScript的支持更加完善,可以更好地进行类型检查和代码提示。

更好的组件封装:Vue3引入了Composition API,可以更好地封装组件逻辑,使得组件更加可复用和易维护。

更好的响应式系统:Vue3的响应式系统进行了重构,可以更好地处理嵌套对象和数组的变化,同时也提供了更多的API来处理响应式数据。

总之,Vue3带来了更好的性能、更小的体积、更好的TypeScript支持、更好的组件封装和更好的响应式系统,使得开发者可以更加高效地开发Vue应用。

二、vue2和vue3响应式原理(Object.defineProperty和Proxy的区别)

一、 vue2的响应式原理
​
1、vue2的响应式实现主要是利用了Object.defineProperty的方法里面的setter 与getter方法,来完成数据劫持
​
2、使用观察者模式来实现响应式的变化。
​
3、具体做法:在组件初始化时会给data的每一个属性注册getter和setter,然后再new 一个自己的Watcher对象,此时watcher会立即调用组件的render函数去生成虚拟DOM。在调用render的时候,就会需要用到data的属性值,此时会触发getter函数,将当前的Watcher函数注册进sub里(订阅)。当data属性发生改变之后,就会遍历sub里所有的watcher对象,通知它们去重新渲染组件(发布)。
3、Vue2的响应式原理存在一些缺陷,例如无法监听数组的变化,需要通过特殊的方法来实现
​
​
二、vue3的响应式原理:
​
vue3用Proxy代替了Object.defineProperty。
​
1、Proxy 可以直接监听对象而非属性,可以直接监听数组的变化;
2、Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的。
3、Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
4、Proxy也解决了数组监听的问题,不用再去单独的对数组做特异性操作vue3.x可以检测到数组内部数据的变化。也解决了用下标修改数组元素不是响应式的问题。
5、Proxy的性能比Object.definePropery高10倍。
6、Vue3使用了WeakMap来存储依赖关系,避免了Vue2中Watcher的内存泄漏问题。
​

三、组合式api和选项式api的区别

1、组织代码的方式:

1)、选项式api是按照选项(配置项)的思路来分隔组织代码的,例如 data、methods 和 mounted。这些选项所定义的属性都会暴露在函数内部的 this 上,指向当前的组件实例。

2)、组合式api是按照业务逻辑的方式分隔组织代码的。组合式api中使用函数的方式代替选项式api的配置项。让代码组织更加灵活,在任何需要的地方调用对应的函数。比如:在需要定义响应式数据的地方,直接调用ref或者reactive函数既可以(不需要像选项式api那样,非得写在data配置项里)。

2、代码的复用:

1)、选项式api使用mixin来完成,mixin里也是配置项。容易发生命名冲突且关系不清。不够灵活(如果:只需要引入某个函数,就需要把整个混入对象都引入,颗粒度不够小)。

2)、组合式api使用组合式函数的方式,让代码更加灵活,复用性更好,组件里面的逻辑可以进行模块化。也不存在this的问题。

3、对TS的支持:

组合式api对TS的支持更好。

4、单元测试:

组合式api由于使用的是函数的方式,所以,更方便做单元测试。

4、使用场景:

1)、选项式api适用于小型和中型项目。否则,代码逻辑复杂后,导致组件关系复杂,单个组件内部的代码太多。

2)、组合式api适用于大型项目,当然小中项目也是没有问题。

四、vue3新增的响应式相关的函数

答案:

ref,reactive,readonly,computed,watch,watchEffect

1、ref的理解:

答案:

1)、功能:接受一个内部值,返回一个响应式的、可更改的 ref 对象,ref对象只有一个属性:value。

2)、使用ref对象:模板上不需要写 .value 属性(会自动解构),在js中,使用 .value 来完成数据的读写。

3)、ref接收基本类型和引用类型

  • ref可以接收基本类型。

  • ref也可以接收引用类型:如果将一个对象传给 ref函数,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。

  • ref还可以获取组件对象。

4)、原理:

对于传入的基本数据类型----采用objct.defineproperty给value属性做一个getter和setter

引用类型----采用proxy进行包装代理

2 、reactive的理解:

答案:

1)、功能: 接受一个对象,返回一个对象的响应式代理(proxy)。返回的对象以及其中嵌套的对象都会通过 ES Proxy 包裹,因此不等于源对象,建议只使用响应式代理,避免使用原始对象。

响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。

2)、注意点:当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。

五、ref和reactive的区别:

相同点:

都是定义响应式数据的

不同点:

1)、可接受的数据类型

ref:可以接受基本类型和引用类型

reactive:只能接受引用类型

2)、使用

ref:模板上不需要写 .value 属性(会自动解构),在js中,使用 .value 来完成数据的读写。

reactive:是Proxy对象,所以,直接使用

3)、原理:

ref:针对基本类型会使用Object.defineProperty给value属性做一个getter和setter,针对引用类型,会使用Proxy。

reactive:背后使用reactive。

六、补充:对比选项式和组合式的对应:

1、定义响应式数据:

选项式在data配置项中,组合式用函数:ref,reactive

2、计算属性:

选项式在computed配置项中,组合式用函数:computed

3、监听(侦听)

选项式在watch配置项中,组合式用函数:watch和watchEffect

4、方法(函数):

选项式在methods配置项中,组合式就使用原生的方式定义函数就行

5、路由: 选项式在main.js中使用Vue.use(router对象),然后在组件里通过 this.$router和this.$route

组合式中使用钩子函数:useRouter()和useRoute()获取

6、vuex

选项式在main.js中使用Vue.use(仓库对象),然后在组件里通过 this.$store

组合式中使用钩子函数:useStore()获取

7、总结:

选项式中的一切,在组合式api中,都用函数完成。

七、toRef和toRefs

1、相同点: ​ toRef 和 toRefs 可以用来复制 reactive 里面的属性然后转成 ref,而且它既保留了响应式,也保留了引用,也就是你从 reactive 复制过来的属性进行修改后,除了视图会更新,原有 ractive 里面对应的值也会跟着更新,如果你知道 浅拷贝 的话那么这个引用就很好理解了,它复制的其实就是引用 + 响应式 。

2、不同点:

toRef: 复制 reactive 里的单个属性并转成 ref

toRefs: 复制 reactive 里的所有属性并转成 ref

八、shallowReactive 与 shallowRef

1、shallowRef:只处理基本数据类型的响应式

2、shallowReactive:只处理对象最外层属性的响应式(浅响应式)

3、浅层作用的响应式数据处理:只处理第一层对象的数据,再往下嵌套的数据,操作数据是不起作用的

4、shallowReative与shallowRef在某些特殊的应用场景下,是可以提升性能的,前者针对对象,用于浅层作用的响应式数据处理,而后者只处理基本数据类型的响应式,不进行对象的响应式处理。

九、readonly 与 shallowReadonly【扩展】

readonly与shallowReadonly都是让响应式数据只具备读的能力,后者是浅层次的只读,也就是只对数据对象第一层起作用,深层次的嵌套,当时用shallowReadonl()处理时,深层次数据支持被修改

1、readonly: 让一个响应式数据变为只读的 (深只读),让一个响应式数据变为只读的,接收一个响应式数据,经过readonly加工处理一下,那么新赋值的数据都不允许修改

2、接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理

3、shallowReadonly: 让一个响应式数据变为只读的 (浅只读),接收一个响应式数据,经过shallowreadonly的处理,变成一个只读的,只考虑对象的第一层数据,不可以修改,但是第一层嵌套里的深层数据却支持修改

4、让一个响应式数据变为只读能力(浅只读)

十、toRaw与markRaw转换为普通数据和标记属性非响应式【扩展】

toRaw,将响应式对象(由 reactive定义的响应式)转换为普通对象,然后赋值给新的变量(不影响原来的对象)

markRaw,标记一个对象,使其不能成为一个响应式对象。

toRaw: 作用: 将一个由 reactive 生成的响应式对象转为普通对象 使用场景:

1、用于读取响应式对象对应的普通对象

2、对这个普通对象的所有操作,不会引起页面更新。

markRaw: 作用:标记一个对象,使其永远不会再成为响应式对象 应用场景: 1、有些值不应被设置为响应式的,例如复杂的第三方类库等 2、当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能

十一、自定义hooks(组合式函数)的规则和作用:

1、函数名以小写的use开头

2、hooks里可以使用vue组件的相关特性(如:生命周期钩子函数,响应式数据等等)

3、hooks调用:

1)、hooks必须在setup函数的根级调用,即:在setup函数执行时,hooks就要被调用

2)、hooks函数可以在其它hooks函数里调用。

4、可以补充:

1)、参数尽量兼容响应式数据和普通数据

2)、返回值最好时由多个响应式数据组成的普通对象(json对象)

5、作用:

以函数形式抽离一些可复用的方法像钩子一样挂着,放在外部的js文件里。随时可以引入和调用,实现高内聚低耦合的目标;引用时将响应式变量或者方法显式解构暴露出来如:const {nameRef,Fn} = useXX()

十二、Vue3自定义Hooks和Vue2时代Mixin的关系:

1)、Mixin 很容易发生冲突:因为每个 mixin 的 property 都被合并到同一个组件中,所以为了避免 property 名冲突,你仍然需要了解其他每个特性。

2)、mixin的可重用性是有限的:我们不能向 mixin 传递任何参数来改变它的逻辑,这降低了它们在抽象逻辑方面的灵活性。

3)、Mixin同名变量会被覆盖,Vue3自定义Hook可以在引入的时候对同名变量重命名

十三、setup配置(setup函数的参数)

Vue 3中的 setup 函数接收两个参数,分别是 props 和 context。

1、props:父组件传来的属性值

包含: 组件外部传递过来。切组件内部声明接收了的属性。需要注意的是,Vue3中的 props 是只读的,即在setup 函数中不能修改 props 的值。如果需要修改传递过来的数据,可以使用响应式对象或ref。

2、context:上下文对象。

1)、attrs:值为对象,包含组件外部传递过来,但没有在props配置中声明的属性。相当于this.$attrs

2)、slots:收到的插槽内容,相当于this.$slots

3)、emit:分发自定义事件的函数,相当于this.$emit

注意:

1、这个钩子会在created之前执行

2、内部不存在this

3、如果返回值是一个对象,那么这个对象中的键值对会被合并到created钩子的this中,而在视图上也能访问相应的数据值

十四、watch与watchEffect

相同点:

watch和watchEffect都是vue3中的监听器,但是在写法和使用上是有区别的,

不同点:

1)、监听源:

watch函数的监听源会明确指定,是watch函数的第一个参数。

watchEffect函数的监听源是回调函数中使用的所有的响应式数据。自动收集依赖。

2)、懒侦听:

watch默认是懒侦听,如果要立即执行,则设置第三个参数的配置项immediate 为true。

watchEffect不是懒侦听,组件一旦执行,那么,回调函数立即会调用

3)、深度侦听:

watch默认不是深度侦听,如果需要,则设置第三个参数的配置项deep为true。

watchEffect默认是深度侦听的。

4)、是否能够拿到旧值:

watch可以。

watchEffect不可以。

十五、provide与inject

1、provide和inject是一对新的API,用于在父组件中提供数据,然后在子组件中注入数据。

2、provide:是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值。如果希望提供的数据是响应式的,那么,值就需要是响应式的数据。

3、inject:一个字符串数组,或者是一个对象。属性值可以是一个对象,包含from和default默认值。

//在父组件中,使用provide提供数据:
//name:定义提供 property的 name。
//value :property的值。setup(){provide('info',"值")}
​
//在子组件中,使用inject注入数据
//name:接收 provide提供的属性名。
//default:设置默认值,可以不写,是可选参数。
setup(){const info = inject("info")inject('info',"设置默认值")return {info}}

十六、vue3新的生命周期钩子

1、Vue3中新增了三个生命周期钩子函数:

onRenderTracked:在组件初次渲染时,可以跟踪到是哪个属性触发了更新。函数的参数(是个对象)里的key就是依赖的属性

onRenderTriggered:在组件更新时,可以跟踪到是哪个属性触发了更新。函数的参数(是个对象)里的key就是依赖的属性

setup函数,调用时机比beforeCreate还早。

2、修改:

vue2中的beforeDetory用beforeUnmount替代

vue2中的detoryed用unmounted替代

3、在组合式api中。

1)、没有提供beforeCreate和created对应的函数,用setup代替

2)、其它的生命周期钩子函数,都是在选项式的单词前面加上了on。

十七、响应式数据的判断isRef、isReactive、isReadonly、isProxy

1、isRef:判断一个值是否为一个 ref 对象

2、isReactive:判断一个对象是否是由 reactive创建的响应式代理

3、isReadonly:判断一个对象是否是由 readonly 创建的只读代理

4、isProxy:判断一个对象是否是由 reactive 或 readonly 创建的代理

十八、vue3中使用插槽?

1、作用:插槽 slot 通常用于父子组件之间,父组件可以改变子组件内部的部分模板,给布局带来了很大的灵活性。

2、使用:

1)、slot 是 Vue 中的内置标签。

2)、在子组件的模板里使用<slot></slot>的方式制定插槽的位置,也就是父组件传递(分发)来的标签所在的位置。在父组件里只需要把插入插槽的标签放在一对子组件标签里(相当于html中,一个标签的内容)。

3、具名插槽:具有名字的 插槽。

定义插槽:

如果在子组件里需要提供多个插槽,就需要给插槽起名字。这就是具名插槽。在组件里写上如此格式的代码:<slot name="xx"> 。

2)、给插槽分发内容。

在父组件里,建议使用<template>标签包裹插槽的内容,代码有几种写法:

2.1)、官方标签上使用slot属性

<div slot="插槽名">我是div1</div>

2.2)、内容比较多时,可以使用<template>包起来。

<template>里,可以使用slot属性,也可以使用v-slot:,v-slot:可以简写为#

2.2.1)、slot属性:

<template slot="插槽名"><div></div><div></div>
</template>
​

2.2.2)、 v-slot:插槽名

<template v-slot:插槽名><div></div><div></div>
</template>

2.2.3)、 v-slot 可以缩写为:#

<template #插槽名><h1>Here might be a page title</h1>
</template>

4 、作用域插槽:

插槽里的内容(在父组件的模板上)的数据希望来自于子组件(即就是插槽的内容的作用域变成子组件)。

1)、子组件里定义插槽(的出口),并传递数据。

<slot name="插槽名" 属性名1="属性值1" 属性名2="属性值2"></slot>

2)、父组件中给插槽分发内容,插槽里使用子组件传递的数据。

声明或者定义一个对象作为插槽名的值#插槽名=对象名。该对象就是slot标签里所有属性组成的键值对。

<子组件标签><template  #插槽名=对象名><div>{{对象名.属性值1}}</div><div>{{对象名.属性值2}}</div></template>
</子组件标签>

十九、vue3中使用vue-router,useRoute和useRouter ?

1、在Vue.js中,useRoute和useRouter是Vue Router提供的两个钩子函数,用于在组件中访问路由信息和路由实例。

2、useRoute()的返回值相当于是vue2中的this.$route

import { useRoute } from 'vue-router'
export default {setup() {const route = useRoute()console.log(route.path) // 当前路由路径return {route}}
}
​

3、useRouter()的返回值相当于是vue2中的this.$router,可以使用useRouter进行路由跳转

import { useRouter } from 'vue-router'
export default {setup() {const router = useRouter()console.log(router.currentRoute.value.path) // 当前路由路径return {router}}
}

二十、vue3中nextTick使用

vue3组合式api中,使用 nextTick()函数,是独立的函数,不需要像选项式api那样,通过this.$nextTick()。作用和vue2中一样。

二十一、vue3中定义全局属性

1、通过config.globalProperties

2、通过provide注入:在应用实例上设置一个可以被注入到应用范围内所有组件中的值。当组件要使用应用提供的 provide 值时,必须用 inject 来接收。

3、在main.js中全局引入,然后在组件中获取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/587536.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据学习第十二天(hadoop概念)

1、服务器之间数据文件传递 1&#xff09;服务器之间传递数据&#xff0c;依赖ssh协议 2&#xff09;http协议是web网站之间的通讯协议&#xff0c;用户可已通过http网址访问到对应网站数据 3&#xff09;ssh协议是服务器之间&#xff0c;或windos和服务器之间传递的数据的协议…

ubuntu-server部署hive-part4-部署hive

参照 https://blog.csdn.net/qq_41946216/article/details/134345137 操作系统版本&#xff1a;ubuntu-server-22.04.3 虚拟机&#xff1a;virtualbox7.0 部署hive 下载上传 下载地址 http://archive.apache.org/dist/hive/ apache-hive-3.1.3-bin.tar.gz 以root用户上传至…

UE4 方块排序动画

【动画效果】 入动画&#xff1a; 出动画&#xff1a; 【分析】 入动画&#xff1a;方块动画排序方式为Z字形&#xff0c;堆砌方向为X和Y轴向 出动画&#xff1a;方块动画排序方式为随机 【关键蓝图】 1.构建方块砌体 2.入/出动画

wife_wife【web 攻防世界】

大佬的wp:WEB&#xff1a;Wife_wife-CSDN博客 知识点&#xff1a; prototype是new class 的一个属性&#xff0c;即__proto__指向new class 的prototype属性__proto__如果作为json代码解析的话会被当成键名处理&#xff0c;但是如果是在类中的话则会被当成子类的原型 如let o…

提升常州小程序软件开发的搜索排名:关键步骤解析

在移动互联网的浪潮中&#xff0c;小程序作为连接用户与服务的桥梁&#xff0c;其重要性日益凸显。对于常州的小程序软件开发企业来说&#xff0c;如何让自己的产品在浩如烟海的互联网信息中脱颖而出&#xff0c;提升搜索排名&#xff0c;成为了亟待解决的问题。本文将为您解析…

windows系统kafka小白入门篇——下载安装,环境配置,入门代码书写

目录 1. kafka 下载 2. 修改配置文件 2.1 文件夹内容 2.2 创建一个 data 空文件夹 2.3 修改 zookeeper.properties 配置文件 2.4 修改 server.properties 配置文件 2.5 创建 "zk.cmd" windows脚本文件 2.6 创建 "kfk.cmd" windows脚本文件 3. 启动…

Windows下用CMake编译PugiXML及配置测试

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 PugiXML是什么&#xff1f; PugiXML 是一个用于解析和操作 XML 文档的 C 库。它提供了简单易用的接口&#xff0c;能够高效地加载…

Javascript/Node.JS中如何用多种方式避免属性为空(cannot read property of undefined ERROR)

>>>>>>问题 "cannot read property of undefined" 是一个常见的 JavaScript 错误&#xff0c;包含我在内很多人都会遇到&#xff0c;表示你试图访问一个未定义&#xff08;undefined&#xff09;对象的属性。这通常是因为你在访问一个不存在的对象…

视频基础学习四——视频编码基础一(冗余信息)

文章目录 前言一、编码压缩的原理1.空间冗余帧内预测 2.时间冗余帧间预测运动估计运动补偿 3.编码冗余4.视觉冗余 二、压缩编码的流程1.编码器2.编解码流程 总结 前言 上一篇文章介绍了视频帧率、码率、与分辨率。也介绍了为什么需要对视频进行压缩&#xff0c;因为720P、rgb2…

C++的并发世界(四)——线程传参

1.全局函数作为传参入口 #include <iostream> #include <thread> #include <string>void ThreadMain(int p1,float p2,std::string str) {std::cout << "p1:" << p1 << std::endl;std::cout << "p2:" <<…

ElementUI 表格横向滚动条时滚动到指定位置

ElementUI 表格横向滚动条时滚动到指定位置 getColumnOffset(columnProp) {this.$nextTick(() > {const table this.$refs.tableRef.$refs.multipleTable;const columns table.columns;const column columns.find((col) > col.property columnProp);if (column) {// …

Docker:探索容器化技术,重塑云计算时代应用交付与管理

一&#xff0c;引言 在云计算时代&#xff0c;随着开发者逐步将应用迁移至云端以减轻硬件管理负担&#xff0c;软件配置与环境一致性问题日益凸显。Docker的横空出世&#xff0c;恰好为软件开发者带来了全新的解决方案&#xff0c;它革新了软件的打包、分发和管理方式&#xff…