-
谈谈对 MVVM 的认识。
- MVVM 分为 Model、View、ViewModel 三者:
- Model:代表数据模型,数据和业务逻辑都在 Model 层中定义
- View:代表 UI 视图,负责数据的展示
- ViewModel:就是与界面(view)对应的 Model。因为,数据库结构往往是不能直接跟界面控件一一对应上的,所以,需要再定义一个数据对象专门对应 view 上的控件。而 ViewModel 的职责就是把 model 对象封装成可以显示和接受输入的界面数据对象
- Model 和 View 并无直接关联,而是通过 ViewModel 来进行联系的,Model 和 ViewModel 之间有着双向数据绑定的联系。因此当 Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步
- 简单来说,ViewModel 就是 View 与 Model 的连接器,View 与 Model 通过 ViewModel 实现双向绑定
- MVVM 分为 Model、View、ViewModel 三者:
-
Vue 框架主要的优点是什么?
- 渐进式构建能力是 vue.js 最大的优势,vue 有一个简洁而且合理的架构,使得它易于理解和构建
- vue 有一个强大的充满激情人群的社区,这为 vue.js 增加了巨大的价值,使得为一个空白项目创建一个综合的解决方案变得十分容易
-
列举 Vue 中常见的指令及其作用。
- 文本插值:{{ }} Mustache
- {{ message }}
- DOM 属性绑定: v-bind
- 鼠标悬停几秒钟查看此处动态绑定的提示信息!
- 指令绑定一个事件监听器:v-on
{{ message }}
- 实现表单输入和应用状态之间的双向绑定:v-model
{{ message }}
- 控制切换一个元素的显示:v-if 和 v-else
现在你看到我了
- 列表渲染:v-for
- {{ todo.text }}
- 根据条件展示元素:v-show
Hello!
- 带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 是简单地切换元素的 CSS 属性 display
- 注意:v-show 不支持
语法,也不支持 v-else
- v-if 与 v-show
- v-if 是“真正的”条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建
- v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块
- 相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换
- 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件不太可能改变,则使用 v-if 较好
- v-if 与 v-for 一起使用
- 当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级
- 文本插值:{{ }} Mustache
-
v-if
与v-show
的区别是什么?- 区别
- v-if
- 动态的向 DOM 树内添加或者删除 DOM 元素;“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;在初始渲染条件为假时,什么也不做
- v-show
- 通过设置 DOM 元素的 display 样式属性控制显示和隐藏;不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换
- 总结:v-if 有更高的切换消耗;v-show 有更高的初始渲染消耗
- v-if
- 应用场景
- v-if 适合运行条件很少改变的情况; v-show 适合频繁切换的情况
- 对于管理系统权限列表的展示,这里可以使用 v-if 来渲染,如果使用 v-show,对于用户没有的权限,在网页的源码中,仍然能够显示
- 特殊情况:在 el-table 中控制某列的显示与隐藏只能使用 v-if
- 区别
-
v-for
中为什么要绑定key
?- 一句话来说: key 值是为了虚拟 dom 的比对
- 展开来说: 页面上的标签都对应具体的虚拟 dom 对象(虚拟 dom 就是 js 对象), 循环中如果没有唯一 key , 页面上删除一条标签, 由于并不知道删除的是那一条! 所以要把全部虚拟 dom 重新渲染, 如果知道 key 为 x 标签被删除掉, 只需要把渲染的 dom 为 x 的标签去掉即可!
-
谈谈 Vue 的响应式原理(双向绑定原理)。
- 响应式原理
- 利用了数据劫持和订阅发布的模式, 当数据模型发生改变的时候, 视图就会响应的进行更新, 那么深入响应式原理是利用 es5 的 Object.defineProperty 中个 getter/setter 来进行数据的劫持的
- vue 双向数据绑定原理
- 效果
- 数据改变 , 视图更改
- 视图改变, 数据更改
- 实现
- 使用 v-model 实现
- 缺点
- v-model 默认绑定 value 属性, 所以 v-model 只能在表单使用
- 原理
- 数据能直接在视图显示的原因
- v-model 默认绑定了 DOM 对象的 value 属性, 当它初次绑定的时候,就会触发 getter,watcher 就会触发, watcher 通知 Vue 生成新的 VDOM 树,再通过 render 函数进行渲染,生成真实 DOM
- 视图修改数据就会修改的原因
- 当视图修改时, 意味着 DOM 的 value 属性值改变,就会触发 setter,watcher 监听机制就会执行,watcher 通知 Vue 生成新的 VDOM 树,再通过 render 函数进行渲染,生成真实 DOM
- 数据能直接在视图显示的原因
- 效果
- 响应式原理
-
谈谈对 虚拟 dom(vdom) 和 diff 算法 的认识。
- 虚拟 DOM
- 虚拟DOM 利用了 js 对象的 object 的对象模型来模拟真实 DOM,结构是树形结构
- 优点:速度比操作真实DOM快。
- 缺点:
- 更多的功能意味着更多的代码。幸运的是 Vue.js 2.0 仍然是相当小的(21.4kb 当前版本)。
- 虚拟DOM需要在内存中的维护一份 DOM 的副本。在 DOM 更新速度和使用内存空间之间取得平衡。
- 应用场景
- 虚拟 DOM 不是适合所有情况,如果虚拟 DOM 大量更改,这是合适的。But ……如果单一的,频繁的更新的话,虚拟 DOM 将会花费更多的时间处理计算的工作。
- 所以,如果你有一个 DOM 节点相对较少页面,用虚拟 DOM,它实际上有可能会更慢。 但对于大多数单页面应用,这应该都会更快。
- diff 算法
- diff 算法是用来比较两个或是多个文件,返回值是文件的不同点
- diff 算法是同级比较的,diff 思维也是来自后端
- diff 算法的比较思维
- 比较后会出现四种情况:
- 此节点是否被移除 -> 添加新的节点
- 属性是否被改变 -> 旧属性改为新属性
- 文本内容被改变-> 旧内容改为新内容
- 节点要被整个替换 -> 结构完全不相同 移除整个替换
- 比较后会出现四种情况:
- 整个 VDOM 的使用流程(Vue)
- 创建 VDOM 树
- 利用 render 函数渲染页面
- 数据改变,生成新的 VDOM
- 通过 diff 算法比较 新、旧两个 VDOM,将不同的地方进行修改,相同的地方就地复用,最后再通过 render 函数渲染页面
- 虚拟 DOM
-
组件有什么作用?- 主要用于保留组件状态或避免重新渲染
- 比如: 有一个列表页面和一个 详情页面,那么用户就会经常执行打开详情 => 返回列表 => 打开详情这样的话 列表 和 详情 都是一个频率很高的页面,那么就可以对列表组件使用
进行缓存,这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染 - 注意:
是用在其一个直属的子组件被开关的情形。如果你在其中有 v-for ,则不会工作。如果有多个条件性的子元素,
要求同时只有一个子元素被渲染
-
请详细说明你对 Vue 的生命周期的理解。
- 总共分为 8 个阶段创建前/后,载入前/后,更新前/后,销毁前/后
- 创建前/后: 在 beforeCreated 阶段,vue 实例的挂载元素 el 还没有
- 载入前/后:在 beforeMount 阶段,vue 实例的 $el 和 data 都初始化了,但还是挂载之前为虚拟的 dom 节点,data.message 还未替换。在 mounted 阶段,vue 实例挂载完成,data.message 成功渲染
- 更新前/后:当 data 变化时,会触发 beforeUpdate 和 updated 方法
- 销毁前/后:在执行 destroy 方法后,对 data 的改变不会再触发周期函数,说明此时 vue 实例已经解除了事件监听以及和dom 的绑定,但是 dom 结构依然存在
-
methods
、computed
、watch
有什么区别?、-
computed
属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用,要 return 出去一个值 -
methods
方法表示一个具体的操作,主要书写业务逻辑 -
watch
一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computed
和methods
的结合体,有 newVal 和 oldVal 两个参数 - 三者比较:
- 相同点:都是一个 function
- 不同点:
- computed 要 return 出去一个值,watch 不用
- methods 里的 function 更侧重业务逻辑(大量)的处理,computed 里的 function 不适合做大量业务逻辑
- 使用场景:
- watch:适合监听一些虚拟的东西,如路由
- methods:适合做一些方法的调用
- computed:可能需要引用一些数据,经过一系列的操作返回一个新的数据
-
-
vm.$nextTick()
方法有什么用?- nextTick(),是将回调函数延迟在下一次 dom 更新数据后调用。简单的理解是:当数据更新了,在 dom 中渲染后,自动执行该函数
- 注意:Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。nextTick,则可以在回调中获取更新后的 DOM
- 需要用 Vue.nextTick() 的地方
- Vue 生命周期的 created() 钩子函数进行的 DOM 操作一定要放在 Vue.nextTick() 的回调函数中,原因是在 created() 钩子函数执行的时候 DOM 其实并未进行任何渲染,而此时进行 DOM 操作无异于徒劳,所以此处一定要将 DOM 操作的 js 代码放进 Vue.nextTick() 的回调函数中。与之对应的就是 mounted 钩子函数,因为该钩子函数执行时所有的DOM挂载已完成
created(){ let that=this; that.$nextTick(function(){ //不使用this.$nextTick()方法会报错 that.$refs.aa.innerHTML="created中更改了按钮内容"; //写入到DOM元素 });
- 当项目中你想在改变 DOM 元素的数据后基于新的 dom 做点什么,对新 DOM 一系列的 js 操作都需要放进 Vue.nextTick() 的回调函数中;通俗的理解是:更改数据后当你想立即使用 js 操作新的视图的时候需要使用它,即vue 改变 dom 元素结构后使用 vue.$nextTick() 方法来实现 dom 数据更新后延迟执行后续代码
changeTxt:function(){ let that=this; that.testMsg="修改后的文本值"; //修改dom结构 that.$nextTick(function(){ //使用vue.$nextTick()方法可以dom数据更新后延迟执行 let domTxt=document.getElementById('h').innerText; console.log(domTxt); //输出可以看到vue数据修改后并没有DOM没有立即更新, if(domTxt==="原始值"){ console.log("文本data被修改后dom内容没立即更新"); }else { console.log("文本data被修改后dom内容被马上更新了"); } }); }
- 在使用某个第三方插件时 ,希望在 vue 生成的某些 dom 动态发生变化时重新应用该插件,也会用到该方法,这时候就需要在 $nextTick 的回调函数中执行重新应用插件的方法
- Vue 生命周期的 created() 钩子函数进行的 DOM 操作一定要放在 Vue.nextTick() 的回调函数中,原因是在 created() 钩子函数执行的时候 DOM 其实并未进行任何渲染,而此时进行 DOM 操作无异于徒劳,所以此处一定要将 DOM 操作的 js 代码放进 Vue.nextTick() 的回调函数中。与之对应的就是 mounted 钩子函数,因为该钩子函数执行时所有的DOM挂载已完成
-
谈谈对前端路由的认识。
- 在 Ajax 出现之前,都是使用后端路由来控制页面的渲染,服务器在接收到不同的url地址后,服务器通过解析 url 去拼接不同的 html,然后返回给前端进行渲染,所以这也是后端路由的一个弊端,每一次的切换都是需要刷新整个页面,同时如果是大量的页面每一个页面都需要做一段逻辑处理也造成了后端实在不堪重负
- 前端路由的出现很好的解决了这个问题,前端路由不需要刷新整个页面,也就不会出现每一次切换都会出现闪烁,也没有网络延迟,大大提升了用户体验,减轻了服务器的压力,但是同时也存在问题是前端的安全性问题
- 目前前端路由的实现主要采用两个方法:hash, history
- 前端路由的实现方式
- 基于 hash,利用
href
或者 location.hash = xxx - 基于 History API,主要利用 history.pushState() 和 history.repalceState()
- 各种 Router 框架,例如 react-router,vue-router...
- 基于 hash,利用
-
history
模式和hash
模式有什么区别?- hash 模式
- hash 模式背后的原理是 onhashchange 事件,可以在 window 对象上监听这个事件
- 因为 hash 发生变化的 url 都会被浏览器记录下来,从而你会发现浏览器的前进后退都可以用了。这样一来,尽管浏览器没有请求服务器,但是页面状态和 url 一一关联起来,后来人们给它起了一个霸气的名字叫前端路由,成为了单页应用标配
- 网易云音乐,百度网盘就采用了hash路由,看起来就是这个样子:
http://music.163.com/#/friendhttps://pan.baidu.com/disk/home#list/vmode=list
- history 模式
- 随着 history api 的到来,前端路由开始进化了,前面的 hashchange,你只能改变 # 后面的 url 片段,而 history api 则给了前端完全的自由
- history api可以分为两大部分,切换和修改,参考MDN,切换历史状态包括back、forward、go 三个方法,对应浏览器的前进,后退,跳转操作,有同学说了,(谷歌)浏览器只有前进和后退,没有跳转,嗯,在前进后退上长按鼠标,会出来所有当前窗口的历史记录,从而可以跳转(也许叫跳更合适)
- 修改历史状态包括了 pushState,replaceState 两个方法,这两个方法接收三个参数: stateObj,title,url
- 通过 history api,我们丢掉了丑陋的 #,但是它也有个问题:不怕前进,不怕后退,就怕刷新
f5
,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的,不玩虚的。 在 hash 模式下,前端路由修改的是 # 中的信息,而浏览器请求时是不带它玩的,所以没有问题.但是在 history 下,你可以自由的修改 path,当刷新时,如果服务器中没有相应的响应或者资源,会分分钟刷出一个 404 来
- hash 模式
-
vuex
是什么,包含哪几个部分?- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新
- 不能直接改变 store 中的状态。改变 store 中状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用
- 每一个 Vuex 应用的核心就是 store(仓库)。store 基本上就是一个容器,它包含着你的应用中大部分的状态 (state)
- VueX 是一个集中式的存储仓库【状态】,类似于 本地存储、数据库,vuex是vue的状态管理工具,它的功能主要是实现多组件间的状态【数据】共享
- VueX 的组成部分:
- state 状态
- action 动作(业务交互)
- type 要求是一个常量,理由:常量比较稳定,安全性高
- mutation 修改 state
- getter 获取数据
- store state 数据存储者
-
Vue中组件传值有哪几种方式?(兄弟、父子、子父、祖孙组件间传值各用什么?)
- 父向子传值
- 父子传值一般在子组件上绑定自定义的属性,然后子组件在实例中通过 props 接收该属性
- 子向父传值
- 这种情况一般使用emit反向传值,通过注册自定义事件,在事件函数中接收值
- 兄弟传值
- 子传给父,父再传给子
- 后代(祖孙)传值
- provide:相当于分享自己实例中的一些东西出去
- inject :用来接受 provide 分享出去的东西
- 父组件 provide 出去的东西,只要下面的后代组件 inject 一下就能拿到,无视组件距离
- 父向子传值