面试题集锦

205 阅读11分钟

css部分

1.css盒模型

box-sizing: content-box(W3C盒模型,又名标准盒模型):元素的宽高大小表现为内容的大小。

box-sizing: border-box(IE盒模型,又名怪异盒模型):元素的宽高表现为内容 + 内边距 + 边框的大小。

2.清除浮动的几种方式

  • 在浮动元素后面添加 clear:both 的空 div 元素
	<div class="container">
	    <div class="left"></div>
	    <div class="right"></div>
	    <div style="clear:both"></div>
	</div>
  • 给父元素添加 overflow:hidden 或者 auto 样式,触发BFC。
	<div class="container">
	    <div class="left"></div>
	    <div class="right"></div>
	</div> 
	.container{
	    width: 300px;
	    background-color: #aaa;
	    overflow:hidden;
	    zoom:1;   /*IE6*/
	}
  • 使用伪元素,也是在元素末尾添加一个点并带有 clear: both 属性的元素实现的。
	<div class="container clearfix">
	    <div class="left"></div>
	    <div class="right"></div>
	</div>
	.clearfix:after{
	    content: ".";
	    height: 0;
	    clear: both;
	    display: block;
	    visibility: hidden;
	}

推荐使用第三种方法,不会在页面新增div,文档结构更加清晰。

3.移动端1px解决方案

  • 伪元素+scale
<style>
    .box{
        width: 100%;
        height: 1px;
        margin: 20px 0;
        position: relative;
    }
    .box::after{
        content: '';
        position: absolute;
        bottom: 0;
        width: 100%;
        height: 1px;
        transform: scaleY(0.5);
        transform-origin: 0 0; 
        background: red;
    }
</style>

<div class="box"></div>
  • border-image
div{
    border-width: 1px 0px;
    -webkit-border-image: url(border.png) 2 0 stretch;
    border-image: url(border.png) 2 0 stretch;
}

4.两边宽度固定中间自适应的三栏布局

  • 圣杯布局
<style>
	body{
	    min-width: 550px;
	}
	#container{
	    padding-left: 200px;
	    padding-right: 150px;
	}
	#container .column{
	    float: left;
	}
	#center{
	    width: 100%;
	}
	#left{
	    width: 200px;
	    margin-left: -100%;
	    position: relative;
	    right: 200px;
	}
	#right{
	    width: 150px;
	    margin-right: -150px;
	}
</style>
<div id="container">
    <div id="center" class="column">center</div>
    <div id="left" class="column">left</div>
    <div id="right" class="column">right</div>
</div>

  • 双飞翼布局
<style>
	body {
	    min-width: 500px;
	}
	#container {
	    width: 100%;
	}
	.column {
	    float: left;
	}
	#center {
	    margin-left: 200px;
	    margin-right: 150px;
	}
	#left {
	    width: 200px;
	    margin-left: -100%;
	}
	#right {
	    width: 150px;
	    margin-left: -150px;
	}
</style>
<div id="container" class="column">
    <div id="center">center</div>
</div>
<div id="left" class="column">left</div>
<div id="right" class="column">right</div>

5.不定宽高的元素怎么水平垂直居中

1.最简单的flex布局,外层容器加上如下样式即可

display:-webkit-flex;
justify-content:center; /*水平居中*/
align-items:center; /*垂直居中*/

2.利用table-cell

外层容器:
display:table-cell;
text-align:center;
vertical-align:middle;
内层元素
display:inline-block;
vertical-align:middle;

3.CSS3 transform

外层容器:
position:relative
内层元素
transform: translate(-50%,-50%);
position: absolute;
top: 50%;
left: 50%;	

6.左边固定宽度,右边自适应怎么布局?

1.使用flex

.outbox {
	width: 100%;
	height: 100%;
	display: flex;
}
.left {
	width: 100px;
	height: 100%;
	background: red;
}
.right {
	flex: 0 0 0 100px;
	width: 100%;
	height: 100%;
	background: green;
}		 	   

2.使用定位

.outbox {
	width: 100%;
	height: 100%;
	position: relative;
}
.left {
	width: 100px;
	height: 100%;
	background: red;
}
.right {
	height: 100%;
	position: absolute;
	top:0;
	right: 0;
	left: 100px;
	background: green;
} 	

7.元素隐藏的几种方法,有啥区别

  1. opacity=0,该元素隐藏起来了,但不会改变页面布局,并且,如果该元素已经绑定一些事件,如click事件,那么点击该区域,也能触发点击事件的
  2. visibility=hidden,该元素隐藏起来了,但不会改变页面布局,但是不会触发该元素已经绑定的事件
  3. display=none,把元素隐藏起来,并且会改变页面布局,可以理解成在页面中把该元素删除掉一样。
  4. 使用定位 定位数值写的特别大 让飞出页面外 比如:left:-9999px;
  5. z-index=-1置于其他元素下面

js部分

1.js中基本的数据类型有几种

七种。分别是number、string、boolean、undefined、null、symbol(es6)、bigint(es10)-任 意精度整数

判断数据类型的方法

1)typeof:返回一个表示数据类型的字符串

优点:能够快速区分基本数据类型 缺点:不能将Object、Array和Null区分,都返回object

typeof Symbol();//symbol 有效
typeof '';//string 有效
typeof 1;//number 有效
typeof true;//boolean 有效
typeof undefined;//undefined 有效
typeof new Function();// function 有效
typeof null;//object 无效
typeof [];//object 无效
typeof {};//object无效
typeof new Date();//object 无效
typeof new RegExp();//object 无效

2)instanceof:只适用于对象、不能检测基本类型

instanceof 是用来判断A是否为B的实例,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性,但它不能检测null 和 undefined

优点:能够区分Array、Object和Function,适合用于判断自定义的类实例对象 缺点:Number,Boolean,String基本数据类型不能判断

2 instanceof Number;        // false
true instanceof Boolean;    // false 
'str' instanceof String;    // false  
[] instanceof Array;        // true
function(){} instanceof Function; // true
{} instanceof Object;  // true
new Date() instanceof Date; //true
new RegExp() instanceof RegExp; //true
null instanceof Null //报错
undefined instanceof undefined //报错
  1. Object.prototype.toString.call() 是最准确最常用的方式

Object.prototype.toString.call().slice(8,-1); 得到准确的类型

优点:精准判断数据类型 缺点:写法繁琐不容易记,推荐进行封装后使用

Object.prototype.toString.call('');// [object String]
Object.prototype.toString.call(1);// [object Number]
Object.prototype.toString.call(true);// [object Boolean]
Object.prototype.toString.call(undefined);// [object Undefined]
Object.prototype.toString.call(null);// [object Null]
Object.prototype.toString.call(new Function());// [object Function]
Object.prototype.toString.call(new Date());// [object Date]
Object.prototype.toString.call([]);// [object Array]
Object.prototype.toString.call(new RegExp());// [object RegExp]
Object.prototype.toString.call(new Error());// [object Error]
  1. constructor

constructor作用和instanceof非常相似。但constructor检测 Object与instanceof不一样,还可以处理基本数据类型的检测。不过函数的 constructor 是不稳定的,这个主要体现在把类的原型进行重写,在重写的过程中很有可能出现把之前的constructor给覆盖了,这样检测出来的结果就是不准确的。

''.constructor === String;//true
[].constructor === Array;//true

2. 变量声明

函数声明优先于变量声明

变量提升只提升函数声明,并不提升函数表达式,所以函数声明可以放在任何地方被调用,函数表达式要在定义后进行使用。

var a; 
function a(){}
console.log(typeof a); //function

或许有人是认为函数声明在后面的原因,那么调换一下位置。

function a(){}
var a; 
console.log(typeof a); //function

调换位置后变量a的类型还是function,这时候声明变量a的语句没有起作用,被函数声明覆盖了。因此函数声明优先于变量的声明。

但是如果我们在声明的同时给a进行赋值。

function a(){}
var a='xyf'; 
console.log(typeof a); //string

我们将其调换一下位置再次进行验证。

var a='xyf'; 
function a(){}
console.log(typeof a); //string

可以看到,给变量a进行赋值后,不管变量a在哪,其类型变为字符串类型,上面两段代码相当于如下代码:

function a(){}
var a;
a='xyf';
console.log(typeof a); //string

3.==和===区别

  • ==, 两边值类型不同的时候,要先进行类型转换,再比较
  • ===,不做类型转换,类型不同的一定不等。

==类型转换过程:

1)如果类型不同,进行类型转换

2)判断比较的是否是 null 或者是 undefined, 如果是, 返回 true .

3)判断两者类型是否为 string 和 number, 如果是, 将字符串转换成 number

4)判断其中一方是否为 boolean, 如果是, 将 boolean 转为 number 再进行判断

5)判断其中一方是否为 object 且另一方为 string、number 或者 symbol , 如果是, 将 object 转为原始类型再进行判断

4.js中浮点数精度问题(0.1+0.2!=0.3怎么处理)

最近在做项目的时候,涉及到商品价格的计算,经常会出现计算出现精度问题。

toFixed奇葩问题

在遇到浮点数运算后出现的精度问题时,刚开始我是使用toFixed(2)来解决的。但是在chrome下测试结果不太令人满意:

1.35.toFixed(1) // 1.4 正确
1.335.toFixed(2) // 1.33  错误
1.3335.toFixed(3) // 1.333 错误
1.33335.toFixed(4) // 1.3334 正确
1.333335.toFixed(5)  // 1.33333 错误
1.3333335.toFixed(6) // 1.333333 错误

使用IETester在IE下面测试的结果却是正确的。

把需要计算的数字升级(乘以10的n次幂)成计算机能够精确识别的整数,等计算完成后再进行降级(除以10的n次幂),即:

(0.1*10 + 0.2*10)/10 == 0.3 //true

5.深拷贝、浅拷贝

参考链接:www.cnblogs.com/echolun/p/7…

深拷贝的实现方法


  1. 使用递归去复制所有层级属性
function deepClone(obj) {
    let result;
    if (typeof obj == 'object') {
        result = isArray(obj) ? [] : {}
        for (let i in obj) {
            //isObject(obj[i]) ? deepClone(obj[i]) : obj[i]
            //多谢"朝歌在掘金"指出,多维数组会有问题
            result[i] = isObject(obj[i])||isArray(obj[i])?deepClone(obj[i]):obj[i]
        }
    } else {
        result = obj
    }
    return result
}
function isObject(obj) {
    return Object.prototype.toString.call(obj) == "[object Object]"
}
function isArray(obj) {
    return Object.prototype.toString.call(obj) == "[object Array]"
}
  1. JSON对象的parse和stringify
  1. JQ的extend方法
$.extend( [deep ], target, object1 [, objectN ] )

deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝

target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。

object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。
  1. 热门的函数库lodash,也有提供_.cloneDeep用来做深拷贝

不完全拷贝(只能拷贝一级属性、二级属性没拷贝成功)的方法


  1. slice() 针对数组
  2. concat() 针对数组

浅拷贝方法:


object.assign() 针对对象

6.给ul下面的li加上点击事件,点击哪个li,就显示那个li的innerHTML

考点:事件委托

var oUl=document.getElementById("ul-test");
oUl.addEventListener("click",function(ev){
	var ev=ev||window.event;
	var target=ev.target||ev.srcElement;
	//如果点击的最底层是li元素
	if(target.tagName.toLowerCase()==='li'){
		alert(target.innerHTML)
	}
}) 

如果是将事件绑定在每一个li标签上,会有什么问题?

var oUl=document.getElementById("ul-test");
var oLi=oUl.getElementsByTagName("li");
for(var i=0,len=oLi.length;i<len;i++){
	oLi[i].addEventListener("click",function(){
		alert(this.innerHTML)
	})
}

1.for循环,循环的是li,10个li就循环10次,绑定10次事件,100个就循环了100次,绑定100次事件!

2.如果li不是本来就在页面上的,是未来元素,是页面加载了,再通过js动态加载进来了,上面的写法是无效的,点击li是没有反应的!

7.比如有一个需求,往ul里面添加10个li,如下代码

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul id="ul-test">

</ul>
</body>
<script type="text/javascript">
	var oUl=document.getElementById("ul-test");
	for(var i=0;i<10;i++){
		var oLi=document.createElement('li');
		oLi.innerHTML=i;
		oUl.appendChild(oLi);
	} 
</script>
</html>

问题:这里相当于操作了10次DOM,有什么方案,减少DOM的操作次数?

  • innerHtml
var oUl=document.getElementById("ul-test");
//定义临时变量
var _html='';
for(var i=0;i<10;i++){
	//保存临时变量
	_html+='<li>'+i+'</li>'
}
//添加元素
oUl.innerHTML=_html;

  • 文档碎片
var oUl=document.getElementById("ul-test"),
_frag = document.createDocumentFragment();
for(var i=0;i<10;i++){
	var oLi=document.createElement('li');
	oLi.innerHTML=i;
	//把元素添加进文档碎片
	_frag.appendChild(oLi);
}
//把文档碎片添加进元素
oUl.appendChild(_frag);

8.跨域

什么是跨域:浏览器有同源策略,不允许ajax访问其他域的接口。协议、域名、端口有一个不同就算跨域

但是有三个标签允许跨域 link script img

怎么解决跨域:

  1. jsonp:即json+padding,动态创建script标签,利用script标签的src属性可以获取任何域下的js脚本,通过这个特性(也可以说漏洞),服务器端不在返回json格式,而是返回一段调用某个函数的js代码,在src中进行了调用,这样实现了跨域.
<script>
    window.callback= function (data) {
        //这里是跨域得到的信息
        console.log(data)
    }
</script>
<script src="xxx.js"></script>
//以上将返回格式为callback({x:100,y:100})
<script>    
	function createJs(sUrl){        
		var oScript = document.createElement('script');        
		oScript.type = 'text/javascript';        
		oScript.src = sUrl;        
		document.getElementsByTagName('head')[0].appendChild(oScript); 
	}    
	createJs('jsonp.js');
	function box(json){alert(json.name);}
	box({'name': 'test'});   
</script>

参考链接🔗:blog.csdn.net/hansexplora…

  1. webpack设置代理 proxyTable属性
  2. 服务器代理

9.this的指向问题

箭头函数:指向函数所在的作用域:注意理解作用域,只有函数的{}构成作用域,对象的{}以及 if(){}都不构成作用域;

10.继承的几种方式

如何理解原型跟原型链??

1)原型链继承:让新实例的原型等于父类的实例。可以继承构造函数里面的属性和方法 也可以继承原型链上面的属性和方法

特点:实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(新实例不会继承父类实例的属性!)

缺点:

  1. 新实例无法向父类构造函数传参。
  2. 继承单一。
  3. 所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)

缺点1代码举例:

function Person(name){
	this.name = name;
	this.run = function(){ 
		alert(this.name+'在运动');
	}
}

Person.prototype.age = 10;
Person.prototype.work = function(){
	alert(this.name+'在工作');
}

//web类继承Person类 原型链继承模式
function Web(name){}
Web.prototype = new Person();

var w = new Web('李四');//实例化子类的时候没办法给父类传承
w.run();//undefined在运动
w.work();//undefined在工作

缺点3代码举例:

function Person() {
  this.name = '张三';
  this.colors = ['red','blue'];
  this.run = function () {
    console.log(this.name + '在运动');
  }
}

Person.prototype.work = function () {
  console.log(this.name + '在工作');
};

//web类继承Person类 原型链继承模式
function Web() {}
Web.prototype = new Person();

var w = new Web();
var w1 = new Web();
w.colors.push('yellow');//一个实例修改了原型属性,另一个实例的原型属性也会被修改!
w.run();//张三在运动
w.work();//张三在工作
console.log(w.colors);//['red','blue','yellow']
w1.run();//张三在运动
w1.work();//张三在工作
console.log(w1.colors);//['red','blue','yellow']

console.log(Web instanceof Person);//true

2)构造函数继承:用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制),可以继承构造函数里面的属性和方法,但是没法继承原型链上面的属性和方法

特点:

  1. 只继承了父类构造函数的属性,没有继承父类原型的属性。
  2. 解决了原型链继承缺点1、2、3。
  3. 可以继承多个构造函数属性(call多个)。
  4. 在子实例中可向父实例传参。

缺点:

  1. 只能继承父类构造函数的属性。
  2. 无法实现构造函数的复用。(每次用每次都要重新调用)
  3. 每个新实例都有父类构造函数的副本,臃肿。
function Person(name) {
    this.name = name;
    this.colors = ['red','blue'];
    this.run = function () {
      console.log(this.name + '在运动');
    }
  }

  Person.prototype.work = function () {
    console.log(this.name + '在工作');
  };

  //web类继承Person类 构造函数继承模式
  function Web(name) {
    Person.call(this, name);
  }

  var w = new Web('张三');
  var w1 = new Web('李四');
  w.colors.push('yellow');

  w.run();//张三在运动
  w.work();//报错 构造函数继承没法继承原型链上面的属性和方法
  console.log(w.colors);//['red','blue','yellow']
  w1.run();//李四在运动
  w1.work();//报错 构造函数继承没法继承原型链上面的属性和方法
  console.log(w1.colors);//['red','blue']
  
  console.log(w instanceof Person); //false

3)组合继承:常用,组合原型链继承和借用构造函数继承

特点:

  1. 使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承。
  2. 可以继承父类原型上的属性,可以传参,可复用。
  3. 每个新实例引入的构造函数属性是私有的

缺点:调用了两次父类构造函数,一次是在创建子类型原型的时候,另一次是在子类型构造函数内部,(消耗内存),子类的构造函数会代替原型上的那个父类构造函数。

function Person(name) {
    this.name = name;
    this.colors = ['red','blue'];
    this.run = function () {
      console.log(this.name + '在运动');
    }
  }

  Person.prototype.work = function () {
    console.log(this.name + '在工作');
  };

  //web类继承Person类 组合继承模式
  function Web(name) {
    Person.call(this, name); //第二次调用 Person() 子类的构造函数会代替原型上的那个父类构造函数。
  }
  Web.prototype = new Person(); //第一次调用 Person()

  var w = new Web('张三');
  var w1 = new Web('李四');
  w.colors.push('yellow');
  w.run();//张三在运动
  w.work();//张三在工作
  console.log(w.colors);//['red','blue','yellow']
  w1.run();//李四在运动
  w1.work();//李四在工作
  console.log(w1.colors);//['red','blue']

4)原型式继承:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。

特点:类似于复制一个对象,用函数来包装。

缺点:

  1. 所有实例都会继承原型上的属性。包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。
  2. 无法实现复用。(新实例属性都是后面添加的)
function createObj(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

var person = {
  name: '111',
  friend: ['aaa', 'bbb']
};

var person1 = createObj(person);
person1.name = '222';
person1.friend.push('ccc');
console.log(person1.name); //222
console.log(person1.friend); //['aaa','bbb','ccc']

var person2 = createObj(person);
person2.name = '333';
console.log(person2.name); //333
console.log(person2.friend); //['aaa','bbb','ccc']

console.log(person.name); //111
console.log(person.friend); //['aaa','bbb','ccc']

5)寄生式继承:寄生继承的思想是创建一个用于封装继承过程的函数,该函数在内部以某种方式来增强对象。最后返回对象。可以理解为在原型式继承的基础上新增一些函数或属性

缺点:没用到原型,无法复用。

function createObj(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

var person = {
  name: '111',
  friend: ['aaa', 'bbb']
};

var person1 = createObj(person);
console.log(person1.name); //111

function createOb(o) {
  var newObj = createObj(o);
  newObj.sayName = function () {// 增强对象
    console.log(this.name);
  };
  return newObj// 指定对象
}

var person2 = createOb(person1);
person2.sayName();//111

6)寄生组合继承:(常用)子类构造函数复制父类的自身属性和方法,子类原型只接收父类的原型属性和方法。

所谓寄生组合继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。

其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型的原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给予类型的原型。

优点: 这种方式的高效率体现它只调用了一次Parent构造函数,并且因此避免了再Parent.prototype上面创建不必要的,多余的属性。普遍认为寄生组合式继承是引用类型最理想的继承方式

function Parent(name) {
    this.name = name;
    this.colors = ['red','blue'];
    this.run = function () {
      console.log(this.name + '在运动');
    }
  }

  Parent.prototype.work = function () {
    console.log(this.name + '在工作');
  };

  //web类继承Person类 原型链继承模式
  function Child(name) {
    Parent.call(this, name);
  }

  function createObj(o) {
    function F() {}
    F.prototype = o;
    return new F();
  }

  // Child.prototype = new Parent(); // 这里换成下面
  function prototype(child,parent) {
    var prototype = createObj(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype
  }
  prototype(Child,Parent);

  var child = new Child('张三');
  console.log(child.name); //张三       

11.函数节流、函数防抖

函数防抖:如果一个事件被频繁触发多次,并且触发的时间间隔过短,则防抖函数可以使得对应的事件处理函数只执行最后触发的一次。函数防抖可以把多个顺序的调用合并成一次。

function debounce(fn, delay, scope) {
    let timer = null;
    // 返回函数对debounce作用域形成闭包
    return function () {
        // setTimeout()中用到函数环境总是window,故需要当前环境的副本;
        let context = scope || this, args = arguments;
        // 如果事件被触发,清除timer并重新开始计时
        clearTimeout(timer);
        timer = setTimeout(function () {
            fn.apply(context, args);
        }, delay);
    }
}

函数节流:如果一个事件被频繁触发多次,节流函数可以按照固定频率去执行对应的事件处理方法。函数节流保证一个事件一定时间内只执行一次。

# 利用时间戳简单实现(先执行目标函数,后等待规定的时间段;)
function throttle(fn, threshold, scope) {
    let timer;
    let prev = 0;
    return function () {
        let context = scope || this, args = arguments;
        let now = Date.now();
        if (now - prev > threshold) {
            fn.apply(context, args);
            prev = now;
        }
    }
}

# 利用定时器简单实现(先等待够规定时间,再执行)
function throttle2(fn, threshold, scope) {
    let timer;
    return function () {
        let context = scope || this, args = arguments;
        if (!timer) {
            timer = setTimeout(function () {
                fn.apply(context, args);
                timer = null;
            }, threshold);
        }
    }
}

# 二者结合(实现一次触发,两次执行(先立即执行,结尾也有执行))
function throttle(fn, cycle) {
  let start = Date.now();
  let now;
  let timer;
  return function () {
    now = Date.now();
    clearTimeout(timer);
    if (now - start >= cycle) {
      fn.apply(this, arguments);
      start = now;
    } else {
      timer = setTimeout(() => {
        fn.apply(this, arguments);
      }, cycle);
    }
  }
}

vue部分

1.delete和Vue.delete删除数组的区别

delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。 Vue.delete直接删除了数组 改变了数组的键值。

var a=[1,2,3,4]
var b=[1,2,3,4]
delete a[1]
console.log(a) //[1,empty,3,4]
this.$delete(b,1)
console.log(b)  //[1,3,4]

2.vue组件间通信六种方式

参考链接🔗: mp.weixin.qq.com/s/xAsGUGOl0…

  1. props / emit  
父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed  
子组件通过emit给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。

  2. emit/on
    这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。

3). vuex

4). attrs /listeners
attrs与listeners 是两个对象, attrs 里存放的是父组件中绑定的非 Props 属性,listeners里存放的是父组件中绑定的非原生事件。

5). provide/inject
Vue2.2.0新增API,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。一言而蔽之:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的

6). parent /children & ref
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
parent /children:访问父 / 子实例
不过,这两种方法的弊端是,无法在跨级或兄弟间通信。

#####常见使用场景可以分为三类:

父子通信: 父向子传递数据是通过 props,子向父是通过 events( emit);通过父链 / 子链也可以通信( parent / children);ref 也可以访问组件实例;provide / inject API; attrs/listeners

兄弟通信: Bus;Vuex

跨级通信: Bus;Vuex;provide / inject API、 attrs/listeners

3.对vue生命周期的理解?

参考链接🔗: segmentfault.com/a/119000001…

总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。

创建前/后: 在beforeCreated阶段,vue实例的挂载元素el和数据对象data都为undefined,还未初始化。在created阶段,计算属性已经完成了,vue实例的数据对象data有了,el还没有。

载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。

更新前/后:当data变化时,会触发beforeUpdate和updated方法。

销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在

其他部分

1.浅谈网站性能之前端性能优化

参考链接🔗: segmentfault.com/a/119000000…
1)减少 HTTP 请求数量

  1. CSS Sprites : 将多张图片合并成一张图片达到减少 HTTP 请求的一种解决方案,可以通过 CSS background 属性来访问图片内容。
  2. 合并 CSS 和 JS 文件:现在前端有很多工程化打包工具,如:grunt、gulp、webpack等。为了减少 HTTP 请求数量,可以通过这些工具再发布前将多个 CSS 或者 多个 JS 合并成一个文件。
  3. 采用 lazyLoad:俗称懒加载,可以控制网页上的内容在一开始无需加载,不需要发请求,等到用户操作真正需要的时候立即加载出内容。这样就控制了网页资源一次性请求数量。

2)控制资源文件加载优先级
主要文件放在 head 内部,次要文件放在 body 底部。一般情况下都是 CSS 在头部,JS 在底部。
3)利用浏览器缓存
4)使用CDN
5)减少重排(Reflow):如果需要在 DOM 操作时添加样式,尽量使用 增加 class 属性,而不是通过 style 操作样式。
6)减少 DOM 操作
7)图标使用 IconFont 替换