经过多次面试,发现面试官问的很多问题是重复的,写篇博客总结归纳下,准备下次的面试,也便于下次复习
-
在vue项目中怎么自适应
a.安装flexible
b. 安装postcss-px2rem
参考博客:
vue自适应 -
2.vuex怎么用,除了状态共享还有什么作用
怎么用:
首先在src的目录下新建一个store文件夹,在里面新建一个index.js文件,在这个文件里面importVUE和VUEX,然后引入store,全局注册,接着export defaultstore,这样各个组件都可以使用this.$store了
作用:
管理多个组件共享状态
全局状态管理
- vue通信方法
(1)父子组件通信, 父传子,儿子props接受;子传父,儿子事件触发,
e
m
i
t
触
发
父
亲
事
件
(
2
)
祖
先
后
代
。
P
r
o
v
i
d
e
和
i
n
j
e
c
t
(
3
)
v
u
e
x
或
b
u
s
(
事
件
总
线
,
通
过
emit触发父亲事件 (2)祖先后代。Provide和inject (3)vuex或bus(事件总线,通过
emit触发父亲事件(2)祖先后代。Provide和inject(3)vuex或bus(事件总线,通过on定义事件,$emit来触发事件)
(4)如果不想采用vue方式存储还可以使用session storage; local storage
- 怎么接收promise
a. promise.then()
b. promise.all()
c. promise.race()
-
Vue请求的封装
参考我之前写的封装博客 -
v-model原理
v-model其实是个语法糖,它实际上是做了两步动作:
1、绑定数据value
2、触发输入事件input
- Vue.nexttick( ) 原理
由于Vue DOM更新是异步执行的,即修改数据时,视图不会立即更新,而是会监听数据变化,并缓存在同一事件循环中,等同一数据循环中的所有数据变化完成之后,再统一进行视图更新。为了确保得到更新后的DOM,所以设置了 Vue.nextTick()方法。
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
参考博客:Vue.nextTick 的原理和用途
-
vue怎么兼容样式
前缀,安装autoprefixer和postcss -
怎么往vue加属性
利用Vue.set(object,key,value)
- vuerouter生命钩子函数
(1)全局钩子
a. BeforeEach :有三个参数,分别是to: router即将要进入的路由对象;from: 当前导航即将要离开的路由;next:function, 进入管道中的一个钩子,如果执行完了,导航的状态是comfirmed; 否则是false,终止导航。
b. aftereach; 不用传next()函数。这类钩子一般作用于全局,一般用来判断权限,以及页面丢失时需要执行的操作。
(2)单个路由里面的钩子
某个路由跳转时需要执行的逻辑
(3)组件路由
有beforerouterenter, beforerouterupdate和beforerouterleave,也是三个参数
常用的:
(1)一般用全局钩子来控制权限,像如果没有登陆就跳到登陆页或者需要达到一定级别才能访问页面都是属于页面控制,可通过beforeEach来实现,一般写在main.js;
(2)afterEach()一般用来重置页面滚动条的位置
注意:beforeRouterEnter触发时vue实例还没创建,钩子函数中不能用this
- vuerouter的url跟普通有什么区别:
vue-router的两种模式:
a. hash模式
vue-router默认的是hash模式—使用URL的hash来模拟一个完整的URL,于是当URL改变的时候,页面不会重新加载,也就是单页应用了,当#后面的hash发生变化,不会导致浏览器向服务器发出请求,浏览器不发出请求就不会刷新页面,并且会触发hasChange这个事件,通过监听hash值的变化来实现更新页面部分内容的操作
对于hash模式会创建hashHistory对象,在访问不同的路由的时候,会发生两件事:
HashHistory.push()将新的路由添加到浏览器访问的历史的栈顶,和HasHistory.replace()替换到当前栈顶的路由
b. history模式
主要使用HTML5的pushState()和replaceState()这两个api来实现的,pushState()可以改变url地址且不会发送请求,replaceState()可以读取历史记录栈,还可以对浏览器记录进行修改
window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)
包括back,forward , go 三个方法
history.go(-2);//后退两次
history.go(2);//前进两次
history.back(); //后退
hsitory.forward(); //前进
区别:
前面的hashchange,你只能改变#后面的url片段。而pushState设置的新URL可以是与当前URL同源的任意URL。
history模式则会将URL修改得就和正常请求后端的URL一样,如后端没有配置对应/user/id的路由处理,则会返回404错误
参考博客:
vue路由
11. vue-router怎么用:
(1)npm安装vue和vue-router,通过vue.use()安装路由功能
(2)创建路由
(3)给出路由显示的位置
(4)将路由对象注入vue实例。
(5)router-link路由跳转
(6)router.push()实现跳转
(7)在src下面新建router文件夹,再新建一个index文件,在里面配置路由规则
-
Router和route的差别:
(1)This. r o u t e r 就 是 v u e r o u t e r 的 实 例 方 法 , 当 跳 转 到 不 同 的 u r l , 可 以 使 用 t h i s . router就是vuerouter的实例方法,当跳转到不同的url, 可以使用this. router就是vuerouter的实例方法,当跳转到不同的url,可以使用this.router上的方法进行处理,如go;
(2)This.$route就是当前页面激活的路由,包含当前url解析得到的数据,如路径,参数;可以从对象中获取到当前地址的一些数据。 -
vue的优缺点
优点:(1)轻量,压缩之后20k左右
(2)虚拟DOM
(3)数据驱动视图
(4)组件化
缺点:
(1)vue不支持ie8
- mvvm的原理
响应式,双向数据绑定,即MVVM。是指数据层(Model)-视图层(View)-数据视图(ViewModel)的响应式框架。它包括:
a.修改View层,Model对应数据发生变化。
b.Model数据变化,不需要查找DOM,直接更新View。
MVVM的实现方式:
(1)发布者-订阅者模式: 一般通过sub, pub的方式实现数据和视图的绑定监听,更新数据方式通常做法是 vm.set(‘property’, value)。
(2)脏值检查: angular.js 是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,在指定的事件触发时进入脏值检测。
(3)数据劫持: vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
思路:
1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
参考博客:
mvvm原理
- vue兼容ie9要做什么
Vue 官方对于 ie 浏览器版本兼容情况的描述是 ie9+,即是 ie9 及更高的版本。
在 ie9 的环境上,es6 的部分新对象、表达式,并不支持,解决方案是使用 babel-polyfill 组件,它可以将 es6 的代码翻译成低版本浏览器可以识别的 es5 代码
安装完成后,在项目的主入口文件 main.js 的首行就可以直接引用
在项目使用 vue-cli 生成的代码中,根目录有一个 .babelrc 文件,这是项目使用 babel 的配置文件。在默认生成的模板内容中,增加 “useBuiltIns”: “entry” 的设置内容,这是一个指定哪些内容需要被 polyfill(兼容) 的设置
加入这些代码后,工程里的大部分内容已可兼容到 ie9 版本
即使在使用 babel-polyfill 做代码翻译后,发现还是有一些 es6 的新特性并没有解决,比如说 Number 对象的 parseInt 和 parseFloat 方法
es6 将全局方法 parseInt() 和 parseFloat() ,移植到 Number 对象上面,行为完全保持不变。这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。
解决这个问题不需要引入包来解决,同样在项目主入口文件 main.js 加入以下代码(代码尽可能靠前,最好是在引用 babel-polyfill 之后 )
if (Number.parseInt === undefined) Number.parseInt = window.parseInt;
if (Number.parseFloat === undefined) Number.parseFloat = window.parseFloat;
- 如何优化vue项目第一次加载慢
a. 使用webpack-bundle-analyzer工具,查看各部分文件大小及编译后文件大小
b. 使用 vue-router 懒加载解决首次加载时资源过多导致的速度缓慢问题
c. 使用CDN加速
d. 工程文件打包的时候不生成.map文件
npm run build编译之后,我们查看编译生成的文件,发现有很多.map文件,这些文件也占了不小的空间。.map文件的作用是帮助编译后的代码调试,但是我们上线的代码已经调试完成,所以上线时可以不生成.map文件。
- vue封装组件最重要的是什么?
开发通用组件是很基础且重要的工作,通用组件必须具备高性能、低耦合的特性
a. 数据从父组件传入
为了解耦,子组件本身就不能生成数据。即使生成了,也只能在组件内部运作,不能传递出去。
父对子传参,就需要用到 props,但是通用组件的的应用场景比较复杂,对 props 传递的参数应该添加一些验证规则
b. 在父组件处理事件
在通用组件中,通常会需要有各种事件,
比如复选框的 change 事件,或者组件中某个按钮的 click 事件,这些事件的处理方法应当尽量放到父组件中,通用组件本身只作为一个中转
c. 记得留一个 slot
一个通用组件,往往不能够完美的适应所有应用场景
所以在封装组件的时候,只需要完成组件 80% 的功能,剩下的 20% 让父组件通过 solt 解决
d. 不要依赖 Vuex
父子组件之间是通过 props 和 自定义事件 来传参,非父子组件通常会采用 Vuex 传参
但是 Vuex 的设计初衷是用来管理组件状态,虽然可以用来传参,但并不推荐
因为 Vuex 类似于一个全局变量,会一直占用内存
在写入数据庞大的 state 的时候,就会产生内存泄露
- vue实现双向数据绑定的原理
vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过**Object.defineProperty()**来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
- scoped实现原理
Vue中的scoped属性的效果主要是通过PostCss实现的。以下是转译前的代码:
<style scoped>
.example{
color:red;
}
</style>
<template>
<div>scoped测试案例</div>
</template>
转译后:
.example[data-v-5558831a] {
color: red;
}
<template>
<div class="example" data-v-5558831a>scoped测试案例</div>
</template>
既:PostCSS给一个组件中的所有dom添加了一个独一无二的动态属性,给css选择器额外添加一个对应的属性选择器,来选择组件中的dom,这种做法使得样式只作用于含有该属性的dom元素(组件内部的dom)。
scoped穿透
scoped看起来很好用,当时在Vue项目中,当我们引入第三方组件库时(如使用vue-awesome-swiper实现移动端轮播),需要在局部组件中修改第三方组件库的样式,而又不想去除scoped属性造成组件之间的样式覆盖。这时我们可以通过特殊的方式穿透scoped。
stylus的样式穿透 使用>>>
外层 >>> 第三方组件
样式
.wrapper >>> .swiper-pagination-bullet-active
background: #fff
sass和less的样式穿透 使用/deep/
外层 /deep/ 第三方组件 {
样式
}
.wrapper /deep/ .swiper-pagination-bullet-active{
background: #fff;
}