vue常见面试题(生命周期、路由是重点)
Vue 实现数据双向绑定的原理:Object.defineProperty()
vue 实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()
来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty
将它们转为getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
vue 的数据双向绑定 将 MVVM 作为数据绑定的入口,整合Observer,Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 的数据变化,通过 Compile 来解析编译模板指令(vue 中是用来解析 {{}}),最终利用 watcher 搭起 observer 和 Compile 之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据 model 变更双向绑定效果
最简单的发布订阅者模式:
// 发布者:收集依赖/收集订阅
class Dep {
constructor() {
// 这个 subs 数组,用来存放所有订阅者的信息
this.subs = [];
}
// 向subs数组中,添加订阅者的信息
addSub (watcher) {
this.subs.push(watcher);
}
// 发布通知的方法
notify () {
this.subs.forEach(watcher => watcher.update());
}
}
// 订阅者的类
class Watcher {
constructor(cb) {
this.cb = cb;
}
// 触发回调的方法
update () {
this.cb();
}
}
const w1 = new Watcher(() => {
console.log('我是第1个订阅者');
})
const w2 = new Watcher(() => {
console.log('我是第2个订阅者');
})
// 收集订阅者的信息
const dep = new Dep();
dep.addSub(w1);
dep.addSub(w2);
dep.notify();
<body>
<div id="app">
<input type="text" id="txt">
<p id="show"></p>
</div>
</body>
<script type="text/javascript">
var obj = {}
Object.defineProperty(obj, 'txt', {
get: function () {
return obj
},
set: function (newValue) {
document.getElementById('txt').value = newValue
document.getElementById('show').innerHTML = newValue
}
})
document.addEventListener('keyup', function (e) {
obj.txt = e.target.value
})
</script>
Vue 的路由实现:hash 模式 和 history 模式
hash 模式
:在浏览器中符号“#”,#以及#后面的字符称之为 hash,用 window.location.hash
读取;
特点
:hash 虽然在 URL 中,但不被包括在 HTTP 请求中;用来指导浏览器动作,对服务端安全无用,hash 不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端
来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
history 模式
:history 采用 HTML5 的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及 popState 事件的监听到状态变更。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id
。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。
vue-router官网里如此描述:不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个index.html 页面,这个页面就是你 app 依赖的页面
。
css 只在当前组件起作用
在 style 标签中写入 scoped 即可 例如:<style scoped></style>
v-if 和 v-show 区别
- v-if 按照条件是否渲染,
- v-show 是 display 的 block 或 none;
$route 和$router
的区别
- $route 是
路由信息对象
,包括path,params,hash,query,fullPath,matched,name
等路
由信息参数 - $router 是
路由实例对象
包括了路由的跳转方法,钩子函数等
vue.js 的两个核心是什么
数据驱动、组件系统
vue 常用的修饰符?
- prevent: 提交事件不再重载页面;.
- stop: 阻止单击事件冒泡;.
- self: 当事件发生在该元素本身而不是子元素的时候会触发;.
- capture: 事件侦听,事件发生的时候会调用
v-on 可以绑定多个方法吗
可以
vue 中 key 值的作用?
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项
的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,
并且确保它在特定索引下显示已被渲染过的每个元素。
key 的作用主要是为了高效的更新虚拟 DOM
。
什么是 vue 的计算属性?
在模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次
使用的情况下,尽量采取计算属性的方式。
好处
:
①使得数据处理结构清晰;
②依赖于数据,数据更新,处理结果自动更新;
③计算属性内部 this 指向 vm 实例;
④在 template 调用时,直接写计算属性名即可;
⑤常用的是 getter 方法,获取数据,也可以使用 set 方法改变数据;⑥相较于 methods,不管依赖的数据变不变,methods 都会重新计算,但是依赖数据不变的时候 computed 从缓存中获取,不会重新计算
vue 等单页面应用及其优缺点
优点
:
Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件,核心是一个响应的数据绑定系统
。MVVM、数据驱动、组件化、轻量、简洁、高效、快速、模块友好。
缺点
:不支持低版本的浏览器,最低只支持到 IE9
;不利于 SEO 的优化(如果要支持 SEO,建议通过服务端来进行渲染组件);第一次加载首页耗时相对长一些;不可以使用浏览器的导航按钮需要自行实现前进、后退
对 keep-alive 的了解
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
在 vue 2.1.0 版本之后,keep-alive 新加入了两个属性: include(包含的组件缓存) 与 exclude(排除的组件不缓存,优先级大于 include)
MVC和MVVM
.....................................什么是MVVM?
MVVM 是 Model-View-ViewModel 的缩写。 MVVM 是一种设计思想
* Model层代表数据模型 (定义数据修改和操作的业务逻辑)
* View 代表UI组件 (将数据模型转化成 UI 展现出来)
* ViewModel 是一个同步 View 和 Model的对象
在 MVVM 架构下, View 和 Model 之间并没有直接的联系,而是通过 ViewModel进行交互,
Model 和 View之间的交互是双向的, 因此 View 数据的变化会同步到 Model 中,
而 Model 数据的变化也会立即反应到 View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接起来,而 View 和Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作 DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由MVVM 来统一管理
...............................mvc和mvvm的区别:
mvc 和 mvvm 其实区别并不大。都是一种设计思想。主要就是 mvc 中 Controller 演变成 mvvm 中viewModel。 mvvm 主要解决了 mvc 中大量的 DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验
父子之间传值和兄弟之间传值
父组件向子组件传值-基本使用:
1.父组件通过属性方式向子组件传值
2.子组件通过props接收
3.通过子组件template,渲染到页面上
子组件向父组件传值:
1.子组件用 $emit() 触发事件
2.$emit() 第一个参数为 自定义的事件名称 第二个参数为需要传递的数据
3.父组件用v-on 监听子组件的事件
通过$emit 方式,通过事件触发向上一层触发事件,子组件触发的事件父组件恰好在监听
监听后就可以获取子组件带过来的内容,实现子组件向父组件传值的功能
兄弟之间传值:
eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件
兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据
1.提供事件中心 var hub = new Vue()
2.传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
3.接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
4.销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
vue是什么?
vue是一套构建用户界面的渐进式框架,采用自底向上增量开发的设计。
Vue 的核心库只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合
如何理解数据的响应式?
数据发生变化--页面也跟着发生变化;不需要操作DOM
所有的 DOM 操作都由 Vue 来处理,编写的代码只需要关注逻辑层面即可
Vue全局桶
Vue有著名的全家桶系列:
包含了vue-router/vuex/vue-resource/.再加上构建工具vue-cli,less样式
就是一个完整的vue项目的核心构成。
概况起来就是:
1.项目构建工具
2.路由
3.状态管理
4.http请求工具
Vue核心思想:组件化开发和数据驱动
Vue-router
vue-router的基本使用:
1.导入 vue 文件:首先 vue.js、然后 vue-router.js
2.添加路由连接: <router-link to="/user">User</router-link>
* router-link是vue中提供的标签,默认会被渲染为a标签
* to属性默认会被渲染为 href属性
* to属性的值默认会被渲染为 #开头的hash地址
3.路由占位符:<router-view></router-view>
* 将来通过路由规则匹配到的组件,将会被渲染到router-view所在的位置
4.创建路由组件 const 组件名 = {template:` `}
5.创建路由实例 且 配置路由规则 routes
* routes是路由规则数组
* 每个路由规则都是一个配置对象,其中至少包含path: 和 component:两个属性
* path 表示当前路由规则---匹配的hash地址
* component 表示当前路由规则---对应要展示的组件
const router =new VueRouter({
routes:[{
path: '/user',
component: 路由名称
}]
})
6.挂载路由对象router,到Vue实例对象中
vue的一些用法
......................自定义指令........................................
1...........全局自定义指令
inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)
1. 自定义指令的语法规则(获取元素焦点)
Vue.directive('focus', {
inserted: function(el) {
// 获取元素的焦点
el.focus();
}
});
2. 自定义指令用法
<input type="text" v-focus>
2...........带参数的布局自定义指令
bind:只调用一次,指令第一次绑定到元素时调用。
Vue.directive('color', {
bind: function(el, binding) {
// 根据指令的参数设置背景色
el.style.backgroundColor = binding.value.color
}
});
指令的用法: <input type="text" v-color='msg'> **在data中定义msg**
3..........布局自定义指令的用法: 放在vue实例里面,作为vue实例对象的属性或者方法
局部指令:只能在本组件中使用。
directives: {
color: {
bind: function(el, binding) {
el.style.backgroundColor = binding.value.color;
}
},
focus: {
inserted: function(el) {
// 获取元素的焦点
el.focus();
}
}
}
......................计算属性.................................
1.为什么需要计算属性?
表达式的计算逻辑可能会比较复杂,使用计算属性可以使模板内容更加简洁!
2.计算属性与方法的区别:计算属性是基于依赖进行缓存的,而方法不存在缓存
...................... 侦听器watch:直接用于vue实例对象中.........
侦听器的应用场景:数据变化时执行异步或开销较大的操作 (验证用户名是否可用)
原理:数据一旦发生变化就通知侦听器所绑定的方法
.......................过滤器............................
1.过滤器的作用:格式化数据。如将字符串格式化为首字母大写,将日期格式化为指定的格式
...............全局过滤器
1.自定义过滤器
Vue.filter('过滤器名称',function(value){
// return 过滤器处理的结果 (业务逻辑)
})
2.过滤器的使用
1、插值表达式 <div>{{msg | upper}}</div>
2、属性绑定 <div :abc='msg | upper'>测试数据</div>
................局部过滤器
filters: {
过滤器名称: function(){
}
}
..............................................................
1.选用vue的原因
-
Vue的核心思想、优点
-
从学习成本来说,Vue 更容易上手,更轻量,结合 Vuex 管理状态,视图逻辑和数据的耦合度低,
项目结构清晰明了,Vue 的可扩展性也非常好
5.谈谈你对组件化的理解
个人理解每个组件就是页面的一个区域,
可以把页面由原来的一个整体,拆分成每个部分;每个部分称为一个组件
好处:合理的拆分组件可以将一个大型的项目,像拼积木一样拼接起来
一个大型的项目业务逻辑比较复杂;但是经过拆分每个组件变得精小,这样每个组件的维护相对容易
组件化开发的好处:
- 提高开发效率
- 方便重复使用
- 提升整个项目的可维护性
- 便于协同开发
- 高内聚性
- 低耦合度
- 组件之间的传值很方便
...........................................模块化的理解?
1.模块化简而言之就是一个模块执行一定的功能,当我们在代码中需要这部分功能时不需要重复编写模块代码,
而是直接 将之前写好的功能模块引入就可以了。
2.前端模块化就是把复杂的代码分成一个个独立的模块。
*但是这样又会引来模块与模块之间的依赖问题,所以就有了CommonJS,AMD,CMD规范,最后又出现了webpack.
浏览器端不能使用 CommonJS规范;因为 CommonJS 下,模块是同步加载的;
AMD/CMD可以理解为是commonjs在浏览器端的解决方案,AMD/CMD下,模块都是异步加载的
1.AMD模块化规范代表:RequireJS
主要特性1:对于依赖的模块,**AMD 是提前执行**
主要特性2:推崇依赖前置;
2.CMD模块化规范代表:SeaJS
主要特性1:对于依赖的模块,**CMD 是延迟执行**
主要特性2:推崇依赖就近;
3. ES6的模块化(大趋势):es6是在语言标准层面上,实现了模块功能,
完全可以取代CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案
----》
模块化规范,认为是一种明文的约定,大家都按照相同的约定写代码,减少了不必要的沟通成本,
极大方便了各个模块之间的调用,方便别人,同时也方便自己。
........................npm的两层含义?
1.NPM 是一个 第三方模块的托管网站
2.NPM 是Node的包管理工具,在我们安装Node时候,就已经顺便也安装了 NPM 这个管理工具
6.vue中computed 和 watch的异同
computed
- 优先读取缓存
- computed 计算属性只有在相关的数据发生变化时才会改变要计算的属性