前端基础面试题


第一章 面试题基础篇

1.1 HTML面试题
面试题:行内元素有哪些?块级元素有哪些? 空(void)元素有哪些?
行内元素:span、img、input...
块级元素:div、footer、header、section、p、h1...h6...
空元素:br、hr...


元素之间的转换问题:
display: inline;  			把某元素转换成了行内元素     	  ===>不独占一行的,并且不能设置宽高
display: inline-block; 		把某元素转换成了行内块元素		 ===>不独占一行的,可以设置宽高
display: block;				把某元素转换成了块元素			  ===>独占一行,并且可以设置宽高
面试题:页面导入样式时,使用link和@import有什么区别?
1.先出现的link,再出现的@import
2.link是html的标签,@import是css的语法规则
3.加载页面时,link引入的css同时加载,页面加载完成后才加载@import
4.link比@import兼容性好
面试题:title与h1的区别、b与strong的区别、i与em的区别?
title与h1的区别:

定义:
    title:概括了网站信息,可以告诉搜索引擎或者用户关于这个网站的内容主题是什么
    h1:文章主题内容,告诉蜘蛛我们的网站内容最主要是什么
区别:
    title他是显示在网页标题上、h1是显示在网页内容上
    title比h1添加的重要 (title > h1 ) ==》对于seo的了解
场景:
    网站的logo都是用h1标签包裹的	
b与strong的区别:

定义:
    b:实体标签,用来给文字加粗的。
    strong:逻辑标签,用来加强字符语气的。
区别:
    b标签只有加粗的样式,没有实际含义。
    strong表示标签内字符比较重要,用以强调的。
题外话:为了符合css3的规范,b尽量少用该用strong就行了。
i与em的区别:

定义:
    i:实体标签,用来做文字倾斜的。
    em:是逻辑标签,用来强调文字内容的
区别:
    i只是一个倾斜标签,没有实际含义。
    em表示标签内字符重要,用以强调的。
场景:
    i更多的用在字体图标,em术语上(医药,生物)。
面试题:img标签的title和alt有什么区别?
区别一:
    title : 鼠标移入到图片显示的值
    alt   : 图片无法加载时显示的值
区别二:
    在seo的层面上,蜘蛛抓取不到图片的内容,所以前端在写img标签的时候为了增加seo效果要加入alt属性来描述这张图是什么内容或者关键词。
面试题:png、jpg、gif 这些图片格式解释一下,分别什么时候用?
png:无损压缩,尺寸体积要比jpg/jpeg的大,适合做小图标。
jpg:采用压缩算法,有一点失真,比png体积要小,适合做中大图片。
gif:一般是做动图的。
webp:同时支持有损或者无损压缩,相同质量的图片,webp具有更小的体积。兼容性不是特别好。
1.2 CSS面试题
面试题:介绍一下CSS的盒子模型
CSS的盒子模型有哪些:标准盒子模型、IE盒子模型
CSS的盒子模型区别:
    标准盒子模型:margin、border、padding、content
    IE盒子模型 :margin、content( border +  padding  + content )
通过CSS如何转换盒子模型:
    box-sizing: content-box;	/*标准盒子模型*/
    box-sizing: border-box;	  /*IE盒子模型*/
面试题:line-height和height区别【大厂】
line-height是每一行文字的高,如果文字换行则整个盒子高度会增大(行数*行高)。
height是一个死值,就是这个盒子的高度。
面试题:CSS选择符有哪些?哪些属性可以继承?
CSS选择符:
    通配(*)
    id选择器(#)
    类选择器(.)
    标签选择器(div、p、h1...相邻选择器(+)
    后代选择器(ul li)
    子元素选择器( >属性选择器(a[href])
    
CSS属性哪些可以继承:
      文字系列:font-size、color、line-height、text-align...
***不可继承属性:border、padding、margin...
面试题:CSS优先级算法如何计算?
优先级比较:!important > 内联样式 > id > class > 标签 > 通配
CSS权重计算:
第一:内联样式(style)  权重值:1000
第二:id选择器  				 权重值:100
第三:类选择器 				  权重值:10
第四:标签&伪元素选择器   权重值:1
第五:通配、>+         权重值:0
面试题:用CSS画一个三角形
用边框画(border),例如:
{
        width: 0;
        height: 0;

        border-left:100px solid transparent;
        border-right:100px solid transparent;
        border-top:100px solid transparent;
        border-bottom:100px solid #ccc;
}
/* 对齐方式 */
justify-content: center;     /* 居中排列 */
justify-content: start;      /* 从行首开始排列 */
justify-content: end;        /* 从行尾开始排列 */
justify-content: flex-start; /* 从行首起始位置开始排列 */
justify-content: flex-end;   /* 从行尾位置开始排列 */
justify-content: left;       /* 一个挨一个在对齐容器得左边缘 */
justify-content: right;      /* 元素以容器右边缘为基准,一个挨着一个对齐, */

/* 分配弹性元素方式 */
justify-content: space-between;  /* 均匀排列每个元素
                                   首个元素放置于起点,末尾元素放置于终点 */
justify-content: space-around;  /* 均匀排列每个元素
                                   每个元素周围分配相同的空间 */
面试题:一个盒子不给宽度和高度如何水平垂直居中?

方式一:flex布局

<div class='container'>
    <div class='main'>main</div>
</div>

.container{
        display: flex;
        justify-content: center; /* 居中排列 */
        align-items: center; /*高度居中*/
        width: 300px;
        height: 300px;
        border:5px solid #ccc;
}
.main{
        background: red;
}

方式二:相对定位、绝对定位

<div class='container'>
    <div class='main'>main</div>
</div>

.container{
        position: relative;
        width: 300px;
        height: 300px;
        border:5px solid #ccc;
}
.main{
        position: absolute;
        left:50%;
        top:50%;
        background: red;
        transform: translate(-50%,-50%);
}
面试题:display有哪些值?说明他们的作用。
none     			隐藏元素
block    			把某某元素转换成块元素
inline   			把某某元素转换成内联元素
inline-block 		把某某元素转换成行内块元素
面试题:对BFC规范(块级格式化上下文:block formatting context)的理解?
BFC就是页面上一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。

举例:ul-->li(float)-->高度塌陷

1. 了解BFC : 块级格式化上下文。
2. BFC的原则:如果一个元素具有BFC,那么内部元素再怎么弄,都不会影响到外面的元素。
3. 如何触发BFC:在父元素加上
        float的值非none
        overflow的值非visible,可以为hidden
        display的值为:inline-block、table-cell...
        position的值为:absoute、fixed
面试题:清除浮动有哪些方式?
1. 触发BFC
2. 多创建一个盒子,添加样式:clear: both;
3. after方式
    ul:after{
            content: '';
            display: block;
            clear: both;
    }
面试题:在网页中的应该使用奇数还是偶数的字体?为什么呢?
偶数 : 让文字在浏览器上表现更好看。

另外说明:ui给前端一般设计图都是偶数的,这样不管是布局也好,转换px也好,方便一点。
面试题:position有哪些值?分别是根据什么定位的?
static [默认]  没有定位
fixed  固定定位,相对于浏览器窗口进行定位。
relative  相对于自身定位,不脱离文档流。
absolute	相对于第一个有relative的父元素,脱离文档流。


relative和absolute区别
1. relative不脱离文档流 、absolute脱离文档流
2. relative相对于自身 、 absolute相对于第一个有relative的父元素
3. relative如果有left、right、top、bottom ==》left、top
     absolute如果有left、right、top、bottom ==》left、right、top、bottom
面试题:写一个左中右布局占满屏幕,其中左、右俩块固定宽200,中间自适应宽,要求先加载中间块,请写出结构及样式。
双飞翼
先写中的div,再写左右的div盒子,父元素浮动布局,左右div设置margin-left:-200px
面试题:什么是CSS reset?
reset.css   		是一个css文件,用来重置css样式的。
normalize.css 		为了增强跨浏览器渲染的一致性,一个CSS 重置样式库。
面试题:css sprite是什么,有什么优缺点
1. 是什么
    把多个小图标合并成一张大图片。
2. 优缺点
    优点:减少了http请求的次数,提升了性能。
    缺点:维护比较差(例如图片位置进行修改或者内容宽高修改)
面试题:重绘和回流display: none;与visibility: hidden;的区别
1. 占用位置的区别
display: none; 				隐藏了,不占用位置的
visibility: hidden;   		虽然隐藏了,但是占用位置

display: none; 				隐藏了,不占用位置的
DOM解析标签,CSS解析样式,但是没有进入下一步render合二为一,等全部流程完成,再进行render流程(回流),所以没有占用位置

visibility: hidden;   		虽然隐藏了,但是占用位置
解析DOM标签和CSS样式-->执行完整个流程-->到展示页面,标签还是存在的,所以占用位置

2. 重绘和回流的问题-->浏览器渲染机制相关

visibility: hidden; 、 display: none;  产生重绘
display: none;     还会产生一次回流

产生回流一定会造成重绘,但是重绘不一定会造成回流。

产生回流的情况:改变元素的位置(left、top...)、显示隐藏元素....
产生重绘的情况:样式改变、换皮肤、布局改变
重绘?
当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

回流?
当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建render tree。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。

区别:
回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流
当页面布局和几何属性改变时就需要回流
比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变
面试题:输入URL发生了什么?
输入URL-->浏览器发送网络请求-->服务器响应,发送数据-->HTML解析数据(分为js解析器,dom解析器,css解析器)解析DOM树和CSSOM树->合并进入render树-->确立位置(没内容),布局layout-->绘制paint(渲染)-->合并展示
面试题:opacity 和 rgba区别
共同性:实现透明效果

1. opacity 取值范围01之间,0表示完全透明,1表示不透明
2. rgba   R表示红色,G表示绿色,B表示蓝色,取值可以在正整数或者百分数。A表示透明度取值01之间

区别:继承的区别
opacity会继承父元素的opacity属性,而RGBA设置的元素的后代元素不会继承不透明属性。
1.3 JavaScript基础面试题
面试题:延迟加载JS有哪些方式?
延迟加载:async、defer、js标签写在body的最后面
        例如:<script defer type="text/javascript" src='script.js'></script>
        
defer : 等html全部解析完成,开始渲染页面的同时,才会执行js代码,顺次执行js脚本。
async : async是和html解析同步的(一起的),不是顺次执行js脚本(谁先加载完谁先执行)。
        js的引用顺序无法控制,引用的js存在依赖关系绝对不能用async
区  别 :多个js的执行是否有序进行,defer有序,async无序
img

<script>

img

<script async>

img

<script defer>

img

面试题:JS数据类型有哪些?
基本类型(栈):string、number、boolean、undefined、null、symbol(ES6)、bigint(google)
引用类型(堆):object 、array、function(函数、数组)

NaN是一个数值类型,但是不是一个具体的数字。

栈 保存基本数据类型,保存地址
堆 保存引用数据类型,引用栈保存的地址,拿到数据
面试题:JS数据类型考题

考题一:

console.log( true + 1 );     		//2
console.log( 'name'+true );  		//nametrue
console.log( undefined + 1 ); 		//NaN
console.log( typeof undefined ); 	//undefined

考题二:

console.log( typeof(NaN) );       //number
console.log( typeof(null) );      //object
console.log( typeof(undefined) ); //undefined
面试题:null和undefined的区别
1. 作者在设计js的都是先设计的null(为什么设计了null:最初设计js的时候借鉴了java的语言)
2. null会被隐式转换成0,很不容易发现错误。
3. 先有null后有undefined,出来undefined是为了填补之前的坑。

null和undefined都表示“无”,取反输出都是true

具体区别:JavaScript的最初版本是这样区分的:
null是一个表示"无"的对象(空对象指针),转为数值时为0。
undefined是一个表示"无"的基本数据类型、原始值,转为数值时为NaN
console.log( typeof null )          	//object
console.log( typeof undefined )			//undefined

console.log( null == undefined ) 		//true
console.log( null === undefined ) 		//false
console.log( !!null === !!undefined )	//true  为什莫?

console.log(null)       //null
console.log(!null)		//true
console.log(!!null)		//false

console.log(undefined)	//undefined
console.log(!undefined) //true
console.log(!!undefined)//false
面试题:==和===有什么不同?
==  :  比较的是值
        
        string == number || boolean || number ....都会隐式转换
        通过valueOf转换(valueOf() 方法通常由 JavaScript 在后台自动调用,并不显式地出现在代码中。)

=== : 除了比较值,还比较类型
面试题:JS微任务和宏任务、事件循环
说一下事件循环?

事件循环中包括同步任务和异步任务,异步任务又包括微任务和宏任务。这些任务的执行顺序就是事件循环。具体来说就是,任务进入执行栈,判断同步任务还是异步任务,在此分为两个分支,同步任务进入主线程执行完毕;异步任务进入事件列表区分宏任务和微任务,接下来注册回调函数,放入执行队列;以上同步异步操作处理完毕后,再次读取队列结果,进入主线程执行队列任务。如果有新任务,循环以上执行过程。

微任务:promise.then .catch
宏任务:setTimeout定时器、事件、<script> 、ajax
先执行微任务,再执行宏任务
单线程:每一次只能完成一个任务,浏览器是多线程,js是单线程语言
    例如:修改的同时删除,增加的同时删除。
    若有多个任务执行,必须按照队列来执行。
同步:
异步操作:宏任务、微任务

执行顺序:js的事件循环,eventLoop任务事件流
    --》任务进入执行栈,判断同步、异步
    同步任务:--》同步任务进入主线程-》执行完全部同步任务
    异步任务:--》放入事件列表区分宏任务、微任务-》注册回调函数-》进入执行队列
    --》以上同步异步处理完成后--》再次读取队列任务结果-》进入主线程执行
    --》当有新任务时,以上过程重复执行
代码执行示例:输出顺序为 1-2-3-4
setTimeout ( 4 异步宏任务)			
new Promise( 1 同步主线程).then( 3 异步微任务) 
console.log( 2 同步主线程)
执行顺序6-1-3-4-5-2

image-20220302205915673

1. js是单线程的语言。
2. js代码执行流程:同步执行完==》事件循环
    同步的任务都执行完了,才会执行事件循环的内容
    进入事件循环:请求、定时器、事件....
3. 事件循环中包含:【微任务、宏任务】
微任务:promise.then .catch
宏任务:setTimeout定时器、事件、<script> 、ajax

要执行宏任务的前提是清空了所有的微任务(先执行微任务,再执行宏任务)

流程:同步==》事件循环【微任务和宏任务】==》微任务==》宏任务=》微任务...
面试题:JS作用域考题
1. 除了函数外,js是没有块级作用域。
2. 作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。
     注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。
3. 注意声明变量是用var还是没有写(window.4. 注意:js有变量提升的机制【变量悬挂声明】
5. 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
预编译、[[scopes]]:保存改函数的作用域链
JS执行前,先进行预编译(GO全局执行期上下文,AO函数执行期上下文)

AO函数执行期上下文:
1. 寻找形参和变量声明
    变量都赋值为undefined,函数都赋值为function(){}
2. 把实参赋值给形参
3. 把函数声明、赋值
4. 执行

GO全局执行期上下文:
1. 找变量
2. 找函数声明-->AO
3. 执行

面试的时候怎么看:

1. 本层作用域有没有此变量【注意变量提升】
2. 注意:js除了函数外没有块级作用域
3. 普通声明函数是不看写函数的时候顺序

考题一:

    (function(){
    var a = b = 10; 
    })()
    console.log( a ); 	// 报错
    console.log( b ); 	// 10 默认全局变量 window.b 

考题二:

function c(){
    var b = 1;
    function a(){
        console.log( b ); // undefined
        var b = 2;
        console.log( b ); // 2
    }
    a();
    console.log( b );     // 1
}
c();

考题三:

var name = 'a';
(function(){
    if( typeof name == 'undefined' ){
        var name = 'b';
        console.log('111'+name);  // 111b
    }else{
        console.log('222'+name);
    }
})()

考题四:

function fun( a ){
    var a = 10;
    function a(){}
    console.log( a ); // 10
}
fun( 100 );
面试题:JS对象考题

JS对象注意点:

1. 对象是通过new操作符构建出来的,所以对象之间不想等(除了引用外)2. 对象注意:引用类型(共同一个地址)3. 对象的key都是字符串类型;
4. 对象如何找属性|方法;
    查找规则:先在对象本身找 ===> 构造函数中找 ===> 对象原型中找 ===> 构造函数原型中找 ===> 对象上一层原型查找

考题一:

 [1,2,3] === [1,2,3]   //false

考题二:

var obj1 = {
    a:'hellow'
}
var obj2 = obj1;
obj2.a = 'world';
console.log(obj1); 	//{a:world}
(function(){
    console.log(a); 	//undefined
    var a = 1;
})();

考题三:

var a = {}
var b = {
    key:'a'
}
var c = {
    key:'c'
}

a[b] = '123';
a[c] = '456';

console.log( a[b] ); // 456
面试题:JS作用域+this指向+原型的考题

考题一:

function Foo(){
    getName = function(){console.log(1)} //注意是全局的window.
    return this;
}

Foo.getName = function(){console.log(2)}
Foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){
    console.log(5)
}

Foo.getName();    //2
getName(); 		  //4
Foo().getName();  //1
getName();		  //1
new Foo().getName();//3

考题二:

var o = {
    a:10,
    b:{
        a:2,
        fn:function(){
            console.log( this.a ); // 2
            console.log( this );   //代表b对象
        }
    }
}
o.b.fn();

考题三:

window.name = 'ByteDance';
function A(){
    this.name = 123;
}
A.prototype.getA = function(){
    console.log( this );
    return this.name + 1;
}
let a = new A();
let funcA = a.getA;
funcA();  //this代表window

考题四:

var length = 10;
function fn(){
    return this.length + 1;
}
var obj = {
    length:5,
    test1:function(){
        return fn();
    }
}
obj.test2 = fn;
console.log( obj.test1() ); 							//1
console.log( fn()===obj.test2() ); 				//false
console.log( obj.test1() == obj.test2() ); //false
面试题:JS判断变量是不是数组,你能写出哪些方法?

方式一:isArray

var arr = [1,2,3];
console.log( Array.isArray( arr ) );

方式二:instanceof 【可写,可不写】

var arr = [1,2,3];
console.log( arr instanceof Array );

方式三:原型prototype

var arr = [1,2,3];
console.log( Object.prototype.toString.call(arr).indexOf('Array') > -1 );

方式四:isPrototypeOf()

var arr = [1,2,3];
console.log(  Array.prototype.isPrototypeOf(arr) )

方式五:constructor

var arr = [1,2,3];
console.log(  arr.constructor.toString().indexOf('Array') > -1 )
面试题:slice是干嘛的、splice是否会改变原数组
1. slice是来截取的
    参数可以写slice(3)、slice(1,3)、slice(-3)
    返回的是一个新的数组
2. splice 功能有:插入、删除、替换
    返回:删除的元素
    该方法会改变原数组
面试题:JS数组去重
1.双重for循环
2.set去重
3.indexOf
4.sort()
5.includs
6.filter
7.Map数据结构

方式一:new set

var arr1 = [1,2,3,2,4,1];
function unique(arr){
    return [...new Set(arr)]
}
console.log(  unique(arr1) );
// new Set() 方法返回对象
// Array.from(new Set(arr)) 把像数组的内容变为数组 ES6
// [...new Set(arr)] 扩展运算符展开

方式二:indexOf

var arr2 = [1,2,3,2,4,1];
function unique( arr ){
    var brr = [];
    for( var i=0;i<arr.length;i++){
        if(  brr.indexOf(arr[i]) == -1 ){
            brr.push( arr[i] );
        }
    }
    return brr;
}
console.log( unique(arr2) );
// arr.indexOf(brr[i]) 查找有没有,没有返回-1;有返回第一次出现的位置

方式三:sort

var arr3 = [1,2,3,2,4,1];
function unique( arr ){
    arr = arr.sort();
    var brr = [];
    for(var i=0;i<arr.length;i++){
        if( arr[i] !== arr[i-1]){
            brr.push( arr[i] );
        }
    }
    return brr;
}
console.log( unique(arr3) );
// sort() 排序 从小到大arr.sort((a,b) => a-b)
// arr[i] !== arr[i-1]
面试题:找出多维数组最大值
arr.join(',').split(',') //多维数组先转字符串再转数组
function fnArr(arr){
    var newArr = [];
    arr.forEach((item,index)=>{
        newArr.push( Math.max(...item)  )
    })
    return newArr;
}
console.log(fnArr([
    [4,5,1,3],
    [13,27,18,26],
    [32,35,37,39],
    [1000,1001,857,1]
]));
面试题:给字符串新增方法实现功能

​ 给字符串对象定义一个addPrefix函数,当传入一个字符串str时,它会返回新的带有指定前缀的字符串,例如:

​ console.log( ‘world’.addPrefix(‘hello’) ) 控制台会输出helloworld

解答:
String.prototype.addPrefix = function(str){
    return str  + this;
}
console.log( 'world'.addPrefix('hello') )
面试题:找出字符串出现最多次数的字符以及次数
var str = 'aaabbbbbccddddddddddx';
var obj = {};
for(var i=0;i<str.length;i++){
    var char = str.charAt(i);
    if( obj[char] ){
        obj[char]++;
    }else{
        obj[char] = 1;
    }
}
console.log( obj );

//统计出来最大值
var max = 0;
for( var key in obj ){
    if( max < obj[key] ){
        max = obj[key];
    }
}

//拿最大值去对比
for( var key in obj ){
    if( obj[key] == max ){
        console.log('最多的字符是'+key);
        console.log('出现的次数是'+max);
    }
}
/* 统计字符出现的次数,返回一个对象 */
var str = 'abccbaam'; //a3 b2 c2 m1
function fUnique(str){
    var obj = {};
    for(var i=0;i<str.length;i++){
        var key = str[i];		
        if(  !obj[key]  ){
            //如果obj对象没有a ,添加一个a,并且赋值为1
            obj[key] = 1;		
        }else{
            //如果obj对象有对应的key了,就让值+1
            obj[key] += 1;
        }
    }
    return obj;	 //返回最终结果的对象
}
console.log(   fUnique(str)   );
面试题:new操作符具体做了什么
1. 创建了一个空对象		
    console.log( new Foo() ) // 空对象
2. 将空对象的原型,指向于构造函数的原型
    console.log( Foo.prototype == new Foo().__proto__ ) //true
3. 将空对象作为构造函数的上下文(改变this指向)
    原来函数内的this指向window,用newthis指向函数对象
4. 对构造函数有返回值的处理判断
    return基本类型 则 忽略返回值 return
    return引用类型,则 new 失效 ,返回引用类型(数组,对象)
function Fun( age,name ){
    this.age = age;
    this.name = name;
}
function create( fn , ...args ){
    //1. 创建了一个空的对象
    var obj = {}; //var obj = Object.create({})
    //2. 将空对象的原型,指向于构造函数的原型
    Object.setPrototypeOf(obj,fn.prototype);
    //3. 将空对象作为构造函数的上下文(改变this指向)
    var result = fn.apply(obj,args);
    //4. 对构造函数有返回值的处理判断
    return result instanceof Object ? result : obj;
}
console.log( create(Fun,18,'张三')   )
面试题:闭包
说一下闭包?

闭包:内部函数可以访问到外部函数的变量,并且可以把变量保存到全局,导致了外部函数的变量不释放,这样就形成了闭包。闭包因为它把变量拿到全局身上,所以我们写太多闭包的话,会导致变量太多而不被销毁,影响性能,也可能会造成变量的污染。解决内存损耗可以在内部变量使用完后,把它赋值为null。

比如说,点击事件获取列表下标时,如果用for循环,就会直接获取最后一个下标的值,如果想拿到对应的下标,这种情况可以使用立即执行函数(把函数括起来,或者在函数括起来后再加个括号),也可以使用闭包的形式来获取,把var改为let也可以。
1. 闭包是什么
    闭包是一个函数(内层函数)加上到创建函数(外层函数)的作用域的连接,闭包“关闭”了函数的自由变量。 
    外层函数{return 内层函数}
    不要写全局变量,不知道啥时候被回收,函数内的变量执行后被销毁回收
    不能确定内层函数是否被回收,所以闭包函数fn()()调用后,不会被销毁
    无意间的函数环境共享(内外层函数之间)
2. 闭包可以解决什么问题【闭包的优点】
    2.1 内部函数可以访问到外部函数的局部变量
    2.2 闭包可以解决的问题
      var lis = document.getElementsByTagName('li');
      for(var i=0;i<lis.length;i++){
        (function(i){  // i驻留在内存中,闭包解决  let解决
          lis[i].onclick = function(){ 
            alert(i);
          }
          list[i] = null; //执行完后设置为null,解决内存损耗
        })(i)
      }
3. 闭包的缺点
    3.1 变量会驻留在内存中,造成内存损耗问题。
                解决:把闭包的函数设置为null
    3.2 内存泄漏【ie】低版本 ==> 可说可不说,如果说一定要提到ie
面试题:原型和原型链
说一下原型链?

提到原型链就不得不说一下原型,js对象都有一个内置的[[Prototype]]私有属性,这个属性指向另一个对象,称这个对象为原对象的原型。函数拥有:prototype,对象拥有:__proto__ 。它的作用就是共享属性和方法,把对象关联起来。

原型链就是一个查找的链条,去查找对象时,先去对象本身查找,如果找不到,就去对象的原型上找,如果找不到,就去原型的原型上找,一次类推,形成了数据的一个链表结构,就是把原型串联起来,也就是原型链。原型链的最顶端是null。

如果查找属性和方法,查找顺序是,先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中Object查找
1. 原型的定义:js的对象都有一个内置的[[Prototype]]私有属性,这个属性指向另一个对象,称这个对象为原对象的原型。
    函数拥有:prototype
    对象拥有:__proto__ 
    实例对象.__proto__ == 构造函数.prototype
    当new一个构造函数的对象时,这个构造函数和实例对象的原型指向同一个原型。
2.每一个原型的constructor指向它的构造函数。实例对象身上并没有constructor属性,当不能获取constructor时,它或从构造函数的原型上获取
    实例对象.constructor==构造函数.prototype.constructor
3.原型的作用:对象共享属性和共享方法,万物皆对象,把对象关联起来。
2. 原型链
    2.1原型链解释:原型链就是一个查找的链条,相当于去查找对象时,先去对象本身查找,如果找不到,就去对象的原型上找,如果找不到,就去原型的原型上找,一次类推,形成了数据的一个链表结构,就是把原型串联起来,也就是原型链。
    2.2 原型链的最顶端是null 找不到为止
3. 对象查找属性或者方法的顺序
    先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中Object查找
面试题: JS继承有哪些方式

方式一:ES6的继承

class Parent{
    constructor(){
        this.age = 18;
    }
}

class Child extends Parent{ //ES6 extends 继承
    constructor(){
        super(); // 不写报错
        this.name = '张三';
    }
}
let o1 = new Child();
console.log( o1,o1.name,o1.age );

方式二:原型链继承

function Parent(){
    this.age = 20;
}
function Child(){
    this.name = '张三'
}
Child.prototype = new Parent(); // prototype 继承 可以共享
let o2 = new Child();
console.log( o2,o2.name,o2.age );

方式三:借用构造函数继承

function Parent(){
    this.age = 22;
}
function Child(){
    this.name = '张三'
    Parent.call(this); // 在子内改变父的this指向子 不能共享
}
let o3 = new Child();
console.log( o3,o3.name,o3.age );

方式四:组合式继承 相对最优

function Parent(){
    this.age = 100;
}
function Child(){
    Parent.call(this); // 改变this
    this.name = '张三'
}
Child.prototype = new Parent(); // prototype 继承
let o4 = new Child();
console.log( o4,o4.name,o4.age );
面试题:说一下call、apply、bind区别

共同点:功能一致

共同点:可以改变函数体内的this指向????????
语法: 函数.call()、函数.apply()、函数.bind()

区别:执行顺序,传入参数

1. 执行顺序不同:
    var obj = fun(){}
    fun.call(obj)、fun.apply(obj)可以立即执行,临时改变this指向,其他调用不受影响。
    fun.bind(obj)不会立即执行,因为fun.bind()返回的是一个函数,需要加入()执行,即fun.bind(obj)()2. 传入参数不同:
    fun.apply(obj,['张三',88]); 
    fun.call(obj,'张三',88); 
    fun.bind(obj,'张三',88)(); 
    apply第二个参数是数组。
    call和bind有多个参数需要挨个写,依次罗列。

场景:

1. 用apply的情况,获取数组内的最大值
var arr1 = [1,2,4,5,7,3,321];
console.log( Math.max.apply(null,arr1) )

2. 用bind的情况,改变点击事件的this指向
var btn = document.getElementById('btn');
var h1s = document.getElementById('h1s');
btn.onclick = function(){
    console.log( this.id ); // 本来的this指向btn这个DOM对象
}.bind(h1s) // 改变this指向,且返回的函数赋值给btn
            // 不能用call和apply,否则不点击就直接返回id
面试题:sort背后原理是什么?
V8 引擎 sort 函数只给出了两种排序 InsertionSort 和 QuickSort,数量小于10的数组使用 InsertionSort,比10大的数组则使用 QuickSort。

之前的版本是:插入排序和快排,现在是冒泡

原理实现链接:https://github.com/v8/v8/blob/ad82a40509c5b5b4680d4299c8f08d6c6d31af3c/src/js/array.js

***710行代码开始***
面试题:深拷贝和浅拷贝
区别:浅拷贝双方不是独立的,别切会互相影响;深拷贝互相不影响,并且是独立的个体。浅拷贝就是把指向别人的值的指针直接赋值;深拷贝就是直接拿过来别人的值。
深拷贝和浅拷贝主要是受数据类型影响,当拷贝的是基本数类型,一般是深拷贝。
拷贝的是引用数据类型,简单的复制就是浅拷贝,需要做处理欸深拷贝
共同点: 复制
区别  :
1. 浅拷贝:只复制引用,而未复制真正的值。
         拷贝前后指向同意个地址,改变值会修改指向的地址,相互影响。
    var arr1 = ['a','b','c','d'];
    var arr2 = arr1; //浅拷贝

    var obj1 = {a:1,b:2}
    var obj2 = Object.assign(obj1); // ES6 浅拷贝

2. 深拷贝:是复制真正的值 (不同引用)
    2.1 采用JSON API,先序列化(转为字符串),再反序列化(转为对象)
    注意:如果obj里存在时间对象、RepExp、ERROR、函数、undefined、NaN等对象,序列化可能会出错。
    var obj3 = {
        a:1,
        b:2
    }
    var obj4 = JSON.parse(JSON.stringify( obj3 ));

    2.2 递归的形式 新建对象存储赋值的值
    function copyObj( obj ){
        if(  Array.isArray(obj)  ){
            var newObj = [];
        }else{
            var newObj = {};
        }
        for( var key in obj ){
            if( typeof obj[key] == 'object' ){
                // 如果被复制的对象里包含对象,调用函数本身
                newObj[key] = copyObj(obj[key]);
            }else{
                newObj[key] = obj[key];
            }
        }
        return newObj;
    }
    console.log(  copyObj(obj5)  );
面试题:localStorage、sessionStorage、cookie的区别
公共点:在客户端存放数据
区别:
1. 数据存放有效期
        sessionStorage : 仅在当前浏览器窗口关闭之前有效。【关闭浏览器就没了】
        localStorage   : 始终有效,窗口或者浏览器关闭也一直保存,所以叫持久化存储。
        cookie				 : 只在设置的cookie过期时间之前有效,即使窗口或者浏览器关闭也有效。
2. localStorage、sessionStorage不可以设置过期时间
     cookie 有过期时间,可以设置过期(把时间调整到之前的时间,就过期了)
3. 存储大小的限制
    cookie存储量不能超过4k
    localStorage、sessionStorage不能超过5M
    
    ****根据不同的浏览器存储的大小是不同的。
1.4 H5/C3面试题
面试题:什么是语义化标签
H5标签
    <header></header>
    <footer></footer>
    <section></section>
    <button></button>
    <nav></nav>
    .....
H5之前:
    <div class="header"></div>

1. 易读性和维护性更好。
2. seo成分会更好,同时搜索引擎蜘蛛抓取更好。
3. IE8不兼容HTML5标签的。解决:可以通过html5shiv.js去处理。
面试题:::before 和 :after中双冒号和单冒号 有什么区别?解释一下这2个伪元素的作用。
1. 区别
    :是伪类、::伪元素  ===》是为了做区分

2.是什么?作用
    元素before之前 、 元素after之后
    作用:给父元素加after清除浮动、样式布局上也有作用
面试题:如何关闭IOS键盘首字母自动大写
兼容性问题
<input type="text" autocapitalize='off'>
面试题:怎么让Chrome支持小于12px 的文字?
Chrome默认字体大小是:16px
**每个浏览器默认字体大小可能都不一样

<style type="text/css">
div{
    font-size:10px;
}
div span{
    display: inline-block;
    -webkit-transform:scale(1.6); /*设置值*/
}
</style>
面试题:rem和em区别,px和rem转换
移动端布局使用
相对于font-size

em针对于父元素的font-size
rem针对于根(html)元素的font-size    px/16---> rem
面试题:ios系统中元素被触摸时产生的半透明灰色遮罩怎么去掉
兼容问题,设置css样式
<style>
    a,button,input,textarea{
        -webkit-tap-highlight-color: rgba(0,0,0,0);
    }
</style>
面试题:webkit表单输入框placeholder字体的颜色值能改变吗?
<style type="text/css">
    input::-webkit-input-placeholder{
        color:red;
    }
</style>
面试题:禁止ios长按时触发系统的菜单,禁止ios&android长按时下载图片
兼容问题,设置css样式
禁止ios 长按时触发系统的菜单,禁止ios&android长按时下载图片
html,body{
    /*禁止ios 长按时触发系统的菜单*/
    touch-callout: none;
    -webkit-touch-callout: none;
    
    /*禁止ios&android长按时下载图片*/
    user-select:none;
    -webkit-user-select:none;
}
面试题:禁止ios和android用户选中文字
html,body{
    user-select:none;
    -webkit-user-select:none;
}
面试题:自适应(适配)
淘宝无限适配【移动端】:移动端meta + 淘宝无限适配js文件 + 布局单位使用rem  (拉钩、网易)
改变页面大小,触发on resize事件
function flexible(){
    let doc = document.documentElement;
    let w = doc.clientWidth/10;
    doc.style.fontSize = w + 'px';
}
flexible();
window.onresize = function(){
    flexible()
}
面试题:响应式
1. 是什么?
    一个URL可以响应多端,页面可以正常使用,url不变
2. 语法结构
    @media only screen and (max-width: 1000px){
        ul li:last-child{
            display: none;
        }
    }

    only : 兼容处理,可以排除不支持媒体查询的浏览器
    screen : 设备类型
    max-width | max-height
    min-width | min-height 
3. 响应式图片【性能优化】
    <picture>
        <source srcset="1.jpg" media='(min-width:1000px)'>
        <source srcset="2.jpg" media='(min-width:700px)'>
        <img srcset="3.jpg">
    </picture>
面试题:布局方案
一、什么情况下采用响应式布局

    数据不是特别多,用户量不是特别大,纯展示类的项目适合响应式布局
    
    例如:公司的官网、专题页面
    
    特别追求性能的项目,不太适合响应式,因为如果添加了很多的响应式 @media 就会造成加载速度变慢。

二、pc + 移动端应该做什么样的布局方案
    注意:访问量还可以或者比较大,类似于淘宝网。
    
    pc是一套,会加入一点点响应式。
    移动端是一套,会使用自适应的布局方式。

三、pc的设计图

    ui:1980
    笔记本电脑:1280
    ui图的宽度和电脑的宽度不对应该怎么办?
        1. 把ui图进行等比缩放,缩放成和电脑一样的尺寸
        2.1980的电脑
        
四、移动端的设计图

    宽度:750
    因为750设计图/2就是375,正好是iphone6的尺寸,我们要把iphone6的尺寸做为基准点。

第二章 面试题进阶篇

2.1 ES6面试题
面试题:var、let、const区别
varletconst 共同点都是可以声明变量的

区别一:
    var 具有变量提升的机制
    letconst没有变量提升的机制
区别二:
    var 可以多次声明同一个变量
    letconst不可以多次声明同一个变量
区别三:
    varlet声明变量的
    const声明常量
    
    varlet声明的变量可以再次赋值,但是const不可以再次赋值了。
区别四:
    var声明的变量没有自身作用域
    letconst声明的变量有自身的作用域
面试题:作用域考题

考题一:let和const没有变量提升性

console.log( str );//undefined
var str = '你好';

console.log( num );//报错
let num = 10;

考题二:

function demo(){
    var n = 2;
    if( true ){
        var n = 1;
    }
    console.log( n );//1
}
demo();


function demo(){
    let n = 2;
    if( true ){
        let n = 1;
    }
    console.log( n );//2
}
demo();

考题三:可以修改

const obj = {
    a:1
}
obj.a = 11111;
console.log( obj )

const arr = ['a','b','c'];
arr[0]= 'aaaaa';
console.log( arr );
面试题:将下列对象进行合并

方式一:Object.assign (ES6浅拷贝)

const a = {a:1,b:4};
const b = {b:2,c:3};

let obj1 = Object.assign(a,b);
console.log( obj1 );

方式二:… (ES6扩展运算符)

let obj2 = {...a,...b};
console.log( obj2 );

方式三:自己封装方法

function extend( target,  source ){
    for(var key in source){
        target[key] = source[key];
    }
    return target;
}
console.log( extend(a,b) );
面试题:箭头函数和普通函数有什么区别?
1. this指向的问题
    箭头函数中的this只在箭头函数定义时就决定的,而且不可修改的(call、apply、bind)
    ****箭头函数的this指向定义时候、外层第一个普通函数的this
2. 箭头函数不能new(不能当作构造函数)
3. 箭头函数没有prototype
4. 箭头函数没有arguments
面试题:Promise有几种状态
有三种状态:
pending(进行中)  初始状态,不是成功或者失败
fulfilled(已成功)
rejected(已失败)

Promise优缺点

优点:
    可以将异步操作以同步操作的流程表达出来,将多个异步操作串联起来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
    .then用来进行链式操作,.catch用来返回错误。
缺点:
    首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成
面试题:async和await

async和await区别:

async:
    用来定义函数的,异步函数,打印函数名 可以得到promise,函数名.then 就是一个promise对象来调用该方法
    内部实现,有返回值,就是调用promise.resolve(),出错promise.reject(),catch捕获
    
await:
    后面跟任意表达式,使用时一般用promise的表达式
    等待后面的promise执行暗壁,拿到promise.resolve()返回值,后面的代码继续执行
    await关键字不能直接在全局或普通函数中使用,只能使用在asyncasyncawait ES7看起来是同步的,提高效率,避免了回调地狱
promise ES6链式操作,最大区别是promise包含catch捕获错误,async需要在函数内自己定义
promise方法更多一些,all、race、finally(清理工作)方法(将多个 Promise 实例,包装成一个新的 Promise 实例)
    多个异步操作并行,可以用await Promise.all(),提升效率
    不能调用forEach,map方法,用for,或者for await
面试题:axios网络请求
axios是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端。简单的理解就是ajax的封装。
axios本身具有以下特征:
    从浏览器中创建 XMLHttpRequest
    从 node.js 发出 http 请求
    支持 Promise API
    拦截请求和响应
    转换请求和响应数据
    取消请求
    自动转换JSON数据
    客户端支持防止 CSRF/XSRF

axios的二次封装

import axios from 'axios'

// 设置默认的请求头
axios.defaults.headers['Content-Type'] = 'application/json;chartset=utf-8'

// 创建axios对象
const service = axios.create()

//请求拦截器
service.interceptors.request.use( req => {
  // 访问这个页面时,不会再inernet里保存临时备份
  req.headers['Cache-Control'] = 'no-cache'
  return req
},error => {
  return Promise.reject(error)
})

// 响应拦截器
service.interceptors.response.use( res => {
  // 未设置状态码时,默认就是成功状态
  if( res.request.responseType == 'blob'){
    return res.data
  }
  return res.data
},error => {
  return Promise.reject(error)
})

export default service
面试题:find和filter的区别
区别一:返回的内容不同
    filter 返回是新数组
    find   返回具体的内容
区别二:
    find :匹配到第一个即返回
    filter : 返回整体(没一个匹配到的都返回)
面试题:some和every的区别
some  ==》 如果有一项符合条件则返回true
every ==》 全部符合条件才会返回true
2.2 webpack面试题
面试题:webpack插件
打包合并文件 执行webpack进行打包
全局安装cnpm i webpack -g   cnpm i webpack-cli -g
开发阶段:可以有空格、回车、注释
上线版本:删除回车、注释、空格
webpack-->本身是用来打包js文件的,打包HTML、css等别的需要配置文件,配置loader
打包html文件,使用html-webpack-plugin插件,配置插件,
面试题: webpack工作原理、配置、优化?

提高构建速度

1.缩小文件搜索范围
    配置exclude、include,合理使用resolve配置路径
2.缓存之前构建过的js
    将Babel编译过的文件缓存起来,下次只需要编译更改过的代码文件即可,这样可以大幅度加快打包时间。
3.提前构建第三方库
    一些情况下会引发重复打包的问题,使用DLL插件,原理是将网页依赖的基础模块抽离出来打包到dll文件中,当需要导入的模块存在于某个dll中时,这个模块不再被打包,而是去dll中获取,而且通常都是第三方库。
4.并行构建而不是同步构建
    受限于 Node 是单线程运行的,所以 Webpack 在打包的过程中也是单线程的,特别是在执行 Loader 的时候,长时间编译的任务很多,这样就会导致等待的情况。HappyPack和ThreadLoader作用是一样的,都是同时执行多个进程,从而加快构建速度。而Thread-Loader是webpack4提出的。
5.HMR 模块热替换
    当一个模块发生改变时,之后只会打包这样一个模块(而不是打包所有模块),极大提升构建速度
6.压缩打包体积
    使用TreeShaking删除无用代码,这里的无用指的是当发现引入模块的某些内容在其他地方并没有使用时,就被当作无用节点,从而被删掉。
7.代码分割实现按需加载
    单页应用的一个问题在于使用一个页面承载复杂的功能,要加载的文件体积很大,不进行优化的话会导致首屏加载时间过长,影响用户体验。做按需加载可以解决这个问题。
8.优化运行速度,资源文件缓存
    对babel-loader在编译时的缓存,如果服务端压力太大,我们这时在服务设置,对构建路径下的文件都设置cookie实现强缓存
9.预加载
    子模块在主页面加载完之后,再“偷偷”地进行异步加载。异步加载不会导致页面卡死的情况。预先加载好等待点击用户体验会更好。
2.3 Git面试题
面试题:git常用命令
//提交所有代码到中转站
git add . 
//提交所以代码到本地仓库
git commit -m "注释内容"
//提交本地仓库到码云
git push
面试题:查看之前提交的代码
1.在仓库里点击提交次数,查看注释和事件
2.点击之前提交的对应代码,点击下载
面试题:解决冲突
https://gitee.com/
1.克隆下载代码
    git clone 。。。
2.配置
    git config user.name 'guxinlei'
    git config --global user.email "zpsthao@163.com"
3.提交代码
    git add .
    git commit -m '注释内容'
4.再git push 可能报错
    如果远程仓库有人更新了代码,那么我们不能直接的去git push
    解决:先把代码拉下来,然后再push
    git pull
    git push
代码冲突:手动解决代码冲突
面试题:分支的操作
    ***默认的主分支 : master
1. 查看本地分支
    git branch
2. 新建分支
    git branch 分支名称
3. 切换分支
    git checkout 分支名称
4. 如何查看远程仓库的分支
    git branch -r
5. 本地分支提交到远程仓库
    git push --set-upstream origin 分支名称
6. 删除本地分支
    git branch -d 分支名称
7. 如何删除远程仓库的分支
    git push origin --delete 分支名称
面试题:合并分支
1. 分支是独立的,不会互相影响
    注意:在一个分支下创建另外一个新的分支,新的分支会继承旧分支之前的操作。
2. 合并分支
    git merge 分支名称
    注意:如果当前是在master分支下写入:git merge dev,那么master和dev会进行合并,相当于master + dev
        master += dev
面试题:GitFlow工作流
    master		用于保存上线版本代码,创建了dev分支 
    develop		用于保存相对稳定版本的代码,所有的feature都是dev分支创建的
    feature		用于开发某几个功能,不同的功能可能会创建不同的分支

                ***feature/login
                ***feature/list

    release		用于代码上线前的准备(测试,bug修复),是dev创建的

                ***release/v1.0

    bugfix		用于修复不紧急bug
    hotfix 		用于修复紧急bug

第三章 面试题框架篇

3.1 Vue面试题
面试题:Vue2.x 生命周期
1.什么是生命周期?
    vue实例从创建到销毁的过程就是生命周期。也就是vue里从开始创建、初始化数据、编译模板、挂载、渲染、更新、销毁等一系列的过程,我们统称为vue的生命周期。
2.vue生命周期有什么作用呢?
    vue实现的功能都是围绕生命周期来进行,在不同的阶段我们要使用不同的钩子函数可以实现对组件数据的管理、DOM渲染的操作。
1. 有哪些生命周期
系统自带:
  beforeCreate
  created
  beforeMount
  mounted
  beforeUpdate
  updated
  beforeDestroy
  destroyed
2. 一旦进入到页面或者组件,会执行哪些生命周期,顺序。
 beforeCreate
 created
 beforeMount
 mounted
3. 在哪个阶段有$el,在哪个阶段有$data
    beforeCreate 啥也没有
    created  有data没有el
    beforeMount 有data没有el
    mounted 都有
4. 如果加入了keep-alive会多俩个生命周期
    activated、deactivated
5. 如果加入了keep-alive,第一次进入组件会执行哪些生命?
 beforeCreate
 created
 beforeMount
 mounted
 activated
6. 如果加入了keep-alive,第二次或者第N次进入组件会执行哪些生命周期?
只执行一个生命周期:activated
7.父子组件的生命周期
    父组件执行到beforeMount->执行子组件,最后是父组件的mounted
    如果有兄弟组件,父组件执行到beforeMount -> 兄弟组件依次执行到beforeMount -> 按照顺序执行mounted ,最后是父组件的mounted
    销毁父组件,先销毁子组件,再销毁父组件
创建:
beforeCreate:
    创建前,实例初始化之后,this指向了创建的实例,数据观察、事件、DOM节点都没形成
    data,methods,watch方法和数据不能使用
created:
    创建后,实例创建了,有数据,初始化了一些依赖
    可以访问data,computed,watch上面的方法和数据
    初始化已经完成了,发送异步请求、结束loading事件
    此时还没有挂载DOM。不能操作DOM,若非要使用在在Vue.nextTick()的回调函数中
    (DOM未渲染,拿到了数据)
挂载:
beforeMount:
    挂在前,拿不到DOM元素,但是根节点已将创建了
    这个阶段就是一个过渡阶段,一般很少用,最多一两次
mounted:
    挂载后,创建了实例,元素节点全部生成,双向数据绑定
    可以操作DOM
    可以向后端发送请求,拿到数据,配合路由作别的事
    (DOM渲染了,拿到了数据,有可能会造成闪屏的问题)
更新:
beforeUpdate:
    数据更新前,数据驱动DOM
    数据在更新后可能没有立即更新数据,但是DOM里的数据会改变
    访问现在的DOM,手动添加或删除事件监听器
updated:
    数据更新后,完成了虚拟DOM的重新渲染
    组件的DOM已经完成更新,操作DOM
    注意:不能在这里修改属性(操作),否则会进入死循环
[
    activated:在用vue-router时用了keep-alive来缓存组件状态
    我们的子组件需要每次加载时都需要进行一些操作的时候,可以在这个周期里
    deactivated:keep-alive组件被移除的时候使用
]
销毁
beforeDestroy:
    销毁前,可以删除定时器、提示信息
destroyed:
    销毁后,当前组件就已经被删除了,销毁监听事件、组件、事件、子组件
    当前组件不存在了,不能在操作里面的任何东西
面试题:谈谈你对keep-alive的了解
1. 是什么
<keep-alive>需要缓存的内容</keep-alive>
vue系统自带的一个组件,可以用来是来缓存组件的,提升性能
2. 使用场景
具体实现比如:首页进入到详情页,如果用户在首页每次点击都是相同的,那么详情页就没必要请求N次了,直接缓存起来就可以了,当然如果点击的不是同一个,那么就直接请求。
除了第一次每次执行activated生命周期,所以可以在activated生命周期内,路由传参this.$route.query.id,判断每次点击的id和缓存的id是否相等,如果不相等,再执行请求新的数据。
面试题:v-if和v-show区别
相同点:都可以让一个元素展示或消失

1. 展示形式不同
v-if是 创建一个dom节点 false时节点不存在
v-show 节点存在,但是display:none 、 block

2. 使用场景不同
初次加载v-if要比v-show好,v-if不使用时页面不会做加载盒子,而v-show会加载
频繁切换v-show要比v-if好,创建和删除的开销太大了,显示和隐藏开销较小

v-if导航条的显示隐藏
v-show购物车页面,点击加入,频繁点击的元素
面试题:v-if和v-for优先级
v-for的优先级要比v-if***是在源码中体现的:function genElement 这个函数里面,使用了if else结构,先走的for 再走的if
面试题:ref是什么?
ref是用来获取dom的,就相当于getElementById等等
Dom在mounted里面拿到或操作,不能在created
标签内ref="imgs"  this.$refs.imgs   
面试题:nextTick是什么?
nextTick不断地把异步任务推到执行栈里,dom更新完毕后执行nextTick,获取更新后的dom内容
this.$nextTick( () => { console.log(333) })
插件数据有延迟的时候,或者直接更改数据,再获取数据时,使用
created(){
    console.log(11)
    this.$nextTick( () => { console.log(333) })
},
mounted(){
    console.log(22)
    this.$nextTick( () => { console.log(4444) })
}
//执行顺序1234
<button @click='btn' ref='box'>{{str}}</button>
  data () {
      return {
          str:'123'
      }
  },
  methods:{
      btn(){
          this.str = 'bbbbbb';
          console.log( this.$refs.box.innerHTML ) //123
          this.$nextTick(()=>{
              console.log( this.$refs.box.innerHTML,'xxxx' )//bbbbbb
          })
      }
  }
// 更改数据后,直接获取dom的内容为更新前的,因为vue组件只更新了视图数据,没有走dom
// 此时可以使用nextTick获取新数据
面试题:scoped原理
1. 作用:让样式只在本组件中生效,不影响其他组件。
2. 原理:给元素节点新增自定义属性,然后css根据属性选择器添加样式。
面试题:Vue中如何做样式穿透
1.scss安装:npm install sass-loader node-sass --save
sass和less使用:修改样式时 父元素加上 /deep/ 在加上子元素修改
<style scoped lang="scss">
    $color:red;
    div{
        background:$color;
    }
<style>

2.安装stylus npm install stylus stylus-loader --save
lang="stylus"
stylus样式穿透使用:>>> 或者  /deep/

通用使用:  :v-deep
面试题:Vue组件传值
父组件-->子组件:

    1. 父组件:name为父组件定义的数据名字  user-detail为子组件
        <user-detail :myName="name" />
    
    export default {
        components: {
            UserDetail
        }
        ......
    }
  2. 在子组件中使用props(可以是数组也可以是对象)接收即可。可以传多个属性。
      export default {
      props: ['myName']
         }

子组件-->父组件: 子组件自定义事件传值,父组件使用事件获取数据,参数即为数据

        1. 子组件
      <button @click="changeParentName">改变父组件的name</button>
      export default {
          methods: {
              //子组件的事件
              changeParentName: function() {
                  this.$emit('handleChange', 'Jack')
              }
          }
      }
    2. 父组件
      <child @handleChange="changeName"></child>

      methods: {
          changeName(name) {  
              this.name = name
          }
      }

兄弟组件之间:通过一个中转传递 bus.js
        通过一个中转(bus)
            import Vue from 'vue';
            export default new Vue;
        A兄弟传值:
            import bus from '@/common/bus'
            bus.$emit("toFooter",this.msg); //methods

        B兄弟接收:
            import bus from '@/common/bus'
            bus.$on('toFooter',(data)=>{  //mounted里
                //data是this.msg数据
            })
        
面试题:computed、methods、watch有什么区别?
1. computed vs methods区别
    computed是有缓存的,多次的计算只执行一次
    methods没有缓存,多次的调用每次都执行
2. computed vs watch区别
    watch是监听,数据或者路由发生了改变才可以响应(执行)
    computed计算某一个属性的改变,如果使用的某一个值改变了,计算属性会监听到进行返回
    watch是当前监听到数据改变了,才会执行内部代码,watch可以监听路由 ’$route‘(){}
面 试题:props和data优先级谁高?
执行顺序:props ===>  methods ===> data ===> computed ===>watch
面试题:Vuex有哪些属性?
vuex:状态管理模式
state、getters、mutations、actions、modules

state 类似于组件中data,存放公共数据
getters 类型于组件中computed
mutations 类似于组件中methods,同步操作方法,写方法
actions 提交mutations的,异步操作方法,提交mutation
modules 把以上4个属性再细分,让仓库更好管理

引入:import {mapState,mapGetters,mapActions} from 'vuex'
  computed:{
      ...mapState(['str']),
      ...mapGetters(['changeArr'])
  },
  methods:{
      ...mapActions(['btns'])
  }
模块:
  computed:{
    ...mapState({
      cartList:state=>state.cart.cartList,
      pathList:state=>state.path.pathList,
      num:state=>state.path.num
    })
  },
面试题:Vuex是单向数据流还是双向数据流?
Vuex是单向数据流,组件中可以使用vuex中的数据,但是不能更改vuex的数据(报错)
mutations方法更改也是使用vuex
面试题:Vuex中的mutaitons和actions区别
mutaitons   :  都是同步事物
actions     :  可以包含任意异步操作

***在调试中就看出来
面试题:Vuex如何做持久化存储
Vuex本身不是持久化存储
持久化存储:localStorage或者cookies,关闭/刷新浏览器,再次进入页面不改变数据或状态

1. 使用localStorage自己写,在vuex的mutations里修改后localStorage.setItem()
2. 使用vuex-persist插件(本质上也是localStorage)
面试题:Vue设置代理

vue.config.js 解决跨域问题

module.exports = {
  publicPath:'./',
  devServer: {
    proxy: 'http://localhost:3000'
  }
}
面试题:Vue项目打包上线
打包完成出现空白页
路由模式:history,而不是哈希 带#,后端处理重定向
在vue.config.js修改,路径修改:/ 改为 ./ ,代理接口处理

1. 自测==>修改路由模式
2. 代理不生效,使用ENV
3. 修改路径
面试题:代理和环境变量
在项目中的根目录新建文件:
开发环境:.env.development
生产环境:.env.production
    VUE_APP_TITLE = '小鹿线'
    VUE_APP_ENV = 'pro'
    VUE_APP_BASE_API = 'http://localhost:3000'

        
搭建后端项目
全局命令:npm install express-generator -g
进入项目:express -- view=ejs server
node-express跨域设置(后端)
router.all('*', function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  res.header('Access-Control-Allow-Methods', '*');
  res.header('Content-Type', 'application/json;charset=utf-8');
  next();
});
面试题:Vue路由模式
路由模式有俩种:history、hash(默认)
区别:
    1. 表现形态不同
            history:http://localhost:8080/about
            hash:http://localhost:8080/#/about
    2. 跳转请求,到一个找不到的id()
            history : http://loc404alhost:8080/id   ===>发送请求
            hash 	  : 不会发送请求
    3. 打包后前端自测要使用hash,如果使用history会出现空白页
面试题:介绍一下SPA以及SPA有什么缺点
SPA是什么?单页面应用
缺点:
    1. SEO优化不好,SEO多页面比较好,蜘蛛抓取不到关键字等
    2. 性能不是特别好,所有的js等加载到一个页面,会造成重绘、回流等一系列的问题
面试题:vue的seo优化?
上线前,做seo,可以预渲染,服务器端渲染,nuxt.js
预渲染:
    适合项目某几个页面使用
    安装预渲染prerender-spa-plugin插件,在vue.config.js配置,打包成多页面
    安装动态描述插件vue-meta-info,解决title、描述、关键词
问题:页面特别多时,比较麻烦
    title描述,不能动态改变
服务端渲染:nuxt.js 、和脚手架差不多
    自动配置路由,只需要建立对应的vue文件,可以直接引用
    设置head方法,在里面return返回title、meta、描述等内容
    设置asyncData请求数据,在页面没加载完成旧已经有数据了
项目上线:
 npm run build生成.nuxt文件,上线时把静态文件、.nuxt文件、package.json,以	及nuxt.config.js上传到服务器就可以了
面试题:Vue路径传参
1. 显式
    http://localhost:8080/about?a=1
    1.1 传递参数,跳转页面:
        this.$router.push({
              path:'/about',
              query:{
                  a:1
              }
          })
  1.2 接收参数:this.$route.query.a
  
2. 隐式
    http://localhost:8080/about
    2.1 传:this.$router.push({
              name:'About',
              params:{
                  a:1
              }
          })
  2.2 接:this.$route.params.a
面试题:路由导航守卫有哪些
全局、路由独享、组件内

1. 全局  router.beforeEach(to,from,next)
    beforeEach、beforeResolve、afterEach
2. 路由独享
    beforeEnter
3. 组件内 (甚少用,不好维护)
    beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
    
使用场景:判断是否登录,如果登录就next否则就跳转到登录页面
面试题:Vue动态路由
场景:详情页(文章、商品),路由的子路由后面就上:id
router.js配置:
    {
    path: "/list",
    name: "List",
    children:[
      {
        path:"/list/:id",
        name:'Details',
        component: () =>
          import("../views/Details.vue"),
      }
    ],
    component: () =>
      import("../views/List.vue"),
  },
List.vue组件中:
    <router-link to='/list/789'>789</router-link>
    <router-view></router-view>	
面试题:Vue模板解析原理
原理:
    vue从本质上来说就是一个类,参数就是我们new Vue时所写的整个内容(里面设置根节点和数据的属性)
    它封装一个操作dom节点的函数来进行模板解析,首先获取app根节点。
    然后进行子节点的遍历,判断子节点的类型,如果nodeType为3就是文本节点,进行正则匹配,给节点赋值,来替换模板里的数据。nodeType为1时,它是元素节点,再递归调用从而替换数据。
    其中还做了一些其他处理,比如要去除模板中的空格trim()等等。
this.$data : 组件内的数据
this.$el :组件的根节点

index.html

<!DOCTYPE html>
<html>
<head><title></title></head>
<body>
    <div id='app'>
        <h1>{{ str }}</h1>
        {{ str }}
        <p>{{b}}</p>
    </div>
    <script type="text/javascript" src='vue.js'></script>
    <script type="text/javascript">
        new Vue({  //新建一个vue类,{}就是参数,并执行构造函数
            el: '#app',
            data: {
                str: '你好',
                b: '这也是data的数据'
            }
        })
    </script>
</body>
</html>

vue.js

class Vue{  //ES6
    constructor( options ){
        //options即 {el:#app}
        this.$el = document.querySelector(options.el); //el节点的出现原理
        this.$data = options.data; //data数据的出现原理
        this.compile(  this.$el );
    }
    compile( node ){ //node父节点
        node.childNodes.forEach((item,index)=>{
            //元素节点
            if( item.nodeType == 1 ){
                //item为<h1>{{111}}</h1>,父节点,递归调用
                this.compile(  item );
            }
            //这是文本节点,如果有{{}}就替换成数据
            if( item.nodeType == 3 ){
                //正则匹配{{}}
                let reg = /\{\{(.*?)\}\}/g;
                let text = item.textContent;
                //给节点赋值
                item.textContent = text.replace(reg,(match,vmKey)=>{
                    vmKey = vmKey.trim(); //文本内容,消除空格
                    return this.$data[vmKey];
                })
            }
        })
    } 
}
面试题:Vue生命周期源码
vue的生命周期就是函数依照一定的顺序进行执行。beforeCreate,created ,beforeMount,mounted,beforUpdate,Updated,beforeDestory,Destoryed。先判断是否为一个函数,接下来使用bind改变this指向。其中data的赋值放在created前,beforedCreate后;节点的获取和模板解析放在mounted之前,beforeMount之后。
class Vue{
    constructor( options ){
        if(  typeof options.beforeCreate == 'function' ){
            options.beforeCreate.bind(this)();
        }
        //这是data
        this.$data = options.data;
        if(  typeof options.created == 'function' ){
            options.created.bind(this)();
        }
        if(  typeof options.beforeMount == 'function' ){
            options.beforeMount.bind(this)();
        }
        //这是节点
        this.$el = document.querySelector(options.el);
        //模版解析
        this.compile(  this.$el );
        if(  typeof options.mounted == 'function' ){
            options.mounted.bind(this)();
        }		
    }
}
面试题:Vue源码-添加事件
1.在vue源码的模板解析器里,判断根节点下的子节点,找到元素节点即nodeType为1时,使用getAttribute判断是否绑定了事件,然后进行消除空格,改变this指向,传递event对象等操作,完成事件的添加。
2.如果元素节点的子节点大于0,还要判断是否有@click属性,继续递归compile解析器
//元素节点
if( item.nodeType == 1 ){
    //判断元素节点是否绑定了@click
    if( item.hasAttribute('@click')  ){
        //@click后绑定的属性值
        let vmKey = item.getAttribute('@click').trim();
        item.addEventListener('click',( event )=>{
            this.eventFn = this.$options.methods[vmKey].bind(this);
               this.eventFn(event);
        })
    }
    if( item.childNodes.length > 0  ){
           this.compile(  item );
    }
}
面试题:data数据劫持
data里的str数据,this.str输出undefined,vue对象下没有定义str的属性,如何设置?
1.给Vue大对象里建立新的属性,也就是把data里的属性复制一份到与data同级,此时就可以直接用this.str拿到。
2.data里的属性与Vue大对象下的属性要保持双向一直,data里面的更改,需要通知外面的也要更改,这就是数据劫持,此时就用到了 精确地添加或修改对象的属性方法 Object.defineProperty()。里面三个参数分别为this,data里的属性,要定义或修改的属性描述符,即设置 get()拿到属性数据,set()更改属性数据。
3.此时数据可以改变,但是页面里的数据没有变化,需要更新视图。
    //1、给Vue大对象赋属性,来自于data中
    //2、data中的属性值和Vue大对象的属性保持双向(劫持) created之前调用
    proxyData(){
        for( let key in this.$data ){
            Object.defineProperty(this,key,{  //js方法
                get(){
                    return this.$data[key];
                },
                set( val ){
                    this.$data[key] = val;
                }
            })
        }
    }
面试题:更新视图(数据的响应式)
1.如何更新视图?
    数据劫持之后,数据可以改变,但是页面里的数据没有变化,需要更新视图。
    创建一个空对象,用来存储data属性数据等。
    实现一个watcher订阅者监听属性的变化,在里面创建执行改变节点数据的(update)操作,更新dom;
    实现一个observe监听器,使用Object.defineProperty(), 数据变化时触发set方法,此时执行订阅者的update方法,从而更新视图。
    
2.在文本节点中判断Vue对象中是否有vmKey,vmKey就是页面中需要渲染的属性(data的属性)
if(  this.hasOwnProperty(vmKey) ){
    let watcher = new Watch(this,vmKey,item,'textContent');

    if( this.$watchEvent[vmKey] ){ //如果没有
        this.$watchEvent[vmKey].push(watcher);
    }else{ //如果有
        this.$watchEvent[vmKey] = [];
        this.$watchEvent[vmKey].push(watcher);
    }
}
class Watch{

    constructor(vm,key,node,attr){
        //对象
        this.vm = vm;
        //属性名称
        this.key = key;
        //节点
        this.node = node;
        //改变文本节点内容的字符串
        this.attr = attr;
    }
    //执行改变(update)操作
    update(){
        //节点的属性 改为 对象的key值
        this.node[this.attr] = this.vm[this.key];
    }

}
    //触发data中的数据发生变化来执行watch中的update
    observe(){
        for( let key in this.$data ){
            let value = this.$data[key];
            let that = this;
            Object.defineProperty(this.$data,key,{
                get(){
                    return value;
                },
                set( val ){ 
                    //this.$data属性修改时,它被赋值为接受的参数(也就是被赋予的新值)
                    value = val;
                    //key就是改变的属性--str
                    if( that.$watchEvent[key] ){
                        that.$watchEvent[key].forEach((item,index)=>{
                            item.update();
                        })
                    }
                }
            })
        }
    }	
面试题:双向绑定原理
vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。
通过Object.defineProperty()来实现数据劫持的。
1.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。
2.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
3.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
v-model双向绑定原理:
    双向绑定原理是再实现vue的模板解析,数据劫持,以及更新视图的基础上进行的。
    模板解析:就是遍历子节点,判断子节点的类型,是元素节点还是文本节点,文本节点通过去除空格、正则匹配来给节点赋值,替换模板里的数据。元素节点的话,需要调用解析器本身来递归赋值。
    数据劫持:是在使用this.加上data里的属性,可以拿到数据时出现的。给Vue大对象里建立新的属性,即data里的属性,两个保持双向一致就是数据的劫持。里面主要通过 Object.defineProperty()实现,三个参数分别是this代表vue对象,data的属性,需要修改的属性操作,用到里面的set方法。
    因为数据的双向绑定等很多地方会用到它,所以也说了一下。
    仅仅修改数据时不够的,还需要dom的修改,更新视图。
    更新视图:就是创建一个空对象,用来存储data属性数据等。实现一个watcher订阅者监听属性的变化,在里面创建执行改变节点数据的(update)操作,更新dom;实现一个observe监听器,使用Object.defineProperty(), 数据变化时触发set方法,在里面执行订阅者的update方法,从而更新视图。
    双向绑定:在元素节点的操作中,判断该节点是否添加了v-model属性,如果添加了,先进行去除空格处理,判断vue里是否有绑定给v-model的值的属性,如果有,就把它赋值给对应节点的value上(比如input框里面)。然后给这个元素节点(比如input框)添加输入事件,把它的value值赋值给vue中相对应的属性,这个属性值发生变化,就触发了observe监听器,调用Object.defineProperty()set方法,进而执行改变节点的update操作,完成了视图的更新。
    数据层变化,视图层随之变化;视图层变化,数据层页随之变化。
    以上就是数据的双向绑定原理。
//判断元素节点是否添加了v-model
if(  item.hasAttribute('v-model') ){
    let vmKey = item.getAttribute('v-model').trim();
    if( this.hasOwnProperty(vmKey) ){ //如果有对应属性,赋值给value
        item.value = this[vmKey];
    }
    item.addEventListener('input',(event)=>{ //如果input框内容改变,赋值给vue的属性
        this[vmKey] = item.value;
    })
}
面试题:什么是虚拟DOM
虚拟dom其实就是数据,diff算法把dom变成数据结构,操作数据,把最终的数据展示在页面上。
操作数据速度更快,也不会产生重绘、回流的问题;直接操作dom,比较影响性能
vue的虚拟dom借鉴了主流的snabbdom、virtual-dom
先使用h函数写虚拟节点生成,然后有了虚拟节点,才能使用patch函数拿旧的虚拟节点和新的虚拟节点作对比,最后把新的虚拟节点变为真实节点渲染到页面中。
1.虚拟dom的生成:就是通过h函数传递参数,到vnode生成一个对象数据,生成虚拟节点
2.先把真实的dom转换为虚拟的节点(判断有无elm属性,转为对象数据),再和新的虚拟dom对比

情况一:如果两个节点不是相同的元素节点(更简单),就暴力删除旧的节点,把新的虚拟节点创建为dom节点
    1)创建节点:判断有没有子节点, children 是不是为undefined
        新的节点有children(类型为数组),需要递归创建节点,加入到dom节点内
    2)插入新节点,删除旧节点
    
情况二:如果两个节点是相同的元素节点
面试题:diff算法
利用diff算法可以更多提升dom之间对比的性能(采用虚拟dom数据进行对比)。
G:\IT_Software\05.小鹿线-就业培训\06-面试题\高频面试录播\Vue面试题\08 Vue\33 手写diff算法-创建与删除
1.搭建环境  	斯拿包的姆  沃抽
    借鉴了主流:snabbdom、virtual-dom  
    npm init -y
    cnpm install webpack@5 webpack-cli@3 webpack-dev-server@3 -S
    cnpm install snabbdom -S
    新建webpack.config.js
    配置webpack.config.js
2.虚拟节点,新老节点的替换,匹配规则、虚拟节点和真实节点

虚拟节点和真实节点

虚拟节点:其实就是数据,vnode,创建和填内容

const vnode1 = h('ul',{},[ //子节点
  h('li',{key:'a'},'a'),   //加入key提升性能,只改变顺序,不删除、创建节点
  h('li',{key:'b'},'b'),
  h('li',{key:'c'},'c')
]);

patch(container,vnode1);//新老节点替换
//虚拟节点的数据
{
  children: undefined
  data: {}
  elm: h1
  key: undefined
  sel: "h1"
  text: "你好h1"
}

真实节点:

<h2>你好</h2>

新老节点替换的规则

​ 1、如果新老节点不是同一个节点名称,那么就暴力删除旧的节点,创建插入新的节点。

​ 2、只能同级比较,不能跨层比较。如果跨层那么就暴力删除旧的节点,创建插入新的节点。

​ 3、如果是相同节点,又分为很多情况 (要加入key)

3.1 新节点有没有children
        如果新的节点没有children,那就证明新节点是文本,那直接把旧的替换成新的文本
3.2 新节点有children
        新的有children,旧的也有children ===》就是diff算法的核心了【3.3】
        新的有children,旧的没有 ===》创建元素添加(把旧的内容删除清空掉,增加新的)
3.3 diff算法的核心(最复杂的情况) while条件里使用 if else 

1、 旧前 和 新前 
        匹配:旧前的指针++ 、 新前的指针++   全部匹配
2、 旧后 和 新后 
        匹配:旧后的指针-- 、 新后的指针--   第一个不匹配,指针到最后
3、 旧前 和 新后
        匹配:旧前的指针++ 、 新后的指针--	12相同,34不同  3指针往下走,4从最后往上走
4、 旧后 和 新前
        匹配:旧后的指针-- 、 新前的指针++  	旧的第一个和新的最后一个不相同 进入查找环节
5、 以上都不满足条件 ===》查找
        新的指针++ 	新的添加到页面上并且新在旧的中有,要给旧的赋值成undefined,新值的指针++,旧的有就					    赋值undefined,直到新的值和旧的相等 --》 旧的指针++,新的不东,遇到undefined,继					续 ++,直到旧的值与新的值相等,执行第一可能,两个指针都++,执行以前的操作,直到遇到没					 有的,进入6,创建郭删除
        前四种策略都不符合,进行查找,旧节点的指针指到的数据在新节点中查找,如果找到,如果旧节点中有新节点的第一个元素,把新节点的第一个元素添加到页面中,把旧节点中的赋值为undefined,同时新节点指针指针++,变为1,旧节点的指针保持为0不变,新指针查找第二个是否在旧节点中存在,存在就给他赋值为undefined,直到新节点的指针指向的值到与旧指针指向的值相等,(第一次查找到的值),即第一种策略,接下来两边指针同时向下走,就指针遇到undefined直接++,新指针不动,直到再遇到相等的,或者就指针没有的,旧到了策略6,创建或删除节点
        
6、 创建或者删除  --while外面,节点指针start和end不符合条件了,也就是新旧节点的数量不一样,进行创建或删						除操作

***注意:如果要提升性能,一定要加入key,key是唯一标示,在更改前后,确认是不是同一个节点。

面试题:讲一下MVVM
web1.0 前后端一个页面,责任不够细分,不好维护,没有静态页面时后端没法工作
       MVC后端先出的
web2.0 ajax出现,解决了前后端可以分离了,后端不用等前端页面了
       htmmls、css、js在一个页面,不好维护
出现了前端框架 MVC、MVVM
        解决问题:可以把一个特别大的页面进行拆分(组件化),单个组件进行维护
M就是data的model层,数据层
V就是view视图层,dom层,界面中展示的内容
VM就是视图模型层,可以理解为vue源码。
    在MVVM架构中,是不允许数据和视图直接通信的,只能通过ViewModel来通信,而ViewModel就是定义了一个Observer观察者。ViewModel是连接View和Model的中间件。
    ViewModel能够观察到数据的变化,并对视图对应的内容进行更新。
    ViewModel能够监听到视图的变化,并能够通知数据发生变化。
    典型的MVVM案例就是:v-model原理实现,通过 view视图层 更新 model数据层,通过源码中的属性数据监听到数据的变化,又会导致 view层的变化。
3.2 微信小程序面试题
面试题:如何自定义头部?
官方api : 
app.json进行配置,使用双引号
"window":{
    "navigationStyle":"custom",
}
面试题:如何自定义底部
配置底部,不需要在app.json配置tabbar,不写就可以了,然后自己配置
面试题:不校验URL
工具==》详情==》本地设置 勾选 不校验合法域名  : 项目上线前URL一定要请求到(不勾选也可以请求到数据)
在接口设置时添加域名

面试题:微信小程序的js文件和普通js有什莫区别?

两者看起来很像,但是有一套自己的规则
3.3 uni-app面试题
面试题:生命周期
应用生命周期、页面生命周期、组件生命周期(和vue一样)
应用生命周期:onLaunch只触发一次
            onShow:从后台进入前台显示
            onHide:从其前台进入后台
页面生命周期:onload监听页面加载
            onshow:监听页面显示
            onready:页面初次渲染完成
            onhide:监听页面隐藏
面试题:条件编译
在工具中,打if出现的条件编译,多端打包

例如:
<!-- #ifdef H5 -->
      <h1>这是h5端</h1>
<!-- #endif -->

第四章 面试题性能优化篇

4.1 加载优化
1. http请求
     尽量减少,能合并的合并
2. 图片的雪碧图,小图标做成一个大图标,使用时切图片定位
3. script标签位置,加defer或放在body最后面
4. link标签(css引入),css引入放在head里面
5. 短时间内大量执行某一函数、或者绑定事件,进行防抖节流操作(返回top)
4.2 图片优化
1. 图片懒加载
2. 响应式图片(主要是移动端),使用img的srcset,根据不同的分辨率显示不同尺寸的图片
3. webp代替其他格式
4. 小图标可以改用字体图标
5. 使用css sprite,小图标合并成大图片
4.3 渲染优化
1. 减少重绘和回流,涉及到样式、尺寸、节点增删的操作,都会触发
2. 动画尽量用absolute脱离文档流,使用transform和opcity,减少使用left、top
3. 动画的执行,requestAnimationFrame,不要用定时器
4.4 首屏优化
1. 长列表,后端给十万条数据,如何放在页面上,先放十条,触底加载更多,
2. 项目的html文件、css文件、图片、js文件压缩打包,删除无效的代码
3. 服务器端渲染或预渲染,加载完html直接渲染,减少白屏时间
4.5 vue优化
1. keep-alive 缓存组件
2. 路由懒加载,点击时再加载路由
3. 内容使用
    v-if和v-show (v-if不用不加载)(v-show频繁切换)
    computed、watch、methods 适当使用
4. Object.freeze :冻结对象
    纯展示类的接口数据,冻结就可以了
5. 使用ui组件按需引入
4.6 防抖、节流
案例:
    回到顶部onscroll事件,连续监测,函数执行频率很高,影响性能
    页面检测onresize
防抖:debounce(防抖)
【制一个函数在某个连续时间段内只执行一次,哪怕它本来会被调用多次。】
    在第一次触发事件时,不立即执行函数,设置延迟(期限值)。
    在200ms内没有再次触发,执行该函数;
    如果200ms内再次触发,则重新计时;
    避免短时间内,大量执行同意个事件,在特定时间内,只执行一次
节流:throttle(节流)
【事件绑定,短时间内触发多次绑定事件造成性能问题,如 onresize,onscroll等,使用节流函数可确保在指定时间内只触发一次】

第五章 面试题兼容篇

5.1 页面样式兼容
1. 在ios键盘中首字母大写的问题?
        <input type="text" autocapitalize='off'>
2. ios日期转换NAN问题
        具体就是,new Date('2020-11-12 00:00:00')在ios中会为NAN
        解决方案:用new Date('2020/11/12 00:00:00')的日期格式,或者写个正则转换
3. 在移动端使用click事件有300ms延迟的问题
     	禁止双击缩放===》meta:user-scalabel=no
4. 移动端touch事件有穿透(点透)的问题,怎么解决?
     	4.1 阻止默认行为 : e.preventDefault();
     	4.2 fastclick.js
5. 安卓部分版本input的placeholder偏上
        input{
      		 line-height:normal;
        }
面试题:提升用户体验?
1.之前做过一个电商页面,有个返回top的操作,不处理的话,滑动标签会一直触发onscroll事件,会严重影响性能。采用防抖节流的操作,进行优化。在第一次触发事件时,不立即执行函数,设置延迟。在200ms内没有再次触发,执行该函数;如果200ms内再次触发,则重新计时;避免短时间内,大量执行同意个事件,在特定时间内,只执行一次。
2.多给页面中的div盒子添加样式,比如鼠标悬停有图片浮动的样式
3.窗口的大小变化,页面页随之变化,这就是自适应布局
4.移动端项目的导航条,手机自带的导航条颜色有时不和app一致,需要设置json文件,开启沉浸式,设置meta、顶部和底部的padding
5.keep-alive缓存组件,用户点击页面跳转后浏览其他页面,再回到本页面,本页面保存之前的状态。

第六章 面试题网络请求篇

6.1 跨域面试题
前端:jsonp(请求第三方、地图sdk)、vue的项目可以设置代理(打包后无效。解决:.ENV文件)
后端:CORS,设置请求头
6.2 http和https的区别?
1. 端口不同
    http :80端口
    https :443端口
2. https比http更加安全
    http采用明文传输,有可能被劫持数据,没有加密验证
    ***https多一个安全层,每次请求会校验证书
6.3 输入url到打开页面做了那些事情?
  1、浏览器的地址栏输入URL并按下回车。

  2、浏览器查找当前URL的DNS缓存记录。

  3、DNS解析URL对应的IP。

  4、根据IP建立TCP连接(三次握手)。

  5、HTTP发起请求。

  6、服务器处理请求,浏览器接收HTTP响应。

  7、渲染页面,构建DOM树。(解析dom、css、js,render,layout,paint,合并展示)

  8、关闭TCP连接(四次挥手)。
6.4 http状态码?
状态码是前端发送请求,服务器响应信息种包括的。
200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误
6.5 tcp三次握手?
    在通过第一步的DNS域名解析后,获取到了服务器的IP地址,在获取到IP地址后,便会开始建立一次连接,这是由TCP协议完成的,主要通过三次握手进行连接。

  第一次握手: 建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认; 

  第二次握手: 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

  第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

  完成三次握手,客户端与服务器开始传送数据。
  https://www.cnblogs.com/daijinxue/p/6640153.html
6.6 http的请求方式?
8种请求方式:
OPTIONS HEAD GET POST PUT DELETE TRACE CONNECT
6.7 get和post请求差别?
1. Get是不安全的,因为在传输过程,数据被放在请求的URL中;Post的所有操作对用户来说都是不可见的。
2. Get传送的数据量较小,这主要是因为受URL长度限制;Post传送的数据量较大,一般被默认为不受限制。
3. Get限制Form表单的数据集的值必须为ASCII字符;而Post支持整个ISO10646字符集。
4. Get执行效率却比Post方法好。Get是form提交的默认方法。 收起 

第七章 WEB安全篇

7.1 XSS攻击
前后端分离安全性本质上来说不会太高,会暴露接口,大型网站知识拿出一些模块做前后端分离,整体来说时不分离的
在网页中嵌入代码、标签,比如 alert(),获取用户信息、页面跳转等
可以在用户输入的文本框,做一些正则,转换为文本,需要替换某些特殊字符( <> ...
7.2 SQL注入
设置:用户输入的文本框中不可以有特殊符号( 引号、空格 )
涉及到登陆注册,后端会使用sql语句,判断账号密码,如果输入时输入了 或 ,可能会直接登录
7.3 接口安全
通常来说,接口数据不做处理的话是可以看到
前后端加密解密,或者后端使用密钥,使用MD5加密等,可以做到相对安全
只要有接口就可能存在安全问题,所以大型网站知识拿出一些模块做前后端分离,整体来说时不分离的

第八章 其他类面试题

8.1 token
token是后端生成的
比如用户登录,每个用户都有对应的token,每个公司的token组成都是不一样的

token + cookie  	  : 前端判断是否过期
token + localStorage  : 后端判断是否过期,过期给前端返回code码,前端判断code码等于多少
8.2 SEO 搜索引擎优化
百度蜘蛛抓取,网站排位
1. 网站一定要多页面,多页面你内容越多
2. title、描述、关键字
3. 主题内容:图片、音频、视频、的标签属性特别关键,alt属性描述
4. 网站不能出现死链接,链接越多越好,网站越老越好

文章作者: gxl
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 gxl !