1、说一下Vue的优点
体积小,更高的运行效率,双向数据绑定,生态丰富、学习成本低
2、v-show和v-if的共同点和不同点?
共同点:都能控制元素的显示和隐藏;
不同点:
实现本质方法不同,v-show本质就是通过控制css中的display设置为none,控制隐藏,只会编译一次;
v-if是动态的向DOM树内添加或者删除DOM元素,若初始值为false,就不会编译了。而且v-if不停的销毁和创建比较消耗性能。
总结:如果要频繁切换某节点,使用v-show(切换开销比较小,初始开销较大)。如果不需要频繁切换某节点使用v-if(初始渲染开销较小,切换开销比较大)。
3、vue.js双向数据绑定原理?
Vue.js 2.0 采用数据劫持(Proxy 模式)结合发布者-订阅者模式(PubSub 模式)的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
Vue.js 3.0, 放弃了Object.defineProperty ,使用更快的ES6原生 Proxy (访问对象拦截器, 也称代理器)
实现步骤:
[if !supportLists]1)[endif]需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
2)compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
3)Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是: ①在自身实例化时往属性订阅器(dep)里面添加自己 ②自身必须有一个update()方法 ③待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
4)MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 --> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
4、你对Vue项目进行了哪些优化
不在模板里面写过多表达式
循环调用子组件时添加key
频繁切换的使用v-show,不频繁切换的使用v-if
尽量少用float,可以用flex
按需加载,可以用require或者import()按需加载需要的组件
5、vue.js的响应式
当VUE实例数据发生改变时,页面也会发生响应。
当创建Vue实例时,vue 会遍历data 选项的属性,利用 Object.defineProperty 为属性添加 getter 和 setter 对数据的读取进行劫持(getter 用来依赖收集,setter 用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化。
依赖的模式:
vue.js采用数据劫持结合发布-订阅模式,通过 Object.defineproperty 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调。
6、data为什么是函数?
如果组件里data 直接写了一个对象的话,那么在模板中多次声明这个组件,组件中的 data 会指向同一个引用。
此时对data 进行修改,会导致其他组件里的 data 也被修改。使用函数每次都重新声明一个对象,这样每个组件的data都有自己的引用,就不会出现相互污染的情况了。
7、介绍一下keep-alive
keep-alive是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染。
1)一般结合路由和动态组件一起使用,用于缓存组件;
2)提供include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
3)对应两个钩子函数activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。8、介绍虚拟DOM
(1)虚拟dom是为了解决浏览器性能问题而被设计出来的,当操作数据时,将改变的dom元素缓存起来,都计算完后再通过比较映射到真实的dom树上。
(2)让我们不用直接操作DOM元素,只操作数据便可以重新渲染页面
9、diff算法
diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁。
diff算法的本质是找出两个对象之间的差异,目的是尽可能复用节点。此处说到的对象其实就对应 vue中的 virtual dom,即使用 js对象来表示页面中的 dom 结构。
diff算法比较新旧虚拟dom
如果节点类型相同,则比较数据,修改数据
如果节点不同,直接干掉节点及所有子节点,插入新的节点
如果给每个节点都设置了唯一的key,就可以准确的找到需要改变的内容,否则就会出现修改一个地方导致其他地方都改变的情况。比如A-B-C-D, 我要插入新节点A-B-M-C-D,实际上改变的了C和D。但是设置了key,就可以准确的找到B C并插入。
10、如何编译template 模板?
compile编译会有三个过程
(1)parse 根据正则进行字符串解析,得到指令、class、style等数据,形成语法树(AST)
(2)对 parse 生成的 AST 进行静态内容的优化,标记静态节点(和数据没有关系不需要每次都刷新的内容),标记静态根节点。
(3)generate 生成 render
生成render 的 generate 函数的输入也是 AST,它递归了 AST 树,为不同的 AST 节点创建了不同的内部调用方法,等待后面的调用。
11、计算属性compoter和watch的区别
computed用于处理复杂的逻辑运算,主要和methods储存方法来进行区分;methods储存方法,computed储存需要处理的数据值;methods每次都会调用,computed有缓存机制,只有改变时才执行,性能更佳;
watch顾名思义,用于监听数据变化,其中可以监听的数据来源有三部分:props、data、computed内的数据;watch提供两个参数(newValue,oldValue),第一个参数是新值,第二个参数保存旧值;
区别:
计算属性computed :
支持缓存,只有依赖数据发生改变,才会重新进行计算
不支持异步,当computed内有异步操作时无效,无法监听数据的变化
computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过的数据通过计算得到的
如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
侦听属性watch:
不支持缓存,数据变,直接会触发相应的操作;
watch支持异步;
监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
当一个属性发生变化时,需要执行对应的操作;一对多;
12、组件传值
1、父传子:父组件中通过v-bind绑定一个属性,子组件中通过props接收父组件中的绑定的属性
2、子传父:子组件通过广播的方式$emit将值传递给父组件,父组件中通过一个函数去接收子组件中传递过来的值
3、兄弟间传值:可以通过中间媒介父组件进行传递值得中转
4、使用vuex状态管理,可以实现数据的随意存储和获取
5、provide、inject 父组件通过provide注入一个依赖,其所有的子孙组件可以通过inject来接收
13、生命周期函数
vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。通俗说就是 Vue 实例从创建到销毁的过程,就是生命周期。
钩子函数 描述
beforeCreate 在实例初始化之后,数据观测(data observer)和 event/watch事件配置之前被调用
created 在实例创建完成后立即被调用,在这一步实例已经完成了:数据观测、属性和方法的运算和 event/watch事件的回调,但是$el属性目前不可见,还无法操作dom。
beforeMount 在挂载开始之前被调用
mounted 在挂载成功后被调用,el被新创建的vm.$el替换,已经可以操作dom
beforeUpdate 数据更新之前调用
update 数据更新完成时调用,组件dom已经更新
activated 组件被激活时调用
deactivated 组件被移除时调用
beforeDestory 组件销毁前调用
destoryed 组件销毁后调用
14、vue中的key的作用是什么?
当Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key的作用让每个item有一个唯一的识别身份,可以下标值index或者id, 主要是为了vue精准的追踪到每一个元素,高效的更新虚拟DOM。
15、什么是MVVM?
MVVM是Model-View-ViewModel的缩写。MVVM是一种设计思想。Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
16、mvvm和mvc区别?它和其它框架(jquery)的区别是什么?哪些场景适合?
mvc和mvvm其实区别并不大。都是一种设计思想。主要就是mvc中Controller演变成mvvm中的viewModel。mvvm主要解决了mvc中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
区别:vue数据驱动,通过数据来显示视图层而不是节点操作。场景:数据操作比较多的场景,更加便捷
17、为什么避免v-if 和 v-for 用在一起
当Vue 处理指令时,v-for 比 v-if 具有更高的优先级,通过v-if 移动到容器元素,不会再重复遍历列表中的每个值。取而代之的是,我们只检查它一次,且不会在 v-if 为否的时候运算 v-for。
18、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 依赖的页面。”
19、$route和$router的区别
$route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
$router是“路由实例”对象包括了路由的跳转方法,钩子函数等。
20、vue常用的修饰符?
1.事件修饰符
.prevent: 阻止默认事件(在指定的事件后进行默认事件的阻止);
.stop: 阻止单击事件冒泡(在指定事件后阻止事件冒泡,阻止指定在最内层事件的标签里);
.self: 当事件发生在该元素本身而不是子元素的时候会触发;
.capture: 事件侦听,事件发生的时候会调用
.once:控制元素指定的事件只执行一次
2. 表单元素修饰符
.number:能够强制的指定表单元素的内容数据类型是number
.trim:能够去除输入内容左右两边的空格
.lazy:只有标签的change事件执行后才会执行数据的双向绑定
[if !supportLists]21、[endif]Vue.js SPA单页面应用的理解及其优缺点?
SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
优点:
1)用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
2)基于上面一点,SPA 相对对服务器压力小;
3)前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
缺点:
1)初次加载耗时多:为实现单页Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
2)前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
3)SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。22、Vuex原理
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
(1)Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
(2)改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
主要包括以下几个模块:
1)State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
2)Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
3)Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
4)Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
5)Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
commit 和dispatch的区别在于commit是提交mutatious的同步操作,dispatch是分发actions的异步操作
1)dispatch:含有异步操作,例如向后台提交数据,写法this.$store.dispatch(‘action方法名’,值)
2)commit:同步操作,写法:this.$store.commit(‘mutations方法名’,值)23、使用过Vue SSR 吗?说说 SSR?
SSR大致的意思就是vue在客户端将标签渲染成整个html片段的工作在服务端完成,服务端形成的html 片段直接返回给客户端这个过程就叫做服务端渲染。
24、Vue 框架怎么实现对象和数组的监听?
Vue 框架是通过遍历数组 和递归遍历对象,从而达到利用Object.defineProperty() 也能对对象和数组(部分方法的操作)进行监听。
25、Proxy 与 Object.defineProperty 优劣对比
Proxy的优势如下:
Proxy可以直接监听对象而非属性;
Proxy可以直接监听数组的变化;
Proxy有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
Proxy作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;
Object.defineProperty的优势如下:
兼容性好,支持IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。
26、let、var、const之间的区别
1)var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
2)let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
3)const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。