vue生命周期是指vue实例对象从创建之初到销毁的过程,vue所有功能的实现都是围绕其生命周期进行的,在生命周期的不同阶段调用对应的钩子函数实现组件数据管理和DOM渲染两大重要功能。
vue生命周期可以分为八个阶段,分别是:
beforeCreate(创建前) 在实例初始化之后,数据观测者( data observer)和 event/ watcher事件配置之前调用。
created(创建后) 完成数据观测,属性和方法的运算,初始化事件,$el属性还没有显示出来
beforeMount(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。
mounted(载入后) 在el 被新创建的 vm.$el 替换,并挂载到实例上之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。
beforeUpdate(更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
updated(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
beforeDestroy(销毁前) 在实例销毁之前调用。实例仍然完全可用。
destroyed(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
MVVM 是 Model-View-ViewModel的缩写,即将数据模型与数据表现层通过数据驱动进行分离,从而只需要关系数据模型的开发,而不需要考虑页面的表现,具体说来如下:
Model代表数据模型:主要用于定义数据和操作的业务逻辑。View代表页面展示组件(即dom展现形式):负责将数据模型转化成UI 展现出来。ViewModel为model和view之间的桥梁:监听模型数据的改变和控制视图行为、处理用户交互。通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
MVVM和MVC的区别
MVC的定义:MVC是Model-View- Controller的简写。即模型-视图-控制器。M和V指的意思和MVVM中的M和V意思一样。C即Controller指的是页面业务逻辑。使用MVC的目的就是将M和V的代码分离。‘MVC是单向通信。也就是View跟Model,必须通过Controller来承上启下。MVC和MVVM的区别并不是VM完全取代了C,ViewModel存在目的在于抽离Controller中展示的业务逻辑,而不是替代Controller,其它视图操作业务等还是应该放在Controller中实现。也就是说MVVM实现的是业务逻辑组件的重用。
**vue的实现原理:**vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
实现结构图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AI3I1Thb-1616335124713)(C:/Users/hffxy/AppData/Roaming/Typora/typora-user-images/image-20210306145017802.png)]
答案:这是 js 中一个非常重要的方法,ES6 中某些方法的实现依赖于它,VUE 通过它实现双向绑定,此方法会直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。
语法:
Object. defineProperty(object, attribute, descriptor)
descriptor
前两个参数都很明确,重点是第三个参数 descriptor, 它有以下取值
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点:
1.低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变
2.可重用性:你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑
3.独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xml代码
4.可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写
1.react整体是函数式的思想,把组件设计成纯组件,状态和逻辑通过参数传入,所以在react中,是单向数据流,推崇结合immutable来实现数据不可变。
react在setState之后会重新走渲染的流程,如果shouldComponentUpdate返回的是true,就继续渲染,如果返回了false,就不会重新渲染,PureComponent就是重写了shouldComponentUpdate,然后在里面作了props和state的浅层对比。
2.vue的思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立Watcher来监听,当属性变化的时候,响应式的更新对应的虚拟dom。
路由就是SPA(单页应用)的路径管理器。再通俗的说,vue-router就是WebApp的链接路径管理系统。vue-router是Vue.js官方的路由插件。路由模块的本质 就是建立起url和页面之间的映射关系。组件包括 router-link和 router-vIew。
vue-router实现原理:SPA(single page application):单一页面应用程序,只有一个完整的页面;它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。单页面应用(SPA)的核心之一是: 更新视图而不重新请求页面;vue-router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式;根据mode参数来决定采用哪一种方式。
vue-router的两种模式区别以及使用注意事项
[Vue Router]是Vue官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。vue-router
默认 hash 模式,还有一种是history模式。
hash模式:
hash模式的工作原理是hashchange事件,可以在window监听hash的变化。我们在url后面随便添加一个#xx触发这个事件。
window.onhashchange = function(event){
console.log(event);
}
hash模式:浏览器url中#后面的内容,包含#。hash是URL中的锚点,代表 的是网页中的一个位置,单单改变#后的部分,浏览器只会加载相应位置的内容,不会重新加载页面。
history模式 :HTML5,History interface提供了两个新的方法:
pushState():浏览器不会向服务端请求数据,直接改变url地址,可以类似的理解为变相版的hash;但不像hash一样,浏览器会记录pushState的历史记录,可以使用浏览器的前进、后退功能作用。
replaceState():不同于pushState,replaceState仅仅是修改了对应的历史记录
调用 history.pushState() 相比于直接修改 hash,存在以下优势:
pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 #后面的部分,因此只能设置与当前 URL 同文档的 URL;
pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串;
pushState() 可额外设置 title 属性供后续使用。
怎么定义 vue-router 的动态路由?怎么获取传过来的动态参数?
答案:在 router 目录下的 index. js 文件中,对 path 属性加上/:id。 使用 router 对象的 params. id
active- class是哪个组件的属性?
它是 vue-router模块的 router-link组件的属性,用来做选中样式的切换。
导航钩子又称导航守卫,又分为全局钩子、单个路由独享钩子和组件级钩子。
全局钩子有 beforeEach、beforeResolve(Vue2.5.0新增的)、 afterEach。
//全局钩子
//定义一个路由
const router = new VueRouter({ ... })
// 点击导航前调用
router.beforeEach((to, from, next) => {
// ...
})
// 点击导航后调用
router.afterEach(route => {
// ...
})
单个路由独享钩子有 beforeEnter。
组件级钩子有 beforeRouteEnter、 beforeRouteUpdate(Vue2.2新增的) beforeRouteLeave。
它们有以下参数。
computed:
计算属性,依赖其他属性,当其他属性改变的时候下一次获取computed值时也会改变,computed的值会有缓存
watch:
类似于数据改变后的回调。如果想深度监听的话,后面加一个deep:true
如果想监听完立马运行的话,后面加一个immediate:true
v-show与v-if都是条件渲染指令。不同的是,无论v-show的值为true或 false,元素都会存在于HTML页面中;而只有当v-if的值为true时,元素才会存在于HTML页面中。v-show指令是通过修改元素的 style属性值实现的。
1.共同点:v-if 和 v-show 都可以动态地显示DOM元素
2.区别
(1)手段:
v-if 是动态的向DOM树内添加或者删除DOM元素;
v-show 是通过设置DOM元素的display样式属性控制显隐;
(2)编译过程:
v-if 切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;
v-show只是简单的基于css切换;
(3)编译条件:
v-if 是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译(编译被缓存?编译被缓存后,然后再切换的时候进行局部卸载);
v-show 是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素保留;
(4)性能消耗:
v-if 有更高的切换消耗;
v-show 有更高的初始渲染消耗;
(5)使用场景:
v-if 适合运营条件不大可能改变;
v-show 适合频繁切换。
组件关系可分为父子组件通信、兄弟组件通信。
1、父组件向子组件:
父组件可以使用props 把数据传给子组件,子组件内用props接收。
2、子组件向父组件:
子组件可以使用$emit
触发父组件的自定义事件。
子组件用$emit()
来触发事件,父组件用$on()来监听子组件的事件。父组件可以直接在子组件的自定义标签上使用v-on 来监听子组件触发的自定义事件。
vm.$emit( event, arg ) //触发当前实例上的事件
vm.$on( event, fn );//监听event事件后运行 fn;
例如:子组件:
3、兄弟之间的通信:
通过实例一个vue 实例Bus作为媒介const bus = new Vue(),要相互通信的兄弟组件之中都引入Bus,之后通过分别调用Bus 事件触发和监听来实现组件之间的通信和参数传递。
加载渲染过程
子组件更新过程
父组件更新过程
销毁过程
方式1: props
1) 通过一般属性实现父向子通信
2) 通过函数属性实现子向父通信
3) 缺点: 隔代组件和兄弟组件间通信比较麻烦
方式2: vue自定义事件
1) vue内置实现, 可以代替函数类型的props
a. 绑定监听: {})
b. 发布消息: PubSub.publish(‘msg’, data)
2) 优点: 此方式可用于任意关系组件间通信
方式4: vuex
1) 是什么: vuex是vue官方提供的集中式管理vue多组件共享状态数据的vue插件
2) 优点: 对组件间关系没有限制, 且相比于pubsub库管理更集中, 更方便
方式5: slot(插槽)
1) 是什么: 专门用来实现父向子传递带数据的标签
a. 子组件
b. 父组件
2) 注意: 通信的标签模板是在父组件中解析好后再传递给子组件的
作用:接收传递给组件的数据
特点:1、可以给组件传递任意类型的数据
2、props是只读的对象,只能读取属性的值,无法修改对象
3、注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数中获取到props!!!
在vue组件中,为了使样式私有化(模块化),不对全局造成污染,可以在style标签上添加scoped属性以表示它的样式只属于当下的模块,这是一个非常好的举措,但是为什么要慎用呢?因为在我们需要修改公共组件(三方库或者项目定制的组件)的样式的时候,scoped往往会造成更多的困难,需要增加额外的复杂度。
scoped三条渲染规则
1.给HTML的DOM节点加一个不重复data属性(形如:data-v-2311c06a)来表示他的唯一性
2.在每句css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如[data-v-2311c06a])来私有化样式
3.如果组件内部包含有其他组件,只会给其他组件的最外层标签加上当前组件的data属性。
如何让 CSS 只在当前组件中起作用
答案:将当前组件的 修改为
什么是路由参数的变化:
当使用路由参数时,例如从 /user/foo
导航到 /user/bar
,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
监测路由参数变化的方法:
方法一watch监听:
watch: { // watch的第一种写法
$route (to, from) {
console.log(to)
console.log(from)
}
},
====
watch: { // watch的第二种写法
$route: {
handler (to, from){
console.log(to)
console.log(from)
},
// 深度观察监听
deep: true
}
},
====
watch: { // watch的第三种写法
'$route':'getPath'
},
methods: {
getPath(to, from){
console.log(this.$route.path);
}
},
方法二:导航守卫
beforeRouteEnter (to, from, next) {
console.log('beforeRouteEnter被调用:在渲染该组件的对应路由被 confirm 前调用')
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this` 因为当守卫执行前,组件实例还没被创建
// 可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
next(vm => {
// 通过 `vm` 访问组件实例
console.log(vm)
})
},
// beforeRouteEnter 是支持给 next 传递回调的唯一守卫。
// 对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
console.log('beforeRouteUpdate被调用:在当前路由改变,但是该组件被复用时调用')
next()
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
const answer = window.confirm('是否确认离开当前页面')
if (answer) {
console.log('beforeRouteLeave被调用:导航离开该组件的对应路由时调用')
next()
} else {
next(false)
}
},
有时候,为了在组件内部可以直接访问组件内部的一些元素,可以定义该属性,此时可以在组件内部通过this. $refs属性,更快捷地访问设置ref属性的元素。这是一个原生的DOM元素,要使用原生 DOM API操作它们,例如以下代码。
< span ref="msg">有课前端网
< span ref=" otherMsg">专业前端技术学习网校
app. $refs. msg. text Content
//有课前端网
app. $refs.otherMsg. textContent
//专业前端技术学习网校
1、ref 加在普通的元素上,用this.$refs.(ref值) 获取到的是dom元素
2、ref 加在子组件上,用this.refs . ( ref 值 ) 获 取 到 的 是 组 件 实 例 , 可 以 使 用 组 件 的 所 有 方 法 。 在 使 用 方 法 的 时 候 直 接 t h i s . refs.(ref值).方法()就可以使用了。
3、如何利用 v-for 和 ref 获取一组数组或者dom 节点
需要使用 key 来给每个节点做一个唯一标识,Diff 算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
所以一句话,key 的作用主要是为了高效的更新虚拟 DOM
1、什么是 Vue. nextTick()?
定义:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
所以就衍生出了这个获取更新后的 DOM 的 Vue 方法。所以放在 Vue. nextTick()回调函数中的执行的应该是会对 DOM 进行操作的 js 代码;
理解:nextTick(),是将回调函数延迟在下一次 dom 更新数据后调用,简单的理解是:当数据更新了,在 dom 中渲染后,自动执行该函数。
注意:Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。 $nextTick
是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick
,则可以在回调中获取更新后的 DOM。
this.$store.dispatch()
与 this.$store.commit()
方法的区别
commit: 同步操作,dispatch: 异步操作。总的来说他们只是存取方式的不同,两个方法都是传值给vuex的mutation改变state
答案:keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
$route
和$router
的区别答案:$route 是路由信息对象,包括path,params,hash,query,fullPath,matched,name 等路由信息参数。
而 $router 是路由实例对象,包括了路由的跳转方法,钩子函数等
答案:提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标. 可以是 CSS 选择器,也可以是一个 HTMLElement 实例
vue-loader 是解析 . vue 文件的一个加载器,将 template/js/style 转换成 js 模块。
用途:js 可以写 es6、style 样式可以 scss 或 less;template 可以加 jade 等。
vuex 整体思想诞生于 flux, 可其的实现方式完完全全的使用了 vue 自身的响应式设计,依赖监听、依赖收集都属于 vue 对对象 Property set get 方法的代理劫持。最后一句话结束 vuex 工作原理,vuex 中的 store 本质就是没有 template 的隐藏着的 vue 组件;
解析:vuex的原理其实非常简单,它为什么能实现所有的组件共享同一份数据?
因为vuex生成了一个store实例,并且把这个实例挂在了所有的组件上,所有的组件引用的都是同一个store实例。
store实例上有数据,有方法,方法改变的都是store实例上的数据。由于其他组件引用的是同样的实例,所以一个组件改变了store上的数据, 导致另一个组件上的数据也会改变,就像是一个对象的引用。
vue 框架中状态管理。在 main. js 引入 store,注入。新建一个目录 store,…. . export 。场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车。
// main.js文件
import store from './store'
new Vue({
el:'#app',
store
})
1、mapState 把vuex中的数据映射到当前组件中的计算属性,
import {mapState} from 'vuex';
computed:{
...mapState(['couter'])
}
2、mapMutations 把vuex中的mapMutations映射到methods中
**小结:**如 果 想 要 把 数 据 映 射 过 来 就 用 mapState , 如 果 想要把方法映射过来就是用mapMutations
有五种,分别是 State、 Getter、Mutation 、Action、 Module
vuex的State特性
A、Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于一般Vue对象里面的data
B、state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
C、它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中
· vuex的Getter特性
A、getters 可以对State进行计算操作,它就是Store的计算属性
B、 虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用
C、 如果一个状态只在一个组件内使用,是可以不用getters
· vuex的Mutation特性
Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作。
1、vue. js:vue-cli 工程的核心,主要特点是 双向数据绑定 和 组件系统。
2、vue-router:vue 官方推荐使用的路由框架。
3、vuex:专为 Vue. js 应用项目开发的状态管理器,主要用于维护 vue 组件间共用的一些 变量 和 方法。
4、axios( 或者 fetch 、ajax ):用于发起 GET 、或 POST 等 http 请求,基于 Promise 设计。
5、vuex 等:一个专为 vue 设计的移动端 UI 组件库。
6、创建一个 emit. js 文件,用于 vue 事件机制的管理。
7、webpack:模块加载和 vue-cli 工程打包器。
答案:npm install、npm run dev、npm run build --report 等
下载 node_modules 资源包的命令:npm install
启动 vue-cli 开发环境的 npm 命令:npm run dev
vue-cli 生成 生产环境部署资源 的 npm 命令:npm run build
用于查看 vue-cli 生产环境部署资源文件大小的 npm 命令:npm run build --report,此命令必答
vue-cli目录解析:
build 文件夹:用于存放 webpack 相关配置和脚本。开发中仅 偶尔使用 到此文件夹下 webpack.base.conf.js 用于配置 less、sass等css预编译库,或者配置一下 UI 库。
config 文件夹:主要存放配置文件,用于区分开发环境、线上环境的不同。 常用到此文件夹下 config.js 配置开发环境的 端口号、是否开启热加载 或者 设置生产环境的静态资源相对路径、是否开启gzip压缩、npm run build 命令打包生成静态资源的名称和路径等。
dist 文件夹:默认 npm run build 命令打包生成的静态资源文件,用于生产部署。
node_modules:存放npm命令下载的开发环境和生产环境的依赖包。
src: 存放项目源码及需要引用的资源文件。
src下assets:存放项目中需要用到的资源文件,css、js、images等。
src下componets:存放vue开发中一些公共组件:header.vue、footer.vue等。
src下emit:自己配置的vue集中式事件管理机制。
src下router:vue-router vue路由的配置文件。
src下service:自己配置的vue请求后台接口方法。
src下page:存在vue页面组件的文件夹。
src下util:存放vue开发过程中一些公共的.js方法。
src下vuex:存放 vuex 为vue专门开发的状态管理器。
src下app.vue:使用标签渲染整个工程的.vue组件。
src下main.js:vue-cli工程的入口文件。
index.html:设置项目的一些meta头信息和提供用于挂载 vue 节点。
package.json:用于 node_modules资源部 和 启动、打包项目的 npm 命令管理。
Proxy 是 ES6 中新增的一个特性,翻译过来意思是"代理",用在这里表示由它来“代理”某些操作。 Proxy 让我们能够以简洁易懂的方式控制外部对对象的访问。其功能非常类似于设计模式中的代理模式。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
使用 Proxy 的核心优点是可以交由它来处理一些非核心逻辑(如:读取或设置对象的某些属性前记录日志;设置对象的某些属性值前,需要验证;某些属性的访问控制等)。 从而可以让对象只需关注于核心逻辑,达到关注点分离,降低对象复杂度等目的。
let p = new Proxy(target, handler);
参数:
get: 读取
set: 修改
has: 判断对象是否有该属性
construct: 构造函数
当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,这意味着 v-if 将分别重复运行于每个 v-for 循环中。通过 v-if 移动到容器元素,不会再重复遍历列表中的每个值。取而代之的是,我们只检查它一次,且不会在 v-if 为否的时候运算 v-for。
//vue 内置标签 在渲染时渲染成a标签在点击的情况路由中的页面在标签中渲染
to属性可以跳转页面
tag=“button” 属性可以渲染成想要的原生标签
写在组件想要渲染的地方,等组件跳转过来就渲染
router-link设置路由跳转,router-view根据路由显示组件
懒加载突出一个“懒”字,懒就是拖延迟的意思,所以“懒加载”说白了就是延迟加载。比如我们加载一个页面,这个页面很长很长,长到我们的浏览器可视区域装不下,那么懒加载就是优先加载可视区域的内容,其他部分等进入了可视区域在加载。
**懒加载的原理:**我们先将img标签中的src链接设置为一样的图片(空白图片),将真正的图片链接放在自定义属性中,如(data-src),当js监听到图片元素进入到可视窗口的时候,将自定义属性中的地址存储到src中,达到懒加载的效果。
在入口文件添加后,在组件任何地方都可以直接使用把 img 里的:src -> v-lazy
<div class="pic">
<a href="#"><img :src="'/static/img/' + item.productImage" alt="">a>
div>
把之前项目中img 标签里面的 :src 属性 改成 v-lazy
<div class="pic">
<a href="#"><img v-lazy="'/static/img/' + item.productImage" alt="">a>
div>
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染
预加载会增加服务器的压力,可以说是牺牲服务器前端性能,换取更好的用户体验,这样可以使用户的操作得到最快的反映。