响应式原理
1. 原理:
- 监听数据的变化,数据劫持
- 收集数据的依赖
- 当数据发生变化的时候更新渲染视图
2. vue2的响应式原理
- 主要使用的是Object.defineProperty( ) ,里面需要传入三个参数,分别是:(源数据的对象,源数据中的需要读写的属性,相对应的对象方法(包含了get和set方法))
3. vue3的响应式原理
- 主要依靠的是ES6新增的 Proxy 以及相配合的 Reflect实现的,需要在Proxy的实例对象中传入两个参数 (源数据对象,处理对象的方法(get,set,deleteProperty…等))
4. vue2和vue3响应式原理对比
- vue3的响应式相对于vue2来说简洁了很多,主要体现的地方就是我们用了Proxy的实例对象之后,不需要在单独的想vue2之中那样(使用的defineProperty)需要特意去知名监控某个对象的变化(name、age属性)。这个是很重要的一个变化。
双向数据绑定
1. 原理
- vue是通过数据劫持和发布订阅者模式来实现双向数据绑定的 就是利用oberver来监听module数据的变化,通过compile来解析模板,最终通过watcher来建立oberver和module之间的通信桥梁 创建一个vue对象,分为el模板和data数据 使用Object.definePropertype来劫持data中的每个属性的getter和setter,在数据发生变化的时候发布消息给订阅者,触发相应的回调。(vue3使用proxy劫持数据)也就是observer的功能 compile 解析模板,将模板中的变量替换成数据,然后动态渲染页面, 将双向绑定的指令对应的节点添加更新函数,添加监听数据的订阅者,一旦数据有变化,就更新视图 Watcher订阅者是Observer和Compile之间通信的桥梁,他负责往dep里添加订阅者,当收到数据变化的通知时,触发compile的回调,更新视图 (vue中就是每当有这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者)
2. 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变更双向绑定效果。
3. js实现简单的双向绑定
```
<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>
```
vue2和vue3的区别
区别
- 变得更轻,更快,对上⼀个版本的升级优化,⽤户体验都是不断地在越来越⽅便,
- 在数据双向绑定原理方面
- Vue2使⽤的是Object.defineProperty()进⾏数据劫持,结合发布订阅的⽅式实现。
- Vue3使⽤的是Proxy代理,使⽤ref或者reactive将数据转化为响应式数据。
- Object.defineProperty监听对象属性。而Proxy监听的是整个对象
- vue3新增了⼀些内置组件和⽅法
- ⽐如vue3可以默认进⾏懒观察,使⽤Function-based API,setup函数,对于插件或对象的⼀ 个按需引⼊,Computed Value ,新加⼊了 TypeScript 以及 PWA 的⽀持等
- 在生命周期方面
- vue3相比vue2,生命周期名字大部分需要+on,功能类似,使用vue3API需要先引入,vue2选项API这直接调用即可。
- vue3相比vue2,生命周期名字大部分需要+on,功能类似,使用vue3API需要先引入,vue2选项API这直接调用即可。
- 虚拟DOM
- Vue3 相比于 Vue2 虚拟DOM 上增加
patchFlag字段。
- Vue3 相比于 Vue2 虚拟DOM 上增加
- 自定义渲染API
- Vue3 提供的
createApp默认是将 template 映射成 html。但若想生成canvas时,就需要使用custom renderer api自定义render生成函数。
//自定义runtime-render函数 import{createApp}from'./runtime-render' import App from './src/App' createApp(App).mount('#app') - Vue3 提供的
- 多根节点
- vue在模板中如果使用多个根节点时会报错,需要去将组件包裹在< div >中。Vue3 支持多个根节点,使用fragment,可以少写一层。
vue3使⽤proxy的优势
- defineProperty只能监听某个属性,不能对全对象监听 可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可)
- 可以监听数组,不⽤再去单独的对数组做特异性操作,通过Proxy可以直接拦截所有对象类型数据的操作,完美⽀持对数组的监听。
Vue、Angular、React的区别?
- 1.Vue与AngularJS的区别
- 相同点: 都支持指令:内置指令和自定义指令;都支持过滤器:内置过滤器和自定义过滤器;都支持双向数据绑定;都不支持低端浏览器。
- 不同点: AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观;在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢;Vue.js使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。
- 2.与React的区别
- 相同点: React采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用;中心思想相同:一切都是组件,组件实例之间可以嵌套;都提供合理的钩子函数,可以让开发者定制化地去处理需求;都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载;在组件开发中都支持mixins的特性。
- 不同点: React采用的Virtual DOM会对渲染出来的结果做出检查;Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作Virtual DOM。
MVVM 和 MVC
MVVM
- (
Model(模型)、View(视图)、ViewModel(视图模型)) - 概念: 在
MVVM框架下视图和模型是不能直接通信的,只能通过ViewModel进行交互,它能够监听到数据的变化,然后通知视图进行自动更新,而当用户操作视图时,VM也能监听到视图的变化,然后通知数据做相应改动,这实际上就实现了数据的双向绑定。并且V和VM可以进行通信。 - 优点: 分离视图和模型,减少了代码的耦合度,自动更新DOM和数据
- 缺点: 当一个视图的状态很多的时候,viewmodule的构建和维护成本比较高
MVC
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。- 概念: VC是单向通信。也就是
View跟Model,必须通过Controller来进行联系。 - 优点: 低耦合、生命周期成本低、可维护性高、部署快、有利软件工程化管理。
区别
- MVC和MVVM的区别并不是VM完全取代了C,ViewModel存在目的在于抽离Controller中展示的业务逻辑,而不是替代Controller,其它视图操作业务等还是应该放在Controller中实现。也就是说MVVM实现的是业务逻辑组件的重用。
- MVC中Controller演变成MVVM中的ViewModel
- MVVM通过数据来显示视图层而不是节点操作
- MVVM主要解决了MVC中大量的dom操作使页面渲染性能降低,加载速度变慢,影响用户体验
Vue的生命周期
beforeCreate(创建前) 在数据观测和初始化事件还未开始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(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
1. 什么是vue生命周期?
- Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
2. vue生命周期的作用是什么?
- 它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
3. vue生命周期总共有几个阶段?
- 它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后。
4. 第一次页面加载会触发哪几个钩子?
- 会触发 下面这几个beforeCreate, created, beforeMount, mounted 。
5. DOM 渲染在 哪个周期中就已经完成?
- DOM 渲染在 mounted 中就已经完成了。
Vue组件通信
1. 父组件向子组件传递
- 父组件通过props向下传递数据给子组件,组件中的数据共有三种形式:data、props、computed。
2. 子组件传给父组件
- 通过事件形式,子组件通过events给父组件发送消息,也就是子组件把自己的数据发送到父组件。通过 events(
$emit)。 - 通过父链 / 子链也可以通信(
$parent/$children)。 - ref 也可以访问组件实例;provide / inject API;
$attrs/$listeners。
3. 兄弟组件之间
- eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。项目比较小时,用这个比较合适。(虽然也有不少人推荐直接用VUEX,具体来说看需求。)
- VueX
4. 跨级通信
- us;Vuex;provide / inject API、
$attrs/$listeners
Vue的路由实现
1. hash、history、abstract
-
hash:默认值 路由从浏览器地址栏中的hash部分获取路径,改变路径也是改变hash的部分,兼容性最好,不刷新页面;
-
history: 路由从浏览器地址栏中的location.pathname部分获取路径,改变路径使用H5的history api(history.pushState(null,null,"/blog")),该模式可以让地址栏友好,看起来舒服,也需要浏览器支持history api;
-
abstract: 路由从内存中获取路径,改变路径也只是改动内存中的值,这种模式通常应用到非浏览器环境中。
-
hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用
window.location.hash读取; 特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。 hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。 -
history模式:history采用HTML5的新特性;且提供了两个新方法:
pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
2. route和router的区别
- route是
路由信息对象,包括path,params,hash,query,fullPath,matched,name等路由信息参数。而router是路由实例对象包括了路由的跳转方法,钩子函数等。
3. vue路由的钩子函数具体参数
- 作用
- 首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。
- 具体参数
-
beforeEach主要有3个参数to,from,next: -
to:route即将进入的目标路由对象, -
from:route当前导航正要离开的路由 -
next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。
-
导航守卫
- vue-router提供的导航守卫主要用来监听监听路由的进入和离开的.
- vue-router提供了beforeEach和afterEach的钩子函数, 它们会在路由即将改变前和改变后触发.
- 导航钩子的三个参数解析:
- to: 即将要进入的目标的路由对象.
- from: 当前导航即将要离开的路由对象.
- next: 调用该方法后, 才能进入下一个钩子
Vuex
1.出现原因:
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理 应用的所有组件的状态
- 简单来说就是把需要共享的变量全部存储在一个对象里面。然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。并且这个对象里的数据都是响应式的,当组件从store中获取了数据,如果store里的数据发生改变,组件里的数据也会发生改变
2.分类:
- 有五种,分别是 State、 Getter、Mutation 、Action、 Module state => 基本数据(数据源存放地) getters => 从基本数据派生出来的数据 mutations => 提交更改数据的方法,同步 actions => 像一个装饰器,包裹mutations,使之可以异步。 modules => 模块化Vuex
3.使用场景
- 比如用户的登录状态、用户名称、头像、地理位置信息等等。
- 比如商品的收藏、购物车中的物品等等。
- 这些状态信息,都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的
- 核心流程
- 组件触发一些动作或事件,也就是actions,想要改变状态或获取数据,但是在vuex中状态是集中管理的,不能直接修改数据,所以就会把这个动作或事件(actions)提交给mutation,然后mutatiion去修改state中的数据,当state数据改变后,就会重新渲染到vue组件去,组件展示新数据。
vue中 key 值的作用
1. 概念:
- key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。
- vue中的key的作用,在vue中我们可能在两种情况下使用key,第一种情况下就是在v-if中,第二种情况下就是在v-for中使用key。下面我们来看一下key在其中起到的作用。
2.在v-if中使用key
- 首先我们先看在vue中出现的一种情况,我们在vue中如果使用v-if进行切换时,此时Vue为了更加高效的渲染,此时会进行前后比较,如果切换前后都存在的元素,则直接复用。如果我们在模板中切换前后都存在input框,此时我们在input框中写入一些数据,并且进行页面切换,则原有的数据就会保存。 此时我们就可以使用key,给每一个input框,添加一个唯一的标识key,来表示元素的唯一性。
3.在v-for中使用key
- 对于用v-for渲染的列表数据来说,数据量可能一般很庞大,而且我们经常还要对这个数据进行一些增删改操作。那么整个列表都要重新进行渲染一遍,那样就会很费事。而key的出现就尽可能的回避这个问题,提高效率。v-for默认使用就地复用的策略,列表数据修改的时候,他会根据key值去判断某一个值是否修改,如果修改则重新渲染该项,否则复用之前的元素。在v-for中我们的key一般为id,也就是唯一的值,但是一般不要使用index作为key。
-
Computed 和 Methods 的区别
- 可以将同一函数定义为一个 method 或者一个计算属性。对于最终的结果,两种方式是相同的
- 不同点:
- computed: 计算属性是基于它们的依赖进行缓存的,只有在它的相关依赖发生改变时才会重新求值;
- method 调用总会执行该函数。
Computed 和 Watch 的区别
- computed 计算属性: 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的值发生改变时,下一次获取 computed 的值时才会重新计算 computed 的值。不支持异步
- watch 侦听器: 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。支持异步操作。
- 运用场景:
- 当需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时都要重新计算。
- 当需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许执行异步操作 ( 访问一个 API ),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
v-if和v-show的区别?
- 首先,它们两个的作用效果是相同的,都能控制元素在页面是否显示。
- 内部控制方式不一样:
v-if显示隐藏是将dom元素整个添加或删除。v-show隐藏则是为该元素添加css--display:none,dom元素依旧还在。
- v-if可以拥有更多的分支,v-else等,会删除和添加元素,控制dom元素的有和没有,有效的减少树的结点和渲染量,但是导致树不稳定
- 而v-show没有分支,只是切换属性样式,不能改动元素,dom元素始终都在
- 树的渲染效率取决于(树的节点,渲染量和树的稳定性)
- 使用场景:
v-if相比v-show开销更大的,直接操作dom节点增加与删除,如果需要非常频繁地切换,则使用 v-show 较好,如果在运行时条件很少改变,则使用 v-if 较好。
data为什么是一个函数而不是一个对象?
js中的对象是引用类型,多个实例引用一个对象时,当一个实例的对象修改时,别的实例都会改变。在Vue中是想要更多的复用组件,每个组件中的数据都要是独立的,所以组件的data值不能是一个对象,使用函数的形式,在复用组件的时候每次都会返回一个新的data,这样每个组件都有自己的数据空间,各自维护,互不影响
对keep-alive 的了解?
keep-alive可以实现组件缓存,当组件切换时不会对当前组件进行卸载。- 主要是有
include、exclude、max三个属性;前两个属性允许keep-alive有条件的进行缓存;max可以定义组件最大的缓存个数,如果超过了这个个数的话,在下一个新实例创建之前,就会将以缓存组件中最久没有被访问到的实例销毁掉。 - 两个生命周期
activated/deactivated,用来得知当前组件是否处于活跃状态。 - 原理
- keep-alive中运用了
LRU(Least Recently Used)算法。 - 获取
keep-alive包裹着的第一个子组件对象及其组件名; 如果 keep-alive 存在多个子元素,keep-alive要求同时只有一个子元素被渲染。所以在开头会获取插槽内的子元素,调用getFirstComponentChild获取到第一个子元素的VNode。 - 根据设定的黑白名单(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例(
VNode),否则开启缓存策略。 - 根据组件ID和tag生成缓存Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该key在
this.keys中的位置(更新key的位置是实现LRU置换策略的关键)。 - 如果不存在,则在
this.cache对象中存储该组件实例并保存key值,之后检查缓存的实例数量是否超过max设置值,超过则根据LRU置换策略删除最近最久未使用的实例(即是下标为0的那个key)。最后将该组件实例的keepAlive属性值设置为true。
- keep-alive中运用了
$nextTick
1. 出现原因
- vue为了高效率的更新DOM,Vue不可能对每一个数据变化都做一次渲染,它会把这些变化先放在一个异步的队列当中,同时它还会对这个队列里面的操作进行去重,然后在一次事件循环结束之后更新DOM,nextTick就是在一次DOM更新完毕之后调用的
2. 实现原理:
- nextTick会将回调函数放在异步任务重中,他是使用Promise.then、MutationObserver和setImmediate或者setTimout来让callback放入异步队列的,这样callbac就会在同步代码执行完了之后调用,此时就可以操作更新好的DOM了
3. 源码是使用三个参数来做的
- callback:我们要执行的操作,可以放在这个函数当中,我们没执行一次$nextTick就会把回调函数放到一个异步队列当中;
- pending:标识,用以判断在某个事件循环中是否为第一次加入,第一次加入的时候才触发异步执行的队列挂载
- timerFunc:用来触发执行回调函数,也就是Promise.then或MutationObserver或setImmediate 或setTimeout的过程
4. 使用场景:
- 比如说,点击按钮出现输入框,输入框出现的时候自动获得焦点,设计的时候是 点击按钮修改isshow,给输入框加ref,用ref得到输入框,然后this. r e f s . i d . f o c u s ( ) 但是会发现输入框并没有获得焦点,因为执行的时候,虽然改了 i s s h o w ,但是页面还没有渲染出来,就获取 r e f ,是获取不到的,这时就要延时一下,等到页面重新渲染之后再获取,这是就使用 refs.id.focus() 但是会发现输入框并没有获得焦点,因为执行的时候,虽然改了isshow,但是页面还没有渲染出来,就获取ref,是获取不到的,这时就要延时一下,等到页面重新渲染之后再获取,这是就使用 refs.id.focus()但是会发现输入框并没有获得焦点,因为执行的时候,虽然改了isshow,但是页面还没有渲染出来,就获取ref,是获取不到的,这时就要延时一下,等到页面重新渲染之后再获取,这是就使用nextTick 在created()钩子函数中,使用$nextTick,因为created()时,DOM还没有渲染,无法进行DOM操作
vue.js的两个核心是什么?
- vuejs的两个核心是数据驱动和组件系统。数据驱动也就是数据的双向绑定,用于保证数据和视图的一致性。组件系统能够把页面抽象成多个相对独立的模块;可以实现代码重用,提高开发效率和代码质量,便于代码维护。
vue几种常用的指令
- v-for 、 v-if 、v-bind、v-on、v-show、v-else
什么是vue的计算属性?
- 在模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次使用的情况下,尽量采取计算属性的方式。 好处:①使得数据处理结构清晰;②依赖于数据,数据更新,处理结果自动更新;③计算属性内部this指向vm实例;④在template调用时,直接写计算属性名即可;⑤常用的是getter方法,获取数据,也可以使用set方法改变数据;⑥相较于methods,不管依赖的数据变不变,methods都会重新计算,但是依赖数据不变的时候computed从缓存中获取,不会重新计算。
vue等单页面应用及其优缺点
优点:Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件,核心是一个响应的数据绑定系统。MVVM、数据驱动、组件化、轻量、简洁、高效、快速、模块友好。缺点:不支持低版本的浏览器,最低只支持到IE9;不利于SEO的优化(如果要支持SEO,建议通过服务端来进行渲染组件);第一次加载首页耗时相对长一些;不可以使用浏览器的导航按钮需要自行实现前进、后退。
动态路由? 怎么获取传过来的值
- 在 router 目录下的 index.js 文件中,对 path 属性加上 /:id,使用 router 对象的 params.id 获取。
父子组件传递时,emit和props实现原理
自定义指令
-
全局自定义指令
- 声明位置:在main.js文件里面通过Vue.directive()进行全局声明。
- 定义方式:(例如)
- 全局引用:
<div v-color="js表达式"></div> -
私有自定义指令
- 声明位置:在每个Vue组件中,可以在directives节点下声明私有指令。
- 定义方式:
- 局部引用:
<div v-color="js表达式"></div>