1、Vue中数据发生变化,vm却知道发生改变
(1)Vue在创建vm时,会将数据配置到实例中,内部通过Object.defineProperty方法,对数据进行get与set方法的绑定,当获取数据的时候会触发get方法,修改数据的时候会触发set方法,当set执行结束吗,内部会进一步触发watcher进行监听,从而数据改变,试图重新渲染
Object.defineProperty:es5新增特性,但是IE678不支持,所以Vue只能应在IE9+的浏览器上
(2)vue内部通过数据劫持&发布订阅模式实现数据的双向绑定:
通过Object.defineProperty方法对所有的数据进行数据劫持,就是给这些数据动态的添加了getter与setter方法。
在数据变化的时候发布消息给订阅者(Watcher),触发响应的监听回调。
-
数据被vm管理,数据劫持,(Object.defineProperty) 动态添加get与set
-
数据发生改变了 vm.msg = “你好” 触发其自身上面的set方法
-
set触发成功后,数据改变完成了,然后通知watcher触发响应的监听回调
2、vue的特点
(1)vue是渐进式javascript框架:用到什么功能引入什么功能模块
(2)易用(使用成本低),灵活(生态系统完善,适用于任何规模的项目),高效(体积小,优化好,性能好)
(3)Vue是一个mvvm的js框架,但是,Vue的核心库只关注视图层,开发者关注的只是m-v的映射
3、mvc/mvp/mvvm区别
Mvc:用户对view操作以后,view捕捉到这个操作会把处理权交给Controller进行处理,Controller会对来自View数据进行预处理、决定调用哪个Model的接口;然后由Model执行相关的业务逻辑(数据请求); 当Model变更了以后, View通过观察者模式收到Model变更的消息以后,然后重新更新界面
【注】缺点:Vue强依赖与model
Mvp:mvp将mvc中的controller改名为presenter同时改变方向
基础操作与mvc一样,在通过观察者模式后将消息传递给presenter而不是传给view,presenter获取到model通过view提供的接口更新界面
【注】缺点:view层非常薄,但是presenter非常的厚所有逻辑部署都在presenter中,导致维护困难
Mvvm:双向绑定原理牛
MVVM的调用关系和MVP一样。但是,在ViewModel当中会有一个叫 Binder。你只需要在View的模版语法中,指令式地声明View上的显示的内容是和Model的哪一块数据进行绑定即可。 当ViewModel对Model进行更新的时候,Binder会自动把数据更新到View上去;当用户对View进行操作(例如表单输入),Binder也会自动的把数据更新到Model上去。这种方式称为:双向数据绑定。
4、v-for为什么加关键key
(key的值是唯一的,不变的,尽量不要使用index下标,除非知道index不会改变)
1、虚拟DOM进行对比的时候,添加、删除元素key值可以提高效率
2、如果没有key,内部添加节点会进行卸载、装载的过程,效率太低
3、添加key以后,本着key值相同DOM节点复用的原则
5、虚拟dom与diff算法
(1)如果节点类型改变,直接将旧节点卸载,替换成新的节点,旧节点包括下面的子节点都将卸载,如果新节点和旧节点仅是类型不同,这样做效率就会很低
(2)节点类型不变属性或属性值改变,不会卸载节点执行节点更新操作
(3)文本改变直接修改文字内容
(4)移动、增加、删除子节点时:就会进行卸载、装载的过程效率很低在没有key值时就会进行这个操作
(5)有key值时:Vue就能根据key值 直接找到具体的位置进行操作,效率很高
6、虚拟dom
-
vue在内存中生成一个虚拟dom树
-
将内存中的虚拟dom进行初始化渲染,渲染成真是dom,浏览器就可以看到
-
当我们修改Vue中data数据时
-
将之前的虚拟dom结合更改后的数据,生成一个新的虚拟dom树
-
将新的虚拟dom与之前的虚拟dom进行diff算法的对比,对比出差异
-
再将对比后的差异部分进行重新的真是dom的渲染操作
7、vue中哪些数组方法会触发跟新视图,哪些不会
可以的:push、pop、shift、unshift、splice、sort、reverse,vue将背侦听的数组的变更的方法进行了包裹,所以它们可以触发视图的更新
不可以:slice、concat、map、filter
Vue中提供了Vue.set全局方法,就可以动态的添加数据也会被Vue管理更新视图,实现双向绑定
Vue.set(vm.xxxx,属性,添加内容)
8、v-model的底层原理
绑定了value属性,监听了input事件
v-model指令只能在组件或者表单控件中使用
9、watch与计算属性的区别
(1)watch监听只能是单个监听,每次只能监听一个变量的修改,不能够跟同事监听多个变量的修改,computed可以依赖多个数据的变化(并且只跟他所依赖的项进行关联)
(2)当需要在数据变化时进行异步操作或者开销较大的操作时,只能采用watch
(3)计算属性与methods:1.计算属性会根据现有的数据生成一个新的数据,并且两者会产生关联,建立永久缓存,当无关数据进行改变时,计算属性不会重新计算,而是直接在缓存里取值使用即可2.methods则会重新计算
(4)计算属性不仅可以获取值,也可以设置值,将计算属性写成对象的形式,提供get/set方法即可
10、v-if与v-show的区别
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做;—直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多;—不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
11、各种通信
父子通信:
子组件通过props接受父组件传递过来的数据
【注】prop是单向数据流,当父组件数据变化时,将传递给子组件,但是翻过爱就不会,并且没吃父组件更新时,子组件props接受的都会是最新数据
子父通信:
(1)通过传递方法实现
\1. 父组件定义自己的数据
\2. 父组件需要写一个更改自身数据的方法
\3. 在调用子组件的时候,父组件需要将更改自身数据的方法通过属性的方式传递给子组件
\4. 子组件通过props接受父组件传递的方法,并且触发这个方法
(2)通过自定义事件实现
1.父组件定义自己的数据
2.父组件需要写一个更改自身数据的方法
3.在调用子组件的时候,给其绑定一个自定义事件,这个时间触发父组件修改自身数据的方法
4.在子组件上写一个方法触发自己身上的自定义事件,this.$emit(自定义事件的名字,参数)
(3)关系链:$parent $children
(4)ref链
\1. 在调用子组件的时候通过ref标记子组件
\2. 父组件发法可以通过this.$refs.xxx找到子组件直接修改相应数据
兄弟通信
(1)关系链
(2)ref链:必须先通过 p a r e n t 找 到 父 节 点 再 通 过 parent找到父节点再通过 parent找到父节点再通过refs找到子组件
(3)bus事件总线
1.创建一个公共的vue实例对象
2.绑定一个事件等待触发 两个参数(“自定义事件名”,“执行的回调函数”)
3.通过$on来事件绑定
4.通过$emit来触发自定义事件
12、声明周期的钩子函数
(1)初始化阶段:
beforeCreated:
这个钩子函数初始化时执行、但是获取不到数据,并且也获取不到真是DOM,基本没用
Created:
可以访问到数据了、但是真实DOM还没有渲染出来;
在这个钩子函数里面,可以进行相关初始化事件的绑定、发送ajax请求
当组件没有挂载完毕之前、更改数据的话、是不会触发运行钩子函数的
beforeMount:
还没渲染出来dom但是马上了,和created基本一样
(render):
生成好虚拟dom,然后内部通过render函数对应的el进行替换,做一个虚拟化的虚拟dom渲染成真是dom的过程
Mounted:
数据挂载完毕、真是dom也渲染出来了;
这个钩子函数内部可以做一些实例化相关的操作
(2)运行阶段:
beforeUpdate:
这个钩子函数初始化时不会执行;
当组件挂载完毕、且数据发生改变时,才会立即执行
这个钩子函数获取的真是dom是数据更新前的真实dom
Updated:
这个钩子函数获取的dom的内容是数据更新之后的内容
生成新的虚拟dom、新的虚拟dom与旧的虚拟dom进行对比,对比差异之后就会渲染真实dom
这个钩子函数内部就可以获取到因diff算法比较差异得出来的真实dom渲染了
(3)销毁阶段:
beforeDestroy:
组件销毁前执行;
可以做一些善后操作、可以清楚初始化事件、定时器相关的东西
Destroyed:
组件销毁是执行、watch数据劫持等功能已经完全消失
13、自定义指令的钩子函数
*(1)钩子函数*
bind:只调用一次,指令第一次绑定到元素时调用。用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
inserted:被绑定元素插入父节点时调用(父节点存在即可调用)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次, 指令与元素解绑时调用。
(2)*钩子函数参数(前两个重点后面无所谓)*
el:指令所绑定的元素,可以用来直接操作 DOM 。
binding:一个对象,包含以下属性
name:指令名,不包含v-的前缀;
value:指令的绑定值;例如:v-my-directive=“1+1”,value的值是2;
oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子函数中可用,无论值是否改变都可用;
expression:绑定值的字符串形式;例如:v-my-directive=“1+1”,expression的值是’1+1’;
arg:传给指令的参数;例如:v-my-directive:foo,arg的值为 ‘foo’;
modifiers:一个包含修饰符的对象;例如:v-my-directive.a.b,modifiers的值为{‘a’:true,‘b’:true}
vnode:Vue编译的生成虚拟节点;
oldVnode:上一次的虚拟节点,仅在update和componentUpdated钩子函数中可用。
14、keep-alive的钩子函数
\1. 当你通过keep-alive标签包裹动态组件的时候,那么动态组件就没有被销毁,也就不会执行beforeDestroy钩子函数,这样就销毁不掉初始化时的状态比如定时器
\2. 所以keep-alive包裹的动态组件的时候,提供了两个钩子函数,activated deactivated 只需要把初始化事件放到activated中,后续销毁时放到deactivated中即可
\3. keep-alive可以通过include属性规定到底缓存那个组件默认都缓存
15、为什么组件中数据要用函数返回对象的形式
为了让每个组件或者实例拥有一份被返回对象独立的拷贝
16、同源策略
协议、域名、端口有一个不一样就是违背同源策略
17、解决跨域问题
(1)Jsonp(缺点:不能解决post请求的跨域、安全低、针对也定接口数据,必须是回调函数的形式)
(2)通过前端代理的方式proxy解决跨域问题
18、正向代理与反向代理的区别
正向代理:顺着请求的方向进行代理,代理的是客户端,想拿目标数据拿不到,找代理帮你去拿
反向代理:反向代理正好与正向代理相反,代理的服务器,需要一个东西但不是指定的谁家的东西只要是这个东西就可以
19、map和spa
(1)mpa:传统多页面应用,通过a标签实现多页面之间的跳转
问题:在网络环境比较卡顿的情况下,实现页面跳转,页面就会产生留白现象,打卡速度比较慢,影响用户体验
(2)spa:spa单页应用
原理:只有一个页面,根据地址栏的不同,来实现对应的路由组件的卸载与安装,整个页面是一个无刷新的效果,知识简单的组件切换
(3)两者的比较:
Spa不利于网络SEO检索,map利用,其他都是spa有点
20、vue.use的原理
调用插件里面的install方法
21、路由跳转的两种方式
(1)声明式跳转:router-link
(2)编程式跳转:this.$router.pish()(push/replace/go/back/forward)
22、路由懒加载
Vue这种单页应用,运用webpack打包后的文件会异常的大,造成进入首页需要加载的内容过多,事件过长,会出现长时间的白屏,即使使用loading也不利用用户体验,运用懒加载可以将页面进行划分,需要哪个页面加载哪个页面的数据,有效的分担首页的负担,减少首页加载用时
23、hash与history的区别
\1. hash模式较丑,history模式较优雅
\2. hash兼容IE8以上,history兼容IE10以上
\3. history模式需要后端配合将所有访问都指向index.html,否则用户刷新页面,会导致404错误
24、路由守卫
(1)全局路由守卫:
router.beforeEach:全局的前置路由守卫,路由跳转之前就会执行
router.afterEach:全局的后置路由守卫,路由跳转之后(就我不需要next)
(2)单个路由守卫:
before.Enter:进入到路由之前
(3)组件路由守卫:
beforeRouteEnter:还没有进入到,访问不到实例
beforeRouteUpdate:配置详情页,路由组件再被切换的时候且组件被复用 这个钩子就会执行
beforeRouteLeave:从组件出来,可以访问到实例了
25、路由模式
hash模式原理:调用window.onhashchange方法hash值的切换
history模式原理:本质使用H5的histroy.pushState方法来更改url
26、vuex的定义
Vuex是一个专门为 Vue.js 应用设计的状态管理架构,统一管理和维护各个vue组件的可变化状态,解决多组件之间数据共享问题,将组件与组件之间的关系进行解耦,强调集中式的管理,将组件与组建之间的关系变成了组件与仓库之间的关系
27、vuex的工作原理
共享状态放在state中储存,组件可以直接使用state中的数据,点击按钮发起异步请求通过,需要把异步请求写在actions中,拿到数据以后调用mutations里的方法去更改state中的数据(mutations是更改状态的唯一方法
28、vuex的项目结构
(1)应用层的状态放在唯一的state中
(2)Mutation是更改state的唯一方法
(3)异步逻辑操作都应该放在actions中
29、vuex的核心概念
store:一个vuex只能有一个store
state:存放状态与数据的
getters:类似于计算属性、getters依赖的vuex的状态,computed依赖的data
mutations:更改state的唯一方法
actions:所有的异步操作必须放在actions中
modules:分类管理状态
30、vuex的数据持久化
Vuex的数据时存在内存中的,一旦页面刷新数据就会初始化
yarn add vuex-persistedstate解决方法数据持久化
31、axios、fetch、ajax的区别
Axios和fetch都是采用promise语法 、ajax是采用callback回调函数的方法
(1)ajax
针对mvc编程,不适合现在的mvvm模式
基于原生的xhr开发,xhr本身的架构不清晰,已经有fecth的替代方案
Jq整个项目太大要使用ajax就要引入整个jq不合理
(2)axios
从node.js创建http请求
支持promise语法
客户端支持防止csrf
提供了一些并发请求的接口(重要、方便了很多的操作)
(3)fetch
· 符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里
· 更好更方便的写法
· 更加底层,提供的API丰富(request, response)
· 脱离了XHR,是ES规范里新的实现方式
· 1)fetchtch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
· 2)fetch默认不会带cookie,需要添加配置项
· 3)fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费
· 4)fetch没有办法原生监测请求的进度,而XHR可以
32、组件开发的优点
组件更加清晰直观、组件关系更加清晰、结果可以预测
最小化重绘(diff算法)、避免不必要的DOM操作
33、什么是面向对象
面向对象是一种编程思想,这种编程思想会直接将生活逻辑映射到我们的程序中,如果我们实现面向对象编程,必须依赖类和对象的语法去实现,但是在ES6之前没有类,所以我们使用构造函数去实现类的存在,通过类和对象实现面向对象编程,具有继承、封装、多态的特点
34、es5新增语法
(1)严格模式:
① 全局变量声明必须加var
② This无法指向全局对象
③ 函数内重名属性
④ Arguments对象不准许被动态改变、永远只储存实参
⑤ 新增保留字:implements, interface, let, package, private, protected, public, static, yield。
35、深拷贝与浅拷贝
浅拷贝:浅拷贝仅仅是指向被复制的内存地址,如果原内容中的对象被改变了,那么浅拷贝复制出来的对象也会相应的改变(用的是同一个地址)
深拷贝:在计算机中开辟了一块新的内存地址用于存放复制的对象,当原内容改变是拷贝的不会发生变化(用的是不同的地址)
36、let、var、constlet:
(1)只要遇到大括号,就形成作用域
(2)let声明的变量,一个作用域只能声明一次
(3)let声明的变量不会声明提升,在let声明之前使用变量,会报错,这个区域叫做暂时性死区
var:通过var声明的变量,只有在函数中声明,才会形成作用域,除函数以外的大括号,并不会形成作用域。
const:
(1)const声明的变量,一旦声明就不能更改
(2)const声明的变量,必须赋值
(3)let有的特点他都有
【注】let、const都是ES6新增,ES6将声明变量做的更加精致