2022年10月 前端面试题总结——(二)

第一章 面试题基础篇

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有什么区别?

区别一:link先有,后有@import(兼容性link比@import兼容);
区别二:加载顺序差别,浏览器先加载的标签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和heigh区别【大厂】

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;
}

一个盒子不给宽度和高 度如何水平垂直居中?

方式一

main
.container
{ display: flex; justify-content: center; align-items: center; width: 300px; height: 300px; border:5px solid #ccc; } .main{ background: red; }

方式二

main
.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就是页面上一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。
1. 了解BFC : 块级格式化上下文。
2. BFC的原则:如果一个元素具有BFC,那么内部元素再怎么弄,都不会影响到外面的元素。
3. 如何触发BFC:
		float的值非none
		overflow的值非visible
		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

左侧固定 300px,右侧自适应的布局

.container {
  display: grid;
  grid-template-columns: 300px 1fr;
}/*或者如下*/
.container {
  display: flex;
}
.left {
  flex-basis: 300px;
  flex-shrink: 0;
}
.main {
  flex-grow: 1;
}

写一个左中右布局占满屏幕,其中左、右俩块固定宽200,中间自适应宽,要求先加载中间块,请写出结构及样式。

双飞翼布局
#left{
    float: left;
    margin-left: -100%;
}
#right{
    float: left;
    margin-left: -200px;
}
.middle-inner{
    margin: 0 200px;
}

什么是CSS reset?

reset.css   		是一个css文件,用来重置css样式的。
normalize.css 	为了增强跨浏览器渲染的一致性,一个CSS 重置样式库。

css sprite是什么,有什么优缺点

1. 是什么
	把多个小图标合并成一张大图片。
2. 优缺点
	优点:减少了http请求的次数,提升了性能。
	缺点:维护比较差(例如图片位置进行修改或者内容宽高修改)

display: none;与visibility: hidden;的区别

1. 占用位置的区别
display: none; 是不占用位置的
visibility: hidden;   虽然隐藏了,但是占用位置

2. 重绘和回流的问题
visibility: hidden;display: none;  产生重绘
display: none;     还会产生一次回流

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

产生回流的情况:改变元素的位置(left、top...)、显示隐藏元素....
产生重绘的情况:样式改变、换皮肤

opacity 和 rgba区别

共同性:实现透明效果
1. opacity 取值范围0到1之间,0表示完全透明,1表示不透明
2. rgba   R表示红色,G表示绿色,B表示蓝色,取值可以在正整数或者百分数。A表示透明度取值0到1之间
区别:继承的区别
opacity会继承父元素的opacity属性,而RGBA设置的元素的后代元素不会继承不透明属性。

1.3 JavaScript基础面试题

延迟加载JS有哪些方式?

延迟加载:async、defer
		例如:
		
defer : 等html全部解析完成,才会执行js代码,顺次执行js脚本。
async : async是和html解析同步的(一起的),不是顺次执行js脚本(谁先加载完谁先执行)。

JS数据类型有哪些?

基本类型:string、number、boolean、undefined、null、symbol、bigint
引用类型:object(String Number Date RegExp Error Function Map Set)

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

null和undefined的区别

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

具体区别:JavaScript的最初版本是这样区分的:null是一个表示"无"的对象(空对象指针),转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。

=有什么不同?

==  :  比较的是值
		string == number || boolean || number ....都会隐式转换
		通过valueOf转换(valueOf() 方法通常由 JavaScript 在后台自动调用,并不显式地出现在代码中。)
=== : 除了比较值,还比较类型

JS微任务和宏任务

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

要执行宏任务的前提是清空了所有的微任务
流程:同步==》事件循环【微任务和宏任务】==》微任务==》宏任务=》微任务...

JS作用域考题

1. 除了函数外,js是没有块级作用域。JS中作用域有:全局作用域、函数作用域。没有块作用域的概念
ES6)中新增了块级作用域。 块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。

2. 作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。
	 注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。
3. 注意声明变量是用var还是没有写(window.)
4. 注意:js有变量提升的机制【变量悬挂声明】
5. 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升

面试的时候怎么看:

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

考题一

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

考题二:

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

考题三

function fun( a ){
	var a = 10;
	function a(){}
	console.log( a );
}
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数组去重

方式一:new set

var arr1 = [1,2,3,2,4,1];
function unique(arr){
	return [...new Set(arr)]
}
console.log(  unique(arr1) );

方式二: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) );

方式三: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) );

找出多维数组最大值

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]
]));//[5, 27, 39, 1001]

给字符串新增方法实现功能

给字符串对象定义一个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);
	}
}

new操作符具体做了什么

1. 创建了一个空的对象
2. 将空对象的原型,指向于构造函数的原型
3. 将空对象作为构造函数的上下文(改变this指向)
4. 对构造函数有返回值的处理判断
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,'张三')   )

闭包

1. 闭包是什么
	闭包是一个函数加上到创建函数的作用域的连接,闭包“关闭”了函数的自由变量。
2. 闭包可以解决什么问题【闭包的优点】
	2.1 内部函数可以访问到外部函数的局部变量
	2.2 闭包可以解决的问题
			var lis = document.getElementsByTagName('li');
      for(var i=0;i<lis.length;i++){
        (function(i){
          lis[i].onclick = function(){
            alert(i);
          }
        })(i)
      }
3. 闭包的缺点
	3.1 变量会驻留在内存中,造成内存损耗问题。
解决:把闭包的函数设置为null
  3.2 内存泄漏【ie】 ==> 可说可不说,如果说一定要提到ie

原型链

1. 原型可以解决什么问题
	对象共享属性和共享方法
2. 谁有原型
函数拥有:prototype
对象拥有:__proto__
3. 对象查找属性或者方法的顺序
	先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中查找
4. 原型链
	4.1 是什么?:就是把原型串联起来
	4.2 原型链的最顶端是null

JS继承有哪些方式

方式一:ES6

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

class Child extends Parent{
	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();
let o2 = new Child();
console.log( o2,o2.name,o2.age );

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

function Parent(){
	this.age = 22;
}
function Child(){
	this.name = '张三'
	Parent.call(this);
}
let o3 = new Child();
console.log( o3,o3.name,o3.age );

方式四:组合式继承

function Parent(){
	this.age = 100;
}
function Child(){
	Parent.call(this);
	this.name = '张三'
}
Child.prototype = new Parent();
let o4 = new Child();
console.log( o4,o4.name,o4.age );

说一下call、apply、bind区别

共同点:功能一致
可以改变this指向
语法: 函数.call()、函数.apply()、函数.bind()
区别:
1. call、apply可以立即执行。bind不会立即执行,因为bind返回的是一个函数需要加入()执行。
2. 参数不同:apply第二个参数是数组。call和bind有多个参数需要挨个写。
场景:
1. 用apply的情况
var arr1 = [1,2,4,5,7,3,321];
console.log( Math.max.apply(null,arr1) )

2. 用bind的情况
var btn = document.getElementById('btn');
var h1s = document.getElementById('h1s');
btn.onclick = function(){
	console.log( this.id );
}.bind(h1s)

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);

2. 深拷贝:是复制真正的值 (不同引用)
var obj3 = {
	a:1,
	b:2
}
var obj4 = JSON.parse(JSON.stringify( obj3 ));

//递归的形式
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面试题

什么是语义化标签

1. 易读性和维护性更好。
2. seo成分会更好,蜘蛛抓取更好。
3. IE8不兼容HTML5标签的。解决:可以通过html5shiv.js去处理。

::before 和 :after中双冒号和单冒号 有什么区别?解释一下这2个伪元素的作用。

1. 区别
	:是伪类、::伪元素  ===》是为了做区分

2.是什么?作用
	元素before之前 、 元素after之后
	作用:清除浮动、样式布局上也有作用

如何关闭IOS键盘首字母自动大写


怎么让Chrome支持小于12px 的文字?

-webkit-transform

Chrome默认字体大小是:16px
**每个浏览器默认字体大小可能都不一样

<style type="text/css">
div{
	font-size:10px;
}
div span{
	display: inline-block;
	-webkit-transform:scale(1.6);
}
</style>

rem和em区别

相对于font-size

em针对于父元素的font-size
rem针对于根(html)元素的font-size

ios系统中元素被触摸时产生的半透明灰色遮罩怎么去掉


webkit表单输入框placeholder的颜色值能改变吗?


禁止ios长按时触发系统的菜单,禁止ios&android长按时下载图片

禁止ios 长按时触发系统的菜单,禁止ios&android长按时下载图片
html,body{
	touch-callout: none;
	-webkit-touch-callout: none;
	
	user-select:none;
	-webkit-user-select:none;
}

禁止ios和android用户选中文字

html,body{
	user-select:none;
	-webkit-user-select:none;
}

自适应布局

**布局特点:**屏幕分辨率变化时,页面里面元素的位置会变化,而大小不会变化。

1.淘宝无限适配【移动端】:淘宝无限适配 + 布局单位使用rem
2.根据屏幕大小不同引入不同的css样式文件
3.高度自适应	
4.宽度自适应:宽度自适应有三种方法,分别是
​	1)利用绝对定位来设置宽度自适应布局
​	2)利用margin,中间模块先渲染来设置宽度自适应布局
​	3)利用自身浮动来设置宽度自适应布局

响应式布局

**布局特点:**每个屏幕分辨率下面会有一个布局样式,即元素位置和大小都会变。

1. 是什么?
	一个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. 响应式图片【性能优化】
	
		"1.jpg" media='(min-width:1000px)'>
		"2.jpg" media='(min-width:700px)'>
		"3.jpg">
	

布局方案:媒体查询+流式布局。

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

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

三、pc的设计图
	ui:1980
	笔记本电脑:1280
	ui图的宽度和电脑的宽度不对应该怎么办?
		1. 把ui图进行等比缩放,缩放成和电脑一样的尺寸
		2. 换1980的电脑
四、移动端的设计图
	宽度:750
	因为750设计图/2就是375,正好是iphone6的尺寸,我们要把iphone6的尺寸做为基准点。

第二章 面试题进阶篇

2.1 ES6面试题

var、let、const区别

var、let、const 共同点都是可以声明变量的

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

作用域考题

考题一: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

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

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

方式二:…

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(已失败)

find和filter的区别

区别一:返回的内容不同
	filter 返回是新数组
	find   返回具体的内容
区别二:
	find :匹配到第一个即返回
	filter : 返回整体(没一个匹配到的都返回)

some和every的区别

some  ==》 如果有一项匹配则返回true
every ==》 全部匹配才会返回true

2.2 webpack面试题

webpack插件

Webpack 是一个项目打包工具。可以压缩代码和图片,把浏览器识别不了的代码转化为能识别的,可以启动一个热加载服务器

  • Webpack可以看做是模块打包机:webpack处理应用程序时,内部常见一个依赖项目结构图映射项目需要的模块,然后将这些依赖生成一个或多个bundle。例如找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。

Webpack可以解决传统构建工具的问题(按需加载)

  • 模块化打包,一切皆模块,js是模块,css也是模块;
  • 语法糖转换:比如ES6转ES5、TypeScript;
  • 预处理器编译:scss,less等;
  • 项目优化:比如压缩,CDN
  • 解决方案分装:通过强大的Loader和插件机制,可以完成解决方案的封装,比如PWA
  • 流程对接:比如测试流程、语法检测等

Webpack构建流程

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:

  • 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler

  • 编译:从 Entry 出发,针对每个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理

  • 输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中

具体为:

  • 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
  • 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
  • 确定入口:根据配置中的 entry 找出所有的入口文件
  • 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
  • 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
  • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
  • 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。

webpack中提高效率的插件

  • webpack-dashboard:可以更友好的展示相关打包信息。
  • webpack-merge:提取公共配置,减少重复配置代码
  • speed-measure-webpack-plugin:简称 SMP,分析出 Webpack 打包过程中 Loader 和 Plugin 的耗时,有助于找到构建过程中的性能瓶颈。
  • size-plugin:监控资源体积变化,尽早发现问题
  • HotModuleReplacementPlugin:模块热替换

优化 Webpack 的构建速度

  • 使用高版本的 Webpack 和 Node.js
  • 多进程/多实例构建:HappyPack(不维护了)、thread-loader
    • thread-loader 是一個可以大幅減少 build time 的一個 loader,在 heavy-loader 之前放上 thread-loader 就可以了,babel-loader 和 css-loader算是heavy-loader,花的时间最长。在babel-loader或者 css-loader 前面放上 thread-loader。
  • 压缩代码
    • webpack-parallel-uglify-plugin
      • 开启多个子进程,把对多个文件的压缩工作分配给多个子进程去完成,每个子进程其实还是通过 UglifyJS 去压缩代码,但是变成了并行执行。
    • uglifyjs-webpack-plugin 开启 parallel 参数 (不支持ES6)
    • terser-webpack-plugin 开启 parallel 参数
    • 多进程并行压缩
    • 通过 mini-css-extract-plugin 提取 Chunk 中的 CSS 代码到单独文件,通过 css-loader 的 minimize 选项开启 cssnano 压缩 CSS。
  • 图片压缩
    • 使用基于 Node 库的 imagemin (很多定制选项、可以处理多种图片格式)
    • 配置 image-webpack-loader
  • 缩小打包作用域
    • exclude/include (确定 loader 规则范围)
    • resolve.modules 指明第三方模块的绝对路径 (减少不必要的查找)
    • noParse :在配置模块相关时,module:{},对完全不需要解析的库进行忽略 (不去解析但仍会打包到 bundle 中,注意被忽略掉的文件里不应该包含 import、require、define 等模块化语句)
    • resolve.mainFields 只采用 main 字段作为入口文件描述字段 (减少搜索步骤,需要考虑到所有运行时依赖的第三方模块的入口文件描述字段)
    • resolve.extensions 尽可能减少后缀尝试的可能性
    • IgnorePlugin (完全排除模块)
    • 合理使用alias
  • 提取页面公共资源
    • 使用 html-webpack-externals-plugin,将基础包通过 CDN 引入,不打入 bundle 中
    • 使用 SplitChunksPlugin 进行(公共脚本、基础包、页面公共文件)分离(Webpack4内置) ,替代了 CommonsChunkPlugin 插件
    • 基础包分离:
  • DLL
    • 使用 DllPlugin 进行分包,使用 DllReferencePlugin(索引链接) 对 manifest.json 引用,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间。
    • HashedModuleIdsPlugin 可以解决模块数字id问题
  • 充分利用缓存提升二次构建速度
    • babel-loader 开启缓存
    • terser-webpack-plugin 开启缓存
    • 使用 cache-loader 或者 hard-source-webpack-plugin
  • Tree shaking
    • 可以用来剔除 JavaScript 中用不上的死代码。
    • purgecss-webpack-plugin 和 mini-css-extract-plugin配合使用(建议)
    • 打包过程中检测工程中没有引用过的模块并进行标记,在资源压缩时将它们从最终的bundle中去掉(只能对ES6 Modlue生效) 开发中尽可能使用ES6 Module的模块,提高tree shaking效率
    • 禁用 babel-loader 的模块依赖解析,否则 Webpack 接收到的就都是转换过的 CommonJS 形式的模块,无法进行 tree-shaking
    • 使用 PurifyCSS(不在维护) 或者 uncss 去除无用 CSS 代码
  • Scope hoisting 作用域提升
    • 构建后的代码会存在大量闭包,造成体积增大,运行代码时创建的函数作用域变多,内存开销变大。Scope hoisting 将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突
    • 必须是ES6的语法,因为有很多第三方库仍采用 CommonJS 语法,为了充分发挥 Scope hoisting 的作用,需要配置 mainFields 对第三方模块优先采用 jsnext:main 中指向的ES6模块化语法
  • 动态Polyfill
    • 建议采用 polyfill-service 只给用户返回需要的polyfill,社区维护。(部分国内奇葩浏览器UA可能无法识别,但可以降级返回所需全部polyfill)

2.3 Git面试题

Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。
Git使用 C 语言编写。 GIT 很快,C 语言通过减少运行时的开销来做到这一点

git常用命令

1)使用我们指定目录作为Git仓库。git init newrepo
2)添加新文件。可以使用add命令添加文件。git add filename
3)提交文件。能真正被保存在Git仓库。git commit -m "Adding files"
4)当我们修改了很多文件,而不想每一个都add,想commit自动来提交本地修改,我们可以使用-a标识。git commit -a -m "Changed some files"
5)删除一个文件 git rm [file name]
6)从git中删除指定文件 git rm 文件名(包括路径)
7)git clone git://github.com/schacon/grit.git 从服务器上将代码给拉下来
8)看所有用户 git config --list
9)看已经被提交的 git ls-files
10)查看本地所有分支。git branch
11)查看当前状态.。git status
12)查看所有的分支。git branch -a
13)查看远程所有分支。git branch -r
14)提交并且加注释。git commit -am "init"
15)将文件给推到服务器上 git push origin master
16)显示远程库origin里的资源 git remote show origin
17)将本地库与服务器上的库进行关联。git push origin master:hb-dev
18)切换到远程dev分支 git checkout --track origin/dev
19)删除本地库develop git branch -D master develop
20)建立一个新的本地分支 dev git checkout -b dev
21)将分支dev与当前分支进行合并 git merge origin/dev
22)切换到本地dev分支 git checkout dev
23)查看远程库 git remote show

分支明细

(1)主分支(master):第一次向 git 仓库中提交更新记录时自动产生的一个分支。

(2)开发分支(develop):作为开发的分支,基于 master 分支创建。

(3)功能分支(feature):作为开发具体功能的分支,基于开发分支创建

分支命令

(1)git branch 查看分支

(2)git branch 分支名称 创建分支

(3)git checkout 分支名称 切换分支

(4)git merge 来源分支 合并分支 (备注:必须在master分支上才能合并develop分支)

(5)git branch -d 分支名称 删除分支(分支被合并后才允许删除)(-D 强制删除)

暂时保存更改

(1)存储临时改动:git stash

(2)恢复改动:git stash pop

解决冲突

开发过程中,每个人都有自己的特性分支,所以冲突发生的并不多,但如公共类的公共方法,当两人以上同时修改同一个文件,先后提交这些情况下就会报冲突的错误。

1)发生冲突,在IDE里面一般都是对比本地文件和远程分支的文件,然后把远程分支上文件的内容手工修改到本地文件,然后再提交冲突的文件。使其保证与远程分支的文件一致,这样才会消除冲突,然后再提交自己修改的部分。特别要注意下,修改本地冲突文件使其与远程仓库的文件保持一致后,需要提交后才能消除冲突,否则无法继续提交。必要时可与同事交流,消除冲突。

2)发生冲突,也可以使用命令。

  • 通过git stash命令,把工作区的修改提交到栈区,目的是保存工作区的修改;

  • 通过git pull命令,拉取远程分支上的代码并合并到本地分支,目的是消除冲突;

  • 通过git stash pop命令,把保存在栈区的修改部分合并到最新的工作空间中;

Git中如果本次提交误操作,如何撤销?

  • 如果想撤销提交到索引区的文件,可以通过git reset HEAD file来撤销;

  • 如果想撤销提交到本地仓库的文件,可以通过git reset –soft HEAD^n恢复当前分支的版本库到上一次提交的状态,索引区和工作空间不变更;

  • 可以通过git reset –mixed HEAD^n恢复当前分支的版本库和索引区至上一次提交的状态,工作区不变更;

  • 可以通过git reset –hard HEAD^n恢复当前分支的版本库、索引区和工作空间至上一次提交的状态。

Git和SVN有什么区别?

Git SVN
1. Git是一个分布式的版本控制工具 1. SVN 是集中版本控制工具
2.Git属于第3代版本控制工具 2.SVN属于第2代版本控制工具
3.客户端可以在其本地系统上克隆整个存储库 3.版本历史记录存储在服务器端存储库中
4.即使离线也可以提交 4.只允许在线提交
5.Push/pull 操作更快 5.Push/pull 操作较慢
6.工程可以用 commit 自动共享 6.没有任何东西自动共享

git pull 和 git fetch 有什么区别?

git pull 命令:从中央存储库中提取特定分支的新更改或提交,并更新本地存储库中的目标分支。

git fetch :也用于相同的目的,但它的工作方式略有不同。当你执行 git fetch 时,它会从所需的分支中提取所有新提交,并将其存储在本地存储库中的新分支中。如果要在目标分支中反映这些更改,必须在 git fetch 之后执行git merge。只有在对目标分支和获取的分支进行合并后才会更新目标分支。为了方便起见,请记住以下等式:

<center><h5>git pull = git fetch + git mergeh5>center>

git config 的功能是什么?

git 使用你的用户名将提交与身份进行关联。 git config 命令可用来更改你的 git 配置,比如说用户名啥的。

假设你要提供用户名和电子邮件 ID 用来将提交与身份相关联,以便你可以知道是谁进行了特定提交。为此,我将使用:

git config –global user.name “Your Name”: 此命令添加用户名。

git config –global user.email “Your E-mail Address”: 此命令添加电子邮件ID。

第三章 面试题框架篇

3.1 Vue面试题

Vue2.x 生命周期

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

v-show、v-if的作用与区别

2.1 v-show

显示和隐藏 : display:none进行隐藏 、display:block进行显示		

2.2 v-if

创建和删除:remove、append		

2.3 区别:

显示和隐藏用:v-show
创建和删除用:v-if

频繁切换用:v-show
不频繁切换用:v-if	

首次加载:用v-if,不用v-show
为什么:如果用v-if可以没有这个盒子,然后再通过v-if进行创建(但是第一次进入页面是没有这个盒子,是不加载的)。
	   如果用v-show这个盒子不管是显示还是隐藏,该盒子都是在的(节点都是存在)

2.4 使用场景:

v-show : 加入购物车、分享、蒙层这种都基本上用v-show
v-if : 首页栏目切换的时候v-if

v-if和v-for的 优先级

v-for的优先级要比v-if的优先级高

证明这个事情,是在vue.js源码种10997行

if (el.staticRoot && !el.staticProcessed) {
  return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
  return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
  return genFor(el, state)
} else if (el.if && !el.ifProcessed) {
  return genIf(el, state)
} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
  return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
  return genSlot(el, state)
} else {

注:v-if和v-for不要写在同一个节点上,这个性能很差。(v-if要写在父节点上)

ref是什么

获取dom。ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素

使用场景:如果项目中使用插件,并且插件是要获取dom的,那么就可以使用ref了。

keep-alive是什么

缓存组件,keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 。

一旦使用keep-alive会多两个生命周期:activated、deactivated

功能:提升性能的

keep-alive作用如何使用

作用就是能够缓存不活动的组件,组件进行切换的时候,默认会进行销毁,如果有需求,某个组件切换后不进行销毁,而是保存之前的状态,那么就可以利用keep-alive来实现。主要用于保留组件状态或避免重新渲染;
使用:简单页面时
缓存: < keep-alive include=”组件名”>< /keep-alive>
不缓存: < keep-alive exclude=”组件名”>< /keep-alive>

nextTick 是什么

核心:当dom更新完毕,执行内部代码

nextTick()是将回调函数延迟在下一次dom更新数据后调用,简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数, nextTick()的原理: 当调用nextTick方法时会传入两个参数,回调函数和执行回调函数的上下文环境,如果没有提供回调函数,那么将返回promise对象。

使用场景:使用插件的时候会用到。例如new Swiper这个插件可能会获取当前元素的宽度或者高度,等dom都加载完毕再去获取宽度和高度就不会有任何问题了。

computed、methods、watch区别

computed:计算属性

methods : 可以放入函数:没有缓存,如果一进入页面调用,就会触发

watch :监听(路由和数据)、当数据发生改变时,才会触发、可以得到现在的值和过去的值

三者的共同点

methods、computed、watch都是以函数为基础的

computed与watch都是以vue的依赖为基础,当所依赖的数据发生变化的时候会触发相关的函数去实现数据的变动

methods里是用来定义函数的,需要手动才能执行,不想computed和watch是“自动执行”函数

三者的不同点

computed是计算属性,computed所依赖的属性发生变化是,计算属性才会重新计算,并进行缓存。当其他数据发生改变的时候computed属性不会重新计算,从而提升性能

watch:

a、watch类似于事件监听+事件机制

b、watch的方法默认是不会执行的,只有依赖的属性发生变化才会执行

c、watch默认第一次是不会执行的,通过声明immediate选项为true可以立即执行一次handler

d、watch用在监听数据变化,给后台发送数据请求

e、watch中的handler默认只能监听属性引用的变化,但内部属性是监听不到的,设置deep为true可以进行深度监听,但是性能开销也会变大

f、watch无法监听数据值的变化(特殊情况下)

computed和watch的使用场景

​ computed一个数据受多个数据的影响  例如:商品购物车的结算,过滤某些商品数据

​ watch一个数据影响多个数据  例如:网络请求、浏览器自适应、监控路由对象

Vue组件的通信(组件的传值)

父传子

父:
<HelloWorld :msg="str" />
<HelloWorld :msg="str" ></HelloWorld>

子:
props:['msg']
props: {
	msg: String,
},

子传父

子:
<button @click="changeParentName">改变父组件的name</button>
export default {
    methods: {
        //子组件的事件
        changeParentName: function() {
            this.$emit('handleChange', 'Jack') // 触发父组件中handleChange事件并传参Jack
            // 注:此处事件名称与父组件中绑定的事件名称要一致
        }
    }
}

父:
<child @handleChange="changeName"></child>
methods: {
    changeName(name) {  // name形参是子组件中传入的值Jack
        this.name = name
    }
}

兄弟组件传值

创建bus作为中转

import Vue from "vue";
export default new Vue;
A组件:
<button @click='btn'>HelloWorld按钮</button>
data () {
    return {
      hlStr:"这是helloWorld组件的数据"
    }
},
methods:{
    btn(){
      bus.$emit('selectItem',this.hlStr);
    }
}
B组件:
created(){
		bus.$on('selectItem',(val)=>{
			console.log( val , 1111);
		})
}

slot插槽

理解为一个占位符,当需要用时可以定义一个slot,不需要用时slot,页面上也不会有痕迹。 插槽显不显示是由父盒子决定的,插槽在哪个位置上显示是由子组件决定的。

使用场景:组件中有些地方的布局可能大多一致,但是细微有些小小变化。

Vue router 前端路由

什么是路由:根据不同的url地址展示不同的页面或者数据

前端路由的理解:a、前端路由用于单页面开发,SPA。b、前端路由是不涉及到服务器的,是前端利用hash或者h5的historyAPI来实现的,一般用于不同的内容展示和切换

前端路由:把不同路由对应不同的内容或页面的任务交给前端来做,之前是通过服务端根据 url 的不同返回不同的页面实现的。在 Web 前端单页应用 SPA(Single Page Application)中,路由描述的是 URL 与 UI 之间的映射关系,这种映射是单向的,即 URL 变化引起 UI 更新(无需刷新页面)。

路由基础语法

  • 基础语法
//index.js配置路由相关的信息
import VueRouter from 'vue-router'
import Vue from 'vue'

import Home from '../component/Home'

//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter)

//2.创建VueRouter对象
const router = new VueRouter({
  routes,
  mode: 'history'  //默认是hash模式,这样设置后会设置成history模式
})
const routes = [
  {
    path: '/',
    redirect: '/home'
  },
  {
    path:'/home',
    component: Home
  }
]

//3.将router对象传入到Vue实例
export default router 
  
--------------------------------------------------------------
//App.vue文件中挂载
首页 //会被渲染成a标签
 //占位
  • 通过代码修改路由
    this.$router.push('/home')
    this.$router.replace('/home')

  • 路由懒加载

    const Home = () => import('../components/Home')

vue路由跳转的方式

①a标签进行跳转  首页

②router-link进行跳转  首页

③编程式路由

​ 1、this.$router.push()跳转到指定的url,并在history中添加记录,点击回退返回到上一个页面

​ 2、this.$router.replace()跳转到指定的url,但是history中不会添加记录,点击回退到上上个页面

​ 3、this.$touter.go(n)向前或者后跳转n个页面,n可以是正数也可以是负数

路径传值

显示:

传:
this.$router.push({
        path:'/about',
        query:{
          key:'你好'
        }
})
接:
this.$route.query

隐示:

传:
this.$router.push({
        name:'About',
        params:{
          key:'你好'
        }
})
接:
this.$route.params

路由的模式

mode: "history"   http://localhost:8080/about
mode:"hash"       http://localhost:8080/#/about

路由导航守卫(拦截、路由钩子函数)

全局

beforeEach
beforeResolve
afterEach

路由独享

beforeEnter

组件内

beforeRouteEnter
beforeRouteUpdate (2.2 新增)
beforeRouteLeave

场景:要去拦截,判断用户是否是登录状态。功能:进入地址管理,用户如果没有登录是进入不了地址管理(在进入之前判断拦截),需要先登录。

子路由、动态路由

子路由:children
动态路由:path: '/user/:id'

双向绑定原理

通过Object.defineProperty劫持数据发生的改变,如果数据发生改变了(在set中进行赋值的),触发update方法进行更新节点内容({{ str }}),从而实现了数据双向绑定的原理。

diff算法

功能:提升性能

虚拟dom ===》其实就是数据( 把dom数据化 )

主流:snabbdom、virtual-dom

snabbdom:https://www.npmjs.com/package/snabbdom

搭建环境

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

虚拟节点 和 真实节点

虚拟节点:

{
  children: undefined
  data: {}
  elm: h1
  key: undefined
  sel: "h1"
  text: "你好h1"
}

真实节点:

你好

新老节点替换的规则

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

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

​ 3、如果是相同节点,又分为很多情况

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

1、 旧前 和 新前
		匹配:旧前的指针++ 、 新前的指针++
2、 旧后 和 新后
		匹配:旧后的指针-- 、 新后的指针--
3、 旧前 和 新后
		匹配:旧前的指针++ 、 新后的指针--
4、 旧后 和 新前
		匹配:旧后的指针-- 、 新前的指针++
5、 以上都不满足条件 ===》查找
		新的指针++,新的添加到页面上并且新在旧的种有,要给旧的复制成undefined
6、 创建或者删除

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

MVVM开发模式的理解

  • MVVM分为Model、View、ViewModel三者。

  • Model 代表数据模型,数据和业务逻辑都在Model层中定义;View 代表UI视图,负责数据的展示;ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作;

  • ModelView 并无直接关联,而是通过 ViewModel 来进行联系的,ModelViewModel 之间有着双向数据绑定的联系。因此当 Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步。

  • 这种模式实现了 ModelView 的数据自动同步,因此开发者只需要专注对数据的维护操作即可,而不需要自己操作 dom

mvvm和mvc的区别

  • MVVM和MVC都是一种设计思想,主要就是MVC中的Controller演变成ViewModel,,MVVM主要通过数据来显示视图层而不是操作节点,解决了MVC中大量的DOM操作使页面渲染性能降低,加载速度慢,影响用户体验问题。主要用于数据操作比较多的场景。
    场景:数据操作比较多的场景,更加便捷。

  • 传统的 MVC 架构通常是使用控制器更新模型,视图从模型中获取数据去渲染。当用户有输入时,会通过控制器去更新模型,并且通知视图进行更新,但MVC 有一个巨大的缺陷就是随着项目功能越多、项目越加复杂,控制器中的代码会越来越多,不利于维护。

  • 在 MVVM 架构中,引入了 ViewModel 的概念。ViewModel 只关心数据和业务的处理,不关心 View 如何处理数据,在这种情况下,View和 Model 都可以独立出来,任何一方改变了也不一定需要改变另一方,并且可以将一些可复用的逻辑放在一个 ViewModel 中,让多个 View 复用这个 ViewModel。

nextTick是什么?

路由导航守卫有哪些?

Vue中如何做样式穿透

scoped属性:持在