前端面试题精选
vue篇
1 vue响应式原理
Vue 的响应式原理是核心数据劫持是通过 ES5 的保护对象的 Object.defindeProperty 中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM 树上。
2 Vue如何做SEO
-
1.SSR服务器渲染;
-
2.静态化;
-
3.预渲染prerender-spa-plugin;
-
4.使用Phantomjs针对爬虫做处理。
SSR权衡之处
开发条件所限,浏览器特定的代码,只能在某些生命周期钩子函数
(lifecycle hook) 中使用;一些外部扩展库 (external library)
可能需要特殊处理,才能在服务器渲染应用程序中运行;
环境和部署要求更高,需要Node.js server 运行环境;
高流量的情况下,请准备相应的服务器负载,并明智地采用缓存策略。
静态方法
纯静态文件,访问速度超快;
对比SSR,不涉及到服务器负载方面问题;
静态网页不宜遭到黑客攻击,安全性更高。
缺点:
如果动态路由参数多的话不适用。
预渲染prerender-spa-plugin
如果你只是用来改善少数营销页面(例如 /, /about, /contact 等)的 SEO,那么你可能需要预渲染。无需使用 web 服务器实时动态编译 HTML,而是使用预渲染方式,在构建时 (build time) 简单地生成针对特定路由的静态 HTML 文件。优点是设置预渲染更简单,并可以将你的前端作为一个完全静态的站点。
使用Phantomjs针对爬虫做处理
这种解决方案其实是一种旁路机制,原理就是通过Nginx配置,判断访问的来源UA是否是爬虫访问,如果是则将搜索引擎的爬虫请求转发到一个node server,再通过PhantomJS来解析完整的HTML,返回给爬虫。
3 couputed和watch的区别
计算属性computed
- 支持缓存,只有依赖数据发生改变,才会重新进行计算
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
- computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
侦听属性watch
- 不支持缓存,数据变,直接会触发相应的操作;
- watch支持异步;
- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 当一个属性发生变化时,需要执行对应的操作;一对多;
4 Vue有哪些生命周期函数,分别什么作用,实现原理是什么
- BeforeCreate函数:
在实例初始化后被调用 ,这个时候的this 不能用,在data中的数据 methods的方法 还有 watcher中的事件 都获取不到 - Created函数:
实例已经创建完成了,实例对象已经完成了,这时候可以访问 data 中的 数据 ,一级 methods中的方法和 watcher 中的事件了,但是 不能操作 dom 节点 - Beforemounte:
在挂载开始前被调用 render函数 第一次被调用 - mounted:
挂载完毕 ,这时候可以使用 dom 节点 ,一些需要dom的操作这时候才可以进行 - BeforeUpdate
组件更新前 也就是说 数据 更新了 但是vue中的组件(事件)对应dom 内部中的数据没有变 所以说叫做组件更新前 - Updated
组件更新完成之后的操作 ue中的组件(事件)已经对应dom 内部中的数据了 - beforeDestroy
实例销毁之前调用 ,在 这个时候实例还是可以用的 - Destroy
摧毁阶段 vue的 生命周期结束 ,实例 不能用了
这里的 update 指的是,view 层的 update,而不是 vue 对象中 data 属性的 update。所以,这两个函数都发生在 data 改变之后(很容易理解,因为就是 data 属性的改变才触发了这两个函数),区别是 beforeUpdate 发生在 view 层的改变之前,也就是页面还没有重新渲染,此时页面仍然显示 “Vue的生命周期”(beforeUpdate 也是在页面重新渲染前修改 data 的最后时机);而 updated 是发生在 view 层改变之后,也就是此时的页面已经重新渲染为 “触发组件更新”。
5 vue组件如何通信
- 1 props和$emit
父组件向子组件传递数据是通过prop传递的,
子组件传递数据给父组件是通过$emit触发事件来做到的。
- 2.$attrs和$listeners
- 3 中央事件总线 eventbus.$emit 和eventbus.$on
var bus=new Vue();
bus.$on('globalEvent',(val)=>{
this.brothermessage=val;
})
bus.$emit('globalEvent',val)
- 4.provide和inject
父组件中通过provider来提供变量
,然后在子组件中通过inject来注入变量。
不论子组件有多深,只要调用了inject那么就可以注入
provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。
inject:['for'],//得到父组件传递过来的数据
data(){
return {
mymessage:this.for
}
},
provide:{
for:'test'
},
- 5.v-model
props:{
value:String, //v-model会自动传递一个
//字段为value的prop属性
},
changeValue(){
this.$emit('input',this.mymessage);//通过如此调用可以改变父组件上v-model绑定的值
}
<input type="text" v-model="mymessage" @change="changeValue">
<child v-model="message"></child>
- 6.$parent和$children
changeValue(){
this.$parent.message = this.mymessage;//通过如此调用可以改变父组件的值
}
changeChildValue(){
this.$children[0].mymessage = 'hello';
}
- 7.vuex处理组件之间的数据交互
如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,
这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法
就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公
共数据进行读写操作,这样达到了解耦的目的。
6 vuex你怎么用,实现原理是怎么样的
7路由守卫
在路由跳转之前 我们主要是利用vue-router提供的钩子函数beforeEach()对路由进行判断。
to 表示将要跳转到的组件 (目标组件)
console.log(from); //(源组件)
next();
next 是一个函数
next() 进入下一个组件的钩子函数
next(false) 阻止跳转 中断导航
next("/login") 进入指定的组件的钩子函数
JS篇
1 call,apply ,bind的区别
- call()、apply()、bind()是用来改变this的指向的。
- call(),apply() 把当前的函数执行了,然后并改变this的指向,只是入口的参数不一样
- bind()预处理的思想,当前函数不执行,只是改变this的指向,存储参数,返回一个匿名函数
call和apply
唯一区别是传递参数的形式不一样
fun.call(obj,num1,num2)
fun.apply(obj,[num1,num2])
非严格模式下
func.call() //不传指向window
func.call(null) //指向window
func.call(undefined) //指向window
func.call(5) //指向 Number(5)
严格模式
是谁指向谁
bind语法
- bind()预处理的思想,当前函数不执行,只是改变this的指向,存储参数
- bind()运用闭包的思想,将this和参数存储在一个不被销毁的内存中,等待下次调用
- func.bind()运行后放回了一个匿名函数,bind()执行时this是当前操作函数,当匿名函数执行时,里面的this和原来bind中的this没有关系
function bind(context = window, ...args) {
return function anonymous(...amArg) {
//this指向func
this.call(context, ...args.concat(amArg));
}
2 什么是严格模式
ECMAScript 5 引入了严格模式(strict mode)的概念。严格模式是为JavaScript 定义了一种不同的解析与执行模型。在严格模式下,ECMAScript 3 中的一些不确定的行为将得到处理,而且对某些不安全的操作也会抛出错误。要在整个脚本中启用严格模式,可以在顶部添加如下代码
use strict
3 说说你对this的理解
- 普通函数执行
- 形成私有上下文(AO)
- 初始化作用域链
- 初始化this
- 初始化arguments
- 形参赋值
- 变量提升
- 代码执行
EC是执行上下文,THIS是执行主体,this要执行才知道指向
- 1 this:全局上下文中this指向window,块级没有自己的this,他是继承所在上下文的this
- 2 事件绑定 :给元素绑定事件方法,this指向元素本身
- 3 普通函数执行:(包括自执行函数,对象成员访问函数)函数执行前有 点 .则this就指向谁,没有点,则this指向window ,跟上下文没有关系
- 4 构造函数执行new: this指向当前实例
- 5 es6箭头函数:没有自己的this,他的this是继承上下文中的this,强制用call()修改也不能修改
- 6 可以基于call、apply、 bind强制手动改变this的指向、
4 原型和原型链的认识
- 所有的实例都天生自带一个属性:prototype(原型),这个属性的值是一个对象,浏览器会默认给开辟一个堆内存
- .在浏览器给prototype开辟的堆内存中有一个天生自带的属性:constructor,这个属性存储的值是当前类本身
- 每一个对象都有一个__proto__ 的(原型链)属性,这个属性指向当前实例所属类的prototype(如果不能确定它是谁实例,都是Object的实例)
原型链的查找方式
它是一种基于_proto向上查找的机制。当我们操作实例的某个属性或者方法的时候,首先找自己空间中私有的属性或者方法
1.找到了,则结束查找,使用自己私有的即可
2.没有找到,则基于_proto_找所属类的prototype,如果找到就用这个公有的,如果没找到,基于prototype上的_proto继续向上查找,一直找到Object.prototype的原型为止,如果在没有,操作的属性或者方法不存在 返回undefined
判断是否为对象属性
- instanceof 检测某一个实例是否属于这个类
- in :检测当前对象是否存在某个属性(不管当前这个属性是对象的私有属性还是公有属性, 只要有 结果就是true)
- hasOwnProperty :检测当前属性是否为对象的私有属性(不仅要有这个属性,而且必须还是私有的才可以)
5 说一说new做了什么
new的过程
- 构造函数执行
- 形成私有上下文(AO) 栈内存
- 初始化作用域链
- 初始化this
- 指向形成一个对象(堆内存)
- 初始化arguments
- 形参赋值
- 变量提升
- 代码执行
- 自动返回这个实例
6 继承
es5继承
1.原型链继承 (B子类 => A父类)
B.prototype = new A();
//原型链继承可以继承构造函数的方法和属
//性,也能继承原型链上,但是实例化实例可以给父类传参
2 CALL继承
function B() {
//CALL继承
A.call(this); //=>this.x = 100; b.x=100;
this.y = 200;
}
//可以继承构造函数的方法和属性,不能继承原型上的属性和方法
3 组合继承
function Web (){
Person.call(this,name,age)
}
Web.prototype=new Person()
let wb=new Web("231",123)
es6继承
- 用关键字extends 和super()
class B extends A {
constructor() {
super(100); //=>一但使用extends实现继承,只要自己写了constructor,就必须写super <=> A.call(this,100)
this.y = 200;
}
getY() {
console.log(this.y);
}
}
7 可拖拽div
DOM2级事件绑定有事件池的机制,可以绑定多个事件
let box = document.getElementById("box");
box.addEventListener("mousedown", down);
function down(ev) {
this.startx = ev.clientX;
this.starty = ev.clientY;
this.boxL = this.offsetLeft;
this.boxT = this.offsetTop;
this._move = move.bind(this);
this._up = up.bind(this);
document.addEventListener("mousemove", this._move);
document.addEventListener("mouseup", this._up);
}
function move(ev) {
let cux = ev.clientX - this.startx + this.boxL,
cuy = ev.clientY - this.starty + this.boxT;
// console.log(cux);
this.style.left = cux + "px";
this.style.top = cuy + "px";
}
function up(ev) {
document.removeEventListener("mousemove", this._move);
document.removeEventListener("mouseup", this._up);
}
8 遍历数组的方法
博客地址
1 传统的for方法
2 for in
for(var i in arr){
console.log(arr[i] +'/' + i);
}
3.for of
for(var item of arr){
console.log(item);
}
推荐在循环对象属性的时候,使用for in,在遍历数组的时候推荐使用for of
for…in 循环出来的是key, for…of循环出来的是value
4 foreach
arr.forEach(function(element,index){
console.log(element + '/' + index);
})
5 map遍历 所有操作结果放入数组中并返回该数组
var arr2 = arr.map(function(item){
return item.toUpperCase();
})
6 filter 所有在回调函数上返回为true的元素新数组
var arr3 = arr.filter(function(item){
if(typeof item == 'number'){
return item;
}
})
7 every() 当数组中的每一个元素在callback上被返回true时就返回true
var bol = arr.every(function(element){
if(typeof element == 'string'){
return element;
}
})
8 some()只要数组中有一项满足在callback上就返回true
var bol = arr.some(function(element){
if(typeof element == 'string'){
return element;
}
})
9 求数组的最大值
1.Math.max.apply(null,arr)
2 Math.max(...arr)
3 sort排序
let arr =arr.sort((a,b)=> return b-a)[0]
4 遍历冒泡排序
let max=arr[0]
arr.foreach(item=>{
max>item?max:item
return max
})
10 箭头函数与普通函数的区别
- 1 箭头函数是匿名函数,不能作为构造函数,不能使用new
- 2 箭头函数不绑定arguments,取而代之用剩余参数...解决
- 3 箭头函数没有this,会捕获其所在的上下文的this值,作为自己的this值
- 4箭头函数通过 call() 或 apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。
- 5 箭头函数没有原型属性
- 6 箭头函数不能当做Generator函数,不能使用yield关键字
11数组扁平化的方式
- 1 reduce 递归
function flatten(arr) {
return arr.reduce((result, item)=> {
return result.concat(Array.isArray(item) ? flatten(item) : item);
}, []);
}
- 2 toString & split
function flatten(arr) {
return arr.toString().split(',').map(function(item) {
return Number(item);
})
}
- 3 join & split
function flatten(arr) {
return arr.join(',').split(',').map(function(item) {
return parseInt(item);
})
}
- 4 扩展运算符
[].concat(...[1, 2, 3, [4, 5]]); // [1, 2, 3, 4, 5]
function flatten(arr) {
while(arr.some(item=>Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
12说一说JS的宏任务和微任务
博客地址
任务队列,单线程 eventloop 事件循环机制
- macro-task(宏任务):包括整体代码script,setTimeout,setInterval
- micro-task(微任务):Promise,process.nextTick
13 JS模块化有哪几种,各有什么特点
-
1、commomJS
require()
在服务器端,所有的模块都存放在本地硬盘,可以同步加载完成,
但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。 -
2 AMD
获取依赖模块用异步加载方式,适合浏览器端 -
3 CMD
延迟加载执行 -
4 ES6 Module
export指令导出接口,以import引入模块
import的语法跟require不同,而且import必须放在文件的最开始,且前面不允许有其他逻 辑代码,这和其他所有编程语言风格一致
13事件委托
利用事件的冒泡传播机制完成,根据冒泡触发最外层盒子,通过判断事件源来处理不同的业务
body.cllick=function(ev){
if(ev.target==botton){
}
if(ev.target==a){
}
}
应用到ztree,菜单栏 ,树形资源管理
类数组转化为数组
-
Array.from(arguments)
-
let arr=[...arguments]
-
let arg=[...new Set(arguments)] 可以数组去重 //借用Array原型上的方法操作
-
let arr=Array.prototype.slice.call(arguments)
-
[].slice.call(arguments)
-
Object.prototype.tostring.call("string") 检测数据类型 如果不是当前的类的实例,但是可以用call方法把让他可以用他原型上的方法
14 类型检测
- 检测数据类型1:typeof
- 检测数据类型2/3:instanceof / constructor 类似的所属类
- 检测数据类型4:Object.prototype.toString.call([value])
({}).toString.call("stknf")
ES6篇
1 ES6的语法有哪些,分别怎么用
- 1新的变量声明方法 let const
- 2 箭头函数
- 3 变量的解构赋值
- 4 新的模块化方法
- 5 promise
- 6 await async
- 7字符串模板
2 Promise Promise.all() Promise.race()
.then()
.catch()
3 手写函数防抖和函数节流
博客地址
函数节流:不断触发一个函数后,执行第一次,只有大于设定的执行周期后才会执行第二次
节流函数:fn:要被节流的函数,delay:规定的时间
3 */
4 function throttle(fn,delay){
5 // 记录上一次函数出发的时间
6 var lastTime = 0
7 return function(){
8 // 记录当前函数触发的时间
9 var nowTime = new Date().getTime()
10 // 当当前时间减去上一次执行时间大于这个指定间隔时间才让他触发这个函数
11 if(nowTime - lastTime > delay){
12 // 绑定this指向
13 fn.call(this)
14 //同步时间
15 lastTime = nowTime
16 }
17 }
18 }
函数防抖:不断触发一个函数,在规定时间内只让最后一次生效,前面都不生效
1 function debounce(fn,delay){
2 var timer = null
3 // 清除上一次延时器
4 return function(){
5 clearTimeout(timer)
6 // 重新设置一个新的延时器
7 timer = setTimeout(() => {
8 fn.call(this)
9 }, delay);
10 }
11 }
4 手写ajax
let req = new XMLHttpRequest();
req.open("get", "mockData/usersinfo.json", true);
req.send();
req.onreadystatechange = function () {
if (req.readyState == 4 && req.status == 200) {
var result= req.responseText;
}
}
简单封装
let $ = {
ajax: function (json) {
let req = new XMLHttpRequest();
let type = json["type"];
let url = json["url"];
let async = json['async']
if (json["data"]) {
let arr = []
for (var i in json["data"]) {
arr.push (i +'='+json["data"][i])
}
let html = arr.join('&')
url += '?'+ html;
}
let success = json["success"];
req.open(type, url, async);
req.send();
req.onreadystatechange = function () {
if (req.readyState == 4 && req.status == 200) {
var result = req.responseText;
if (json["dataType"] == "json") {
result = JSON.parse(result);
}
success(result['result']);
}
}
}
}
5 闭包
使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,
缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
- 闭包有三个特性:
- 1.函数嵌套函数
- 2.函数内部可以引用外部的参数和变量
- 3.参数和变量不会被垃圾回收机制回收
6 深浅拷贝
-
1 浅拷贝
基本类型数据直接复制,就拷贝了
引用类型的数据,直接复制,只是增加了一个新的指向,两个数据会相互影响牵制, -
slice concat
这两种方法都可以生成新的引用数据,但是不能拷贝深层的二维数组也属于浅拷贝
var arr1 = ['red','green'];
var arr2 = arr1.concat();//复制
console.log(arr2)//['red','green'];
arr1.push('black') ;//改变color1的值
console.log(arr2)//["red", "green"]
console.log(arr1)//["red", "green", "black"]
var arr1=[1,2,3,['1','2','3']];
var arr2=arr1.concat();
arr1[3][0]=0;
console.log(arr1);//[1,2,3,['0','2','3']]
console.log(arr2);//[1,2,3,['0','2','3']]
let obj2 = {... obj}
- 2 深拷贝
1 JSON.Parse(JSON.stringify())
JSON.Parse(JSON.stringify(arr))
2.递归遍历
function deepClone(obj){
//判断参数是不是一个对象
let objClone = new obj.constructor();
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
7 如何实现数组去重
- 简易的方法
let arr1 = [...new Set(arr)];
let arr1 = Array.from(new Set(arr));
- 原生的去重方法(双链法)
let arr = [12, 32, 45, 65, 34, 32, 65];
for (var i = 0; i < arr.length; i++) {
let item = arr[i];
for (var j = i + 1; j < arr.length; j++) {
if (item === arr[j]) {
arr.splice(j, 1);
i--;
}
}
}
8 Object 的assign方法
博客地址
1,合并多个对象
const merge = (target, ...sources)
=> Object.assign(target,sources);
2.克隆对象
function clone(a) {
return Object.assign({}, a);
}
3为对象添加方法
Object.assign(SomeClass.prototype, {
someMethod(a, b) {
},
anotherMethod() {
}
});
4 为对象添加属性
class Point {
constructor (a,b) {
Object.assign(this, {a,b})
}
}
CSS篇
1.两种盒模型分别说一下,兼容性问题
-
1,标准盒模型
在CSS3中,新增的box-sizing的默认属性content-box,即设置为W3C标准盒模型; -
2.IE盒模型
在CSS3中,新增的box-sizing的默认属性border-box,即设置为IE模型;
IE盒模型和W3C标准盒模型的兼容性
声明后所有浏览器都默认标准盒模型
2 flex怎么用?常用属性有哪些
博客地址
父级身上的属性
display: flex;
justify-content: 子元素水平排列方式
center 居中 √
space-between 两端对齐 √
space-around 子元素拉伸分布 √
flex-start 居左
flex-end 居右
align-items: 子元素垂直排列
center 居中 √
flex-start 开始
flex-end 底部
align-content: 多行的时候,垂直排列
center 居中
flex-direction: 排列方式
row 横向排列
row-reverse 横向翻过排列
column 纵向排列
column-reverse 纵向翻过排列
flex-wrap: 子元素是否在一行显示
no-wrap 不换行
wrap 换行
flex-flow: <flex-direction> <flex-wrap>
子级身上属性
order
flex-grow 放大
flex-shrink 缩小
flex-basis 固定大小
flex
align-self
flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。
3 BFC是什么
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此
- BFC 特性(功能)
- 利用BFC避免margin重叠塌陷。
- 使 BFC 内部浮动元素不会到处乱跑;
- 浮动元素产生边界
- BFC产生的条件
- 1、float的值不是none
- 2、position的值不是static或者relative。
- 3、display的值是inline-block、table-cell、flex、table-caption或者inline-flex
- 4、overflow的值不是visible
4 CSS选择器优先级
博客地址
!important 特殊性最高
对于内联样式,加1000
对于选中器中给定的ID属性值,加0100
对于选择器中给定的类属性值,属性选择或伪类,加0010
对于选择器中给定的元素选择器和伪元素,加0001.
结合符和通配符选择器对特殊性没有任何贡献,0000
内联样式 > ID选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 元素选择器 = 关系选择器 = 伪元素选择器 > 通配符选择器
5 清除浮动说一下
1 最后加一个div 盒子 clear: both
2 使用 :after 伪元素的方法 给伪元素添加
overflow:hidden clear: both;
3 overflow:hidden 方法形成bfc清除浮动 ,通过形成BFC的方式清除浮动
6 如何画一个三角形
- 将盒子的宽度和高度设置为0,左右border透明
.triangle{
width: 0px; /*设置宽高为0,所以div的内容为空,从才能形成三角形尖角*/
height: 0px;
border-bottom: 200px solid #00a3af;
border-left: 200px solid transparent; /*transparent 表示透明*/
border-right: 200px solid transparent;
}
7 position的属性有哪些
-
position: relative;相对定位
1> 不影响元素本身特性(无论区块元素还是内联元素会保留其原本特性)
2> 不会使元素脱离文档流(元素原本位置会被保留,即改变位置也不会占用新位置)
3> 没有定位偏移量时对元素无影响(相对于自身原本位置进行偏移)
-
position: absolute;绝对定位
1> 使元素完全脱离文档流(在文档流中不再占位)
2> 使内联元素在设置宽高的时候支持宽高(改变内联元素的特性)
3> 使区块元素在未设置宽度时由内容撑开宽度(改变区块元素的特性)
4> 相对于最近一个有定位的父元素偏移(若其父元素没有定位则逐层上找,直到document——页面文档对象)
-
position: fixed;固定定位
fixed生成固定定位的元素,相对于浏览器窗口进行定位。
-
position:static:默认值
默认布局。元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明)。
8 calc的书写要注意什么
-
符号前后要加空格
-
注意浏览器兼容问题,加前缀
calc的好处就是不同单位之间可以互相运算,百分比,像素,em,rem等是可以掺杂运算的。
9 说一下transition和animation
- 1.触发方式animation配合@keyframes可以不用触发事件,自动触发动画效果,transition这需要通过:hover和JS配合来触发(一般通过JS触发得就用transition)
- 2.animation可以通过animation-iteration-coun设置循环次数transition没有只能触发是执行一次
- 3.animation适合做复杂得动画通过@keyframes设置每一帧,transition只有俩帧开始和结束
.box{
width:100px;
height:100px;
backgound-color:red;
position:absolute;
top:0;
right: 300px;
animation: myanimation 2s ease-in;
-moz-animation:myanimation 2s ease-in;
-webkit-animation:myanimation 2s ease-in;
-o-animation: myanimation 2s ease-in;
}
@keyframes myanimation{
0% {
right: 100px;
}
20% {
right: 300px;
}
100% {
right: 100px;
}
}
.box{
width:80px;
height:80px;
background-color:red;
position:absolute;
top:0;
left:0;
transition:all 1s linear;
-moz-transition: all 1s linear;
-webkit-transition: all 1s linear;
-o-transition:all 1s linear;
}
.box:hover{
top:200px;
left:200px;
}
10减少回流和重绘
- 1 批量修改DOM或者样式
最核心的思想就是对于对引起回流和重绘的操作不要一次一次的修改,应该用一个容器装起来,一次性修改,少用offsetTop,clientX,等 - 2 避免触发同步UI渲染
- 3、对于复杂动画效果,使用绝对定位让其脱离文档流
- 4 css3硬件加速(GPU加速)transform、opacity、filters这些动画不会引起回流重绘
HTTP篇
1 状态码
- [2开头的基本都是代表成功]
+200 OK 正常返回数据 - [3开头的一般也是成功了,只不过中间做了一些额外处理]
- 301 Moved Permanently 永久性转移/重定向 一般应用于网站域名更换,访问老域名,永久都跳转到新的域名上
- 302 Move Temporarily 临时转移
- 307 Temporary Redirect 临时重定向 一般应用于服务器的负载均衡
- 304 Not Modified 读取的是缓存中的数据 这个是客户端和服务器端共建的协商缓存(把不经常更新,请求过的资源文件做缓存,后期在访问这些资源直接走缓存数据,除非服务器端更新了此资源,或者客户端强制清缓存刷新等)
- [4开头的都是失败:失败的原因一般都是客户端的问题]
- 400 Bad Request 请求参数错误
- 401 Unauthorized 无权限访问
- 404 Not Found 地址错误
- 405 Method Not Allowed 当前请求的方式服务器不支持
- [5开头的都是失败:失败的原因一般都是服务器问题]
- 500 Internal Server Error 未知服务器错误
- 503 Service Unavailable 服务器超负荷
2 http缓存有哪几种
- 强制缓存
服务端设置响应头Cache-Control:max-age=xxx,并且设置Expires响应头过期时间,客户端自行判断是否读取缓存
通常我们对于强制缓存的设置是服务端告诉客户端你刚刚已经请求过一次了,我们约定好十分钟内你再过来请求都直接读取缓存吧,意思也就是当客户端在十分钟内多次请求的话只有第一次会下载页面内容,其他的请求都是直接走缓存,不管我们页面在这期间有没有变化都不会影响客户端读取缓存。 - 协商缓存
协商缓存 通过状态码304告诉客户端该走缓存
上面的强制缓存我们就发现了 就是我们平时改完bug上线要苦苦等待的一个原因了,那么有没有其他的好的缓存处理方法呢,我们设想一下 假如我们能够知道我们文件有没有修改,假如我们修改了服务器就返回最新的内容,假如没有修改 就一直默认缓存,通过文件最后修改时间来缓存,我们通过获取到文件状态来拿到文件的最后修改时间 也就是ctime 我们把这个时间通过响应头Last-Modified来告诉客户端,客户端再下一次请求的时候会通过请求头If-Modified-Since把这个值带给服务端,我们只要判断这两个值是否相等,假如相等那么也就是说 文件没有被修改那么我们就告诉客户端304 你直接读缓存吧 - 通过文件内容来缓存
3 get和post的区别
- 1 GET请求传递给服务器的信息有大小的限制2k post没有限制
- 2 GET请求相对POST请求来说不太安全,也是因为传参是基于地址栏问号传参,会被劫持
- 3 GET请求容易产生缓存,原因还是因为GET是基于问号传参传递信息的,浏览器在每一次获取数据后,一般会缓存一下数据,下一次如果请求的地址和参数和上一次一样,一般都添加随机数
4cookie、session、localstorage、sessionstorage
博客地址
用session去管理cookie
session是由cookie进行标记的。当需要记住用户时,比如前面说的登录,在服务端会设置一个响应头Set-Cookie,返回给客户端,例如:Set-Cookie:SESSIONID=12345678;客户端接收到这个响应后,此后发送的每一个请求浏览器都会自动带上Cookie请求头,对应内容是Cookie:SESSIONID=12345678。在服务端内存中存有session,将客户端发送的请求中的cookie值与内存中的session进行对比,就可以识别这个客户端了,也就能避免上图中那种尴尬的情况。
5 如何解决跨域的问题
1JSONP
2 CORS跨域资源共享
3 webpack-dev-serve
4 nginx反向代理
6 前端性能优化点
1. 减少HTTP请求的次数和大小
- 合并压缩 webpack(代码比较少的情况下,尽可能使用内嵌式)
- 雪碧图或者图片BASE64
- 尽量使用字体图标
- 对于动态获取的图片,采用图片懒加载(数据也做异步分批加载:开始只请求加载第一屏的数据,滑动到第几屏在加载这一屏的数据和图片)
- 骨架屏技术(首屏内容由服务器渲染;再或者开始展示占位结构,客户端在单独获取数据渲染;)
- 音视频取消预加载(播放的时候再去加载音视频文件,对于自动播放采取延迟播放的处理)
- 服务器采用GZIP压缩
- ....
2.建立缓存机制
把一些请求回来的信息进行本地存储(缓存存储),在缓存有效期内,再次请求资源,直接从缓存中获取数据,而不是服务器上从新拉取
- DNS预获取
- 资源文件的强缓存和协商缓存(304)
- 数据也可以做缓存(把从服务器获取的数据存储到本地:cookie/localStorage/redux/vuex等,设定期限,在期限内,直接从本地获取数据即可)
- 离线存储(一般很少用)manifest
- CDN区域分布式服务器开发部署(费钱 效果会非常的好)
- ....
3.代码上的优化
- 减少DOM的重绘和回流
- 在JS中尽量减少闭包的使用(内存优化)
- 在JS中避免“嵌套循环”和“死循环”
- 尽可能使用事件委托
- 尽量减少CSS表达式的使用(expression)
- CSS选择器解析规则是从右向左解析(基于less/sass开发的时候尽可能减少层级嵌套,目的是让选择器的前缀短一点) 【 a{} 和 .box a{}】
- 尽可能实现JS的封装(低耦合高内聚),减少页面中的冗余代码
- 在CSS导入的时候尽量减少使用@import导入式
- 使用window.requestAnimationFrame(JS中的帧动画)代替传统的定时器动画(能用CSS3动画的绝对不用JS动画)
- 减少递归的使用,避免死递归,避免由于递归导致的栈内存嵌套
- 基于SCRIPT调取JS的时候,可已使用 defer或者async 来异步加载
……
7 移动端适配
百分比和bootstrap的栅格系统
通过媒体查询
rem VM vh
8 图片懒加载
实现懒加载有四个步骤,如下:
1.加载loading图片
2.判断哪些图片要加载【重点】
3.隐形加载图片
4.替换真图片