CSS
1、实现水平垂直居中
- 没有宽高(也适用于有宽高)
- flex布局
.parent {
display: flex;
justify-content: center;
align-items: center;
}
或者
.parent {
display: flex;
}
.children {
margin: auto;
}
- grid布局
.parent {
display: grid;
justify-self: center;
align-self: center;
}
或者
.parent {
display: grid;
}
.children {
margin: auto;
}
- table 表格布局
.parent {
display: table-cell;
text-align: center;
vertical-align: middle;
}
.children {
display: inline-block;
}
- 子绝父相
.parent {
position: relative;
}
.children {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
- 有宽高(必须有宽高才生效)
子绝父相
- 1.1
-
.parent { position: relative; } .children { position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; }
- 1.2
-
.parent { position: relative; } .children { position: absolute; top: 50%; left: 50%; margin-top: -(自身高度一半)px; margin-left: -(自身宽度一半)px; }
2、flex布局
flex布局是指弹性布局,可以灵活的设置盒子的自适应布局,任何容器都能设置flex布局,当设置了flex布局后, float、clear、vertical-align 属性失效
父盒子属性:
- flex-direction 设置主轴
- flex-wrap 是否换行
- flex-flow 是flex-direction 和 flex-wrap 的简写 flex-flow: row nowrap; ——设置x为主轴,不换行
- justify-content 设置子项目在主轴上的对齐方式
- align-items 设置子项目在侧轴上的对齐方式(一行时)
- align-content 设置子项目在侧轴上的对齐方式,多行时才生效
子盒子属性:
- align-self 单独设置某个子项目的布局
阮一峰老师:Flex 布局教程:语法篇 - 阮一峰的网络日志
3、Grid 布局
Grid ——网格布局,强大的CSS布局方案。
Grid容器属性 :
- display:grid ——指定一个容器使用grid布局
- display:inline-grid ——设置为行内元素( 注意,设为网格布局以后,容器子元素(项目)的
float
、display: inline-block
、display: table-cell
、vertical-align
和column-*
等设置都将失效。) - grid-template-columns,grid-template-rows 分别定义每一列的列宽,和每一行的行高
- grid-template-columns/rows:1px 1px 1px 设置三列,且宽度/高度都为1px
- grid-template-columns/rows: 33.3% 33.3% 33.3%
- grig-template-columns/rows: 1fr 1fr 2fr 按比例设置
- grid-template-columns/rows: repeat(3, 10px, 20px, 30px) 第一个参数是重复的数字,第二个后面是要重复的值,即设置三行/三列,大小分别是10px, 20px,30px
- gird-template-columns: repeat(auto-fill, 10px) 让子项目个数根据容器的大小自动填充
- justify-items/align-items/place-items ——设置单元格内容在水平/垂直位置(place为两者的合并简写)
- justify-content/align-content ——设置整个内容区域在水平/垂直位置
项目属性 :
grid-columns-start、grid-columns-end、grid-rows-start、grid-rows-end...
Grid 布局与 Flex 布局有一定的相似性,都可以指定容器内部多个项目的位置。但是,它们也存在重大区别。
Flex 布局是轴线布局,只能指定"项目"针对轴线的位置,可以看作是一维布局。Grid 布局则是将容器划分成"行"和"列",产生单元格,然后指定"项目所在"的单元格,可以看作是二维布局。Grid 布局远比 Flex 布局强大。
阮一峰老师的 :CSS Grid 网格布局教程 - 阮一峰的网络日志
4、什么是BFC
- BFC是指块级格式化上下文,是一个独立渲染的区域,不会影响到外部的元素
- BFC的特性
- 同一个BFC下,元素的margin 会重叠
- 计算BFC高度时会算上浮动元素
- 内部的元素垂直排列
- BFC区域不会与float元素重叠
- BFC不会影响外部的元素
- 如何触发BFC
- position 设置为 absolute 或 fixed
- overflow 设置为hidden
- float 设置为none
- display为 inline-block/ table-cell/ flex
5、如何清除浮动
- 额外标签法
- 在需要清除浮动的最后一个标签后面添加一个空标签,并且空标签设置样式 clear: both;
利用BFC——三种方法
- 父元素设置 overflow:hidden
- 父元素添加 after 伪元素
- 父元素添加 双伪元素
6、DOM事件流,事件委托
- 事件流分为三个阶段:
- 捕获阶段
- 目标阶段
- 冒泡阶段
- 把 addeventListener() 的第三个参数(userCapture)改为true 时,就会在捕获阶段运行,默认是false 冒泡阶段
- 事件委托:利用冒泡的原理,子元素向父元素层层穿透,把子元素的事件绑定到父元素的上,以实现事件委托
7、常见的行内元素和块级元素
- 行内元素inline:
- img、input、textarea、span、label、select
- 块级元素block:
- div、p、h1/h2/h3/h4/h5/h6、ul、ol、li、table
8、选择器的优先级
!important > 行内样式(又称内联样式) > id选择器 > class类选择器、伪类选择器 、属性选择器 > 标签选择器、伪元素选择器 > * 通配符选择器 > 继承 > 浏览器默认的属性
9、盒模型
标准盒模型:box-sizing:content-box; // 默认
怪异盒模型(IE盒模型):box-sizing:boder-box;
10、transition 过渡
transition-property: width; // 设置过渡效果的css属性,有多个属性用逗号间隔
transition-duration: 1s; // 过渡花费时间
transition-timing-function: linear; // 加速度变化曲线
transition-delay: 2s; // 何时开始
transition: width 1s linear 2s; // 以上简写
11、animations 动画
/* @keyframes duration | timing-function | delay | iteration-count | direction | fill-mode | play-state | name */
animation: 3s ease-in 1s 2 reverse both paused slidein;
12、transform
设置平移、缩放、旋转、倾斜
13、定位
position:relative/absolute/fixed;
top: 10px;
- static:常规流布局,默认
- relative:相对定位,不脱离文档流(标准流),原来的位置预留
- absolute:绝对定位,脱离文档流
- fixed:固定定位,脱离文档流
14、z-index 层级
JS
1、数据类型
基本数据类型:7种
Number、String、Boolean、null、Undefined、Symbol、bigInt(bigInt是ES6中新增的)
引用数据类型(复杂数据类型):
Object、Array、Functiony、Date、RegExp
基本数据类型和引用数据类型的区别(如何存储):基本数据类型的大小是确定的,存储在栈中;引用数据类型的大小是不确定的,存储在堆中,而它们的引用地址存储在栈中
2、判断数据类型的方法
-
typeof
console.log(typeof 1); // number
缺点:不能区分 Object、Array 和 Null,返回都是Object
-
instanceof
console.log(1 instanceof Number); // false
优点:可以区分 Array、Object、Function,更适合判断自定义的类实例对象
缺点:不能区分 Number、Boolean、String,返回的都是false;只能判断对象是否存在于目标对象的原型链上
-
Object.prototype.toString.call ()
var toString = Object.prototype.toString;
console.log(toString.call(1)); //[object Number]
优点:能精确判断数据类型
缺点:写法繁杂,可以进行封装后使用;不能细分是谁的实例
-
constructor 构造器
let arr = [];
let obj = {};
console.log(arr.constructor); // ƒ Array() { [native code] }
console.log(obj.constructor); // ƒ Object() { [native code] }
3、作用域和作用域链
作用域:作用域是变量和函数的可访问范围,分为全局作用域和局部作用域
全局作用域:在程序的任何地方都能被访问,window 对象的内置属性都拥有全局作用域。
局部作用域:在特定的代码片段才能被访问
作用域链:当需要取某个变量时,首先会去创建该变量的函数作用域中找,如果找不到该,就会往上一级的作用域中找,直到找到全局作用域,这个查找过程就形成了一个作用域链。
4、闭包
闭包,是指那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现
总的来说:闭包是指有权访问其他函数内部变量的函数。
优点:延长局部变量的生命周期
缺点:相比其他函数更耗费内存,过度使用可能会造成内存泄漏
闭包的使用:
(1)模仿块级作用域
(2)封装私有化变量
(3)保护外部函数的变量,阻止其被回收
5、垃圾回收机制
垃圾回收机制是找出那些不会再被使用的变量,然后释放他们占用的内存空间。垃圾回收机制是为了防止出现内存泄漏
两种主要的回收方法:
标记清除法:
JS中最常用的垃圾回收策略是标记清除法。当变量进入上下文(比如函数中声明一个变量)的时候,该变量会被进行标记,当该变量离开上下文的时候(变量使用完,被推出执行栈),将其再一次标记,随后删除,并回收该变量的内存
引用计数法:
跟踪每个值被引用的次数,声明变量并给它赋一个引用值时,该值的引用次数为1,每被引用一次,数值加1。当保存对该值引用的变量被其他值覆盖时,引用次数减1,当引用次数为0时,就会对该值内存进行回收
内存泄漏:是指不再被使用的内存继续被占有,没有及时被释放出来,导致该内存无法被使用,而设备的内存有限,积少成多最后出现内存泄漏(内存泄漏的表现是,系统卡顿,数据加载缓慢)
JS中,常见的造成内存泄漏的情况有4种:全局变量、定时器、闭包、DOM元素的引用
6、原型与原型链 ⭐⭐⭐⭐⭐
原型:
- 所用函数都有一个显式原型 prototype 属性,属性值是一个普通对象
- 所用引用类型(实例对象?)都有一个隐式原型 _proto_ 属性,属性值是一个普通对象
- 所用引用类型的 _proto_ 属性指向他构造函数的 prototype
原型链:当访问一个对象的某个属性时,会先在这个对象本身的属性上找,如果没有找到,则会去它的_proto_ 上找,而这个_proto_是指向它的构造函数的 prototype,如果还没有找到,再去它构造函数的prototype 的 _proto_ 上查找,这样一层一层的往上查找就形成了一个链式结构,称为原型链
Object.prototype._proto_ === null,直到到null还没有找到,则返回undefined
7、== 和 ===的区别
== 不全等,是非严格意义上的相等,值相等就相等
=== 全等,三等号是严格意义上的相等,会比较两边的数据类型和值大小,只有当数据类型和值都相等才相等
== 存在强制转换
注意:undefined == null // true
8、this指向
(1)作为普通函数执行时,this 指向 window
(2)如果有new关键字,this 指向 new 出来的实例对象
(3)当函数作为对象的方法被调用时,this 指向该对象
(4)在箭头函数中,this 指向被创建时所在函数下的对象。如果存在嵌套,则绑定在最近一级的对象上
(5)改变this指向
9、变量和函数如何进行提升,以及优先级
-
函数提升:对所有的函数声明进行提升到最顶部执行,引用类型的赋值不会提升(箭头函数和函数表达式——匿名函数属于函数表达式)
-
变量提升:对所有变量提升带到顶部执行,只声明不赋值时,值为undefined (let,const声明的变量不提升)
函数的提升优先于变量的提升,函数内部如果用 var 声明了与外部变量相同的名称,函数将不再向上寻找。
10、遍历数组和对象的方式
-
遍历数组:
-
for
-
for...in 返回数组的索引值
-
for...of 返回数组的元素
-
forEach
-
map
-
遍历对象:
-
for...in 返回对象的属性名
-
for...of ( + Object.keys )
-
使用Object.keys(obj) 遍历对象,返回值是对象的属性名
-
obj = {
name: 'tony'
age: 18
}
for( let item of Object.keys(obj)) {
console.log(obj.key + ':' item)
}
for...of 不能单独遍历对象,需要配合 Object.keys 使用
-
Object.getOwnPropertyNames(obj)
map与forEach的区别
forEach 方法,是最基本的方法,就是遍历与循环,默认有 3 个传参:分别是遍历的数组元素 item、数组索引 index、和当前遍历数组 Array
var array = ['邓紫棋', '刘德华', '成龙']
array.forEach((a, b, c) => {
console.log(a) //数组中的元素
console.log(b) //数值中元素的索引号
console.log(c) //遍历的数组
map 方法,基本用法和三个参数与 forEach 一致,但是不同的,它不会改变原数组,而是返回一个新的数组,所以 callback需要有 return 值,如果没有,会返回 undefined;forEach会改变原数组
11、JS中常用的继承方式以及优缺点
-
原型继承
把父类的实例作为子类的原型,
缺点:子类的实例会共享父类构造函数的引用属性,不能传参
-
组合继承
优点:可以传参,不共享父类的引用属性
缺点:调用了两次父类的构造函数,造成不必要消耗,父类的方法可复用
-
寄生组合继承
-
ES6中的 extend (寄生组合继承的语法糖)
12、箭头函数和普通函数的区别
- 箭头函数是普通函数的简写,但是不具备普通函数的很多特性
- 箭头函数不存在函数提升
- this指向的是定义时所在的对象,而不是调用的对象
- 没有arguments 对象,如果需要获取参数时,可以使用 rest 运算符
- 不能作为构造函数(不能使用new):
- 没有自己的 this,不能使用call 和 apply
- 没有原型prototype ,new 关键字内部需要把实例对象的_proto_指向函数的 prototype
13、var、let、const 的区别
- var可以进行变量的提升,let 和 const 不会进行变量的提升
- var在函数作用域外声明的变量是挂在window上;let,const声明的变量只在块级作用域起作用(存在块级作用域)
- var可以重复声明;let,const不能重复声明,当const声明的是对象时,值可以改变,但是地址不能改变
- 使用 let、const 声明的变量存在暂时性死区,只有执行到声明变量的那一行代码时,才能获取得到和使用该变量
14、数组的方法(14种)
- 修改数组:
- push、pop、unshift、shift、splice(三个参数,会改变原来的数组)、slice(三个参数,不改变原来的数组)
- splice(index, 需要删除的个数, 插入新的元素 )
- push在数组最后添加元素,返回值是修改后的数组长度
- pop删除数组的最后一个元素,返回值是删除的元素
- shift删除数组的第一个元素,返回值是被删除的元素
- unshift在数组最前面添加元素,返回值的修改后的数组长度
- push、pop、unshift、shift、splice(三个参数,会改变原来的数组)、slice(三个参数,不改变原来的数组)
- 遍历数组:
- map
- 筛选数组:
- indexOf、lastIndex、some、every
- 数组排序
- sort(顺序)、revense(倒序)、
- 连接数组
- concat 连接两个或多个数组,不改变原数组
15、深拷贝、浅拷贝
- 浅拷贝
- Object.assign(copyObj)
- 使用函数库lodash的.clone(copyObj)方法
- 解构(拓展)运算符...
- for...in
- copyObj.prototype.concat()
- copyObj.prototype.slice()
- 深拷贝
- 通过JSON.parse(JSON.stringify(copyObj)) 使用json内部的自动深拷贝
- ---这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)
- 使用函数库lodash的.cloneDeep(copyObj) 方法
- jQuery.extend()方法
- $.extend(deepColne, target, copyObj, [ copyObjN]) // 第一个参数为true,为深拷贝
- 手写递归的方法
- 原理:遍历数组、对象,直到里面都是基本数据类型,再去复制
- 通过JSON.parse(JSON.stringify(copyObj)) 使用json内部的自动深拷贝
16、JS为什么是单线程,如何实现异步编程
因为JS的DOM是可见的,只能是单线程,如果是多线程,当操作一个dom的 时候,一个事件修改dom,另一个事件删除dom,这时候浏览器就会出现混乱,到底是删除还是修改
异步任务又分为微任务和宏任务
微任务:promise.then,nextTick,async的wait等
宏任务:setTimeout,点击事件,ajax等
实现异步编程:
回调函数
17、面向对象的编程思想
以类、对象、基本思想
18、项目的性能优化
19、
20、Promise
promise是解决多个异步回调可能出现的回调地狱的问题
promise本身是同步的立即执行函数,当在 executor中执行 resolve 或者 reject 的时候,此时是异步操作,会先执行 then/catch,当主栈完成后,才会去调用 resolve/ reject 中存放的方法执行。
// 简易版的 promise
class MyPromise2 {
constructor(executor) {
this.state = "pending" // 规定状态
this.value = undefined // 保存 `resolve(res)` 的res值
this.reason = undefined // 保存 `reject(err)` 的err值
this.successCB = [] // 成功存放的数组
this.failCB = [] // 失败存放的数组
let resolve = (value) => {
if (this.state === "pending") {
this.state = "fulfilled"
this.value = value
this.successCB.forEach(f => f())
}
}
let reject = (reason) => {
if(this.state === "pending") {
this.state = "rejected"
this.reason = reason
this.failCB.forEach(f => f())
}
}
try {
executor(resolve, reject) // 执行
} catch (error) {
reject(error) // 若出错,直接调用 reject
}
}
then(onFulfilled, onRejected) {
if(this.state === "fulfilled") {
onFulfilled(this.value)
}
if(this.state === "rejected") {
onRejected(this.reason)
}
if(this.state === "pending") {
this.successCB.push(() => { onFulfilled(this.value) })
this.failCB.push(() => { onRejected(this.reason) })
}
}
}
// all函数
Promise.all = function(promises) {
let list = []
let count = 0
function handle(i, data) {
list[i] = data
count++
if(count == promises.length) {
resolve(list)
}
}
return Promise((resolve, reject) => {
for(let i = 0; i < promises.length; i++) {
promises[i].then(res => {
handle(i, res)
}, err => reject(err))
}
})
}
20、宏任务和微任务
执行的顺序:同步任务 > 微任务 > 宏任务
先执行宏任务script,进入script后,同步任务的在主线程中执行,所有异步宏任务放入宏任务队列中,微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕
宏任务:script、setTimeOut、setInterval、setImmediate
微任务:promise.then、process.nextTick、Object.observe、MutationObserver
注意:promise是同步任务,宏任务和微任务属于异步任务
总结
- 先执行同步任务和立即执行任务,比如说
console.log()——立即执行函数、new Promise()
- 再依次执行微任务,比如说
thenable
函数和catchable
函数 - 当微任务执行完成后开始执行宏任务,比如说
定时器、事件回调
等
21、防抖和节流
- 防抖:防抖是指事件被触发n 秒后再执行回调,如果在n秒内被再次触发,则重新计算时间(即当事件被触发时,设定一个周期延迟执行动作,若期间又被触发,则重新计算周期,直到周期结束,执行动作)
- 主要应用场景:
- scroll 事件滚动触发
- 搜索框输入查询
- 表单验证
- 按钮提交事件
- 浏览器窗口缩放,resize事件
- 主要应用场景:
- 节流:固定周期内,只执行一次动作,若有新事件触发,不执行。周期结束后,又有事件触发时,开始新的周期
- 主要应用场景:
- DOM元素的拖拽功能实现
- 射击游戏类
- 计算鼠标移动的距离
- 监听scroll事件
- 主要应用场景:
22、JS性能优化的方式
- 防抖节流
- 清除闭包的对象
- script中加defer和async
- 少用whit
- 垃圾回收
- CDN
- 事件委托
- 分批加载(setInterval,加载10000个节点)
- 图片懒加载 / 路由懒加载
- UI插件的按需引入
23、重排和重绘
- 重排/回流(Reflow):
- 当DOM 的变化影响了元素的几何信息(尺寸、布局、显示隐藏等)或元素内部的文字结构发生了变化,浏览器需要重新计算元素的几何属性,重新构建布局,这个过程叫重排
- 重绘(Repaint):
- 当一个元素只是外观风格发生改变(比如颜色等),但是没有改变宽高、布局等,重新把元素的外观绘制出来的过程,叫重绘。
- 什么时候会进行重排:
- 第一次渲染页面的时候
- 修改DOM的尺寸,位置
- 修改内部文字
- 显示/隐藏元素
- 重绘不一定会出现重排,重排一定会出现重绘。
- 页面至少经历一次重排和重绘(第一次加载的时候)
重排和重绘的代价是,破坏用户体验,并且让UI 展示非常迟缓,相比之下重排的性能影响更大
ES6
1、let、const 命令
2. 模板字符串 `` (用于换行/ 字符串的拼接)
name = Ganeoi,
age = 18,
height = 18,
function doubleAge() {
return age * 2;
}
const message = `my name is ${name}, height is ${height * 2}, age is ${doubleAge()}`
// ${} 大括号里面可以写变量,表达式或调用函数
箭头函数
symbol(唯一)
promise (同步任务)
map、set方法(add() 添加,deleet() 删除,has() 返回,clear() 清除)
for...of (数组新方法,进行遍历数组,返回数组元素)
拓展运算符 ... (三点,进行解构/ 拷贝/ 赋值)
includes() (字符串新方法——判断字符串是否包含某字符)
module 模块化(import导入,export导出)
?? 空值合并运算符 (如果第一个参数不是 null/undefined,即未定义
,则 ??
返回第一个参数。否则,返回第二个参数)
字面量增强写法(简写)&&
- 属性
var name = "Ganeoi",
var age = 18,
var obj = {
//property shorthand 属性的简写
name,
age,
}
- 方法
// es6之前
fun: funtion() {
console,log(this)
}
// method shorthand 方法的简写
fun1() {
console.log(this)
}
- 计算属性名
对象和数组的解构
调用函数获取时,就可以对需要的参数进行解构
Vue
1、Vue的双向绑定/ MVVM响应式的原理 ⭐⭐⭐⭐⭐
vue是采用 数据劫持 ,结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持属性的 set和 get方法,在数据变动时,发布消息给订阅者(依赖收集器dep中的subs),去通知观察者(notify),触发相应的监听回调函数,来更新视图
2、vue的生命周期
- beforeCreate :
- 创建之前,此时还没有 data和 Method
- Created :
- 创建完成,虚拟DOM已经 生成,但是还不能在页面上显示解析好的内容
- 在Created之后 beforeMount之前,如果没有el选项的话,那此时生命周期结束,停止编译,如果有则继续
- beforeMount :在渲染挂载之前
- mounted :
- 页面已经渲染完成,并且VM实例中已经添加完 $el 了,已经替换掉那些DOM元素了(双括号中的变量),这个时候可以进行DOM操作(但是是获取不了元素的高度等属性的,如果想要获取,需要使用$nextTick() ,$nextTick后的回调函数,会在在下一次DOM更新后在执行)
- beforeUpdate :
- data变化后,对应的组件重新渲染之前。即数据是新的,但是页面上的数据是旧的
- updated :
- data变化后,对应的组件重新渲染完成。数据是新的,页面也是新的
- beforeDestory :
- 在实例销毁之前,此时实例仍然可以使用
- destoryed :
- 实例销毁之后
- 最常用的是mounted和beforeDestroy?
- mounted:发送ajax请求,开启定时器,绑定自定义事件,订阅消息等初始化操作
- beforeDestory:清除定时器,解绑自定义事件,取消订阅等收尾工作。
3、Vuex
action
mutation
4、vue-router 的使用
- 常见用法(功能):
- 嵌套路由映射(children)
- 动态路由选择
- 导航守卫
- 模块化、基于组件的路由配置
- vue-router 的模式
- history模式:基于window.history对象的方法,利用 pushstate 和 replacestate 来将url替换但不刷新,但是有一个致命点就是,一旦刷新的话就可能出现404,因为没有当前的真正路径,想要解决这个问题需要后端配合,将不存在的路径重定向到入口文件
- hash模式: 通过 window.onhashchange ,来监听hash的变化,并根据hash值修改页面的内容,以达到前端路由的目的。hash模式下,url变化时浏览器不会重新发送请求(页面不会重新加载)
- 如何使用
5、vue中key的作用
key为每一个VNode指定唯一的 id,在数据修改的时候,Diff算法会根据 key值判断某个DOM是否有发生变化,如果有则根据key值来找正确的位置区插入新节点或添加新内容,如果没有则复用之前的元素
总的来说:key的作用主要是高效的更新虚拟 DOM
6、vue2 和vue3的区别
- v-if和v-for的优先级(vue2中 v-for 优先于 v-if、vue3中 v-if 优先于 v-for )
- .sync修饰符
- 全局API
- Vue.prototype 替换成config.globalProperties
- key在template和v-if上的使用
- $listeners被移除
- this
- typescript支持
最大的区别就是: Vue2使用 选项类型API(Options API) 对比Vue3 合成型API(Composition API)
双向数据绑定原理发生了改变,使用proxy替换Object.defineProerty,使用Proxy的优势:
1、可直接监听数组类型的数据变化,监听的目标为对象本身,不需要像Object.defineProperty一样遍历每个属性,有一定的性能提升
2、可直接实现对象属性的新增/删除
3、默认使用懒加载
在2.x版本里。不管数据多大,都会在一开始就为其创建观察者,在数据很大时,就会造成性能的问题。在3.x中,只会对渲染出来的数据创建观察者,而且3.x的观察者更高效。
4、3.0新加入了TypeScript以及PWA支持
5、生命周期的区别
Vue2--------------vue3
beforeCreate > setup() 开始创建组件之前,创建的是data和method
created > setup()
beforeMount > onBeforeMount 组件挂载到节点上之前执行的函数。
mounted > onMounted 组件挂载完成后执行的函数
beforeUpdate > onBeforeUpdate 组件更新之前执行的函数。
updated > onUpdated 组件更新完成之后执行的函数。
beforeDestroy > onBeforeUnmount 组件挂载到节点上之前执行的函数。
destroyed > onUnmounted 组件卸载之前执行的函数
但是在vue3的setup 选项api中,还是可以使用vue2的生命周期
activated > onActivated 组件卸载完成后执行的函数
deactivated > onDeactivated
7、vue组件中的data必须必须是一个函数
这是javascript的特性所导致的,在component中,data必须以函数的形式存在,不可以是对象。
组件中的data写成一个函数时,数据会以函数返回值的形式定义,这样每次复用组件的时候,都会返回一个新的data实例,相当于每一个组件实例都拥有自己私有的数据空间,各自负责自己的数据,不会造成混乱。而如果写成对象的形式,每次复用组件时,他们指向的都是同一个data地址,会互相造成影响
8、vue中的组件是如何通信的(父子传值) ⭐⭐⭐⭐⭐
- props传递
适用于:(1)父组件 ====> 子组件 (2)子组件 ====> 父组件(首要条件是必须先要求父先给子一个函数用来接收回调)
- 组件的自定义事件
适用于:子组件 ====> 父组件
子组件中有触发事件,向父组件同时发出一个事件和需要传递的信息 this.$emit( "发出的事件",“传递的数据” )
- ref 和 $refs
ref:这个属性用在子组件上,它的作用是指向了子组件的实例,可以通过实例来访问组件的数据和方法
- eventBus事件总线 ( $emit / $on )
适用于父子组件和非父子组件
- Vuex
9、vue项目优化方法 ⭐⭐⭐⭐⭐
- 代码层面的优化
- 长列表性能优化
- 图片懒加载
- 路由懒加载
- 事件销毁(在beforeDstory生命周期前,手动 removeEventListener 销毁 addEventListener )
- webpack层面的优化
- 提取公共的代码(组件化)
- 提取组件的CSS
关于长列表性能优化:
- 有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,能够很明显的减少组件初始化的时间。
- 可以通过 Object.freeze() 方法来冻结一个对象,从而阻止数据劫持, 一旦被冻结的对象就再也不能被修改了。
10、vue-router 的模式 ⭐⭐⭐⭐⭐
- hash 模式:通过监听hastchange事件,利用url中的hash 来模拟一个hash,以保证url变化时,页面来实现局部刷新
- history模式:通过 pushstate 和 replacestate 来替换url,但是有一个致命的缺点:当刷新页面时,可能会出现404错误(当前请求网址不存在),因为没有当前正确的url。要想解决需要配合后端,将不存在的路径重定向到入口文件
11、diff 算法 ⭐⭐⭐⭐⭐
diff 算法是指对新旧的 DOM 节点进行对比,并且返回一个patch 对象,用来存储两个节点之间不同的地方,最后根据patch 记录的信息进行 dom 的局部更新
网络原理/ 网络协议/ 浏览器
1、三次握手 和四次挥手
- 三次握手:客户端先向服务端发送一个SYN包,进入SYN—SENT 状态;服务端收到 SYN 包后,给客服端返回一个 SYN + ACK 包,表示已收到SYN,并进入 SYN_RECEIVE 状态;客户端接收到 SYN + ACK 后,再给服务端发送一个 ACK 包表示确认,然后双方进入 establish 状态 (链接状态?)
- 为什么握手是三次,而不是两次或者四次?
答:两次不安全,四次没必要。tcp通信需要确保双方都具有数据收发的能力,得到ACK响应则认为对方具有数据收发的能力,因此双方都要发送SYN确保对方具有通信的能力。第一次握手是客户端发送SYN,服务端接收,服务端得出客户端的发送能力和服务端的接收能力都正常;第二次握手是服务端发送SYN+ACK,客户端接收,客户端得出客户端发送接收能力正常,服务端发送接收能力也都正常,但是此时服务器并不能确认客户端的接收能力是否正常;第三次握手客户端发送ACK,服务器接收,服务端才能得出客户端发送接收能力正常,服务端自己发送接收能力也都正常。( 总结:假如只有两次,即在服务端发送SYN包和ACK包后,就进入 establish状态,万一这个请求中间遇到网络情况而没有传给客户端,而客户端一直试处于等待状态,后面服务端发送的信息客户端也接收不到 )
- 四次挥手:首先客户端向服务端发送一个 FIN 包,进入 FIN_WAIT1 的状态;服务端接收到后,给客户端发送 ACK 确认包,进入 CLOSE_WAIT 状态,客户端接到 ACK 包后,进入 FIN_WAIT2 的状态;然后服务端再把未传完的数据继续发送给客户端,发送完毕后,服务端向客户端发送一个 FIN 包,进入 LAST_ACK(最后确认)状态;客户端接收到 FIN 包后,再向服务端发送一个 ACK 确认包,等待两个周期后关闭链接
- 之所以等待两个周期是因为,最后客户端发送的ACK可能会丢失,如果不等待2个周期,服务端在没有收到ACK包之前,会不停的重复发送FIN包而不关闭。
- 【MSL指的是报文在网络中最大生存时间。在客户端发送对服务端的FIN确认包ACK后,这个ACK包有可能到达不了,服务器端如果接收不到ACK包就会重新发送FIN包。所以客户端发送ACK后需要留出2MSL时间(ACK到达服务器器+服务器发送FIN重传包,一来一回)等待确认服务器端缺失收到了ACK包。也就是说客户端如果等待2MSL时间也没收到服务器端重传的FIN包,则就可以确认服务器已经收到客户端发送的ACK包)】
2、常见的状态码
- 2开头的表示成功
- 一般常见的是200
- 3开头的表示重定向
- 301 永久重定向
- 302 临时重定向
- 304表示可以从缓存中获取数据(协商缓存)
- 4开头的表示客户端错误
- 400(错误请求)服务器不理解请求的语法
- 401(未授权)请求要求身份验证。对于需要要求登录的网页,服务器可能返回此响应
- 403(禁止)跨域,即服务器拒绝请求
- 404(未找到)请求资源不存在,即服务器找不到请求的网页
- 5开头的表示服务端错误
- 500
3、同源策略
同源:是指域名、协议、端口号都相同
同源策略:同源策略/SOP 是一种约定(限制),它阻止了不同[域]之间进行数据交互
4、解决跨域问题
什么是跨域:当一个url访问一个域名、协议、端口号有任意一个不同的网站时就是跨域
解决跨域的方法:
- JSONP(利用了js访问不受同源策略限制的特性,例如 link 中的href,img中的src,script中的src 都没有被同源策略限制到),JSONP只能get请求⭐⭐⭐⭐⭐
function addScriptTag(src) {
var script = document.createElement("script")
script.setAttribute('type', 'text/javascript')
script.src = src
document.appendChild(scrpit)
}
// 回到函数
function endFn(res) {
console.log(res.message)
}
-
CORS(Cross-Origin Resource Sharing)跨域资源共享机制⭐⭐⭐⭐⭐
- 通过自定义请求头来让服务器和浏览器进行沟通
- 有简单请求和非简单请求
-
nginx代理跨域⭐⭐⭐⭐
- nginx模拟一个虚拟服务器,因为服务器与服务器之间是不存在跨域的,
- 发送数据时 ,客户端->nginx->服务端
- 返回数据时,服务端->nginx->客户端
5、从浏览器输入 url 后都经历了什么
- 先进行DNS域名解析,先查看本地的hosts文件,看有没有当前域名对应的IP地址,如有直接发起请求,没有的话会在本地域名服务器去查找,该查找属于递归查找;如果本地域名服务器没有查找到,会从跟域名服务器查找,该过程属于迭代查找,根域名会告诉你从哪个与服务器查找,最后查找到对应的IP地址后,把对应规则保存到本地的hosts文件中
- 如果想加速以上及之后的HTTP请求过程的话,可以使用缓存服务器CDN,CDN的过程如下:
- 用户输入url地址后,本地DNS 会解析url地址,不过会把最终解析权交给CNAME 指向的CDN的DNS服务器
- CDN的DNS服务器会返回给浏览器一个全局负载均衡IP
- 用户会根据全局负载均衡IP去请求全局负载均衡服务器
- 全局负载均衡服务器会根据用户的IP地址,url地址,告诉用户一个区域负载均衡设备,让用户去请求它
- 区域负载均衡服务器会为用户选择一个离用户较近的最优的缓存服务器,并把IP地址给到用户
- 用户想缓存服务器发送请求,如果请求不到想要的资源的话,会一层层向上一级查找,直到查到为止
- 进行HTTP请求,三次握手四次挥手建立、断开连接
- 服务器处理,可能返回304也可能返回200
- 返回304说明用户缓存可用,直接使用用户端缓存即可,该过程属于协商缓存
- 换回200的话会同时返回对应的数据
- 客户端自上而下执行代码
- 其中遇到CSS加载的时候,CSS不会阻塞DOM树的解析,但是会阻塞DOM树的渲染,并且CSS会阻塞下面的JS的执行
- 然后是JS加载,JS加载会影响DOM的解析,是因为JS可能会删除/添加节点,如果先解析后加载的话,DOM树还得重新解析,性能比较差,如果不想阻塞DOM树的解析的话,可以给script添加一个 defer 或者 async 标签
- defer:不会阻塞DOM解析,等DOM解析完之后再运行,在DOMContentloaed 之前
- async:不会阻塞DOM解析,等该资源下载完成之后立刻运行
- 进行DOM渲染和Render树渲染
- 获取html并解析为DOM树
- 解析css并形成一个cssom(css树)
- 将cssom和dom合并成渲染树(render树)
- 进行布局(layout)
- 进行绘制(painting)
- 回流重绘
- 回流必将引起重绘,重绘不一定引起回流
6、get请求和 post请求的区别
- 报文上:
- 带参数时,get的参数在请求行中,post的参数在请求体中
- 后退按钮/刷新:
- get无害
- post的数据会被重新提交
- 书签:
- get可以收藏为书签
- post不能收藏为书签
- 缓存
- get能被缓存
- post不能缓存
- 历史
- get 参数保留在浏览器历史中
- post 参数不会保存在浏览器的历史中
- 对数据长度的限制
- get 请求中url的长度受限(url最大长度是2048个字符)
- post无限制
- 可见性
- get 的参数数据在url中对所有人都是可见的
- post 的参数数据不会显示在url中,用户看不见
算法
1.排序
- 冒泡排序
function bubbleSort(arr) {
for( var i = 0, i < arr.length, i++) {
for( var j = 0, j < err.length, j++) {
if( arr[j] > arr[j+1] ) {
var temp = arr[j]
arr[j] = arr[j+1]
arr[j+1] = temp
}
}
}
}
var Arr = [13, 5, 90, 28, 17, 6]
bubbleSort(Arr)
Web标准
web标准主要是为了不同开发人员之间编写的代码有一个统一的规范标准,方便后续的维护、SEO搜索引擎、提高性能(使用精灵图)
1.结构(html)、表现(css)、行为(js)相分离
2.命名规范(文件命名、标签语义化使用)
Bootstrap框架
用于开发响应布局的前端样式组件库
jQuery库
是一个javascript库,jQuery凭借着简洁的语法和跨平台的兼容性,极大简化了JavaScript开发人员遍历HTML文档,操作DOM、处理事件、执行动画和开发ajax的操作
Git指令
1.将本地项目推送到远程
- 1.在本地仓库项目目录下,依次运行以下终端命令
- git init (初始化本地git仓库)
- git add . (追踪所有本地文件)
- git commit -m '提交备注' (提交到本地仓库,并且添加注释)
- 2.在github上,依次进行以下操作
- 创建接收本地项目的仓库(不要勾选初始化?)
- 3.回到本地仓库项目目录下
- git remote add origin https://github.com/仓库地址 (关联远程仓库)
- git remote -v (查看远程仓库是否关联成功)
- git pull --rebase origin main (拉取远程仓库代码)—— 可选
- git push -u origin main (推送本地代码到远程仓库)
2.常用git指令
- git status —— 查看分支下的文件或未被追踪的文件
- git checkout ‘分支名’ —— 切换分支
- git branch ‘分支名’ —— 创建新分支
- git merge ‘要合并的分支名’ —— 合并分支