new操作符可以干什么
• 创建一个空对象,将它的引用赋给 this,继承函数的原型。
• 通过 this 将属性和方法添加至这个对象
• 最后返回 this 指向的新对象,也就是实例(如果没有手动返回其他的对象)
promise的特点
- new Promise时需要传递一个executor执行器,执行器会立刻执行
- 执行器中传递了两个参数:resolve成功的函数、reject失败的函数,他们调用时可以接受任何值的参数value
- promise状态只能从pending态转onfulfilled,onrejected到resolved或者rejected,然后执行相应缓存队列中的任务
- promise实例,每个实例都有一个then方法,这个方法传递两个参数,一个是成功回调onfulfilled,另一个是失败回调onrejected
- promise实例调用then时,如果状态resolved,会让onfulfilled执行并且把成功的内容当作参数传递到函数中
- promise中可以同一个实例then多次,如果状态是pengding 需要将函数存放起来 等待状态确定后 在依次将对应的函数执行 (发布订阅)
promise的核心原理
promise的核心原理其实就是发布订阅模式,通过两个队列来缓存成功的回调(onResolve)和失败的回调(onReject)
.then的特点
- 因为promise状态确定后就是不能更改,所以每次promise执行then后都会返回一个新的promise而不是this,那么状态永远为resolve或jeject,将存在异步调用
- onfulfilled或onrejected是一个可选参数,需要做没有传递时的处理
- 如果then中onfulfilled或onrejected返回的是一个普通值的话会把这个结果传递下一次then中的成功回调
- 如果then中onfulfilled或onrejected出现异常,会走下一个then的失败回调,将err传递到失败回调中
- 如果失败后还可以成功,如果返回undefined,会把undefined传递给下一次
- 如果then方法返回的是一个promise,那么会等待这个promise执行完决定返回的是成功还是失败
什么是回流和重绘
**回流:**页面会因为元素的规模尺寸,布局,隐藏等改变而需要重新构建
**重绘:**当页面中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color
什么是vue生命周期
vue生命周期是指vue实例对象从创建到销毁的过程
vue生命周期的作用是什么
在vue生命周期的不同阶段通过对应的钩子函数来实现组件数据管理和DOM渲染两大重要功能
创建阶段:
beforecreate:实例已经初始化,但不能获取DOM节点。(没有data,没有el)
created:实例已经创建,仍然不能获取DOM节点。(有data,没有el)
载入阶段:
beforemount:模板编译完成,但还没挂载到界面上。(有data,有el)
mounted:编译好的模板已挂载到页面中(数据和DOM都已经渲染出来)。
更新阶段:
beforeupdate:数据发生变化立即调用,此时data中数据是最新的,但页面上数据仍然是旧的(检测到数据更新时,但DOM更新前执行)。
updated:更新结束后执行,此时data中的值和页面上的值都是最新的。
销毁阶段:
beforedestroy:当要销毁vue实例时,在销毁之前执行。
destroy:在销毁vue实例时执行
每个生命周期钩子函数的使用场景
beforecreate(创建前) : 在实例创建以前调用,没有实例化,数据访问不到
created(创建后) : 实例被创建完成后调用,能拿到数据,能修改数据,
并且修改数据不会触发updated beforeUpdate钩子函数
beforemounted(挂载前):真实的dom节点挂载到页面之前,可以访问和更改数据,
并且修改数据不会触发updated beforeUpdate钩子函数
mounted(挂载后) : 真实的dom节点挂载到页面以后,可以访问和更改数据,
并且修改数据会触发updated beforeUpdate钩子函数
beforeupdated(更新前):修改之前调用
updated(更新后) : 修改之后调用
beforeDestroy(销毁前) : 实例卸载以前调用,可以清理非vue资源,防止内存泄露
destoryed(销毁后):在实例销毁之后调用。调用后,所有的事件监听器会被移除,
所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用
第一次页面加载会触发哪几个钩子函数?
beforeCreate, created, beforeMount, mounted
你的接口请求一般放在哪个生命周期中
接口请求一般放在mounted中,但需要注意的是服务端渲染时不支持mounted,需要放到created中
created和mounted的区别
created:实例已经创建,但不能获取DOM节点。
mounted:模板已经挂载到页面上,可以操作DOM元素
vuex 是什么
Vuex是一个专为vue.js应用程序开发的状态管理模式
状态管理:
它的五个属性,state,getters,mutation,action,modules
状态就是数据,它相当于一个仓库,是用于存放数据用的,
它可以把存储的数据共享给其他组件
如何使用vuex
在main.js中引用,在src里面创建一个store文件,state里面存放的是数据,mutation存放的同步逻辑.action是异步逻辑,getter是计算属性,module是模块化管理
为什么使用vuex
实现多组件状态管理,可以方便的实现组件之间的数据共享
那些场景会使用到vuex
组件之间的状态、音乐播放、登录状态、加入购物车
vuex的五个核心概念
state
负责状态管理,类似于vue中的data,用于初始化数据
mutation
存放的是动态修改Vuex的state中保存的数据状态的方法,通过commit触发
action
可以处理异步,通过dispatch触发,不能直接修改state,首先在组件中通过dispatch触发action,然后在action函数内部commit触发mutation,通过mutation修改state状态值,
将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据
getter
Vuex中的计算属性,相当于vue中的computed,依赖于state状态值,状态值一旦改变,
getter会重新计算,也就是说,当一个数据依赖于另一个数据发生变化时,就要使用getter
module
模块化管理
vue组件中的data 定义必须是一个函数
如果data是对象的话,由于对象是引用类型,组件被复用的话,就会创建多个实例。本质上,这些实例用的都是同一个构造函数。这样就会影响到所有的实例,所以为了保证组件不同的实例之间data不冲突,data必须是一个函数
vue组件中 data ,computed 和 watch,methods的区别
data: 组件中存放数据的主要属性(不用处理的数据,单纯的存放)
比如:ajax请求来,需要渲染的数据 / 通过各种传参方式过来的参数
computed: 计算属性。 (存放着需要计算的数据),具有缓存性
只有它依赖的属性发生变化才会更新视图
该数据的值, 通过依赖目标的改变,而改变。
例如:
a = 1 + 5 + 4;
watch : 监听属性 (存放着需要监听的数据),
更多的是「观察」的作用,类似于某些数据的监听回调,用于观察props,$emit或者本组件的值,当数据变化时来执行回调进行后续操作
当监听的数据发生改变时,其他相对应的数据也发生改变
它里面有两个配置项
immediate:true(初始化立即执行)
deep:true(深度监听)监听对象(或数组)中的属性
methods:
在重新渲染的时候每次都会被重新的调用
什么是声明式路由,什么是编程式路由
声明式:用 <router-link to=’'跳转的路径 "> < /router-link>
编程式:就是js写法,即this.$router.push(‘跳转的路径’)
vue-router的作用是什么? 为什么不使用a标签
路由,主要用于组件切换,通过设置不同的path,切换视图,
向服务器发送的不同的请求,获取不同的资源
通过a标签和vue-router对比,vue-router避免了重复渲染,
不像a标签一样需要重新渲染,导致一些动态添加路由的机制失效
vue传参
params
加冒号 设置props:true (path:’/line/ : id ')
query
加问号 query的path传参需要加 /
params:/router1/:id,这里的 id 叫做 params。例如/router1/123
query:/router1?id=123,这里的 id 叫做 query。例如/router1?id=456
传递参数
this.$router.push({ path:’/xxx’ query:{ id:id } })
接收参数
this.$route.params.id
vue传参中params与query的区别
1)引入方式不同: query要使用path来引入,params只能使用name来引入,接受参数格式类似,引用分别是this. $ route.query.name和this. $ route.params.name
注意:params 传参,push 里面只能是 name:‘xxxx’,不能是 path:’/xxx’,因为 params 只能用 name 来引入路由,如果这里写成了 path,接收参数页面会是 undefined!!!
2)形成的路径不同(或者url地址显示不同):
使用query传参的话,会在浏览器的url栏看到传的参数类似于get请求,使用params传参的话则不会,类似于post请求。
params传递后形成的路径:/router/123,/router/zhangsan
query传递后形成的路径:/router?id=666&name=zhangsan
3)是否受动态路径参数影响
Query传递的参数不会受路径参数的影响,会全部展示到路径上,刷新不会丢失query里面的数据;
params传递的参数会受路径参数的影响,只会展示含有动态路径参数的部分,刷新会丢失没有设置动态路径参数的params的数据
Vue中hash模式和history模式的区别
在最明显的显示上:hash模式的URL中会夹杂着#号,而history没有。
Vue底层对它们的实现方式不同:
hash模式是依靠onhashchange事件(监听location.hash的改变)
history模式是主要是依靠的HTML5 history中新增的两个方法,pushState()可以改变url地址且不会发送请求,replaceState()可以读取历史记录栈,还可以对浏览器记录进行修改
当真正需要通过URL向后端发送HTTP请求的时候,比如常见的用户手动输入URL后回车,或者是刷新(重启)浏览器,这时候history模式需要后端的支持。因为history模式下,前端的URL必须和实际向后端发送请求的URL一致,例如有一个URL是带有路径path的(例如www.libai.wang/blogs/id),如果后端没有对这个路径做处理的话,就会返回404错误。所以需要后端增加一个覆盖所有情况的候选资源,一般会配合前端给出的一个404页面
使用vue中的v-for时,为什么要绑定 :key? 如果不绑定会有什么效果?
Key可以标识组件的唯一性,为了更好地区别各个组件
key的作用主要是为了高效的复用虚拟DOM保持数据的唯一
不绑定会导致所有列表DOM重新渲染
nextTick
nextTick可以让我们在下次 DOM 更新循环结束之后执行延迟回调,用于获得更新后的 DOM
nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用
Promise
MutationObserver
setImmediate
如果以上都不行则采用setTimeout
定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列
ref的作用
v2:
绑定到普通元素上:获取dom元素this.$ refs.box
绑定到子组件上:
获取子组件中的data,this.$ refs.box.msg
调用子组件中的方法this.$ refs.box.open()
v3:
定义一个响应式数据(基本类型)
在Vue2中我们通过this.$refs来获取dom节点,Vue3中我们通过ref来获取节点
说说你对 proxy 的理解
vue的数据劫持有两个缺点:
无法监听通过索引修改数组的值的变化
无法监听object 也就是对象的值的变化
所以vue2.x中才会有$set 属性的存在
proxy是 es6中推出的新 api,可以弥补以上两个缺点,所以 vue3.x版本用 proxy 替换object.defineproperty
Vue 的响应式原理中 Object.defineProperty 有什么缺陷?
Object.defineProperty 无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy 可以劫持整个对象,并返回一个新的对象
Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性
Proxy 相比于 defineProperty 的优势?
数组变化也能监听到
不需要深度遍历监听
vue怎么实现双向数据绑定
vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果
采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调
什么是数据劫持
在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果
setter设置属性
getter获取属性
v-model的双向数据绑定原理
v-model本质就是一个语法糖,可以看成是value + input方法的语法糖。 可以通过model属性的prop和event属性来进行自定义
例:
< input v-model=“sth” />
// 等同于
< input :value=“sth” @input=“sth = $event.target.value” />
//这个语法糖必须是固定的,也就是说属性必须为value,方法名必须为:input
Vue事件绑定原理
原生事件绑定是通过addEventListener绑定给真实元素的,组件事件绑定是通过Vue自定义的$on实现的
$ route和$ router的区别
$ route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
而$router是“路由实例”对象包括了路由的跳转方法,钩子函数等
监听路由变化
监听子路由的变化有两种方式
-
watch:
watch里面有三个值
-
handler:其值是一个回调函数。即监听到变化时应该执行的函数
-
immediate:true(初始化监听,当监听数据是初始值时,页面打开立即触发此监听函数)
-
deep:true(深度监听,一般监听时是不能监听到对象属性值的变化的,深度监听时数组的值变化可以听到)
- beforeRouteUpdate守卫
什么时候使用动态路由参数
把某种模式匹配到的所有路由映射到同个组件
路径参数用 : 标记
例子: /about/:id
不用v-model也能实现双向绑定数据
数据存放在vuex的state中
例:
组件模板这样写:
script中这样写:
需要使用map辅助函数引入Actions
例子:
commit 和dispatch的区别
commit是提交mutatious的同步操作,dispatch是分发actions的异步操作
(1) dispatch:含有异步操作,例如向后台提交数据,写法this.$store.dispatch(‘action方法名’,值)
(2)commit:同步操作,写法:this.$store.commit(‘mutations方法名’,值)
vue中常用指令
v-text v-html
v-bind v-model
v-if v-show
v-html与v-text
相同点:都能为标签渲染文本
不同点:
text 元素内容整体替换为纯文本数据
html 整体会替换为指定的html 文本
text 不会解析标签
html 会解析标签
v-bind与v-model
相同点:用来绑定数据到标签中,符合数据驱动试图
不同点:
bind :数据影响视图,视图不会影响数据,是单向的
model:数据影响视图,视图影响数据是双向的
v-if与v-show
相同点:都是处理标签的显示与隐藏
不同点:
if通过DOM来创建和销毁
show通过css的display
使用场景不同:
if用于需要整体删除一个标签时(不频繁切换显示状态)
show用于通过一个数据来处理标签的显示(频繁切换显示状态)
v-if与v-for的优先级
vue2.0中 v-for优先级比v-if高
vue3.0中 v-if优先级比v-for高
如何自定义指令
注册一个自定义指令有全局注册与局部注册
全局注册注册主要是用过Vue.directive方法进行注册
Vue.directive第一个参数是指令的名字(不需要写上v-前缀),第二个参数可以是对象数据,也可以是一个指令函数
局部注册通过在组件options选项中设置directive属性
然后你可以在模板中任何元素上使用新的 v-focus property
例:
什么是vue
是一套用于构建用户界面的渐进式 javascript 框架, 在传统的前端开发中,是基于 jQuery+模板引擎 来构建界面的搭建用户页面的渐进式框架
什么是渐进式框架
可以和传统的网站开发架构融合在一起,例如可以简单的把它当作一个类似 JQuery 库来使用。
也可以使用Vue全家桶框架来开发大型的单页面应用程序
vue.js的特性
数据驱动视图
组件化开发
数据驱动视图
数据变化会自动更新到对应元素中,无需手动操作DOM,对于输入框等可输入的元素,可设置双向数据绑定,Vue.js的数据驱动视图,是基于M-V-VM模型实现
mvvm
MVVM 是一种软件开发思想
- Model: 代表数据
- View : 代表视图模板
- ViewModel: 代表业务逻辑处理的代码
MVVM模式简化了界面与业务的依赖,解决了数据频繁更新。MVVM 在使用当中,利用双向绑定技术,使得 Model 变化时,ViewModel 会自动更新,而 ViewModel 变化时,View 也会自动变化
举例:
model相当于script中的data数据,view相当于模板中的标签,viewmodel可以看做成computed,methods等方法,它可以操作data中的数据,然后在模板中渲染页面,从而实现view与model双向绑定
数据驱动视图的优缺点
优点:
基于MVVM 实现的数据驱动视图,解放了DOM操作
View 与 Model 分离处理, 降低代码的耦合度
- 什么是耦合?
模块与模块之间很多是存在关联的,如果改动一个,其他若干模块都会发生改变,模块之间的关系越是紧密,独立性就越不好。(这种关系,我们称作: 耦合度)
- 什么内聚?
模块内部的代码,相互之间的联系越强,内聚就越高,一个模块应该尽量去独立,独立的去完成一个功能! 如果有新的代码,非得引入到独立的模块中,建议拆分成多模块
缺点:
- 当双向数据绑定时,Bug调试难度增大
- 大型项目中,View和Model过多,维护成本过高
组件化开发
允许我们将网页中的功能,样式,标签封装成可复用的模块。 每个模块之间是彼此独立但相互联系的
导航守卫
全局:
beforeEach
afterEach
局部:
beforeRouteEnter : 当守卫执行前,组件实例还没被创建,不能获取组件实例的this
beforeRouteUpdate : 在当前路由改变,但是该组件被复用时调用,可以访问组件实例 this
beforeRouteleave : 导航离开该组件的对应路由时调用,可以访问组件实例 this
路由独享守卫:
beforeEnter
每个守卫方法接收三个参数:
to:目标路由对象
from:准备要离开的路由
next():进行管道中的下一个钩子。可传递参数有布尔值false,直接写路径’/‘或{path:’/’},回调函数
注意:后置钩子函数afterEach不会接受next函数也不会改变导航本身
router-link传参方式
-
第一种
query.name
-
第二种
query.path
-
第三种
params.name
-
第四种
-
动态路由匹配参数
用 : (冒号)标记
路由组件传参如何解耦
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性
使用 props 将组件和路由解耦:
取代与 $route 的耦合
设置props为true
这样便可以在任何地方使用该组件,使得该组件更易于重用和测试
导航守卫的执行顺序
失活组件的组件内离开守卫 beforeRouteLeave —> 全局前置守卫 beforeEach —> 路由独享守卫 beforeEnter —> 组件内进入守卫 beforeRouteEnter ----> 全局解析守卫 beforeResolve —> 全局后置守卫 afterEach —> beforeCreate —>created —> 失活组件的销毁destory —> 当前组件的beforeMount —>mounted
当组件更新时:
全局前置守卫 beforeEach —> 组件内更新守卫 beforeRouteUpdate ----> 全局解析守卫 beforeResolve —> 全局后置守卫 afterEach
vue-router 实现组件的缓存之 keep-alive
keep-alive可以实现组件缓存,当组件切换时,主要用于保留组件状态或避免重新渲染
如果只想router-view里面某个组件被缓存
可以使用:
1、使用include/exclude
- include-字符串或正则表达式,只有匹配的组件会被缓存
- exclude-字符串或正则表达式,任何匹配的组件都不会被缓存
2、增加router.meta属性
什么是mixins(混入)
是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项,当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项
mixins的特点
- 方法和参数在各组件中不共享
- 值为对象的选项,如methods,components等,选项会被合并,键冲突的组件会覆盖混入对象的
- 值为函数的选项,如created,mounted等,就会被合并调用,混合对象里的钩子函数在组件里的钩子函数之前调用
mixins与vuex的区别
vuex:用来做状态管理的,里面定义的变量在每个组件中均可以使用和修改,在任一组件中修改此变量的值之后,其他组件中此变量的值也会随之修改。
Mixins:可以定义共用的变量,在每个组件中使用,引入组件中之后,各个变量是相互独立的,值的修改在组件中不会相互影响
mixins与组件的区别
组件:在父组件中引入组件,相当于在父组件中给出一片独立的空间供子组件使用,然后根据props来传值,但本质上两者是相对独立的。
Mixins:则是在引入组件之后与组件中的对象和方法进行合并,相当于扩展了父组件的对象与方法,可以理解为形成了一个新的组件
使用路由懒加载的好处
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件
组件内的六种通信方式
父传子 props
在父组件中定义一个数据 msg:‘hello 李湘’
在模板中绑定这个属性 < Child :msg=‘msg’ >
在子组件使用props:[‘msg’] 接收数据
在模板中使用msg数据
代码展示:
父组件:
子组件:
子传父 $emit
在父组件方法中定义一个点击 自定义事件,
在子组件通过 $ emit接收这个自定义事件名,第二个参数是你想要传入修改的数据
代码示例:
父组件:
子组件:
$on/ $emit
先设置一个 空实例,作为公共组件
在a组件通过 $on(‘事件名’,想传的的数据),
在b组件通过 $emit()接收
回调函数(callback)
在父组件方法中定义一个changeMsg,它可以修改data中的msg
这时changeMsg可以作为另外一个props传递给Child(子组件),此时Child(子组件)就接收了msg和changeMsgFn
在子组件使用props接收msg和changeMsgFn
此时点击按钮的时候就可以执行changeMsgFn
代码示例:
父组件:
子组件:
$parent
在父组件定义一个msg数据
在子组件就可以通过$parent.msg访问到值
代码示例:
父组件:
子组件:
$children
在组件先定义一个数据 number:10
在父组件通过$children第0项(因为一个父组件可能会有很多个子组件,此目的为找到第一个子组件),然后把它的number修改成50
在父组件模板中点击按钮时触发自定义事件
从而修改子组件中number的值
代码示例:
父组件:
子组件:
provide + inject
有俩种方式
对象形式:
在父组件使用provide
在子组件使用inject接收数据
代码示例:
父组件:
子组件:
函数形式:
provide(){
return{
msg:this.msg
}
}
$ attrs
包含了父作用域中不作为 props接受的属性
在子组件通过v-bind = '$attrs’
在父组件里使用子组件,在子组件中使用孙组件 (三层嵌套)
在父组件中定义两个数据 name:‘李湘’,age:18
传递给子组件
在子组件中不去使用它,而是传递给孙组件
在子组件中 <grandChild v-bind=’$attrs’
通过v-bind绑定事件绑定 $attrs属性
这个 $attrs就包含了父组件的name和age
因此孙组件姐可以获取到父组件的name和age
代码示例:
父组件:
子组件:
孙组件:
$listenenrs
在父组件定义一个事件监听器,在子组件中通过$listeners点击这个事件监听器,就可以触发父组件中的事件监听函数
代码示例:
父组件:
子组件:
孙组件:
ref
ref可以把子组件注册起来
在父组件使用子组件child 这里面有一个ref属性
在方法中触发 this.$refs.childComp.age获取到这个实例
设置一个点击事件从而可以获取到子组件中的数据age,子组件中也可以定义一个改变事件,修改age的值
代码示例:
父组件:
子组件:
vue2中如何实现响应式
对于数组
$set (第一个参数是数组,第二个参数是下标,第三个参数是新的内容)
vue.set (第一个是操作的数据,第二个是添加的内容,第三个是添加的内容的值)
splice (删除,插入,替换)
对于对象
$set
vue.set
object.assign (第一个参数是目标对象,后面的参数都是源对象)
强制刷新
this.$forceUpdate()
vue3如何实现响应式
reactive()(定义多个数据响应式)
VUE3的特点
1)性能的提升
打包大小减少
初始化渲染和更新渲染更快
内存占有减少
2)源码的升级
使用proxy代替defineProperty实现了响应式数据
重写了虚拟dom
3)集合了 TypeScript
4)新的特性
创建vue3.0工程
1.通过脚手架 vue-cli
2.Vite
什么是vite
新一代前端构建工具
vite的优势
开发环境中,无需打包操作,可快速冷启动
轻量快速的热重载
真正的按需编译,不再等待整个项目编译完成
传统构建与vite构建图对比
请参考此博主文章:
https://blog.youkuaiyun.com/majing0520/article/details/114893365?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163119206416780261939849%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=163119206416780261939849&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v29_ecpm-1-114893365.first_rank_v2_pc_rank_v29&utm_term=%E4%BC%A0%E7%BB%9F%E6%9E%84%E5%BB%BA%E4%B8%8Evite%E6%9E%84%E5%BB%BA%E5%9B%BE%E5%AF%B9%E6%AF%94&spm=1018.2226.3001.4187