基础面试题
DOM和BOM
- BOM是浏览器对象模型,用来获取或设置浏览器的属性、行为,例如:新建窗口、获取屏幕分辨率、浏览器版本号等。
- DOM是文档对象模型,操作文档HTML的JS处理方式
虚拟dom
遍历真实dom找到指定dom,消耗资源,所以vue建立一个与dom树对应的虚拟dom对象,以对象嵌套方式表示dom树,dom更改,就变成对js对象属性增删改查.这样查找对象属性,要比查找dom树开销小。
dom操作会引起浏览器回流重绘使得性能开销巨大。
当尺寸、布局、隐藏、等等,改变了,引起重新构建,称为回流。
当属性只影响外观、风格、而不影响布局就是重绘。回流一定会引发重绘。
1.解析html:浏览器有专门的html解析器,解析过程中还会构建dom树。
2.构建dom树:跟解析同步执行的
3.构建呈现树rendertree:将css用到dom节点上
4.布局:计算 呈现树 大小和位置
5.绘制:布局完成,将呈现树绘制显示到屏幕上
盒模型
盒子有4个边界:外边距、边框、内边距、内容边界
css3中有个box-sizing属性可以控制盒子计算方式
content-box: padding和border不包含在定义的宽高之内。盒子实际宽度等于设置的width值和border、padding之和。(W3C盒子模型)
border-box:padding 和 boder被包含在定义的width和height之内。对象的实际宽度就等于设置的width值。(IE6盒子模型)
居中方法
- 利用子绝父相
#container{
width:500px;
height:500px;
position:relative;
}
#center{
width:100px;
hight:100px;
position: absolute;
top: 50%;
left: 50%;
margin-top:-50px;
margin-left:-50px;
}
- 利用Css3的transform
#container{
position:relative;
}
#center{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
- 利用弹性布局
#container{
display:flex;
justify-content:center;
align-items: center;
}
px、em、rem
px实际上就是像素,与物理像素有一定区别,用px设置字体大小,比较精确,但是有缺陷,浏览器页面缩放时,px不能跟随变化。
em是根据父元素来对应大小,是一种相对值,并不是绝对值。
rem是根元素html的font-size来对应大小。
var let const
var:存在变量提升,没有块作用域限制
let:存在暂时性死区(先声明后使用),有块级作用域限制,
const:存在暂时性死区(先声明后使用),有块作用域限制,不能重复声明变量,声明时必须赋值,不可以重复赋值,可以修改引用类型值
检测数据类型
- typeof: 只能检测出基本数据类型,数组、对象、null返回object
let a = 1;
console.log(typeof a); //number
- instanceof:不能检测原始值类型,通过原型链来判断实例的__proto__是否与检测类型的prototype一致
let houdunren = {};
console.log(hd instanceof Array); //true
- constructor:无法判断基本类型值,原理原型
prototype
上都有一个constructor
属性,自身没有这个属性,向原型上找然后比对
console.log(arr.constructor === Array); //true
- Object.prototype.toString.call:可以返回所有数据类型,以字符串输出
Object.prototype.toString.call(true); //'[object Boolean]'
数据类型
类型 | 说明 |
---|---|
number | 数值型 |
string | 字符串 |
boolean | 布尔 |
null | 空 |
undefined | 未定义 |
object | 对象 |
Symbol | 唯一性,防止属性名冲突 |
BigInt | 大整数 |
数组方法API
push:末尾压入数组,改变原数组,返回值为数组元素数量
pop:删除原数组最后一个,改变原数组,返回值为删除元素
shift:删除原数组第一个,改变原数组,返回值为删除元素
unshift:从数组前面添加数组,改变原数组,返回数组元素数量
fill:在数组固定位置替换内容,改变原数组,返回替换后数组
let a = [1,6,6,3]
console.log(a.fill("后盾人", 1, 2)); //[1, '后盾人', 6, 3] a=[1, '后盾人', 6, 3]
slice:截取元素,不会改变原数组,返回截取数组,slice(1, 3)截取索引1和索引2元素
splice:可以添加、删除、替换数组中的元素,会对原数组进行改变,返回值为删除的元素
arr.splice(1, 3, 'hdcms', '后盾人') //删除索引123的元素,然后替换成'hdcms', '后盾人'
join:将数组连接为字符串,不改变原数组,返回连接后数组
let arr = [1, "后盾人", "hdcms"];
console.log(arr.join('-')); //1-后盾人-hdcms 使用join可以指定转换的连接方式
split:将字符串分割成数组,不改变原字符串,返回连接后数组
let price = "99,78,68";
console.log(price.split(",")); //["99", "78", "68"]
indexOf:从前向后查找元素出现的位置,如果找不到返回 -1
arr.indexOf(2)
includes:查找字符串返回值是布尔类型更方便判断 arr.includes(6)
reverse:翻转数组顺序 arr.reverse()
遍历
sort:数组排序,默认从小于大排序数组元素,arr.sort()
console.log(arr.sort(function (v1, v2) {
return v2 - v1; //从大到小
return v1 - v2; //从小到大
})); //[9, 4, 2, 1]
find:方法找到后会把值返回出来
findIndex:方法找到后会把索引返回出来
every:用于递归的检测元素,要所有元素操作都要返回真结果才为真
some:如果有一个返回true,表达式结果就是真
filter:返回所有符合条件元素
reduce:可以迭代数组的所有元素
this指向问题
1、普通函数中的this指向window
2、定时器中的this指向window
3、箭头函数没有this,会继承定义函数时的上下文,可以理解为和外层函数指向同一个this
4、事件中的this指向事件的调用者
5、 构造函数中this和原型对象中的this,都是指向构造函数new 出来实例对象
6、类 class中的this 指向由constructor构造器new出来的实例对象
7、自调用函数中的this 指向window
apply/call/bind
相同点:改变this指针。
不同点:
apply:用数组传参
call: 需要分别传参
bind:不会立即执行函数
闭包
闭包:闭包是指有权访问另外一个函数作用域里变量的函数,js中所有函数都是闭包。
特点:
-
在子函数本身作用域以外执行,即延申作用域。
-
子函数可以访问外部作用域变量。
-
容易造成内存泄漏,因为内部使用外部变量,垃圾回收机制认为变量一直被使用,得不到回收,解决方法,用完让他地址指向null。
-
将变量定义在内部,使内部拥有自己的变量,同时可以不污染全局变量
Promise
Promise 是一个对象,一般作为构造函数来使用(new Promise),它里面有 3 种状态,pending(准备)、fulfilled(成功)、rejected(失败)。
- 怎么用
拿到一个 Promise 实例后,原型上具有 then、finally 和 catch 方法,then里面有2个回调函数,第一个成功,第二个失败,catch 可以统一处理失败,finally无论状态是resolve
或 reject
都会执行此动作。
Promise有reject、resolve,race、all、allSettled这几个API:reject生成失败的Promise;resolves生成成功Promise ;race哪个resolve执行快执行哪个;all可以发多个异步请求,有失败状态就不往下走;allSettled不管成功失败状态都返回结果,控制台不报错。
多个请求中,有错误请求可以用then失败回调接收错误,或者用catch统一接收错误,让代码接着往下执行
- 解决了什么问题
它主要解决一个异步请求套着一个异步请求,一个异步请求依赖于另一个的执行结果,使用回调的方式相互嵌套。
回调地狱的问题。
- 替代方案
一般工作的时候都会配合 async/await 语法糖来使用,这样既解决了回调地狱又简化了代码,第一个await 之前代码同步执行,后面代码异步执行,await关键字只能出现 async修饰符里面。
缺点:一旦执行无法结束。
es6新增特性
特性 | 说明 |
---|---|
Let和const关键字 | let a = [],const names = []; |
结构赋值 | |
字符串模板 | |
展开运算符 | […fruits,…vegetables] |
箭头函数 | name => name.toUpperCase() |
symbol | 表示独一无二的值 |
set | 使用Set实现数组去重new Set(guaiguai) |
promise |
常见http的状态码
状态码 | 说明 |
---|---|
200 | 请求成功 |
400 | 错误请求 |
401 | 未授权 |
403 | (禁止) 服务器拒绝请求 |
404 | 未找到 |
304 | 协商缓存 |
防抖节流
共同点:限制函数执行次数
不同点:
-
节流(throttle): 所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。
-
防抖(debounce): 所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
-
防抖可以用于用户输入,节流可以用于页面滚动,无限加载
都是用setTimeout实现,节流通过判断变量boolean值 return出去,防抖通过判断变量是否为空删除定时器,但是要保证代码往下走。
eventloop事件循环
事件轮询就是解决javaScript单线程对于异步操作的一些缺陷,让 javaScript做到既是单线程,又绝对不会阻塞的核心机制,是用来协调各种事件、用户交互、脚本执行、UI 渲染、网络请求等的一种机制。
执行顺序:
- 同步任务由javaScript 主线程次序执行
- 异步任务委托给宿主环境执行
- 已完成的异步任务对应的回调函数,会被加入到任务队列中等待执行
- javaScript 主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行
- javaScript 主线程不断重复上面的4步
异步任务又分为:
- 宏任务:当前调用栈中执行的代码称为宏任务。(主代码块,定时器等)
- 微任务:当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务。可以理解为回调事件。(promise.then , nextick等)
http缓存
作用:减少网络请求和数量
强缓存:是取决于服务器判断是否缓存,不需要前端做任何事,服务器会在请求头设置设置相应缓存时间
协商缓存:第一次请求服务器会返回资源标识,后续相同请求携带相同标识发送给服务器,服务器判断资源更新与否,未更新返回304状态码,直接从本地缓存拿资源,更新返回最新数据和200状态码
描述输入url地址到网页展示的过程
-
DNS域名解析(解析成ip地址)
-
TCP 3次握手(第一次浏览器发出,第二次服务器,第三次浏览器)
-
发送请求报文
-
接受响应
-
渲染页面
-
4次挥手
跨域问题及解决方案
跨域:域名,端口,协议三者中只要有一个不同就是跨域。对于端口和协议的不同,只能通过后台来解决。我们要解决的是域名不同的问题。
解决:
- jsonp,利用在页面创建
哈希路由和history路由的区别
hash路由
-
hash就是指url尾巴后的#号以及后面的字符
-
刷新 不会导致浏览器向服务器发出请求
-
hash传参是基于url的,传参有体积限制
-
hash不会被包含在HTTP请求中,对后端没有影响,改变hash不会重新加载页面
history模式
-
history路由地址栏URL上没有#
-
history模式利用了HTML5 History Interface 中新增的pushState()和replaceState()方法
-
刷新 history路由url必须和实际后端发起请求的url一致,刷新是网络请求,没有后端准备时会报错404
-
history模式不仅可以在url里放参数,还可以将数据存放在一个特定的对象中
深拷贝浅拷贝
浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
方法:
• Object.assign ( {} , 对象1,对象2 ),第一级属性深拷贝,从第二级属性开始就是浅拷贝。
• 使用 es6 展开符
深拷贝:会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。
方法:
• JSON.parse( JSON.stringify (object) ) JSON.stringify 拷贝一个字符串会新辟一个新的存储地址,这样就切断了引用对象的指针联系,所以先转成一个字符串,在JSON.parse解析出对象,这样就可以深拷贝一个对象
https和http的区别
HTTPS 协议需要到 CA (Certificate Authority,证书颁发机构)申请证书,一般免费证书较少,因而需要一定费用。(以前的网易官网是http,而网易邮箱是 https 。)
HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协议。
HTTP 和 HTTPS 用的端口不一样,前者是80,后者是443。
如何减少重绘和回流
重绘:当前节点需要更改外观而不会影响布局的,比如更改颜色
回流:回流是布局或者几何属性需要改变就称为回流
回流必定会发生重绘,重绘不一定引发回流
• 减少回流方案:
1、使用 css3 的 transform
2、减少 display 使用
3、css 选择符从右到左匹配查找,避免节点层级过多
垃圾回收机制
垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。开销比较大,所以垃圾回收器会按照固定的时间执行。
策略:标记清除
对于脱离作用域的变量进行回收,当进入作用域时,进行标记,离开作用域时,标记并回收这些变量。
get和post区别
1.get参数url可见,post不可见,所以 post更安全
2.get通过拼接url传递参数,post用请求头传递
3.get请求支持http缓存机制,post不支持
4.get请求页面后退,不产生影响,post会重新提交请求
5.get一般传输数据大小不超过4k,post可以无限大
6.get只能是json数据格式,post可以是json、form等格式
如何添加和删除事件
通过addEventListener添加事件,必须通过removeEventListener移除事件
document.addEventListener(‘事件类型’, 事件名);
document.removeEventListener(‘事件类型’, 事件名);
事件冒泡和事件捕获
事件冒泡:当某个元素事件触发时,那么他的父级同类型元素也会触发,一直触发到根源上。
事件铺获: 从根元素(HTML)到事件源,当某个元素事件触发时,朝同类型子级触发,一直到事件源。
事件委托
将所有元素的事件绑定委托给同一个父元素,根据事件冒泡捕获机制,可以在父元素绑定事件中获取的触发事件的这个元素。根据这个元素不同特征,做不同处理。
原型
当我们定义普通对象时候会生成一个__proto__ ,他指向这个对象的构造函数的prototype,也就是原型。我们都知道对象都有一个toString方法,而实例化对象没有toString方法,那就会沿着他的proto向他的构造函数prototype原型对象去找,prototype也是一个对象,也有proto属性,也没有toString方法的话,就会沿着prototype.proto向上找。而他指向顶级Object.prototype。Object.prototype再找的话就是null.
举例:Object.prototype.toString.call()判断数据类型,利用call调用Object原型对象上未被改造的toString方法。
继承
- 原型链继承
缺点:2个实例使用同一个原型对象,存在空间共享,当一个发生改变,另外一个也发生变化
- 构造函数继承借助call
缺点:只能继承父类的实例属性和方法,不能继承原型属性或者方法。
- 组合继承:原型继承构造函数继承结合
缺点:增加了性能开销
- 原型式继承:利用ES5 里面的 Object.create 方法
缺点:多个实例的引用类型属性指向相同的内存,存在篡改的可能
-
寄生式继承:在原型式继承浅拷贝基础上再添加一些方法
-
寄生组合继承:所有继承方式里面相对最优的继承方式
Vue面试题
常用的指令
指令 | 用法 |
---|---|
v-text | 更新标签包含的文本 |
v-html | 绑定一些包含html代码的数据在视图上 |
v-show | 取值为true/false,分别对应着显示/隐藏 |
v-if | 取值为true/false,控制元素是否需要被渲染 |
v-else | 当v-if的值false,v-else才会被渲染出来 |
v-for | 遍历data中存放的数组数据 |
v-bind | 用于动态绑定DOM元素的属性 |
v-on | 可以绑定事件的监听器 |
v-model | 实现表单控件和数据的双向绑定 |
v-once | 只渲染一次,后面元素中的数据再更新变化,都不会重新渲染 |
v-if和v-for不能同时使用
首先,vi-for比v-if优先级高,影响性能
第一种情况:v-if值不依赖列表,可以在循环外部用v-if判断
第二种情况:v-if值依赖列表,在vue实例,用计算属性过滤掉不需要显示项
计算属性监听属性
计算属性:
- 观察多个属性动作,是计算值
- 具有缓存性,依赖值不变,不会重新计算
watch特性:
- 是观察一个属性的动作
- 无缓存性,页面重新渲染时值不变化也会执行
key作用
-
key的作用主要是利用diff算法更加高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。
-
另外,若不设置key还可能在列表更新时引发一些隐藏的bug
-
vue2中diff算法采用递归+双指针方法
data为什么是一个函数
data必须是函数为了保证组件的独立性和可复用性,data是一个函数,组件实例化的时候这个函数就会被调用,返回一个对象,计算机会给这个对象分配地址,实例化几次,分配几个地址,他们地址不一样,所以每个组件中数据不会互相干扰。
el、template、render属性的优先级
在进行dom树渲染时,render函数优先级最高,template次之且需要编译成渲染函数,而挂载点el属性对应的元素若存在,则在前二者不存在时,其outerHTML才会用于编译与渲染。
生命周期
Vue 实例从创建到销毁的过程,就是生命周期。
阶段 | 方法名 | 方法名 |
---|---|---|
初始化 | beforeCreate 没有初始化 | created 初始化完成 |
挂载 | beforeMount 获取到dom没有渲染视图 | mounted 渲染视图了 |
更新 | beforeUpdate 拿到更新数据没有渲染 | updated 渲染到视图 |
激活 | activated | deactivated |
销毁 | beforeDestroy 销毁前,data,method可以用 | destroyed 销毁后 |
Vue中v-if和v-show的区别
原理:
v-if满足条件是会渲染到html中,不满足条件时是不会渲染到html中的,是通过操纵dom元素来进行切换显示
v-show原理是修改元素的css属性来决定实现显示与隐藏的
应用场景:
v-if需要操作dom元素,有更高的切换消耗,v-show只是修改元素的css属性有更高的初始渲染消耗,如果需要非常频繁的切换,建议使用v-show较好,如果运行条件很少改变,使用v-if较好
v-model和双向数据绑定原理
原理:
Object.defineProperty()劫持data里面各个数据,将它们转为 getter/setter,在属性被访问和修改时通知变化。整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化。通过Compile来解析编译模板指令。最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
缺点:无法追踪局部状态变化,增加了排错难度
用法:包含两个指令
一个v-bind,把message的值时时放到value中,
另一个v-on,给当前元素绑定input事件,将event.target.value赋值给message
组件创建过程
1.首先,创建一个组件
2.然后,使用Vue.component()方法注册组件
3.接着,子组件可以通过props中接受父组件数据
4.最后,子组件修改数据,可以使用emit()方法
组件传值
props;$emit/$on
$bus => $emit / $on;vuex
$attrs / $listeners
$attrs / $listeners
provide / inject
localStorage / sessionStorage
sync
v-model
ref
组件缓存
在keep-alive里面放入要缓存的组件,他有include以及exclude属性:
-
include
- 字符串或正则表达式。只有名称匹配的组件会被缓存。 -
exclude
- 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
生命周期钩子:
activated :路由组件被激活时触发。(当进入缓存的路由组件时触发)
deactivated:路由组件失活时触发。(当离开缓存的路由组件时触发)
父子组件生命周期顺序
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新生命周期
父beforeUpdate->子beforeUpdate->子updated->父updated
组件销毁生命周期
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
Vue性能优化
编码优化:
- 不要将所有的数据都放在data中,data中的数据都会增加getter和setter,会收集对应的watcher
- key保证唯一性
- .合理使用路由懒加载、异步组件
- 拆分组件( 提高复用性、增加代码的可维护性,减少不必要的渲染 )
- 页面采用keep-alive缓存组件
Vue加载性能优化:
- 第三方模块按需导入
- 图片懒加载
- 滚动到可视区域动态加载
SEO优化:
- 预渲染插件
- 服务端渲染
打包优化:
- 使用cdn的方式加载第三方模块
- 多线程打包 happypack
- 抽离公共文件
缓存压缩:
- 客户端缓存、服务端缓存
- 服务端gzip压缩
路由钩子函数
- 全局路由勾子
- beforeEach:
- afterEach
- beforeResolve
- 路由所对应组件勾子(路由配制中的component所对应组件)
- beforeRouteEnter:进入前,进入路由所对应组件前,在beforeCreate前产生的,它不能使用实例化对象this
- beforeRouteUpdate:更新前,路由更新了,但是路由所对应组件不变,它是更新前触发,更新没有完成,如果需要获取更新完成后信息,要加$nextTick
- beforeRouteLeave:离开前,切换页面离开前就会触发,它在beforeDestroy前执行
- 路由配制所对应勾子
- beforeEnter:读取路由配制前会执行该勾子
进入一个页面时路由勾子执行先后
- beforeEach==>beforeEnter==>beforeRouteEnter==>beforeResolve==>afterEach
路由更新时路由勾子执行先后
- beforeEach==>beforeResolve==>afterEach
路由传参
router:路由实例化对象,里面有很多属性子对象。
route:提供当前路由基本信息
路由传参有3种模式,基本路由传参,动态路由匹配,name传参
- 基本路由传参
- 传
- this.$router.push(’/path地址?参数名=值&参数2=值2‘)
-
this.$router.push({ path:'/path地址', query:{ 参数名:值, 参数2:值2 } })
- 收:this.$route.query.参数名
- 动态路由匹配
-
路由配制
-
{ path:'/xxx/:参数名?', // ?代表参数值可传可不传,不写?一定要传 component... }
-
-
传:this.$router.push(’/xxx/123’)
-
收:this.$route.params.参数名
- name传参
-
路由配制
-
{ path:'/', name:'home', .... }
-
-
传:(刷新不受影响):this.$router.push({name:‘home’,query:{参数名:参数值}})—和以前基本路由传参一样
-
收:this.$route.query.参数名
-
传(params):刷新后传参就没了,数据长度与数据类型不受限制
- this.$router.push({name:‘home’,params:{参数名:参数值}})—和以前基本路由传参一样
-
收(params):this.$route.params.参数名
动画
传统动画class切换结合c3的transition,他有4个属性:transition-property过渡属性,transition-duration完成过渡时间,transition-timing-function指定完成过渡曲线;transition-delay延迟时间
vue提供了单元素动画和多元素动画封装,只能给元素组件添加进入离开动画。单元素动画用transition包裹动画标签,多元素动画用transition-group包住动画元素,每个动画都要有key值。
vuex
vuex是状态数据管理工具,是通过全局注入store对象,来实现组件间状态数据共享。当vue.use注册全局时,会调用install方法,每个组件实例化过程中,会调用钩子beforeCreate,通过mixin混入机制,把store注入到组件中。store中有5个组成部分:
state:是存储基本数据的;
mutations:更改state数据的,
getter:对state加工,跟vue计算属性一样,
actions:处理异步,通过store.commit方法触发mutations中方法更改state值;
module:是store分割的模块,每个模块拥有state,mutations,getter,actions;
调用vuex其他模块的方法在后面加上{root:true}表示从根查找。
还有4个辅助函数:
mapstate,mapgetters,mapActions,mapmutations
vue自定义指令
主要是通过Vue.directive来定义。每个指令有5个钩子函数如下:
bind:绑定时,自定义指令绑定于相应dom时执行,获取dom,没有渲染到视图
inserted:已经渲染到视图
update:更新时
componentUpdated,:更新完成时,指令所在组件更新完成
unbind:解除绑定
每个指令都有3个参数,dom:指令所在dom, obj:值,vnode:虚拟dom节点信息
Axios 封装了哪些东西
创建了一个请求实例、baseURL(基地址),timeout、拦截器(请求拦截器:统一携带token,token超时。响应拦截器:统一错误处理信息,401页面)
rem适配
安装淘宝 flexible.js ,在 main 文件引入,他把 html 下的 font-size 设置成屏幕10分之一,1rem = 屏幕/设计稿/10,手动计算太麻烦了,可以安装 postcss-pxtorem 插件,根据文档配置,就可以自动把 px 转换 rem .转换过程读取文件路径,根据文件路径判断是组件库的,还是自己设计搞得,进行分开计算。一般 ui 设计稿比较大,因为大图片缩小清晰,小图片放大模糊。
rem 在开发中会出现一些大大小小问题,官方也说了不建议使用 flexible.js ,以前很多公司不用 vw 百分比是兼容问题,现在很多浏览器兼容了。
还是安装插件,postcss-px-to-viewport ,在根目录下 postcss.config.js 中配置,他只做一件事,把 ps 转为百分比。
token过期处理
服务器生成token时会有2时间,token失效时间,和刷新时间,刷新时间比失效时间长。利用axios响应拦截,interceptors.response,里面有2函数,响应成功,把值返回return出去,响应失败进入第二个函数拦截401,判断vuex有没有数据,然后发请求获取新的token替换vuex里面token,然后把请求结果返回给请求体。
webpack
主要 api 有 entry(项目入口),output(出口文件),modul(模块的处理),plugin(loader不能做的处理都能交给plugin来做)
常用的loader有 label-loader :把es6转为es5 ,style-loader:把 CSS 代码注入到 JavaScript 中, eslint-loader:通过eslint检查代码
常见Plugin有:commons-chunk-plugin;提取公共代码 uglifyjs-webpack-plugin:压缩代码
loader是加载器,webpack只能解析js,打包其他文件得用到,plugin是插件,可以扩展webpack功能
loader在module中配置,plugin在plugins中配置
权限
- 用户登录成功之后,后端返回当前用户权限标识
- 前端根据后端返回得权限标识删选出有权限路由
- 删选出来后做了2件事
第一件事把删选出来得动态路由通过addRoutes 添加到路由实例,目的:能够通过地址栏访问。
第二件事把删选出来得动态路由添加到Vuex中,目的:给菜单栏使用