文章目录
课程资源:链接:https://pan.baidu.com/s/1X26XjeSeWLzRxo00f-M6iA 提取码:fa3o
1 HTML
01 如何理解 HTML 语义化?
让人更容易读懂(增加代码可读性)
让搜索引擎更容易读懂 (SEO)
02 默认情况下,哪些 HTML 标签是块级元素、哪些是内联元素?
display: block/table; 有 div h1 h2 table ul ol p 等
display: inline/inline-block; 有 span img input button 等
2 CSS
01 盒子模型的宽度如何计算?
offsetWidth = ( 内容宽度 + 内边距 + 边框 ),无外边距,答案是 122px
补充:如果让 offsetWidth 等于 100px ,该如何做? 加上:box-sizing = border-box
02 margin 纵向重叠问题
相邻元素的 margin-top 和 margin-bottom 会发生重叠;空白内容的也会重叠;答案:15px
03 margin 负值问题
对 margin 的 top left right bottom 设置负值,有何效果?
margin-top 和 margin-left 负值,元素向上、向左移动
margin-right 负值,右侧元素左移,自身不受影响
margin-bottom 负值,下方元素上移,自身不受影响
04 BFC 理解与应用
什么是 BFC?如何应用?
Block format context ,块级格式化上下文,一块独立渲染区域,内部元素的渲染不会影响边界以外的元素
形成 BFC 的常见条件:
float 不是 none;position 是 absolute 或 fixed;display 是 flex、inline-block ;overflow 不是 visible(常用)
BFC 的常见应用:清除浮动
05 如何实现圣杯布局和双飞翼布局
圣杯布局和双飞翼布局的目的
- 三栏布局,中间一栏最先加载和渲染(内容最重要)
- 两侧内容固定,中间内容随着宽度自适应
- 一般用于 PC 网页
圣杯布局和双飞翼布局的技术总结
使用 float 布局
两侧使用 margin 负值,以便和中间内容横向重叠
防止中间内容被两侧覆盖,一个用 padding 一个用 margin
实现步骤
-
圣杯布局
<div class="main col">main</div> <div class="left col">left</div> <div class="right col">right</div>
/* 1.设置main left right:float布局 */ float: left; /* 2.给父元素container设置左右padding */ padding-left: 100px; padding-right: 150px; /* 3.left往左移100%,上移到main中最左侧,覆盖main */ margin-left: -100%; /* 4.left再往左移自身宽度,注意要设置当前元素定位为relative,使其相对自身移动 */ position: relative; right: 100px /* 5.让right往左移自身宽度 */ margin-right: -150px;
-
双飞翼布局
<div class="main col"> <div class="main-content">main</div> </div> <div class="left col">left</div> <div class="right col">right</div>
/* 1.设置main left right:float布局 */ float: left; /* 2. 在main中加一个div,对其设置margin,留出left right的位置*/ margin: 0 150px 0 100px; /* 3.left左移100%,使其上移并到main左侧 */ margin-left: -100%; /* 4.right左移自身宽度 */ margin-left: -150px;
多一个div,少相对布局
-
补充:flex布局
<div class="main">main</div> <div class="left">left</div> <div class="right">right</div>
/* 1.将container设置为flex容器 */ display: flex; /* 2.通过order设置排列顺序,left在第一位 order默认为0,越小越靠前 */ order: -1; /* 3.设置main的方法比例,将剩余空间用main填充 */ flex-grow: 1; /* 4.设置left的固定宽度 */ flex-basis: 100px; /* 5.设置right的固定宽度 */ flex-basis: 100px;
完整代码
- 圣杯布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>圣杯布局</title>
<style>
.container {
/* 2.给父元素设置左右padding */
padding-left: 100px;
padding-right: 150px;
}
.col {
/* 1.设置float布局 */
float: left;
}
.main {
background-color: skyblue;
width: 100%;
}
.left {
position: relative;
background-color: yellow;
width: 100px;
margin-left: -100%; /* 3.left往左移100%,上移到main内最左侧,覆盖main */
right: 100px /* 4.再往左移自身宽度,注意要设置当前元素定位为relative,使其相对自身移动 */
}
.right {
background-color: pink;
width: 150px;
margin-right: -150px; /* 5.让right往左移自身宽度 */
}
</style>
</head>
<body>
<div class="container">
<div class="main col">main</div>
<div class="left col">left</div>
<div class="right col">right</div>
</div>
</body>
</html>
-
双飞翼布局
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>双飞翼布局</title> <style> .col { /* 1.设置float布局 */ float: left; } .main { background-color: skyblue; width: 100%; } .main-content { /* 2. 在main中加一个div,对其设置margin,留出left right的位置*/ margin: 0 150px 0 100px; } .left { background-color: yellow; width: 100px; /* 3.left左移100%,使其上移并到main左侧 */ margin-left: -100%; } .right { background-color: pink; width: 150px; /* 4.right左移自身宽度 */ margin-left: -150px; } </style> </head> <body> <div class="container"> <div class="main col"> <div class="main-content">main</div> </div> <div class="left col">left</div> <div class="right col">right</div> </div> </body> </html>
-
flex布局
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>双飞翼布局</title> <style> .col { /* 1.设置float布局 */ float: left; } .main { background-color: skyblue; width: 100%; } .main-content { /* 2. 在main中加一个div,对其设置margin,留出left right的位置*/ margin: 0 150px 0 100px; } .left { background-color: yellow; width: 100px; /* 3.left左移100%,使其上移并到main左侧 */ margin-left: -100%; } .right { background-color: pink; width: 150px; /* 4.right左移自身宽度 */ margin-left: -150px; } </style> </head> <body> <div class="container"> <div class="main col"> <div class="main-content">main</div> </div> <div class="left col">left</div> <div class="right col">right</div> </div> </body> </html>
06 手写 clearfix
.clearfix::before,
.clearfix::after {
content: '';
display: table;
clear: both;
}
.clearfix {
*zoom: 1; /* 兼容IE低版本 */
}
07 flex 实现一个三点的色子
语法回顾:
flex-direction:弹性元素的排列方式 (row column row-reverse column-reverse)
justify-content:如何分配主轴空白空间/对齐 (center flex-start flex-end space-around space-between)
flex-flow:是否自动换行 (wrap nowrap wrap-reverse)
align-item:元素在辅轴上如何对齐/垂直对齐 (center flex-start flex-end stretch baseline )
align-self:用来覆盖当前弹性元素上的align-items
flex-grow:增长
flex-shrink:收缩
flex-basis:初始长度
order:顺序 (默认为0,值越小越靠前)
flex:flex-grow、flex-shrink 以及 flex-basis 属性的简写属性
08 absolute 和 relative 分别依据什么定位?
-
relative 依据自身定位
-
absolute 依据 最近的定位元素 定位
定位元素:absolute relative fixed body
09 居中对齐有哪些实现方式?***
水平居中
-
inline 元素:text-align: center
-
block 元素:margin: auto
-
absolute 元素:left: 50% + margin-left 负值
left:50%; 往右移50% margin-left: -1/2width; 再往左移自身宽度的一半
垂直居中
-
inline 元素:line-height = height 值
-
absolute 元素:top: 50% + margin-top 负值
top:50%; margin-top: -1/2height; 注意:必须知道子元素的高度
-
absolute 元素: transform(-50%, -50%) 垂直水平居中
left: 50%; top: 50%; transform: translate(-50%, -50%); 老浏览器兼容有问题
-
absolute 元素:top, left, bottom, right = 0 + margin: auto
top: 0; bottom: 0; left: 0; right: 0; margin: auto; 既没有兼容性问题,也可以不知道子元素宽高
注意:2必须知道子元素宽高,3 4可以不知道
10 line-height 如何继承
答案:40px
-
写具体数值,如 30px ,则继承该值
-
写比例,如 2 / 1.5 ,则继承该比例
-
写百分比,如 200% ,则继承计算出来的值(考点)
20px * 200% = 40px
11 响应式 - rem 是什么?
rem 是一个长度单位
- px ,绝对长度单位,最常用
- em ,相对长度单位,相对于父元素,不常用
- rem ,相对长度单位,相对于根元素,常用于响应式布局
html {
font-size: 100px;
}
div {
font-size: .16rem; /* 0.16rem*100px=16px */
}
vw/vh
-
网页视口尺寸
window.screen.height:屏幕高度
window.innerHeight:网页视口高度
document.body.clientHeight:body高度
-
vw/vh
1vh表示网页视口高度的1/100
1vw表示网页视口宽度的1/100
vmax表示取vh和vw中的最大值;vmin表示取vh和vw中的最小值
12 响应式布局的常见方案?
-
media-query,根据不同的屏幕宽度设置根元素 font-size
-
rem,基于根元素的相对单位
/* media-query举例 */
@media only screen and (max-width: 374px) {
/* iphone5 或者更小的尺寸,以 iphone5 的宽度(320px)比例设置 font-size */
html {
font-size: 86px;
}
}
@media only screen and (min-width: 375px) and (max-width: 413px) {
/* iphone6/7/8 和 iphone x */
html {
font-size: 100px;
}
}
@media only screen and (min-width: 414px) {
/* iphone6p 或者更大的尺寸,以 iphone6p 的宽度(414px)比例设置 font-size */
html {
font-size: 110px;
}
}
3 JS
面试题 -> 第一时间看考点
3.1 变量类型和计算
知识点:
1. 变量类型:值类型 & 引用类型
值类型和引用类型的存储是分开的
2. typeof运算符
识别所有值类型、识别函数、判断是否是引用类型(不可再细分)
3. 深拷贝
/**
* 深拷贝
* @param {object} obj
*/
function deepClone(obj = {}) {
// obj是null,或不是对象和数组,即是基本数据类型,直接返回
if (typeof obj !== 'object' || obj == null) {
return obj
}
// 初始化返回结果
let res
if (obj instanceof Array) {
res = []
} else {
res = {}
}
for (let key in obj) {
// 保证key不是原型属性
if (obj.hasOwnProperty(key)) {
// 递归调用
res[key] = deepClone(obj[key])
}
}
return res
}
4. 变量计算-类型转换
字符串拼接、==、if语句和逻辑运算
if语句和逻辑运算:
- truly变量:经过两步非运算为true的变量
- falsely变量:经过两步非运算为false的变量
01 typeof能判断哪些类型
-
识别所有值类型
-
识别函数
-
判断是否为引用类型
02 何时使用 === / ==
除了 == null 之外,其他都一律用 ===
03 值类型和引用类型的区别
值类型占用内存小,所以存的是值;引用类型占用内存大,所以存的是内存地址。
04 手写深拷贝
见知识点
3.2 原型和原型链
知识点:
1. class和继承
class:constructor、属性、方法
继承:extends、super
class People {
constructor(name) {
this.name = name;
}
eat() {
console.log('People eat')
}
}
class Student extends People {
constructor(name, number) {
super(name);
this.number = number;
}
sayHi() {
console.log(
`姓名 ${this.name},学号 ${this.number}`
)
}
eat() {
super.eat();
}
}
2. 类型判断 instanceof
[] instanceof Array // true
[] instanceof Object // true
{} instanceof Object // true
-
instanceof可以判断引用类型
-
Object是所有类的父类
3. 原型和原型链
原型
原型关系:
- 每个class都有显式原型
prototype
- 每个实例都有隐式原型
__proto__
- 实例的
__proto__
指向class的prototype
基于原型的执行规则:
- 获取属性或执行方法时,先找自身的属性和方法,找不到再自动去
__proto__
中找
原型链
关键语句
- 函数可以产生对象,所有对象都是靠函数产生的
new 函数()
- 函数本身也是一个对象,由
new Function()
创建 - 所有函数都有显式原型
prototype
,默认指向一个空的object对象 - 所有对象都有隐式原型
__proto__
,指向其构造函数的显式原型prototype
- 在查找对象的属性和方式时,现在自身查找,没有就去
__proto__
中找 - 特殊:所有Function的
__proto__
指向Function的显式原型prototype
,即Function = new Function fn.__proto__ === Function.prototype
;Object的__proto__
指向null
instanceof
判断实例对象__proto__
与其构造函数的prototype
是否是同一个引用,只要在其原型链上就返回true
05 如何判断一个变量是不是数组
使用instanceof进行判断,typeof是无法判断数组的。在ES6中新增了Array.isArray方法进行判断。
06 手写一个简易的jQuery,考虑插件和扩展性
class jQuery {
constructor(selector) {
const result = document.querySelectorAll(selector)
const length = result.length
for (let i = 0; i < length; i ++) {
this[i] = result[i]
}
this.length = length
this.selector = selector
}
get(index) {
return this[index]
}
each(fn) {
for (let i = 0; i < this.length; i ++) {
const elem = this[i]
fn(elem)
}
}
on(type, fn) {
return this.each(elem => {
elem.addEventListener(type, fn, false)
})
}
}
// const $p = new jQuery('p')
// $p.get(1)
// $p.each(elem => console.log(elem.nodeName))
// $p.on('click', () => alert('clicked'))
// 插件
jQuery.prototype.dialog = function (info) {
alert(info)
}
// "造轮子"
class myJQuery extends jQuery {
constructor(selector) {
super(selector);
}
// 扩展自己的方法
addClass(className) {
}
}
07 class的原型本质,怎么理解
原型和原型链图示+属性和方法的执行规则
在class中 ,执行时会先在自身的属性和方法上找,找不到会顺着原型去它的上一级的原型上去找
3.3 作用域和闭包
知识点
1. 作用域和自由变量
-
作用域:全局作用域、函数作用域、块级作用域(ES6)
-
自由变量:
一个变量在当前作用域中没有定义但被使用了
向上级作用域中一层层依次寻找,直到找到
如果全局作用域中都没找到,则报错undefined
2. 闭包
闭包中,所有自由变量的查找,是在函数定义的地方,向上级作用域查找,不是函数执行的地方
结果:100;100
3. this
this取值是在函数执行时确定的,不是定义时确定的
-
普通函数调用:全局对象window
-
使用call apply bind:第一个参数(为空则默认指向window)
-
作为对象方法被调用:
对象.函数名()
:当前对象 -
构造函数调用:
new 函数()
:新创建的对象; -
箭头函数:永远指向上级作用域的this
08 this的不同应用场景如何取值
见知识点,5种场景
09 手写bind函数
// 模拟bind
Function.prototype.bind1 = function () {
// 将参数拆解为数组
const args = Array.prototype.slice.call(arguments)
// 获取this(数组第一项)
const t = args.shift() // shift获取数组第一项,且修改数组本身
// this:fn1.bind()中的fn1
const self = this
// 返回一个函数
return function () {
return self.apply(t, args)
}
}
// 测试
function fn1(a, b, c) {
console.log('this', this)
console.log(a, b, c)
return 'fn1'
}
const fn2 = fn1.bind1({x: 100}, 10, 20, 30)
const res = fn2()
console.log(res)
// 结果
// this {x: 100}
// 10 20 30
// fn1
10 实际开发中闭包的应用场景,举例说明
隐藏数据,eg.做题个简单的cache工具
// 闭包影藏数据,只提供API
function createCache() {
const data = {} // 闭包中的数据,被隐藏,不被外界访问
return {
set: function (key, val) {
data[key] = val
},
get: function(key) {
return data[key]
}
}
}
const c = createCache()
c.set('a', 100)
console.log(c.get('a'))
11 创建10个<a>
标签,点击时弹出对应序号
let a
for (let i = 0; i < 10; i++) { // 将i作为块级作用域
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', e => {
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
3.4 异步和单线程
知识点
1. 异步和单线程
-
JS是单线程语言,同时只能做一件事
-
浏览器和nodejs以支持JS启动进程,如Web Worker
-
JS和DOM渲染共用同一个线程,因为JS可修改DOM结果
-
遇到等待(网络请求、定时任务)不能卡住,需要异步
-
异步是基于callback函数形式
2. 应用场景
- callback hell Promise
12 同步和异步的区别是什么
异步是JS单线程,异步不会阻塞代码执行,同步会阻塞
13 手写用Promise加载一张图片
// Promise
function loadImg(src) {
return new Promise((resolve, reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
const err = new Error(`图片加载失败 ${src}`)
reject(err)
}
img.src = src
})
}
// 单张
// const url = 'http://www.dell-lee.com/imgs/vue3/tomato.png'
// loadImg(url).then(img => {
// console.log(img.width)
// return img
// }).catch(err => console.log(err))
// 多张
// 使用Promise解决 回调地狱
const url1 = 'http://www.dell-lee.com/imgs/vue3/tomato.png'
const url2 = 'http://www.dell-lee.com/imgs/vue3/cherry.png'
loadImg(url1).then(img1 => {
console.log(img1.width)
return loadImg(url2) // return的是Promise实例
}).then(img2 => {
console.log(img2.width)
}).catch(err => console.log(err))
14 前端使用异步的场景有哪些
- 网络请求,eg. ajax图片加载
- 定时任务,eg. setTimeout
15 setTimeOut
输出顺序:1 3 5 4 2
3.5 JS异步-进阶
知识点
1. event loop(事件循环、事件轮询)
- JS是单线程运行的
- 异步要基于回调来实现
- event loop就是异步回调的实现原理
JS如何执行?
- 从前到后,一行一行执行
- 如果某一行执行报错,则停止下面代码的执行
- 先把同步代码执行,再执行异步
event loop过程
- Browser console:打印输出
- Call Stack:真正触发执行某行代码
- callback queue 回调函数的队列
- Web APIs:处理定时或异步的API
- Event Loop:同步代码执行完毕时触发
执行详细过程:
-
现将第一行代码console.log(‘Hi’)放入调用栈Call Stack,执行完后再Browser console打印输出,最后清空调用栈
-
执行setTimeout函数,将setTimeout函数放入调用栈Call Stack,setTimeout函数是由浏览器定义的,也就是Web APIs
在setTimeout中,把第一个参数cb1放到定时器timer中,设置定时5s,放入Web APIs, 5s之后再将cb1放入Callback Queue
执行完成之后,清空Call Stack调用栈,Web APIs不变
-
执行最后一行,与第一行一样。此时Web APIs中cb1依然存在,因为5s定时还没结束,执行最后一行只需要几毫秒
-
此时所有代码执行完毕,不会再有内容进入Call Stack,即同步代码执行完成,启动Event Loop
Event Loop不断循环,等待获取Callback Queue中的内容
当定时的5s结束之后,cb1从Web APIs进入Callback Queue
此时Event Loop获取到cb1,则立即将cb1放入Call Stack,清空Callback Queue
Call Stack执行cb1函数,在Browser console打印输出,最后清空调用栈
总结过程:
- 同步代码,一行一行放在Call Stack执行
- 遇到异步,会先记录下,等待时机(定时、网络请求)
- 时机到了,移动到Callback Queue
- 如果Call Stack为空(即同步代码执行完成)Event Loop开始工作
- 轮询查找Callback Queue,如有则移动到Call Stack执行,然后继续轮询查找
- 异步(setTimeout、ajax)DOM事件都是使用回调,基于event loop实现的
2. promise 进阶
-
三种状态
-
pending -> resolved 或 pending -> rejected 变化不可逆
// 1. pending const p1 = new Promise((resolve, reject) => {}) console.log('p1', p1) // pending // 2.resolved const p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve() }) }) console.log('p2', p2) // pending setTimeout(() => console.log('p2-setTimeout', p2)) // resolved // 3.rejected const p3 = new Promise((resolve, reject) => { setTimeout(() => { reject() }) }) console.log('p3', p3) setTimeout(() => console.log('p3-setTimeout', p3)) // rejected
-
-
状态的变化和表现
-
pending不会触发then、catch
-
resolved会触发then
-
rejected会触发catch
-
-
then catch对状态的影响
-
then正常执行返回resolved,有报错返回rejected
-
catch正常执行返回resolved,有报错返回rejected
-
then catch 会继续返回 Promise ,此时可能会发生状态变化!!!
// 1. then正常执行返回resolved const p1 = Promise.resolve().then(() => { return 100 }) console.log('p1', p1) // resolved // 2.then有报错返回rejected const p2 = Promise.resolve().then(() => { throw new Error('err') }) console.log('p2', p2) // rejected // 3. catch正常执行返回resolved const p3 = Promise.reject('my error').catch( error => { console.log(error) }) console.log('p3', p3) // resolved p3.then(() => { // 注意 resolved触发then回调 console.log('p3 then ') // p3 then }) // 4.catch有报错返回rejected const p4 = Promise.reject('my error').catch(error => { throw new Error('catch err') }) console.log('p4', p4) // rejected // rejected会触发catch p4.then(() => { console.log('p4 then') }).catch(() => { console.log('p4 catch') // p4 catch,此处then正常执行,返回一个resolved的Promise })
-
-
then catch的链式调用(常考)
-
3个面试题
// Promise.resolve()返回一个resolved的Promise,会触发then,不会触发catch Promise.resolve().then(() => { console.log(1) }).catch(() => { console.log(2) }).then(() => { // 前面的then正常执行,返回resolved的Promise,会触发then console.log(3) }) // 结果: 1 3
// Promise.resolve()返回一个resolved的Promise,会触发then Promise.resolve().then(() => { console.log(1) throw new Error('erro1') // then有报错,返回rejected的Promise,会触发catch }).catch(() => { // catch正常执行,返回resolved的Promise,会触发then console.log(2) }).then(() => { console.log(3) }) // 结果:1 2 3
// Promise.resolve()返回一个resolved的Promise,会触发then Promise.resolve().then(() => { console.log(1) throw new Error('erro1') // then有报错,返回rejected的Promise,会触发catch }).catch(() => { // catch正常执行,返回resolved的Promise,不会触发catch console.log(2) }).catch(() => { console.log(3) }) // 结果:1 2
-
3. async、await
-
语法介绍
-
异步回调 callback hell
-
Promise then catch链式调用,但也是基于回调函数
-
async/await用同步的语法编写异步代码,彻底消灭回调函数
function loadImg(src) { return new Promise((resolve, reject) => { const img = document.createElement('img') img.onload = () => { resolve(img) } img.onerror = () => { reject(new Error(`图片加载失败 ${src}`)) } img.src = src }) } async function loadImg1() { const src1 = 'http://www.imooc.com/static/img/index/logo_new.png' return await loadImg(src1) } async function loadImg2() { const src2 = 'https://avatars3.githubusercontent.com/u/9583120' return await loadImg(src2) } // 匿名函数:await要执行,需要用一个匿名函数包裹 // 同步的写法,异步的代码 (async function () { const img1 = await loadImg1() const img2 = await loadImg2() console.log(img1.width, img2.width) })()
-
-
和Promise的关系
-
async/await是消灭异步回调的终极武器,但和Promise并不互斥,反而两者相辅相成
-
执行async函数,返回的是Promise对象
-
await相当于Promise的then
-
try…catch可捕获异常,代替了Promise的catch
// 1.执行async函数,返回的是一个Promise对象 // 如果返回的是一个值,会封装成Promise对象返回 async function fn1() { return 100 } // const res1 = fn1() // console.log('res1', res1) // Promise对象,resolved状态,可以触发then // res1.then(data => { // console.log('data', data) // 100 // }) // 2.await相当于Promise then !(async function () { const p1 = Promise.resolve(300) const data = await p1 console.log('data', data) // 300 })() !(async function () { const data1 = await 400 // await Promise.resolve(400) 将值封装成Promise console.log('data1', data1) // 400 })() !(async function () { const data2 = fn1() console.log('data2', data2) // 100 })() // 3.try...catch相当于Promise的catch !(async function () { const p3 = Promise.reject('err') // rejected状态 try { const res = await p3 console.log('res', res) } catch (e) { console.log(e) } })() // 注意:常考 !(async function () { const p4 = Promise.reject('err1') // rejected状态 const res = await p4 // await->then,此处不是catch,报错 => 解决办法:上面的try catch console.log('res', res) })()
-
-
总结
- async 封装 Promise
- await 处理 Promise 成功
- try…catch 处理 Promise 失败
-
-
异步的本质
-
async/await是消灭异步回调的终极武器
-
JS还是单线程,还是有异步,还是基于event loop
-
async/await只是一个语法糖,但是糖很香!
async function async1 () { console.log('async1 start') // 2 执行async函数时,要立即执行函数的 (重要) await async2() // 关键在这一步,await后面,都可以看作是callback中的内容,最后执行,即异步 // 类似:event loop, setTimeout(cb1) // eg1. setTimeout (function () { console.log('async1 end') }) // eg2. Promise.resolve().then(() => { console.log('async1 end') }) // 微任务、宏任务 console.log('async1 end') // 5 } async function async2 () { console.log('async2') // 3 (重要) } console.log('script start') // 1 async1() console.log('script end') // 4 同步代码执行完毕
-
-
for…of
-
for…in(以及forEach 、for)是常规的同步遍历
-
for…of 常用于异步的遍历
function muti(num) { return new Promise(resolve => { setTimeout(() => { resolve(num * num) }, 1000) }) } const nums = [1, 2, 3] // 同步 // nums.forEach(async i => { // const res = await muti(i) // console.log(res) // 1s之后,1 4 9同时出来 // }) // 异步 // await需要async的匿名函数包裹 !(async function() { for (let i of nums) { const res = await muti(i) console.log(res) // 每隔1s输出1 4 9 } })()
-
async/await总结
- 解决了异步回调,是一个很香的语法糖
- async/await和Promise的关系 重要
- 异步的本质
- for…of的使用
4. 微任务、宏任务
-
什么是宏任务macroTask 什么是微任务microTask
-
宏任务:setTimeout,setInterval,Ajax,DOM事件
-
微任务:Promise async/await
-
微任务执行时机比宏任务要早
console.log(100) // 宏任务 setTimeout(() => { console.log(200) }) // 微任务 Promise.resolve().then(() => { console.log(300) }) console.log(400) // 结果: 100 400 300 200
-
-
event loop和DOM渲染
-
JS是单线程的,和DOM渲染共用一个线程
-
JS执行的时候,需要留一些时机供DOM渲染
event loop补充环节:
- 每次Call Stack空闲后,即每次轮询结束,即同步任务执行完成
- 需要尝试DOM渲染,DOM结构如果改变则重新渲染
- 之后再触发下一次轮询event loop
-
-
微任务和宏任务的区别
-
宏任务:DOM渲染后触发,如setTimeout
-
微任务:DOM渲染前触发,如Promise
const $p1 = $('<p>一段文字</p>') const $p2 = $('<p>一段文字</p>') const $p3 = $('<p>一段文字</p>') $('#container') .append($p1) .append($p2) .append($p3) console.log('length', $('container').children().length) // 3 // alert('本次 call stack结束,DOM结果已更新,但尚未触发渲染') // alert会阻断js执行,也会阻断DOM渲染,便于查看效果 // 微任务:DOM渲染前触发 Promise.resolve().then(() => { console.log('length1', $('container').children().length) // 3 alert('Promise then') // DOM没有渲染 }) // 宏任务:DOM渲染后触发 setTimeout(() => { console.log('length2', $('container').children().length) // 3 alert('setTimeout') // DOM渲染了 })
-
从event loop解释,为什么微任务执行更早?
- 执行setTimeout时,先将Call Stack中的setTimeout放入Web APIs中,等待时机,时机到了再放入Callback Queue中,有event loop机制在回到Call Stack中执行。
- Event Loop触发之前,会有2步操作:1.等待Call Stack空闲,2.DOM渲染
- 执行Promise或async/await时,先等待时机,再将其放入micro task queue,不会放入Web APIs
- 微任务是ES6语法规定的,宏任务是浏览器规定的
- 执行setTimeout时,先将Call Stack中的setTimeout放入Web APIs中,等待时机,时机到了再放入Callback Queue中,有event loop机制在回到Call Stack中执行。
-
宏任务、微任务总结:
- 宏任务有哪些?微任务有哪些》微任务触发时机更早 (重点)
- 微任务、宏任务、DOM渲染的关系,以及在event loop的过程
16 描述 event loop 运行机制(可画图)
- 回顾event loop过程
- 和DOM渲染的关系
- 微任务、宏任务在event loop过程中的不同处理
17 宏任务和微任务的区别
- 宏任务:setTimeout,setInterval,Ajax,DOM事件
- 微任务:Promise async/await
- 微任务执行时机比宏任务要早
18 Promise 哪几种状态,如何变化?
-
pending -> resolved 或 pending -> rejected 变化不可逆
-
状态的变化和表现
-
pending不会触发then、catch
-
resolved会触发then
-
rejected会触发catch
-
then catch对状态的影响
-
then正常执行返回resolved,有报错返回rejected
-
catch正常执行返回resolved,有报错返回rejected
-
then catch 会继续返回 Promise ,此时可能会发生状态变化!!!
-
-
19 场景题:Promise catch 连接 then
见知识点中,then catch的链式调用的3个面试题
20 场景题:async/await语法
async function fn() {
return 100
}
!(async function () {
const a = fn() // ??
const b = await fn() // ??
})()
-
执行async函数,返回的是Promise对象,所有a是一个Promise对象
-
await是Promise的then,所以b是100
!(async function () {
console.log('start')
const a = await 100
console.log('a', a)
const b = await Promise.resolve(200)
console.log('b', b)
const c = await Promise.reject(200)
console.log('c', c)
console.log('end')
})()
// 执行完毕,打印哪些内容 ?
- 结果:start 100 200
- await是Promise的then,当c的await后面是rejected时,其没有then,所有后面都不会执行
21 场景题:Promise 和 setTimeout 顺序
console.log(100)
// 宏任务
setTimeout(() => {
console.log(200)
})
// 微任务
Promise.resolve().then(() => {
console.log(300)
})
console.log(400)
// 结果: 100 400 300 200
22 场景题:各类异步执行顺序问题
async function async1() {
console.log('async1 start') // 2
await async2()
// await后面都作为回调内容 —— 微任务
console.log('async2 end') // 6
}
async function async2() {
console.log('async2') // 3
}
console.log('script start') // 1
setTimeout(() => { // 宏任务 setTimeout
console.log('setTimeout') // 8
}, 0)
async1()
// 初始化Promise时,传入的函数会立刻执行
new Promise(resolve => {
console.log('promsie1') // 4
resolve()
}).then(() => { // 微任务
console.log('promsie2') // 7
})
console.log('script end') // 5
// 同步代码执行完毕 event loop - call stack被清空
// 接下来执行微任务
// 尝试触发DOM渲染
// 触发 Event Loop,执行宏任务
4 JS Web API
JS基础知识:变量的类型和计算、原型和原型链、作用域和闭包
JS Web API:DOM、BOM、事件绑定、ajax、存储
4.1 DOM
Document Object Model
知识点
1. DOM本质
- 从html文件解析出来的树
2. DOM节点操作
-
获取DOM节点
-
attribute:修改html属性,会改变html结构
-
property:修改对象属性,不会体现到html结构中 (推荐)
-
attribute、property都可能引起DOM重新渲染
// 1. 获取DOM节点 const div1 = document.getElementById('div1') // 元素 const divList = document.getElementsByTagName('div') // 集合 console.log('divList.length', divList.length) console.log('divList[1]', divList[1]) // 2. property 形式 const pList = document.getElementsByTagName('p') const p1 = pList[0] p1.style.width = '100px' console.log( p1.style.width ) p1.className = 'red' console.log( p1.className ) console.log(p1.nodeName) console.log(p1.nodeType) // 1 // 3. attribute p1.setAttribute('data-name', 'imooc') console.log( p1.getAttribute('data-name') ) p1.setAttribute('style', 'font-size: 50px;') console.log( p1.getAttribute('style') )
3. DOM结构操作
-
新增、插入节点
-
获取子元素列表,获取父元素
-
删除子元素
const div1 = document.getElementById('div1') const div2 = document.getElementById('div2') // 新建节点 const newP = document.createElement('p') newP.innerHTML = 'this is newP' // 插入节点 div1.appendChild(newP) // 移动节点 const p1 = document.getElementById('p1') div2.appendChild(p1) // 获取父元素 console.log( p1.parentNode ) // 获取子元素列表 const div1ChildNodes = div1.childNodes console.log( div1.childNodes ) const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child => { if (child.nodeType === 1) { return true } return false }) console.log('div1ChildNodesP', div1ChildNodesP) // 删除节点 div1.removeChild( div1ChildNodesP[0] )
4. DOM性能
-
避免频繁操作DOM
-
对DOM查询做缓存
// 不缓存DOM查询结果 for (let i = 0; i < document.getElementsByTagName('p').length; i ++) { // 每次徐娜换,都会计算length,频繁进行DOM查询 } // 缓存DOM查询结果 const pList = document.getElementsByTagName('p') const length = pList.length for (let i = 0; i < length; i ++) { // 缓存 length,只进行一次DOM查询 }
-
将频繁操作改为一次性操作
const list = document.getElementById('list') // 创建一个文档片段,此时还没有插入到 DOM 结构中 const frag = document.createDocumentFragment() // 执行插入 for (let i = 0; i < 20; i++) { const li = document.createElement('li') li.innerHTML = `List item ${i}` // 先插入文档片段中 frag.appendChild(li) } // 都完成之后,再统一插入到 DOM 结构中 list.appendChild(frag)
23 DOM是哪种数据结构
- 树 (DOM树)
24 DOM操作的常用API
- DOM节点操作
- DOM结构操作
- attr & property
25 attr和property的区别
-
attribute:修改html属性,会改变html结构
-
property:修改对象属性,不会体现到html结构中 (推荐)
-
attribute、property都可能引起DOM重新渲染
26 一次性插入多个DOM节点,考虑性能
// 创建一个文档片段,此时还没有插入到 DOM 结构中
const frag = document.createDocumentFragment()
// 执行插入
for (let i = 0; i < 20; i++) {
const li = document.createElement('li')
li.innerHTML = `List item ${i}`
// 先插入文档片段中
frag.appendChild(li)
}
// 都完成之后,再统一插入到 DOM 结构中
list.appendChild(frag)
4.2 BOM
Browser Object Model
知识点
- navigator
- screen
- location
- history
// navigator
const ua = navigator.userAgent
const isChrome = ua.indexOf('Chrome')
console.log(isChrome)
// screen
console.log(screen.width)
console.log(screen.height)
// location
console.log(location.href) //网址
console.log(location.protocol) //协议
console.log(location.host) //域名
console.log(location.search) //取网址中的参数,一般是?后面的符号
// history
history.back() //后退
history.forward() //前进
27 如何识别浏览器的类型
28 分析拆解url各个部分
4.3 事件
1. 事件绑定
// 通用的事件绑定函数
function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn);
}
// 事件绑定
const btn1 = document.getElementById('btn1')
bindEvent(btn1, 'click', event => {
console.log(event.target) // 获取触发的元素
event.preventDefault(); // 阻止默认行为
alert('click')
})
2. 事件冒泡
<div id="div1">
<p id="p1">激活</p>
<p id="p2">取消</p>
<p id="p3">取消</p>
<p id="p4">取消</p>
</div>
<div id="div2">
<p id="p5">取消</p>
<p id="p6">取消</p>
</div>
// 事件冒泡
const p1 = document.getElementById('p1')
bindEvent(p1, 'click', event => {
event.stopPropagation(); // 阻止冒泡
console.log('激活')
})
const body = document.body
bindEvent(body, 'click', event => {
console.log('取消')
})
3. 事件代理
-
基于冒泡机制
-
代码简洁
-
减少浏览器内存占用
-
不要滥用
// 事件代理 const div3 = document.getElementById('div3') bindEvent(div3, 'click', event => { event.preventDefault() // 阻止默认行为 const target = event.target if (target.nodeName === 'A') { alert(target.innerHTML) } })
29 编写一个通用的事件监听函数
// 更完整的写法
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
elem.addEventListener(type, event => {
const target = event.target
if (selector) {
// 代理绑定
if (target.matches(selector)) {
fn.call(target, event)
}
} else {
// 普通绑定
fn.call(target, event)
}
})
}
// 普通绑定
const btn1 = document.getElementById('btn1')
bindEvent(btn1, 'click', function (event) {
// console.log(event.target) // 获取触发的元素
event.preventDefault() // 阻止默认行为
alert(this.innerHTML)
})
// 代理绑定
const div3 = document.getElementById('div3')
bindEvent(div3, 'click', 'a', function (event) {
event.preventDefault()
alert(this.innerHTML)
})
注意:使用到this,注意不要用箭头函数
30 描述事件冒泡的流程
- 基于DOM树形结构
- 事件会顺着触发元素往上冒泡
- 应用场景:代理
31 无线下拉的图片列表,如何监听每个图片的点击?
- 事件代理
- 用e.target 获取触发元素
- 用matches来判断是否触发元素
4.4 Ajax
Ajax:Asynchronous Javascript And XML ,异步 JavaScript 和 XML,是指一种用于创建快速动态网页的技术。
在无需重新加载整个网页的情况下,能够更新部分网页的技术
知识点
1. XMLHttpRequest
// get请求
const xhr = new XMLHttpRequest()
xhr.open('GET', './test.json', true) // true:异步 false:童女不
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
} else {
console.log('其他')
}
}
}
xhr.send(null)
// post请求
const xhr = new XMLHttpRequest()
xhr.open('POST', '/login', true)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
}
}
}
const postData = {
username: 'mys',
password: '111'
}
xhr.send(JSON.stringify(postData))
2. 状态码
-
xhr.readyState
-
0:未初始化,还没有调用send()方法
-
1:载入,已调用send()方法,正在发送请求
-
2:载入完成,send()方法执行完成,已经接收到全部响应内容
-
3:交互,正在解析响应内容
-
4:完成,响应内容解析完成,可以在客户端调用
-
-
xhr.status
- 2xx:表示成功处理请求
- 200: 请求成功
- 3xx:需要重定向,浏览器直接跳转
- 301:永久重定向
- 302:临时重定向
- 304:Not Modified,未修改过
- 4xx:客户端请求错误
- 401:未授权
- 403:没有访问权限
- 404: 没有对应资源
- 5xx:服务端错误
- 501:
- 2xx:表示成功处理请求
3. 跨域:同源策略,跨域解决方案
-
什么是跨域(同源策略)
-
ajax请求时,浏览器要求当前网页和server必须同源(安全)
-
同源:协议、域名、端口,三者必须一致
-
加载图片 css js可无视同源策略
<img src=跨域的图片地址 />
<link href=跨域的CSS地址 />
<script src=跨域的js地址></script>
<img/>
可用于统计打点,可使用第三方统计服务<link/> <script>
可使用CDN,CDN一般都是外域script>
可实现JSONP
-
跨域
- 所有的跨域都必须经过server端允许配合
- 未经server端允许就实现跨域,说明浏览器有漏洞,危险信息号
-
-
JSONP
- 问题:访问一个网址,服务端一定返回一个html文件吗?
- 不是,服务器可以任意动态拼接数据返回,只要符合html格式要求
- 同理
<script src="xxx/getData.js">
动态拼接数据,符合js格式即可
<script>
可绕过跨域限制- 服务器可任意动态拼接数据返回
- 只要服务端愿意返回,
<script>
就可以获得跨域数据
- 问题:访问一个网址,服务端一定返回一个html文件吗?
-
CORS(服务端支持)
-
实际项目中,ajax常用工具
-
jQuery (不常用)
- 没有用到Promise
-
fetch
- 使用XMLHttpRequest
- 注意:fetch返回的Promise不会被标记为reject,即使状态码是404或500,只有当网络故障或请求被阻止时,才会被标记为reject
- fetch不会从服务端发送或接收任何cookie
- 详细可参考:https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
-
axios (常用)
- 使用XMLHttpRequest
- 支持Promise
- 详细可参考:http://axios-js.com/docs/
-
示例
// 1.jQuery $(function(){ //请求参数 var list = {}; $.ajax({ //请求方式 type : "POST", //请求的媒体类型 contentType: "application/json;charset=UTF-8", //请求地址 url : "http://127.0.0.1/admin/list/", //数据,json字符串 data : JSON.stringify(list), //请求成功 success : function(result) { console.log(result); }, //请求失败,包含具体的错误信息 error : function(e){ console.log(e.status); console.log(e.responseText); } }); }); // 2.fetch fetch('http://example.com/movies.json') .then(response => response.json()) .then(data => console.log(data)); async function postData(url = '', data = {}) { const response = await fetch(url, { method: 'POST', // *GET, POST, PUT, DELETE mode: 'cors', cache: 'no-cache', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' }, redirect: 'follow', referrerPolicy: 'no-referrer', body: JSON.stringify(data) }); return response.json(); } postData('https://example.com/answer', { answer: 42 }) .then(data => { console.log(data); }); // 3.axios const axios = require('axios'); axios.get('/user?ID=12345') .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }) .then(function () { });
-
32 手写一个简易的ajax
function ajax(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.responseText))
} else if (xhr.status === 404) {
reject(new Error('404 not found'))
}
}
}
xhr.send(null)
})
}
const url = './test.json'
ajax(url)
.then(res => console.log(res))
.catch(err => console.log(err))
33 跨域的常用实现方式
- JSONP
- CROS
4.5 存储
知识点
1. cookie
- 本身用于浏览器和server通讯
- 被“借用”到本地存储(localStorage和sessionStorage是由html5提出来的)
- 可用
document.cookie=''
来修改 - 缺点
- 存储最大:4KB
- http请求时需要发送到服务端,增加请求数量
- 只能用
document.cookie=''
来修改,太简陋,并且每次有新的变量就是向后追加
2. localStorage和sessionStorage
- HTML5专门为存储而设计,最大可存5M
- API简单易用 setItem getItem
- 不会随着http请求被发送出去
- localStorage 数据会永久存储,除非代码或手动删除
- sessionStorage数据只存在于当前会话,浏览器关闭则清空
- 一般用localStorage会更多一些
可以通过浏览器F12的Application操作
34 描述cookie localStorage sessionStorage区别
- 容量
- API易用性
- 是否跟随http请求发送
5 HTTP
需要调用后端的接口,提交、获取数据——http协议
事先需要掌握ajax
知识点
1. http状态码
- 状态码分类
- 1xx:服务器收到请求
- 2xx:请求成功
- 3xx:重定向
- 4xx:客户端错误
- 5xx:服务端错误
- 常见状态码
- 200 成功
- 301 永久重定向(配合location,浏览器自动处理)
- 浏览器会记住301所返回的location,下次直接访问location
- 应用:当老的域名到期,使用301使得浏览器记住新的域名,每次访问新域名即可
- 302 临时重定向(配合location,浏览器自动处理)
- 下次还是访问的原来的地址
- 应用:比如在百度搜索css,随便点开一个链接,浏览器会先跳转到百度的一个地址,再通过302跳转到指定链接
- 304 资源未被修改
- 404 资源为找到
- 403 没有权限
- 500 服务器错误
- 504 网关超时
- 关于协议和规范
- 就是一个规范,要求大家都跟着执行,不要违反规范(例如IE)
2. http methods
这些都是规范!
-
传统的methods
- get 获取服务器数据
- post 向服务器提交数据
- 简单的网页功能,就这两个操作
-
现在的methods
- get 获取数据
- post 新建数据
- patch/put更新数据
- delete 删除数据
-
Restful API
-
一种新的API设计方法
-
传统的API设计:把每个url当做一个功能
-
Restful API设计:把每个url当做一个唯一的资源
-
如何设计成一个资源?
-
尽量不用url参数
-
传统的API设计:/api/list?pageIndex=2
-
Restful API设计:/api/list/2
-
-
用method表示操作类型
- 传统的API设计:
- post 请求:/api/create-blog
- post 请求:/api/update-blog?id=100
- get 请求:/api/get-blog?id=100
- Restful API设计
- post 请求:/api/blog
- patch 请求:/api/blog/100
- get 请求:/api/blog/100
- 传统的API设计:
-
-
-
3. http headers
-
常见的Request Headers
- Accept 浏览器可接受的数据格式
- Accept-Encoding 浏览器可接收的压缩算法,如 gzip
- Accept-Language 浏览器可接收的语言,如 zh-CN
- Connection:keep-alive 一次TCP连接重复使用
- cookie:同域请求都会带上cookie
- Host
- User-Agent:UA,浏览器信息
- Content-type:发送数据的格式,如application/json
-
常见的Response Headers
-
Content-type:返回数据的格式
-
Content-length:返回数据的大小,字节
-
Content-Encoding:返回数据的压缩算法
-
Set-Cookie
-
自定义headers
-
axios-js.com/docs/#Request-Config
headers: { 'X-Requested-With': 'XMLHttpRequest' },
-
-
缓存相关的headers
- Cache-Control Expires
- Last-Modified If-Modified-Since
- ETag If-None-Match
4. http缓存
webpack 哈希
-
关于缓存的介绍
- 什么是http缓存?
- 第一次访问一个网站时,需要加载网站的资源,比如html css js img等,让服务器返回相应的资源
- 当再次请求这个网站时,可能会请求同样的资源,所以可以利用缓存策略,将资源先缓存起来
- 为什么需要缓存?
- 提高网页访问的速度
- 减少请求次数,降低服务器的压力,提高性能
- 减少冗余的数据传输,节省网络流量和带宽
- 哪些资源可以被缓存?——静态资源(js css img)
- 什么是http缓存?
-
http缓存策略(强制缓存+协商缓存)(重要)
-
强制缓存
-
Cache-Control
- 在Response Headers中
- 控制强制缓存
- eg.缓存一年的时间: Cache-Control: max-age=31536000(单位:s)
- max-age:设置s级别的最大缓存时间
- no-cache:不用本地缓存,直接向服务端请求
- no-store:不用本地缓存和服务端的缓存措施 (不常见)
- private:只允许最终用户做缓存,如电脑、手机
- public:允许中间路由做缓存
-
Expires
- 同在Response Headers中
- 同为控制缓存过期
- 已被Cache-Control代替
-
-
-
协商缓存(对比缓存)
- 服务端缓存策略(不是缓存在服务端,是服务端来判断能不能用缓存的资源)
- 服务端判断客户端资源,是否和服务端资源一样
- 一致返回304,否则返回200和新的资源
资源标识:在Response Headers中,有2种
-
Last-Modified 资源的最后修改时间
- If-Modified-Since带的是最新Last-Modified的值,如果没有被修改,返回204,否则返回资源新的Last-Modified
-
ETag 资源的唯一标识(一个字符串)
-
Last-Modified和ETag
- 优先使用ETag
- Last-Modified只能精确到秒级
- 如果资源被重复生成,而内容不变,则ETag更精确
-
刷新操作方式,对缓存的影响
- 三种刷新操作
- 正操操作:地址栏输入url,跳转链接,前进、后退
- 手动刷新:F5,点击刷新按钮,右击菜单刷新
- 强制刷新:ctrl+F5
- 不同的刷新操作,不同的缓存策略
- 正操操作:强制缓存有效,协商缓存有效
- 手动刷新:强制缓存失效,协商缓存有效
- 强制刷新:强制缓存失效,协商缓存失效
- 三种刷新操作
01 http常见的状态码有哪些?
-
范围
-
常见状态码
02 http常见的header有哪些?
- Request
- Response
- 自定义
- 缓存相关
03 什么是Restful API?
- methods
- 传统API与Restful API
04 描述一下http的缓存机制(重要)
- 强制
- 协商
- 304状态码
- 图
6 开发环境
- 了解实际工作情况
- 开发环境的工具,能体现工作产出的效率
- 聊天为主,不会问具体问题
- git
- 调试工具
- 抓包
- webpack/babel
- linux常用命令
1. git
-
介绍
-
最常用的代码版本管理工具
-
大型项目,需要多人协作,必须熟用git
-
下载安装
-
git服务端常见的有:github coding.net
-
大公司自己搭建内网git服务
-
-
常用git命令
- git add .
- git checkout xxx
- git commit -m ‘xxx’
- git push origin master
- git pull origin master
- git branch
- git checkout -b xxx / git checkout xxx
- git merge xxx
-
演示 coding.net
-
登录并创建项目
-
新增电脑公钥,我的公钥位置:C:\Users\联想.ssh\id_rsa.pub
-
通过ssh克隆项目:
git clone git@e.coding.net:qianmys/git-demo/git-demo.git
自己仓库的ssh地址
进入项目:cd git-demo 查看状态:git status 查看分支:git branth 查看新增内容:git diff 提交某个文件:git add xxx 提交全部文件:git add . 记录修改:git commit -m '第一次修改' 查看提交记录:git log 做配置:git config user.name mys 查看当前提交的内容:git show 撤销所有修改:git checkout . 撤销某个修改:git checkout xxx 提交:git push origin master 拉取:git pull origin master // 多人合作 创建新的分支:git checkout -b xxx 切换分支:git checkout master 切换到自己的分支,在自己的分支中进行操作 // 合并 eg:分支:master login 1.拉取全部分支:git fetch 2.切换到login:git checkout login 3.拉取login:git pull origin login 4.切换到master:git checkout master 5.合并:git merge login 6.推送到远程:git push origin master // 如果当前进度没有写完,需要修改别的分支的bug 把当前进度先放到一边:git stash 切换了分支,修改BUG后,再回到原来的分支,原来的内容不变 把当前进度重新开放出来:git stash pop
-
2. chrome调试工具
- 一般面试不会考察,但是是必备技能
- 调试工具
- Elements:展示DOM结构,调试CSS
- Console:打印内容
- debugger:断点(可以在代码中写debugger进行调试)
- Network
- Application:cookie Storage的位置
- Source:源码
3. 抓包
-
移动端h5网页,查看网络请求,需要工具抓包
-
windows一般用fiddler,MaxOS一般用charles
-
抓包过程
- 手机和电脑连在同一个局域网
- 将手机代理到电脑上
- 手机端浏览网页即可抓包
- 查看网络请求
- 网址代理
- https
4. webpack和babel
-
安装与使用步骤
- 安装babel包
npm install @babel/core @babel/preset-env babel-loader -D
- 使用步骤:xxx
- 安装babel包
-
模块化规范
-
import
-
export
-
default
-
-
webpack生产环境
-
scripts: "build": "webpack --config webpack.prod.js" npm run build output: filename: 'bundle.[contenthash].js'
-
5. linux命令
-
公司的线上机器一般都是linux
-
测试机也需要用linux
-
测试机或线上机出现问题,本地不能复现,需要排查
常用linux命令
7 运行环境
- 运行环境即浏览器(server端有node.js)
- 浏览器要下载网页代码,渲染出页面,期间会执行若干JS
- 要保证代码在浏览器中:稳定且高效
7.1 网页加载过程
知识点
1. 加载资源的形式
-
html代码
-
媒体文件,如图片、视频
-
js css
2. 加载资源的过程
-
DNS解析:域名 -> IP地址
域名更好记忆
不同地方IP地址不一样
需要域名解析服务
-
浏览器根据IP地址向服务器发起http请求
真正发送请求的核心模块是操作系统内部服务
http请求还涉及到tcp连接,三次握手等
-
服务器处理http请求,并返回给浏览器
3. 渲染页面的过程
-
根据HTML代码生成DOM Tree
-
根据CSS生成CSSOM
-
将DOM Tree和CSSOM整合,形成Render Tree
-
根据Render Tree渲染页面
- 遇到
<script>
则暂停渲染,优先加载并执行JS代码,完成后再继续- JS和DOM共有同一个线程
- 遇到
-
直至把Render Tree渲染完成
4. 问题
-
为什么要把CSS放在head中?
-
CSS放在head标签中时, 先加载CSS, 之后解析CSS构建CSSOMTree, 于此同时构建DOMTree, CSSOMTree和DOMTree都构建完毕之后开始构建RenderTree, 计算布局渲染网页
-
如果放在body尾部,DOMTree构建完成之后便开始构建RenderTree,并计算布局渲染网页, 等加载解析完css之后, 开始构建CSSOMTree, 并和DOMTree重新构建RenderTree, 重新计算布局渲染网页
-
-
为什么遇到
<script>
要停止渲染?- 因为JS代码可能会修改当前的渲染结果
-
为何把JS放到body最后?
- 因为浏览器在渲染HTML的时候是从上到下依次执行,遇到script标签则会停止DOM树的渲染,优先下载js文件,如果文件很大,则导致页面加载时间过长,影响用户体验
-
window.onload和DOMContentLoaded
-
window.addEventListener('load', () => { // 页面的全部资源加载完才会执行,包括图片、视频 }) document.addEventListener('DOMContentLoaded', () => { // DOM 渲染完即可执行,此时图片、视频可能还没有加载完 }) // 所有会选择使用:第二种 DOMContentLoaded
-
01 从输入url到渲染出页面的整个过程
- 下载资源:各个资源类型,下载过程
- 渲染页面:结合html css js 图片等
02 window.onload和DOMContentLoaded的区别
- window.onload 资源全部加载完才能执行
- DOMContentLoaded DOM渲染完即可执行,图片可能尚未下载
7.2 性能优化 体验优化
1. 性能优化
-
综合性问题,没有标准答案,尽量全面
-
细节问题可能会单独提问:手写防抖、节流
-
性能优化原则:
- 多使用内存、缓存或其他方法
- 减少CPU计算量,减少网络加载耗时
- 适用于所有编程性能优化——空间换时间
2. 如何入手
- 让加载更快
- 减少资源体积:压缩代码
- 减少访问次数:合并代码,SSR服务器端渲染,缓存
- 使用更快的网络:CDN
- 让渲染更快
- CSS放在head,JS放在body下面
- 尽早开始执行JS,用DOMContentLoaded触发
- 懒加载(图片懒加载,上滑加载更多)
- 对DOM查询进行缓存
- 频繁DOM操作,合并到一起插入DOM结构
- 节流throttle 防抖 debounce
3. 示例
- 资源合并
- 缓存
- 静态资源加hash后缀,根据文件内容计算hash
- 文件内容不变,则hash、url不变
- hash、url不变,则会自动触发http缓存机制,返回304
- CDN
- 快
- SSR
- 服务端渲染:将网页和数据一起加载,一起渲染
- 非SSR(前后端分离):先加载网页,再加载数据,再渲染数据
- 早先的JSPASP PHP,现在的Vue React SSR
- 懒加载
- 预览图
- 缓存DOM
- 多个DOM操作一起插入DOM结构
- 尽早开始JS执行
4. 防抖 debounce
-
场景
- 监听一个输入框,文字变化后触发change事件
- 直接用keyup事件,则会频繁出发change事件
- 防抖:用户输入结束或暂停时,才会触发change事件
-
示例
function debounce(fn, delay = 500) { let timer = null // timer是闭包中的 重要 return function() { // 如果timer有值,先清空 if (timer) { clearTimeout(timer) } // 设置定时器 timer = setTimeout(() => { fn.apply(this, arguments) // 传入this和参数 timer = null // 清空定时器 }, delay) // delay延时时间 } } input1.addEventListener('keyup', debounce(function () { // 模拟触发change事件 console.log(input1.value) }, 600))
5. 节流 throttle
-
场景
- 拖拽一个元素时,要随时拿到该元素被拖拽的位置
- 直接用drag事件,会频繁触发,很容易导致卡顿
- 节流:无论拖拽速度多快,都会每隔100ms触发一次
-
示例
function throttle(fn, delay = 100) { let timer = null return function() { if (timer) return timer = setTimeout(() => { fn.apply(this, arguments) // arguments:在函数里面也能监听event timer = null }, delay) } } div1.addEventListener('drag', throttle(function (event) { console.log(event.offsetX, event.offsetY) }, 100))
7.3 安全
1. XSS跨站请求攻击
-
场景
- 一个博客网站,在上面发表一篇博客,其中嵌入
<script>
脚本 - 脚本内容:获取cookie,发送到我的服务器(服务器配合跨域)
- 发布这篇博客,有人查看,可以获取到访问者的cookie
- 一个博客网站,在上面发表一篇博客,其中嵌入
-
预防
-
替换特殊字符,如:
<
变为<
>
变为>
-
<script>
变为<script>
直接显示,不会作为脚本执行 -
前后端都要做替换
-
<script>alert(document.cookie)</script> <script>alert(document.cookie)</script>
-
-
工具使用参考:https://www.npmjs.com/package/xss
2. XSRF跨站请求伪造
- 场景
- 你正在购物,看中了某个商品,商品id是100
- 付费接口是xxx.com/pay?id=100,但没有任何验证
- 我是攻击者,我看中了一个商品,id是200
- 我向你发送一封电子邮件,邮件标题很吸引人
- 但邮件正文隐藏着
<img src=xxx.com/pay?id=200 />
- 你一看邮件,就帮我买了id是200的商品
- 预防
- 使用post接口
- 增加验证,如密码、短信验证码、指纹等
01 常见的web前端攻击方式有哪些?
- XSS
- XSFR