文章目录
Vue面试题
v-on事件修饰符和键盘修饰符的作用?
事件修饰符主要是用于处理浏览器上的默认行为,比如阻止事件的冒泡,阻止a标签、form表单的默认事件
键盘修饰符主要针对keyup、keydown,如果希望事件由指定的键进行触发,可以用键盘修饰符进行指定
- 阻止默认行为
<a v-on:click.prevent = 'jump' href = '#'></a>
<form @click.prevent = 'jump'></form>
once
:只触发一次事件self
:保证事件由自己触发,不会经过冒泡或者捕获进行触发
<div @click = 'divClick'>
<p @click.self = 'pClick'></p>
</div>
<input @keyup.f1 = 'inpKey'></input>
// 自定义键盘修饰符
// Vue.config.keycodes.f2 = 112
stop
:阻止冒泡
v-if和v-show的区别,使用场景?
v-if和v-show都是用来控制元素的显示和隐藏,当值是true,元素显示,值为false,元素隐藏
区别: v-if当切换布尔值时,会创建/删除元素;v-show当切换布尔值时,会改变元素的样式,
display:block
使用场景:当元素显示隐藏切换频繁时使用v-show,反之使用v-if; 例如:页面加载数据时的loading动画可以使用v-if;
页面中某个元素需要使用动画效果,这个动画效果需要人为进行操作控制,那么最好使用v-show,加入购物车时的小球飞入动画
v-cloak解决插值表达式闪烁的原理?
为什么出现闪烁问题?
由于网速等原因导致vue.js没有被加载回来,此时页面中的插值表达式不会被vue实例解析,浏览器进行解析的时候回直接当做字符串;然后vue.js加载回来之后,vue实例又能够进行解析,那么插值表达式就会被解析成具体的值,这个过程会出现闪烁现象
v-cloak原理
使用v-cloak指令绑定到元素上面之后,再配合
[v-cloak]{display:none}
这个样式进行控制;
在浏览器进行解析时,浏览器会将属性选择器的样式作用于元素身上,该元素会被隐藏;原后vue进行解析时,会将
v-cloak
从元素身上删除,样式也随之失效,插值表达式中的值也能够显示出来。
<style>
[v-cloak]{
display:none
}
</style>
<div v-cloak>{{msg}}</div>
new Vue({
data:{
msg:'hello word'
}
})
<script src = './vue.js'></script>
什么是Vue双向数据绑定?原理?
v-model指令、数据视图同步更新、使用的是ES5提供的
Object.defineProperty()
这个方法实现数据劫持
数据劫持(Object.defineProperty())
数据如何从模型同步到视图?当模型中数据发生变化时会触发Object.defineProperty的set方法,在这个方法内部能够劫持到数据的改变,然后就可以在该方法内部通知视图更新
视图中的数据如何同步到模型中?(v-model指令是怎么实现改变了元素中的数据同步到模型中)监听表单元素的change事件,在change事件中可以拿到用户输入的数据,然后给模型中的数据赋值
var obj = {}
// 给对象设置属性
obj.name = 'zs'
// 获取对象的某个属性
console.log(obj.name)
var value = 'zs'
Object.defineProperty(obj,'name',{
get(){
return value
},
set(v){
// 设置值的时候就是数据劫持
// 只要走这个方法表示模型中的数据在发生变化
// 调用视图更新的函数
update()
value = v
}
})
obj.name = 'ls'
console.log(obj.name) // ls
function update(){
// 专门用来更新视图
}
Angular双向数据绑定?
脏数据检测: 会使用定时器进行轮询,并不是将定时器一直开着,只有触发了指定的一些方法时才会进行轮询,$apply,发送异步请求,触发了定时器时也会轮询
<div>{{msg}}</div>
<input @change = 'inpChange($event)'></input>
<button @click = 'change'></button>
new Vue({
data:{
msg:'hello',
name:'zs'
},
methods:{
change(){
this.msg = 'word'
},
inpChange(v){
this.msg = v.targe.value
}
}
})
scoped作用原理?怎么解决加了scoped后动态渲染的HTML标签样式不能修改的问题?
scoped可以隔离组件之间的样式,避免样式污染
作用原理:当一个组件的style加了
scoped
后,首先vue解析当前组件时,会给当前组件中所有元素加上一个随机的属性,然后style中书写的样式会全部变成属性选择器,那么即使其他组件有相同的样式或者相同的标签,由于随机添加的属性是不一样的,组件之间的样式也不会相互影响
当在父组件中取修改子组件中标签样式,可以使用vue-loader深度作用选择器,在父组件的样式名和子组件的样式名之间添加
>>>
或者/deep/
Vue生命周期函数的理解?
生命周期就是在vue实例执行过程中会触发的一批函数,这些函数可以帮助我们处理不同时间段的业务逻辑
生命周期的四个阶段:
实例创建阶段:
beforeCreate
:vue实例被创建时触发,此时仅仅是分配了内存,vue实例上的属性和方法都还没被绑定created
:vue实例被创建完毕,data中的属性和methods中的方法都已经被挂载到了vue实例上
DOM渲染阶段:
beforeMount
:vue实例中的数据已经被解析渲染到了内存中的虚拟DOM上,但真实DOM中指令还没有被解析mounted
:vue实例中的数据已经完全被渲染到了真实DOM中
数据更新阶段:vue实例上的数据发生变化时触发
beforeUpdate
:模型中数据已经发生变化,但还没有同步更新到视图中updated
:模型中数据发生了变化,而且已经同步到了视图中
实例销毁阶段:
beforeDestroy
:vue中的数据和方法还能继续使用,但是指令不能再被vue解析destroyed
:vue中的数据和方法都已经被销毁
vue-router路由模式有几种,原理分别是什么?
vue-router路由库是用哪种技术实现的,总共有两种,分别叫hash模式和history模式,默认是history模式
hash模式:地址上带有#号;url地址可以放在任意标签中打开;可以兼容低版本的浏览器
hash模式原理:监听
hashchange
事件,可以调用window.location.hash
获取到锚点值,和路由规则进行匹配,匹配到之后将路由规则中定义的组件渲染到页面
history模式:地址上没有#号,更加符合URL形式;url地址不能重复打开;
history模式原理:利用HTML5新提供的
history.pushState
API 来完成 URL 跳转而无须重新加载页面
history模式需要后台进行相关配置:要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面
vue-router导航钩子函数使用场景?
beforeEach
:全局守卫,在路由跳转时会对路由进行拦截,只有调用了next
函数才会释放路由,使用场景:通常在后台管理系统中,页面是需要登录之后才能访问,那么对于所有的页面跳转都需要使用beforeEach
进行拦截判断是否登录
beforeEnter
:路由独享守卫,只会拦截加了独享守卫的路由跳转。使用场景:如果整个项目中只有某一部分页面是需要登录之后才能访问,此时只能针对这一部分页面的路由规则加上独享守卫进行拦截。
vue-router路由懒加载怎么使用?(单页应用程序的性能优化)
路由懒加载:使用懒加载可以在跳转到具体路由时才去加载对应的组件代码,没有访问的路由的组件代码永远不会加载回来。
用法:将导入组件的方式换成:
const Foo = () => import('./Foo.vue')
Vue.use()方法作用?Vue插件实现?
Vue.use是用来安装Vue的插件
插件可以实现的功能:
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或属性
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 2. 添加全局资源(指令、过滤器、组件)
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
})
// 注册过滤器
Vue.filter('datefmt',function(){
return '2018-06-12'
})
// 注册组件
Vue.component('com',{})
// 3. 混入组件(将一个组件的功能混入到另一个组件中)
Vue.mixin({
created: function () {
// 逻辑...
}
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}
如何实现插件:插件必须实现一个
install
方法,这个方法接收一个参数是Vue,这个方法内部就可以去添加全局的组件以及实例的属性。然后在使用插件时,调用Vue.use(插件对象),则install方法中的形参Vue就会接收到实参Vue。
axios拦截函数的使用场景?请求拦截/响应拦截
请求拦截:在发送请求之前,对请求对象做相关配置,给请求头添加验证属性(登录校验)、设置参数数据格式(Content-Type)
响应拦截:当接口返回的信息是统一的类型时,如果都在具体的请求函数里面去做处理,比较繁琐,可以在响应拦截器中先做统一处理,在分发到具体的函数中。例如当后台的token过期时,如果在每一个请求函数中都做判断,然后跳转页面比较麻烦,由于token过期后台会返回规定的统一状态码,那么就可以使用响应拦截在拦截器中进行判断然后跳转
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
if(response.code = 400){
this.$router.push('/login')
}
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
JSONP跨域原理?其他解决跨域方式?
-
JSONP:JSON数据格式的一种使用方式,用于解决跨域问题
-
跨域: 由于浏览器同源策略的限制,不同源的URL不能互相通信,这种现象叫做跨域
-
同源策略: 两个域名只有 协议 主机地址 端口号完全一致时才能相互通信访问
-
JSONP实现原理:利用script标签的src属性发送请求不受同源策略的限制这个机制去实现跨域资源共享(script标签能够跨域访问是历史遗留问题,并不安全)
- 动态创建一个script标签,并且将需要请求的URL地址设置给script标签的src属性,同时在URL地址后面拼接上一个回调函数名称
function getJson(data){
console.log(data)
}
// getJson就是在前端本地定义好的一个方法的方法名
<script src = 'http://lovegf.cn/jsonp?callback=getJson'>
2. 后台接收到该请求后,先根据参数判断是否是JSONP请求,如果是,则将客户端请求的数据整理好,和前端发送过来的方法名一起拼接成一个函数调用的字符串,客户端需要的数据就是这个函数调用时传的参数
// 假设要发送给前端的数据是 'ok'
'getJson('ok')'
// 响应给前端
res.end('getJson('ok')')
3. 由于后台响应回来的数据是一个字符串,而且是函数调用,所以前端拿到响应回来的数据相当于调用了该方法,那么前端定义好的方法会自动执行,而且方法内的形参可以接收到实参值,也就是后台拼接的数据
function getJson(data){
console.log(data) // ok
}
4. 数据拿到之后,一般这个动态创建的script标签会被删除掉
CORS
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods","*");
代理(解决的是开发阶阶段跨域问题)
代理是通过服务器向服务器发送数据请求不存在跨域问题的机制解决浏览器向存在跨域问题的服务器发请求的技术
C -> S
在客户端所在电脑上搭建一个服务器P
C -> P -> S
// 跨域通常存在于开发阶段
http://192.168.0.111/api/getlist
http://192.168.0.112/index.html
http://172.16.273.73/api/getlist
http://172.16.273.73/index.html
什么是深拷贝?深拷贝浅拷贝的区别?如何实现?
深拷贝讨论的对象 主要是指定js中的复杂数据类型
最简单的实现方式:JSON.parse(JSON.stringify(obj)),这种方式的弊端不能实现对象中函数的拷贝
比较常见的实现方式: for-in循环加上递归
for-in 循环需要判断不同的数据类型,如果是对象使用递归克隆,如果是数组,可以直接调用concat方法或者splice方法进行深拷贝,如果是函数/Date,使用constructor重新实例化函数和时间类型
-
开发最常用
var obj = {name:'zs',arr:[1,2]}; var copyobj = JSON.parse(JSON.stringify(obj)) // 注意:此种方式如果对象中存在函数则不使用,即不能拷贝函数
-
简单版本
function deepClone(obj){
if (obj === null || typeof obj !== 'object') {
return obj;
}
var copy = {};
for (var key in obj) { //递归
if (obj.hasOwnProperty(key)) {
copy[key] = deepClone(obj[key]);
}
}
return copy;
}
-
较完整版本(未考虑循环引用)
function deepClone(obj) { if (obj === null || typeof obj !== 'object') { return obj; } if (obj instanceof Array) { var copy = []; for (var i = 0; i < obj.length; i++) { copy.push(obj[i]); } return copy; } if (obj instanceof Date) { var copy = new Date(); copy.setTime(obj.getTime()); return copy; } if (obj instanceof Object) { var copy = {}; for (var key in obj) { //递归 if (obj.hasOwnProperty(key)) { copy[key] = deepClone(obj[key]); } } return copy; } }
工作中处理过的兼容性问题?
移动端滚动穿透
移动端输入框被键盘挡住问题
IOS滚动不平滑的问题
click 300ms 延时响应
处理过的性能优化问题?
webpack里面
- 路由懒加载(组件按需加载)
- 分离第三方包(抽取第三方模块安装的包的代码,较少build.js的体积)
- 分离CSS
JS
- 对高频触发的事件进行节流或消抖(touchmove)
- 尽量减少 HTTP 请求个数——须权衡(精灵图是合并请求,分离第三方包/css是拆分请求)
- 避免空的 src 和 href
- 减少 DOM 访问,减少DOM访问层级(迫不得已需要访问DOM,可以将DOM进行缓存)
- 使用 CDN(内容分发网络)(静态资源使用CDN加速,用户访问时从CDN获取资源,CDN根据IP地址直接返回当前城市服务器中的资源)
工作中遇到的难解决的问题?后来是如何解决的?
-
scoped问题(父组件无法直接修改子组件样式,vue-loader深度作用选择器)
-
mui.css引入到脚手架,打包时会报SVG图片属性错误(需要将mui.css文件中引入SVG图片的单引号改成双引号)
-
mui严格模式(当webpack项目中引入mui.js,会报caller,callee,arguments在严格模式下无法使用的错误)
// 真正问题产生是由于babel-loader在编译代码时会加严格模式限制
// 方法一: .babelrc文件中忽略不需要使用严格模式转换的文件路径
"ignore": [
"./src/js/mui/mui.min.js"
]
// 方法二: babel-loader配置中排除掉不需要严格模式转换的文件
{
test: /\.js$/,
use: 'babel-loader',
// exclude: /mui\.min\.js$/
exclude:["./src/js/mui/mui.min.js"]
}
// 方法三: babel-plugin-transform-remove-strict-mode 移除整个项目打包编译时的严格模式
// https://www.npmjs.com/package/babel-plugin-transform-remove-strict-mode
1. 安装babel-plugin-transform-remove-strict-mode
npm install babel-plugin-transform-remove-strict-mode --save-dev
2. babelrc中添加
{
"plugins": ["transform-remove-strict-mode"]
}
- 简历书写
- 梳理项目中的vue知识
后台管理系统:
vue-cli(vue+webpack+vue-router) + element-ui + axios(基于promise实现跨平台的请求库)
+ vuex + v-for + v-on(native修饰符) + v-model(双向数据绑定的具体实现) + v-bind(属性绑定)
+ v-if/v-show + vue生命周期函数(created/mounted) + 导航守卫(全局、路由独享) + axios拦截器
axios.interceptors.request.use(function (config) {
// if(config.method == 'GET'){
// config.params = {id:1}
// }else if(config.method == 'POST'){
// config.data = {id:2}
// }
// 有的时候后台只接收x-www-form-url-encoded // key=value&key=value
if(config.method == 'POST'){
config.data
}
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
var router = new VueRouter({
routes:[
{
path:'/me',
component:Login,
beforeEnter(to,form,next){
// 这里也是需要先处理好业务逻辑《然后调用next释放路由
}
},
{
path:'/home',
component:Home
}
]
})
// 只要写了这个函数,所有的路由跳转(router-link、编程式导航、a标签、地址栏输入链接、next('/login'))
// 都会被beforeEach拦截下来,如果不调用next(),则跳转就不会生效
router.beforeEach(to,from,next){
// 处理好业务逻辑之后,必须调用next
}
初始化阶段:
- 数据初始化:beforeCreate、created
- DOM渲染:beforeMount(DOM已经渲染,但是DOM上绑定的指令值还没有被解析)、mounted(DOM渲染完毕,代表DOM上绑定的指令都已经被解析完毕)
运行阶段:(当指令身上绑定数据发生变化的时候会被触发)
beforeUpdate:绑定额数据仅仅是模型中的数据发生了变化,视图中的数据还没有被及时更新
updated:模型和视图中的额数据都已经被更新完毕
销毁阶段:
(在这两个函数中可以清楚一些无用变量,防止内存泄露,由于浏览器有垃圾回收机制,及时不手动销毁无用变量,其实也不会造成内存泄露,但是如果涉及到有定时器,则一定要记得手动清除掉)
beforeDestroy
destroyed
<input v-modle='value'/>
new Vue({
data:{
value:'123'
},
created(){
this.value = 456
}
})
v-if:实现元素隐藏显示切换的时候,是直接操作DOM元素,会添加和删除DOM节点
v-show:实现元素隐藏显示切换的时候,是操作DOM元素的样式,display:block/display:none
如果元素显示隐藏切换的比较频繁,用v-show
如果元素一开始不显示,后期满足某个条件时才显示,而且切换不频繁,用v-if
<div v-for='(item,index) in list' :key='index'></div>
// 用v-on给第三方组件绑定事件的时候,有时候不一定能绑上去,需要使用native修饰符
<button v-on:click.native = 'btnClick'></button>
双向数据绑定原理:
M-V:数据劫持,Object.defineProperty,get、set(数组下标改变数据、length属性改变数据、如果数据不是提前在data中进行定义,这三种情况Object.defineProperty都无法进行劫持)
Vue3.0以后使用 ES6 中的Proxy(代理)进行数据劫持
V-M:监听表单元素的change事件
axios:(如何坚持当前js运行环境是Node还是浏览器?直接访问window对象,如果存在是浏览器,如果不存在是node;直接全局访问this,如果是window则是浏览器,如果是undefined则是node)
浏览器平台:XMLHttpRequest
Node平台:http.request()
vue-resource:专门为vue开发的请求库
移动端项目: vue-cli + vuex + axios + mui + mint-ui + 组件传值(父向子用属性传递/子向父用$emit) + watch(监听路由) + vue-loader(深度样式作用选择器/deep/ >>>) + vue-preview(缩略图预览) + flex布局 + vue过渡动画(css类/动画钩子函数) + sass