编辑排版 | 宋大狮
平台运营 | 小唐狮
一、说一下vue2的生命周期?
创建:
beforecreate:
实例创建前
此阶段的data、methods、computed、watch的数据和方法不能被访问
created:
实例创建完成后
此阶段完成数据监听,可以使用数据、更改数据。无法与Dom进行交互,想要的话可以通过nextTick来访问。
挂载:
beforeMount:
页面渲染前
此阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。
mounted:
页面渲染完成后
此阶段真实Dom渲染完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。
更新:
beforeUpdate:
响应式数据更新前
此阶段更改数据,不会造成页面重新渲染。
updated:
响应式数据更新完成后
避免在此阶段更改数据,因为这可能会导致无限循环的更新。
销毁:
beforeDestroy:
实例销毁前
我们可以在这时进行善后收尾工作,比如清除定时器、解除绑定事件
destroyed:
实例销毁完成后
缓存:
activited:
keep-alive专属,组件被激活时调用
deactivited:
keep-alive专属,组件被销毁时调用
1、异步请求放在created还是mounted:
如果是单单的一个父级组件,放哪里都无所谓。
如果涉及到了要控制子父组件先后显示正确内容的时候,就可以考虑下父组件的请求要放在哪个钩子里了。想要子组件先拿到数据渲染就放在mounted中,想要父组件先拿到数据就放在created中。
2、第一次加载页面触发了创建、挂载生命周期
3、父子组件生命周期执行顺序:
组件渲染的顺序是先父后子,渲染完成的顺序是先子后父
组件更新的顺序是先父后子,更新完成的顺序是先子后父
组件销毁的顺序是先父后子,销毁完成的顺序是先子后父
二、说一下v-show、v-if、v-for的理解?
v-show和v-if的区别:
v-if:通过操控DOM增删来实现显示隐藏,不适合频繁切换,数据多不建议用v-if,每一次切换则重新消耗性能
v-show:修改元素的display实现显示隐藏,适合频繁切换,只在第一次渲染时消耗性能
v-if和v-for的优先级:
当v-if与v-for一起使用时,v-for比v-if优先级高,如果连用的话会把v-if给每个元素都添加一下,会造成性能问题,所以不推荐v-if和v-for在同一个标签中同时使用。
解决办法:在计算属性中,将v-if中的判断转化成对v-for数组的过滤
v-for中key的作用?
key代表的是唯一,作用是更高效的更新虚拟dom,diff算法时便于区分新旧虚拟dom,新旧虚拟dom的key相同时不会重新渲染,提高性能
为何不推荐index作为key值:当以数组下标index作为key值时,当其中一个元素发生了变化(增删改查),就有能导致所有的元素的key值发生改变,导致更新dom时浪费性能
三、说一下组件通信有哪些方式?
父传子
1、自定义属性 + props:在父组件中,给子组件绑定一个自定义属性,在子组件中,通过props进行接收
2、$parent:直接访问父组件实例的属性和方法
3、$attrs:在父组件中,给子组件绑定一个自定义属性,在子组件中,通过$attrs进行接收【与props不能共存】
4、插槽
子传父
1、$emit + 自定义事件:在父组件中,给子组件绑定一个自定义事件,绑定事件的值为接收参数的函数,在子组件中,通过$emit发送数据
2、$refs:直接访问子组件实例的属性和方法
3、$children:直接访问子组件实例的属性和方法【返回数组,必须遍历赋值后才能渲染】
祖传孙
1、provide函数传,inject数组收
2、自定义属性 + v-bind="$attrs"【中间人】 + $attrs
孙传祖
1、$emit + v-on="$listeners"【中间人】 + 自定义事件
兄弟间
1、$bus 全局事件总线:给vue原型添加一个vue实例,用this.$bus.$emit发送,用this.$bus.$on接收
2、Vuex
3、pubsub 发布订阅
路由传参
params传参(地址栏不显示参数)
query传参(地址栏显示参数)
四、watch和computed的区别
computed计算属性:
1、如果一个数据需要经过复杂计算就用computed
2、支持缓存,只有依赖数据发生改变时,才会重新进行计算
3、不支持异步,当计算属性内有异步操作时,无法监听到数据的变化。原因:定义的函数接收return的结果,return属于同步执行的,是没办法拿到异步请求结果的
4、计算属性只能取不能存,即不能对计算属性直接赋值。如需要存,要给计算属性手动添加setter
watch监听器:
1、如果一个数据需要被监听并且对数据做一些操作就用watch
2、不支持缓存,数据变化会直接触发相应的操作
3、支持异步
设置deep:true 开启深度监听,如果监听的是对象类型,当手动修改对象的某个属性时,可以被监听到
设置immediate:true watch初始化时会立即执行一次handler方法
五、说一下为什么data是一个函数?
data必须是函数,是为了保证不同组件的独立性和可复用性,防止不同组件实例间data的引用关系,避免变量污染
六、说一下什么是路由守卫?
是路由跳转前、后的一些钩子函数
分类:
全局守卫:【写在main.js中 或 router文件夹下的index.js中】
beforeEach: 进入路由之前的验证(常用,如:判断用户是否登录)
afterEach: 路由进入之后的验证(常用,如:修改页面标题)
局部守卫:【写在路由组件内单独的守卫】
beforeRouteEnter: 进入路由之前的验证
beforeRouteLeave: 离开路由之前的验证(常用,离开当前页面提示是否保存内容)
beforeRouteUpdate: 组件路由更新前的验证
独享守卫:【相当于写在路由配置里的全局守卫,只有前置守卫】
beforeEnter:进入路由之前的验证
三个参数:
to: 要进入的路由对象
from: 要离开的路由对象
next: 放行函数
七、说一下路由传参方式query和params的区别?
1、传参方式
query通过path进行跳转传参,接收的时候通过this.$route.query进行接收
params通过name进行跳转传参,接收的时候通过this.$route.params进行接收
2、安全性
params传值不会显示到地址栏,安全性高,但页面刷新数据会丢失
query传值会暴露在地址栏,页面刷新不会丢失
八、说一下路由模式hash和history的区别?
在单页面应用SPA中,路由描述的是URL与视图之间的映射关系,这种映射是单向的,即URL变化引起视图更新(无需刷新页面)。
1、hash模式
原理:
用 url + #后面的hash值 来模拟一个完整的url,直接刷新页面不会导致浏览器向服务器发出新的请求,路由切换时不会
当调用$router.push方法,会改变hash值触发hashchange事件,前进到指定的url。vue-router会根据url做路由匹配来修改页面内容,实现路由切换的效果
改变hash值触发hashchange事件,hashchange事件对象中会记录变化的url。点击浏览器的前进后退,会改变hash值,实现路由切换的效果
特点:
地址栏有#,影响美观,直接刷新页面不会报404
2、history模式
原理:
用 url + 路径 真正实现一个完整的url,直接刷新页面会导致浏览器向服务器发出新的请求,路由切换时不会
当调用$router.push方法,会改变路径调用pushState方法,前进到指定的url。vue-router会根据url做路由匹配来修改页面内容,实现路由切换的效果
改变路径调用pushState方法,pushState方法中会记录变化的url。点击浏览器的前进后退 或者 手动调back、forward、go方法,会触发popstate事件,实现路由切换的效果
特点:
地址栏没有#,不影响美观,直接刷新页面会报404,需要后端在Nginx中做代理地址的配置
九、说一下对keep-alive的理解?
缓存组件,减少请求次数
根据条件缓存页面的方式:
:include="['组件name']" 指定想要缓存的组件,其他组件不缓存
:exclude="['组件name']" 指定不想要缓存的组件,其他组件缓存
利用路由的元属性meta,配合v-if动态判断
两个钩子函数:
activated():进入这个缓存组件时触发
deactivated():离开这个缓存组件时触发
十、说一下对vuex的理解?
五种状态:
state: 存储公共数据 this.$store.state
mutations:同步操作,改变store的数据 this.$store.commit()
actions: 异步操作,让mutations中的方法能在异步操作中起作用 this.$store.dispatch()
getters: 计算属性 this.$store.getters
modules: 子模块
使用场景:
用户信息、菜单信息、购物车信息
解决vuex页面刷新数据丢失问题的方式:
办法一:将vuex中的数据直接保存到浏览器缓存中(sessionStorage、localStorage、cookie)
办法二:在页面刷新的时候再次请求远程数据,使之动态更新vuex数据
办法三:在某一组件向后台请求远程数据保存在vuex,并且在页面刷新前将vuex的数据保存至sessionStorage。在另一组件优先使用vuex内的数据,只有刷新后还没取到后台数据,才会从sessionStorage里取。
优点:每次刷新页面更新sessionStorage,确保数据的安全性。刷新后还没取到后台数据就从sessionStorage中取,防止网络延迟、数据量大时vuex数据丢失问题。
十一、说一下对MVVM的理解?
M是model,指的是数据层,即data
V是view,指的是视图层,即template
VM是viewModel,指的是视图模型,即vue实例,是连接view和model的桥梁。数据和视图不能直接通信,必须经过VM实现通信。
十二、说一下vue2的响应式原理?
vue2采用数据代理+数据劫持+发布订阅模式的方法。
在初始化vue实例时,会把data对象和data对象的属性都添加到vm对象中,通过object.defineProperty()进行数据代理,用vm对象的属性来代理data对象的属性,
并在Observer类中递归遍历data对象,对data对象中的每个属性都进行数据劫持,都指定一个getter、setter。
例外的,对于数组,不能通过object.defineProperty()进行数据代理,因为监听的数组下标变化时会出现数据错乱问题,所以数组是调用数组重写的原生方法来实现响应式。
当通过vm对象修改data对象中的属性时,会触发data属性的setter方法,然后触发它Dep实例的notify方法进行依赖分发,通知所有依赖的Watcher实例执行内部回调函数。
最后会触发renderWatcher回调,会重新执行render函数,重新对比新旧虚拟DOM,重新渲染页面。
【Watcher回调是异步任务,它的执行会遵循事件循环机制,且重复的Watcher回调不会放到任务队列中,所以多次重复数据更新时,只会重新渲染一次页面】。
当通过vm对象读取data对象中的属性时,会触发data属性的getter方法,然后触发它Dep实例的depend方法进行依赖收集。
当data对象中数组元素发生变化时,会调用数组重写的原生方法,然后触发它Dep实例的notify方法进行依赖分发,通知所有依赖的Watcher实例执行内部回调函数。
最后会触发renderWatcher回调,会重新执行render函数,重新对比新旧虚拟DOM,重新渲染页面。
当读取data对象中数组元素时,会触发数组的getter方法,然后触发它Dep实例的depend方法进行依赖收集。
十三、说一下v-model数据双向绑定原理?
是一个语法糖,做了两件事
v-bind绑定value,更新数据层
v-on给元素绑定input事件,监听输入框中的内容,当发生改变时来执行一些事情,并更新视图层
十四、说一下$set的作用和原理?
作用:
对象:
响应式原理:通过触发setter实现更新
对象中后追加的属性、删除已有属性,Vue默认不做响应式处理
解决:this.$set()
数组:
响应式原理:调用重写的原生方法实现更新
数组中修改某下标的元素、更新length,Vue默认不做响应式处理
解决:
1. this.$set()
2. 使用原生API:push、pop、shift、unshift、splice、sort、reverse
原理:
如果目标是数组,直接使用数组的splice方法通知实现更新
如果目标是对象,先给对象属性用数据代理添加getter、setter,再通过触发setter通知实现更新
十五、说一下虚拟DOM的原理?
虚拟dom是对真实dom的抽象,本质是JS对象
在生成真实DOM之前,vue会把模板编译为一个虚拟dom,当里面某个DOM节点发生变动时,通过diff算法对比新旧虚拟DOM,发现不一样的地方直接修改在真实的DOM上
优点:
可以减少DOM操作
可以跨平台渲染
缺点:
严重依赖打包工具
节点规模过大时,虚拟DOM的稳定性不如原生DOM
十六、说一下vue的模板编译过程?
1、获取到template
2、template 转 AST树
AST树:
抽象语法树(一个对象的格式)
源代码的抽象语法结构的树状描述
用于将vue的指令、语法等 转为 源代码的抽象语法结构
3、AST树 转 render函数
render函数:
返回包含_c、_v、_s函数的字符串
4、执行render函数 转 虚拟DOM
5、执行patch方法,通过diff算法对比新旧虚拟DOM,打补丁,生成真实DOM
十七、说一下单页面应用和多页面应用的区别?
单页面应用(SPA):
项目只有一个html页面,只向服务器请求一次静态资源,所有页面跳转都是通过路由组件切换完成的
优点:页面跳转快、组件化开发
缺点:首屏加载慢、不利于SEO
多页面应用(MPA):
项目有多个html页面,每次页面跳转都要向服务器请求静态资源,返回新的html页面
优点:首屏加载快、利于SEO
缺点:页面跳转慢
十八、说一下CSS预处理器Less和Sass的区别?
Sass和Less都属于CSS预处理器,都需要编译器编译成css文件运行
一般常用的功能是:变量、运算符、混合、嵌套层级
区别:
1、编译环境不一样
Sass基于Ruby环境
Less基于JS环境
2、变量符不一样
Less是@,而Sass是$
3、功能性不一样
Sass功能性强大,有函数、条件判断、循环遍历、数据结构的概念
Less没有
十九、说一下对跨域的理解?
http请求分为两大类:普通http请求(如百度请求)和ajax请求(跨域是出现在ajax请求)
同源策略:在浏览器发起ajax请求时,当前的网址和被请求的网址协议、域名、端口号必须完全一致,目的是为了防止跨站脚本攻击。
跨域是浏览器的同源策略导致的,而服务器之间不受同源策略影响!!!
前端本地开发存在跨域,但前端部署到服务器上就不存在跨域了!!!
解决方式:
1、CORS跨域资源共享
在配置类中,添加一个CORS的过滤器,在响应头上添加允许访问的请求源 addAllowedOrigin("*")
2、Jsonp
利用srcipt标签的src属性来实现,前端声明好一个函数,后端返回执行函数,执行函数参数中携带所需的数据。
可以使用jquery的ajax快速实现Jsonp。
使用jsonp只能解决get请求的跨域,因为script标签中的src请求就是get请求。
3、vue脚手架正向代理
跨域解决:vue脚手架自动在本地开启一个基于node的代理服务器,IP和端口号与本地浏览器相同
浏览器【http://localhost:8080】 -> 本地代理服务器【http://localhost:8080】 -> 目标服务器【http://47.94.222.12:8080】
4、Nginx反向代理
跨域解决:location配置中,在响应头上添加允许访问的请求源 Access-Control-Allow-Origin:*
浏览器【http://localhost:8080】 - Nginx代理服务器【添加放行请求源】 - 目标服务器【http://192.168.0.120:8080】
二十、说一下nextTick的使用场景和原理?
等待DOM更新后,再执行内部传入的回调函数
使用场景:
created中想要获取DOM
响应式数据变化后获取DOM更新后的状态,如 获取列表更新后的高度
原理:
把nextTick回调方法放在renderWatcher回调之后执行,这样就能拿到更新后的DOM
二十一、说一下对单向数据流的理解?
概念:父级给子组件是啥,子组件可以用,也可以不用,但是不能修改【单向数据流针对于组件通信】
作用:保证了数据的可控性,方便预测和调试
注意:Vue中的单向数据流是针对基本数据类型,而引用类型是对数据地址的引入,子组件修改数据,父组件能接收到数据的更改
应用:
父子传参:子组件不能直接修改父组件的数据,要么通过props从父组件传递过来,要么通过emit在父组件中修改
vuex:改变store中的状态state的唯一途径就是显式地提交mutation,不能直接修改state
- END -