2023年最新50道Vue全套vue2+vue3面试题带答案汇总此文章不断更新,欢迎大家在评论区补充1.什么是MVVM

M-Model数据:数据实体,用来保存页面要展示的数据。比如ajax获取的数据。V-View视图,显示数据的页面,一般为html。C-Controller控制器:控制整个业务逻辑,负责处理数据,比如数据的获取,以及数据的过滤,进而影响数据在视图上的展示。MVC是应用最广泛的软件架构之一,一般MVC分为:Model(模型),View(视图),Controller(控制器)。这主要是基于分层的目的,让彼此的职责分开.View一般用过Controller来和Model进行联系。Controller是Model和View的协调者,View和Model不直接联系。基本都是单向联系。M和V指的意思和MVVM中的M和V意思一样。C即Controller指的是页面业务逻辑。MVC是单向通信。也就是View跟Model,必须通过Controller来承上启下。

-Proxy可直接监听对象添加/删除属性;

-Proxy直接监听数组的变化

-Proxy监听的目标是对象本身,不需要像Object.defineProperty那样遍历每个属性,有一定的性能提升

-setup配置

-ref与reactive

-watch与watchEffect

-provide与inject

Vue3.0提供了CompositionAPI形式的生命周期钩子,与Vue2.x中钩子对应关系如下:

beforeCreate===>setup()

created===>setup()

beforeMount===>onBeforeMount

mounted===>onMounted

beforeUpdate===>onBeforeUpdate

updated===>onUpdated

beforeUnmount===>onBeforeUnmount

unmounted===>onUnmounted

-移除v-on.keyCode修饰符,同时也不再支持config.keyCodes

-移除v-on.native修饰符,

-移除过滤器(filter)

-不再建议使用mixins,组合式函数本身就是更好的mixin代替品

对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)

数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

存在问题:

1.新增属性、删除属性,界面不会更新。

2.直接通过下标修改数组,界面不会自动更新。

补充:vue2中$set可以解决这些问题。

通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、添加、删除等。

通过Reflect(反射):对源对象的属性进行操作。

newProxy(data,{//拦截读取属性值get(target,prop){returnReflect.get(target,prop)},//拦截设置属性值或添加新属性set(target,prop,value){returnReflect.set(target,prop,value)},//拦截删除属性deleteProperty(target,prop){returnReflect.deleteProperty(target,prop)}})proxy.name='tom'7.Vue3.0里为什么要用ProxyAPI替代definePropertyAPI?definePropertyAPI的局限性最大原因是它只能针对单例属性做监听。

Vue2.x中的响应式实现正是基于defineProperty中的descriptor,对data中的属性做了遍历+递归,为每个属性设置了getter、setter。这也就是为什么Vue只能对data中预定义过的属性做出响应的原因,在Vue中使用下标的方式直接修改属性的值或者添加一个预先不存在的对象属性是无法做到setter监听的,这是defineProperty的局限性。b.ProxyAPI的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作,这就完全可以代理所有属性,将会带来很大的性能提升和更优的代码。

Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。c.响应式是惰性的

在Vue.js2.x中,对于一个深层属性嵌套的对象,要劫持它内部深层次的变化,就需要递归遍历这个对象,执行Object.defineProperty把每一层对象数据都变成响应式的,这无疑会有很大的性能消耗。在Vue.js3.0中,使用ProxyAPI并不能监听到对象内部深层次的属性变化,因此它的处理方式是在getter中去递归响应式,这样的好处是真正访问到的内部属性才会变成响应式,简单的可以说是按需实现响应式,减少性能消耗。

Vue.js2.x中,如果有一个组件传入了slot,那么每次父组件更新的时候,会强制使子组件update,造成性能的浪费。Vue.js3.0优化了slot的生成,使得非动态slot中属性的更新只会触发子组件的更新。动态slot指的是在slot上面使用v-if,v-for,动态slot名字等会导致slot产生运行时动态变化但是又无法被子组件track的操作。

当页面第一次页面加载时会触发beforeCreate,created,beforeMount,mounted这几个钩子函数

性能更好

体积更小

更好地ts支持

更好的代码组织

更好的逻辑抽离

更多新的功能

1、性能提升

响应式性能提升,由原来的Object.defineProperty改为基于ES6的Proxy,使其速度更快,消除警告。

重写了Vdom,突破了Vdom的性能瓶颈。

进行模板编译优化。

更加高效的组件初始化。

2、更好的支持typeScript

有更好的类型推断,使得Vue3把typeScript支持得非常好。

3、新增CompositionAPI

CompositionAPI是vue3新增的功能,比mixin更强大。它可以把各个功能模块独立开来,提高代码逻辑的可复用性,同时代码压缩性更强。

4、新增组件

Fragment不再限制template只有一个根几点。

Teleport传送门,允许我们将控制的内容传送到任意的DOM中。

Supense等待异步组件时渲染一些额外的内容,让应用有更好的用户体验。

5、Tree-shaking:支持摇树优化

摇树优化后会将不需要的模块修剪掉,真正需要的模块打到包内。优化后的项目体积只有原来的一半,加载速度更快。

6、CustomRendererAPI:自定义渲染器

实现DOM的方式进行WebGL编程。

setup是围绕beforeCreate和created生命周期钩子运行的,所以不需要显示的定义它们。其他的钩子都可以编写到setup内。

vue2中我们把一个vue文件中data、methods、props、mounted、computed等定义属性和方法,共同处理页面逻辑,这种方式叫做OptionsAPI。

这种方式开发的复杂组件,同一个功能的往往需要在不同vue配置项中定义属性和方法,代码比较分散。如果功能比较复杂,维护代码的时候往往会很难分清每个方法对应的功能,增加了代码维护成本。所以vue3舍弃了OptionsAPI,换用CompositionAPI。

CompositionAPI是vue3新增的,所以vue2没有。在CompositionAPI中,根据代码逻辑功能来组织的,一个功能所定义的所有API都会放到一起,这样即使功能复杂,代码量增大,都可以一下子定位到某个功能的所有代码,代码维护方便。它的最大特点就是:高内聚,低耦合。

vue3仍然支持optionsAPI,但我们更推荐使用CompositionAPI。优劣比较:

更好的可编程性。

更优的代码组织。

更好的逻辑抽象能力。

对tree-shaking友好,代码也更容易压缩。

没有this

watch和watchEffect都是监听器,watchEffect是一个副作用函数。它们之间的区别有:

watch需要传入监听的数据源,而watchEffect可以自动手机数据源作为依赖。

watch可以访问倒改变之前和之后的值,watchEffect只能获取改变后的值。

watch运行的时候不会立即执行,值改变后才会执行,而watchEffect运行后可立即执行。这一点可以通过watch的配置项immediate改变。

答:vue双向数据绑定是通过数据劫持结合发布订阅模式的方式来实现的,也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;核心:关于VUE双向数据绑定,其核心是Object.defineProperty()方法,getset方法。简单地说,就是用这个方法来定义一个值。当调用时我们使用了它里面的get方法,当我们给这个属性赋值时,又用到了它里面的set方法;

Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。

beforeDestroy(){//在组件销毁前执行的逻辑//示例1:清理定时器//在`beforeDestroy`钩子中调用`cleanupTasks`方法,用于清理定时器等任务,以防止在组件销毁后出现内存泄漏。this.cleanupTasks();//控制台输出调试console.log('Componentisabouttobedestroyed.');}destroyed:在实例销毁之后调用。在这个阶段,实例的所有指令都已解绑,所有的事件监听器都已移除,所有的子组件实例也都被销毁。允许开发者在组件已经解绑定所有东西之后执行最终的清理工作destroyed(){//在组件销毁后执行的逻辑//示例1:释放资源//在`destroyed`钩子中调用`releaseResources`方法,用于释放资源等最终清理工作。this.releaseResources();//控制台输出调试console.log('Componenthasbeendestroyed.');},19.vue2生命周期的作用:他的生命周期中有多个事件钩子,让我们控制整个vue实例的过程时更容易形成良好的逻辑。生命周期钩子的一些使用方法:

如果是把之前的vue2项目升级到vue3,先卸载旧版本的vue-cli,安装最新版本。安装完成之后,检查vue的版本。然后需要注意,把项目中vue3发生改变或被废弃的特性需要进行修改。如:

filters被移除,更改为computed。

$destory被移除,需要删除掉。

插槽的新变化。

Vuex使用发生改变。

vue-router使用发生改变等等。

在vue2中v-for的优先级更高,但是在vue3中优先级改变了。v-if的优先级更高。

在vue2中v-for和v-if同时出现时,可放在一个标签内在vue3中这样写会报错,就是因为v-if的优先级更高,写到一个标签渲染的情况下会导致报错

scrtptsetup是vue3的语法糖,简化了组合式API的写法,并且运行性能更好。使用scriptsetup语法糖的特点:

属性和方法无需返回,可以直接使用。

引入组件的时候,会自动注册,无需通过components手动注册。

使用defineProps接收父组件传递的值。

useAttrs获取属性,useSlots获取插槽,defineEmits获取自定义事件。

默认不会对外暴露任何属性,如果有需要可使用defineExpose。

vue3支持webpack5的tree-shaking,因此现在所有api都需要导入后再使用:

ref:

常用:ref()、isRef()、unRef()、toRef()、toRefs()、shallowRef()

注:toRef()、toRefs()用在解构时仍希望数据为响应式时使用

reactive:

常用:reactive()、isReactive()、shallowReactive()、isProxy()、toRaw()readonly:只读,不可修改computed:

//写法1:接受一个getter函数,并根据getter的返回值返回一个不可变的响应式ref对象consta=computed(()=>{})//写法2:接受一个具有get、set函数的对象,用来创建可写的ref对象constb=computed({get:()=>{},set:(newValue)=>{},})watch:

constobj=reactive({a:{b:1}})conste=ref('e')constf=ref('f')watch(obj,(newValue,oldValue)=>{})watch(()=>obj.a.b,(newValue,oldValue)=>{})//监听对象里的某个属性,必须用getter返回,即必须写成()=>obj.a.b的形式(直接写成obj.a.b会报错)watch(()=>_.cloneDeep(obj),(newValue,oldValue)=>{})//深拷贝过后,监听的newValue,oldValue才会是前后值不一样,否则newValue,oldValue打印值一样watch([e,f],(newValue,oldValue)=>{})//vue3新增的写法,可同时监听多个ref数据,写成数组形式conststop=watch(obj,()=>{})stop()//停止监听watchEffect:立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数(写在watchEffect里的数据会被收集为其依赖,当这些依赖改变时才会触发watchEffect)

conststop=watchEffect(()=>{},{flush:'post'})//对写在回调函数里的所有数据监听stop()//停止监听24.Vue3中的Teleport是什么?它的作用是什么?Vue3中的Teleport是控制渲染位置的新指令。它的作用是在DOM中移动一个组件的内容而不改变组件的父级。

Vue3中的Suspense是Vue3中新增的一个组件,它的作用是实现延迟加载和错误处理。在组件中加入Suspense,可以让异步组件可以渲染出加载状态,并且如果异步组件加载时出现错误,也能够处理这些错误。

响应式系统允许在状态发生变化时更新视图。Vue3中的响应式系统更新包括Proxy、Reflect和WeakMap等。

Vue3CompositionAPI是Vue3中的一个新特性,它的作用是将组件中的逻辑分解成可复用的可组合函数。通过使用CompositionAPI,可以更好地组织代码和管理状态。

OptionsAPI生命周期

CompositionAPI生命周期

1.不丢失响应式的情况下,把对象数据进行分解和扩散2.针对响应式对象,不是普通对象3.不创造响应式,而是延续响应式32.setup中如何获取组件实例?setup和其他CompositionAPI中都没有this在OptionsAPI中仍然可以使用thisCompositionAPI中可以使用getCurrentInstance方法获取33.CompositionAPI和ReactHooks的对比前者setup(生命周期create)只会被调用一次,后者函数会被多次调用前者无需useMemo,useCallback,因为setup只调用一次前者无需考虑调用顺序,后者需要保证hooks的顺序一致34.Vue3中的事件修饰符有哪些?Vue3中的事件修饰符与Vue2基本相同,包括stop、prevent、capture和self等。

Vue3中使用元素和v-bind:is属性来实现动态组件。例如,。

Object.defineProperty和Proxy的区别如下:

Vue3中的指令包括v-if、v-for、v-bind、v-on、v-html、v-model、v-show、v-slot、v-text等。

vue2中is是通过组件名称切换的,vue3中setup是通过组件实例切换的。直接把组件实例放到reactive中代理,vue会发出警告。告知我们可以通过shallowRef或者markRaw跳过proxy代理。对组件实例进行响应式代理毫无意义,且浪费性能

markRaw:标记一个对象,使其不能成为一个响应式对象。toRaw:将响应式对象(由reactive定义的响应式)转换为普通对象。shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理。shallowReactive:只处理对象最外层属性的响应式(浅响应式)。

组件的逻辑膨胀导致组件的可读性变差;

无法跨组件重用代码;

vue2对TS的支持有限

代码量少,更好的重用逻辑代码

没有引入新的语法,只是单纯函数

异常灵活

工具语法提示友好,因为是单纯函数所以很容易实现语法提示、自动补偿

更好的Typescript支持

在复杂功能组件中可以实现根据特性组织代码,代码内聚性强

组件间代码复用

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

2、context:上下文对象。

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

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

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

2、provide:是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值。

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

//在父组件中,使用provide提供数据://name:定义提供property的name。//value:property的值。setup(){provide('info',"值")}//在子组件中,使用inject注入数据//name:接收provide提供的属性名。//default:设置默认值,可以不写,是可选参数。setup(){constinfo=inject("info")inject('info',"设置默认值")return{info}}//需要注意的是,provide和inject只能在setup函数中使用,而且provide提供的数据只能在其子组件中使用。如果要在兄弟组件中共享数据,可以使用一个共享的对象或者使用Vuex等状态管理库。44.shallowReactive与shallowRef1、shallowRef:只处理基本数据类型的响应式

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

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

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

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

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

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

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

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

父传子:

1、在父组件的子组件标签上通过:传到子组件的数据名=‘需要传递的数据’

2、子组件中通过props进行接收并在模板中使用

子传父:

1、子组件触发事件通过setup函数的第二个参数,context.emit来实现子传父

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

reactive都不需要.value取值

解构赋值会使reactive丢失响应性,而{ref(1),ref(2)}不会

ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)。

reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据。

isRef:检查一个值是否为一个ref对象

isReactive:检查一个对象是否是由reactive创建的响应式代理

isReadonly:检查一个对象是否是由readonly创建的只读代理

isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理

作用:创建一个ref对象,其value值指向另一个对象中的某个属性。

语法:constname=toRef(person,'name')

应用:要将响应式对象中的某个属性单独提供给外部使用时。

扩展:toRefs与toRef功能一致,但可以批量创建多个ref对象,语法:toRefs(person)

1.Fragment在Vue2中:组件必须有一个根标签

在Vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中

好处:减少标签层级,减小内存占用

2.Teleport什么是Teleport?——Teleport是一种能够将我们的组件html结构移动到指定位置的技术。

使用步骤:

异步引入组件

import{defineAsyncComponent}from'vue'constChild=defineAsyncComponent(()=>import('./components/Child.vue'))

THE END
1.过滤器路径/和/*区别JAVA之家TY过滤器路径/和/*区别 分类: Web 好文要顶 关注我 收藏该文 微信分享 JAVA之家TY 粉丝- 8 关注- 7 +加关注 0 0 升级成为会员 ? 上一篇: Layui对话框的提示图标 ? 下一篇: 正则中文和字母数字混合 posted @ 2020-06-12 19:39 JAVA之家TY 阅读(667) 评论(0) 编辑 收藏 举报 ...https://www.cnblogs.com/java-ty/p/13109764.html
2.Hbase过滤器使用如果column_a的单元值被更改为value_new,过滤过程中发现与设定的单元值过滤条件不符,这一行数据将会被全部过滤掉。 2,SingleColumnValueExcludeFilter 单列值不包含过滤器:与单列值过滤器类似,只是无论是否符合过滤条件,返回的结果都不会包含指定的列。 构造函数: SingleColumnValueExcludeFilter(byte[] family, ...https://www.jianshu.com/p/18ef91fbb090
3.无菌室阳性对照室微生物限度室效价室等等区别7、尘埃粒子计数仪)检测尘埃粒径0.5m的粒数不得超过3.5个/升,5m的粒数为0,空气流速应0.35m/s,可根据无菌状况必要时置换过滤器。阳性对照和限度室的区别:功能上不一样,硬件上应该是一样的。限度室主要是做限度检查的即规定量样品中含菌量。阳性对照室是做阳性对照的,和一些加菌试验或检测。 限度室是提供一...https://www.renrendoc.com/paper/212112155.html
4.细胞培养板的选择注意每一小瓶的量不要太满,若20ml的瓶子,只装15ml可,若太满,放于-2度有可能会将瓶子弄碎,因冻后体积增加。(我就有这个教训) 另外烧杯,小瓶、移液管都要灭菌处理的,泡酸高压什么的。我们用的是一种用注射器过滤的过滤器,一次性的。一个能最多有效 过滤200ml。 http://m.whb-bio.com/nd.jsp?id=33&id=33
5.HEPA过滤器与ULPA超过滤器的区别ULPA超过滤网,ULPA是在HEPA的基础上经进一步加强了HEPA的过滤效果,ULPA对0.3微米以上的微粒子的过滤效率达到99.99%以上。ULPA与HEPA相比,滤料结构更紧凑、过滤效率更高。ULPA过滤器过滤面积更大,容尘能力更强。 当HEPA过滤器达到HEPA标准的过滤网,对于0.1微米和0.3微米的有效率达到99.998%,HEPA过滤器特点就是具有无缝...https://www.chem17.com/tech_news/detail/1380758.html
1.拦截器与过滤器的区别,在理解/和/*的区别拦截器与过滤器的区别,在理解/ 和 /* 的区别 过滤器: 依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些...https://blog.csdn.net/code_mzh/article/details/107196935
2.生物滤池和流化床有什么区别25. 筛网过滤器的类型很多,有旋转式、振动式、转鼓式等。 26. 沉淀是固液分离或液液分离的过程。 27. 悬浮物在水中的沉淀可分为四种基本类型:自由沉淀、絮凝沉淀。拥挤沉淀和压缩沉淀。 28. 沉淀池的形式按池内水流方向的不同,可分为平流式、竖流式、辐流式和斜流式四种。 https://www.cinmm.com/35191.html
3.双非本科跨专业5面京东,8600小时后收到通知,流下喜悦泪水(4)布隆过滤器原理 3、Mysql (1)三大范式,为什么用范式 (2)事务的四大特性,具体说一下 (3)隔离级别,分别解决什么问题 (4)说一下脏读 4、Java (1)collection集合 (2)ArrayList和linkedList的区别 (3)线程安全的有哪些 (4)synchronized 5、算法题:反转链表 ...https://maimai.cn/article/detail?fid=1632778078&efid=DWfu7WeSgiHv2qWOUabF1g
4.过滤隔板和与无隔板的区别在哪里?形成空气通道。 除了某些耐高温和高安全性要求的特殊场合,无隔板过滤器均可取代有隔板高效过滤器。https://www.to8to.com/ask/k7230192.html
5.SpringBoot面试突击之过滤器和拦截器区别详解java过滤器(Filter)和拦截器(Interceptor)都是基于 AOP(Aspect Oriented Programming,面向切面编程)思想实现的,用来解决项目中某一类问题的两种“工具”,但二者有着明显的差距,接下来我们一起来看+ 目录实现过滤器和拦截器首先,我们先来看一下二者在 Spring Boot 项目中的具体实现,这对后续理解二者的区别有很大的帮助。https://www.jb51.net/article/265166.htm
6.Spring学习笔记(8)一、实现原理区别 过滤器和拦截器 底层实现方式大不相同:过滤器是基于函数回调的,拦截器则是基于Java的反射机制(动态代理)实现的。 过滤器(Filter):它依赖于servlet容器。在实现上是基于函数回调。《Java过滤器Filter详解》 在我们自定义的过滤器中都会实现一个doFilter()方法,这个方法有一个FilterChain参数,而实际上...https://cloud.tencent.com/developer/article/1981326
7.过滤器和净水器的区别有哪些我们平时在家中或者是在公司都希望能够喝到健康的水源,健康的水源来源于过滤器以及净水器,用过滤器和净水器过滤过的水源都是比较健康和干净的,今天小编就来跟大家说一说过滤器和净水器的区别有哪些,希望可以给大家带来帮助。 一、过滤器和净水器的区别有哪些 1、过滤器是可以去除一些颗粒状的物体,但是不能够去除...https://m.jia.com/zixun/jxwd/943958.html
8.保安过滤器滤芯5um和1微米的区别是什么??保安过滤器滤芯滤芯5um和1微米的区别是过滤精度不同。 1微米 等于 2500目 等于 每英寸2500个筛孔 5um 等于 12500目 等于 每英寸12500个筛孔 当然在实际应用环境当中还需要考虑温度、腐蚀性等一系列其它实际生产环境中应该考虑到的问题,以此来选择不同过滤材料的滤芯。 https://www.china.cn/zixun/20220809/n155468.html
9.SpringCloud最全面试题整理,全是干货可以对路由指定 Predicate(断言)和 Filter(过滤器); 集成Hystrix的断路器功能; 集成Spring Cloud 服务发现功能; 易于编写的 Predicate(断言)和 Filter(过滤器); 请求限流功能; 支持路径重写。 45、Spring Cloud Gateway 与 Zuul的区别? Zuul 1.x,是一个基于阻塞 I/ O 的 API Gateway Zuul 1.x 基于...https://www.nowcoder.com/discuss/1044144