html
SEO
- title、description、keywords
- 利用好 html 语义化
- 重要的东西放前面
- 少用 iframe
HTML 语义化的理解?
- (1) 用正确的标签做正确的事情。
- (2) html 语义化让页面的内容结构化,结构更清晰,便于对浏览器、搜索引擎解析;
- (3) 即使在没有样式 CSS 情况下也以一种文档格式显示,并且是容易阅读的;
- (4) 搜索引擎的爬虫也依赖于 HTML 标记来确定上下文和各个关键字的权重,利于 SEO ;
- (5) 使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。 我认为 html 语义化主要指的是我们应该使用合适的标签来划分网页内容的结构。html 的本质作用其实就是定义网页文档的结构,一个语义化的文档,能够使页面的结构更加清晰,易于理解。这样不仅有利于开发者的维护和理解,同时也能够使机器对文档内容进行正确的解读。比如说我们常用的 b 标签和 strong 标签,它们在样式上都是文字的加粗,但是 strong 标签拥有强调的语义。 对于一般显示来说,可能我们看上去没有差异,但是对于机器来说,就会有很大的不同。如果用户使用的是屏幕阅读器来访问网页的话,使用 strong 标签就会有明显的语调上的变化,而 b 标签则没有。如果是搜索引擎的爬虫对我们网页进行分析的话,那么它会依赖于 html 标签来确定上下文和各个关键字的权重,一个语义化的文档对爬虫来说是友好的,是有利于爬虫对文档内容解读的,从而有利于我们网站的 SEO 。从 html5 我们可以看出,标准是倾向于以语义化的方式来构建网页的,比如新增了 header 、footer 这些语义标签,删除了 big 、font 这些没有语义的标签。
doctype 文档类型声明
html<!DOCTYPE html>
link 与 @import
- link 可以引入所有的资源,包括图片,RSS 等。@import 只能导入 css
- link 同时加载, @import 引入的 CSS 将在页面加载完毕后被加载, 解析时加载,会阻塞页面渲染
- DOM 可控性区别。可以通过 JS 操作 DOM ,插入 link 标签来改变样式;由于 DOM 方法是基于文档的,无法使用 @import 的方式插入样式。
- 使用link预加载js
html<!-- 预加载资源 --> <link rel="preload" href="main.js" as="script" crossorigin="use-credentials" /> <link rel="prefetch" href="main.js" />
- ppreload加载的资源是在浏览器渲染机制之前进行处理的,并且不会阻塞onload事件;
- preload可以支持加载多种类型的资源,并且可以加载跨域资源;
- preload加载的js脚本其加载和执行的过程是分离的。即preload会预加载 相应的脚本代码,待到需要时自行调用;
- prefetch加载的资源可以获取非当前页面所需要的资源,并且将其放入缓存至少5分钟(无论资源是否可以缓存);并且,当页面跳转时,未完成的prefetch请求不会被中断;
script async defer
<script src="xxx"></script>
js 加载和执行会阻塞页面加载<script src="xxx" async></script>
js 异步加载,不会阻塞页面加载,js 加载完毕立即执行,执行过程中会阻塞页面加载, 多个 js 执行顺序不定<script src="xxx" defer></script>
js 异步加载,不会阻塞页面加载,js 和页面全部加载完毕,顺序执行。DOMContentLoaded 事件触发前执行。
行内元素与块级元素的区别
- (1) 格式上,默认情况下,行内元素不会以新行开始,而块级元素会新起一行。
- (2) 内容上,默认情况下,行内元素只能包含文本和其他行内元素。而块级元素可以包含行内元素和其他块级元素。
- (3) 行内元素与块级元素属性的不同,主要是盒模型属性上:行内元素设置 width 无效,height 无效(可以设置 line-height),设置 margin 的上下不会生效
页面布局
1.假设高度已知,请写出三栏布局,其中左栏、右栏宽度各为 300px,中间自适应
<div class="wrapper">
<div class="left"></div>
<div class="mid"></div>
<div class="right"></div>
</div>
/*第一种 浮动布局*/
.left {
float: left;
}
.right {
float: right;
}
/*第二种 绝对定位*/
.left,
.right,
.mid {
position: absolute;
}
.left {
left: 0;
width: 300px;
}
.right {
right: 0;
width: 300px;
}
.mid {
left: 300px;
right: 300px;
/*width: 100%;
box-sizing: border-box;
padding: 0 300px;*/
}
/*第三种 弹性盒子*/
.wrapper {
display: flex;
}
.left,
.right {
width: 300px;
}
.mid {
flex: 1;
}
/*第四种 表格布局*/
.wrapper {
display: table;
}
.left,
.right,
.mid {
display: table-cell;
}
.left,
.right {
width: 300px;
}
/*第五种 网格布局*/
.wrapper {
display: grid;
grid-template-rows: 100px;
grid-template-columns: 300px auto 300px;
}
CSS
css 盒模型
css 盒模型的认识
- 基本概念:标准模型+IE 模型(怪异盒模型)
margin+border+padding+content
- 标准模型和 IE 模型的区别
width height 不同,ie 的 width 包含 padding+border
- CSS 如何设置两种模型
cssbox-sizing: border-box; /* IE盒模型 */ box-sizing: content-box; /*(默认)标准盒模型 */
- JS 如何设置获取盒模型对应的宽和高
jsdom.style.width / height; dom.currentStyle.width / height; //ie window.getComputedStyle(dom).width / height; dom.getBoudingClientRect().width / height;
- 实例题(根据盒模型解释边距重叠)
css.parent { background: red; } .child { height: 100px; margin-top: 20px; background: yellow; } /*margin 会重叠到父元素 overflow BFC解决*/
BFC(边距重叠解决方案)
概念: 块级格式化上下文
原理:
- 垂直方向边距发生重叠
- BFC 区域不会与浮动元素的 box 重叠
- 是一个独立的容器不会相互影响
- 计算高度时浮动元素也会参与计算
- 如何创建 BFC:
- overflow: hidden/auto;
- float 不为 none;
- position 不为 static/relative;
- display table 相关;
- BFC 使用场景: 清除浮动影响 垂直边距
MARGIN 重叠问题的理解
块级元素的上外边距(margin-top)与下外边距(margin-bottom)有时会合并为单个外边距,这样的现象称为“margin合并”。
产生折叠的必备条件:margin必须是邻接的!而根据w3c规范,两个margin是邻接的必须满足以下条件:
- 必须是处于常规文档流(非float和绝对定位)的块级盒子,并且处于同一个BFC当中。
- 没有线盒,没有空隙,没有padding和border将他们分隔开
- 都属于垂直方向上相邻的外边距,可以是下面任意一种情况
- 元素的margin-top与其第一个常规文档流的子元素的margin-top
- 元素的margin-bottom与其下一个常规文档流的兄弟元素的margin-top
- height为auto的元素的margin-bottom与其最后一个常规文档流的子元素的margin-bottom
- 高度为0并且最小高度也为0,不包含常规文档流的子元素,并且自身没有建立新的BFC的元素的margin-top和margin-bottom
margin合并的3种场景:
(1)相邻兄弟元素margin合并。
解决办法:
- 设置块状格式化上下文元素(BFC)
(2)父级和第一个/最后一个子元素的margin合并。 解决办法:
- 对于margin-top合并,可以进行如下操作(满足一个条件即可):
- 父元素设置为块状格式化上下文元素;
- 父元素设置border-top值;
- 父元素设置padding-top值;
- 父元素和第一个子元素之间添加内联元素进行分隔。
- 对于margin-bottom合并,可以进行如下操作(满足一个条件即可):
- 父元素设置为块状格式化上下文元素;
- 父元素设置border-bottom值;
- 父元素设置padding-bottom值;
- 父元素和最后一个子元素之间添加内联元素进行分隔;
- 父元素设置height、min-height或max-height。
- 对于margin-top合并,可以进行如下操作(满足一个条件即可):
(3)空块级元素的margin合并。
解决办法:
设置垂直方向的border;
设置垂直方向的padding;
里面添加内联元素(直接Space键空格是没用的);
设置height或者min-height。
CSS 中哪些属性可以继承?
每个CSS属性定义的概述都指出了这个属性是默认继承的,还是默认不继承的。这决定了当你没有为元素的属性指定值时该如何计算值。
当元素的一个继承属性没有指定值时,则取父元素的同属性的计算值。只有文档根元素取该属性的概述中给定的初始值(这里的意思应该是在该属性本身的定义中的默认值)。
当元素的一个非继承属性(在Mozilla code里有时称之为reset property)没有指定值时,则取属性的初始值initial value(该值在该属性的概述里被指定)。
有继承性的属性:
- (1)字体系列属性
font、font-family、font-weight、font-size、font-style、font-variant、font-stretch、font-size-adjust
- (2)文本系列属性
text-indent、text-align、text-shadow、line-height、word-spacing、letter-spacing、text-transform、direction、color
- (3)表格布局属性
caption-side border-collapse empty-cells
- (4)列表属性
list-style-type、list-style-image、list-style-position、list-style
- (5)光标属性
cursor
- (6)元素可见性
visibility
- (7)还有一些不常用的;speak,page,设置嵌套引用的引号类型quotes等属性
注意:当一个属性不是继承属性时,可以使用inherit关键字指定一个属性应从父元素继承它的值,inherit关键字用于显式地指定继承性,可用于任何继承性/非继承性属性。
关于伪类 LVHA 的解释?
a标签有四种状态:链接访问前、链接访问后、鼠标滑过、激活,分别对应四种伪类:link、:visited、:hover、:active
当链接未访问过时:
- (1)当鼠标滑过a链接时,满足:link和:hover两种状态,要改变a标签的颜色,就必须将:hover伪类在:link伪类后面声明;
- (2)当鼠标点击激活a链接时,同时满足:link、:hover、:active三种状态,要显示a标签激活时的样式(:active),必须将:active声明放到:link和:hover之后。因此得出LVHA这个顺序。当链接访问过时,情况基本同上,只不过需要将:link换成:visited。 这个顺序能不能变?可以,但也只有:link和:visited可以交换位置,因为一个链接要么访问过要么没访问过,不可能同时满足,也就不存在覆盖的问题。
CSS3 新增伪类有那些?
- (1)elem:nth-child(n)选中父元素下的第n个子元素,并且这个子元素的标签名为elem,n可以接受具体的数值,也可以接受函数。
- (2)elem:nth-last-child(n)作用同上,不过是从后开始查找。
- (3)elem:last-child选中最后一个子元素。
- (4)elem:only-child如果elem是父元素下唯一的子元素,则选中之。
- (5)elem:nth-of-type(n)选中父元素下第n个elem类型元素,n可以接受具体的数值,也可以接受函数。
- (6)elem:first-of-type选中父元素下第一个elem类型元素。
- (7)elem:last-of-type选中父元素下最后一个elem类型元素。
- (8)elem:only-of-type如果父元素下的子元素只有一个elem类型元素,则选中该元素。
- (9)elem:empty选中不包含子元素和内容的elem类型元素。
- (10)elem:target选择当前活动的elem元素。
- (11):not(elem)选择非elem元素的每个元素。
- (12):enabled 控制表单控件的禁用状态。
- (13):disabled控制表单控件的禁用状态。
- (14) :checked单选框或复选框被选中。
POSITION 的值 RELATIVE 和 ABSOLUTE 定位原点是?
- absolute 生成绝对定位的元素,相对于值不为static的第一个父元素的padding box进行定位,也可以理解为离自己这一级元素最近的一级position设置为absolute或者relative的父元素的padding box的左上角为原点的。
- fixed(老IE不支持)生成绝对定位的元素,相对于浏览器窗口进行定位。
- relative 生成相对定位的元素,相对于其元素本身所在正常位置进行定位。
- static 默认值。没有定位,元素出现在正常的流中(忽略top,bottom,left,right,z-index声明)。
- inherit 规定从父元素继承position属性的值。
你知道 CSS 中不同属性设置为百分比%时对应的计算基准?
公式:当前元素某CSS属性值 = 基准 * 对应的百分比
- 元素的 position 为 relative 和 absolute 时,top和bottom、left和right基准分别为包含块的 height、width
- 元素的 position 为 fixed 时,top和bottom、left和right基准分别为初始包含块(也就是视口)的 height、width,移动设备较为复杂,基准为 Layout viewport 的 height、width
- 元素的 height 和 width 设置为百分比时,基准分别为包含块的 height 和 width
- 元素的 margin 和 padding 设置为百分比时,基准为包含块的 width(易错)
- 元素的 border-width,不支持百分比
- 元素的 text-indent,基准为包含块的 width
- 元素的 border-radius,基准为分别为自身的height、width
- 元素的 background-size,基准为分别为自身的height、width
- 元素的 translateX、translateY,基准为分别为自身的height、width
- 元素的 line-height,基准为自身的 font-size
- 元素的 font-size,基准为父元素字体
如何让去除 INLINE-BLOCK 元素间间距?
移除空格、使用margin负值、使用font-size:0、letter-spacing、word-spacing
JS
介绍 JS 的基本数据类型
js 一共有六种基本数据类型,分别是 Undefined、Null、Boolean、Number、String,还有在 ES6 中新增的 Symbol 和 ES10 中新增的 BigInt 类型。 Symbol 代表创建后独一无二且不可变的数据类型,它的出现我认为主要是为了解决可能出现的全局变量冲突的问题。 BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。
NULL 和 UNDEFINED 的区别
首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。
undefined 代表的含义是未定义,null 代表的含义是空对象。一般变量声明了但还没有定义的时候会返回 undefined,null 主要用于赋值给一些可能会返回对象的变量,作为初始化。
undefined 在 js 中不是一个保留字,这意味着我们可以使用 undefined 来作为一个变量名,这样的做法是非常危险的,它 会影响我们对 undefined 值的判断。但是我们可以通过一些方法获得安全的 undefined 值,比如说 void 0。
当我们对两种类型使用 typeof 进行判断的时候,Null 类型化会返回 “object”,这是一个历史遗留的问题。当我们使用双等 号对两种类型的值进行比较时会返回 true,使用三个等号时会返回 false。
什么是 DOM 和 BOM
- DOM 指的是文档对象模型,它指的是把文档当做一个对象来对待,这个对象主要定义了处理网页内容的方法和接口。
- BOM 指的是浏览器对象模型,它指的是把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的法和接口。BOM的核心是 window,而 window 对象具有双重角色,它既是通过 js 访问浏览器窗口的一个接口,又是一个 Global(全局)对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象的一个属性或者方法存在。window 对象含有 location 对象、navigator 对象、screen 对象等子对象,并且 DOM 的最根本的对象 document 对象也是 BOM 的 window 对象的子对象。
DOM 操作——怎样添加、移除、移动、复制、创建和查找节点
- 创建新节点
- createDocumentFragment(node);
- createElement(node);
- createTextNode(text);
- 添加、移除、替换、插入
- appendChild(node)
- removeChild(node)
- replaceChild(new,old)
- insertBefore(new,old)
- 查找
- getElementById();
- getElementsByName();
- getElementsByTagName();
- getElementsByClassName();
- querySelector();
- querySelectorAll();
- 属性操作
- getAttribute(key);
- setAttribute(key, value);
- hasAttribute(key);
- removeAttribute(key);
DOM 事件类
DOM 事件的级别
js// DOM0 element.onclick = function() {} // DOM2 element.addEventListenner('click', function() {}, false); // DOM3 element.addEventListenner('keyup', function() {}, false);
DOM 事件模型
- 捕获
- 冒泡
DOM 事件流
捕获 --> 目标 --> 冒泡
jsdocument.addEventListener('click', function () {}, false); // false (默认) 冒泡阶段触发 true 捕获阶段触发
描述 DOM 事件捕获的具体流程(事件委托)
window --> document --> html --> body -->其它元素... --> 目标元素 捕获事件可能发生在目标元素事件之前
event 对象的常见应用
jsevent.preventDefault(); // 阻止默认行为 event.stopPropagation(); // 阻止冒泡 event.stopImmediatePropagation(); // 注册了两个click事件后 可以在一个响应函数中执行这句,可以阻止另一个事件执行 event.currentTarget; // 绑定元素 event.target; // 目标元素
自定义事件
jsvar eve = new Event('custom'); ev.addEventListener('custom', function () {}); ev.dispatchEvent(eve); // 触发 // CustomEvent; // 可以添加参数
原型链
创建对象有几种方法
js// 字面量 var o1 = { name: 'o1' }; var o11 = new Object({ name: 'o11' }); // 构造函数 var M = function () { this.name = 'o2'; }; var o2 = new M(); // Object.create var P = { name: 'o3' }; var o3 = Object.create(P);
原型、构造函数、实例、原型链
instanceof 的原理
左边是对象 右边是函数(构造函数) 判断的是他们两个是否引用了一个对象(原型对象)
new 运算符
- 一个新对象被创建。继承构造函数的原型对象
- 构造函数被执行,被执行的时候相应的传参会被传入,同时上下文 this 会被指定为这个新实例,new foo 等同于 new foo(),只能用在不传递任何参数的情况
- 如果构造函数返回了一个“对象”,那么这个对象会取代整个 new 出来的结果。如果构造函数没有返回对象,那么 new 出来的结构为步骤 1 创建的对象
面向对象
/*
* 1、构造函数实现继承
*/
function Parent1() {
this.name = 'parent1';
}
Parent1.prototype.say = function () {};
function Child() {
Parent1.call(this);
this.type = 'child1';
}
// 无法继承父类原型对象上的属性
/*
* 2、借助原型链实现继承
*/
function Parent2() {
this.name = 'parent2';
}
function Child2() {
this.name = 'child2';
}
Child2.prototype = new Parent2();
// 原型对象共用,一个实例修改原型链上的属性时,另一个也会跟着改变
/*
* 3、组合方式
*/
function Parent3() {
this.name = 'parent3';
}
function Child3() {
Parent3.call(this);
this.name = 'child3';
}
Child3.prototype = new Parent3();
// 执行了两次父级构造函数
/*
* 4、组合继承的优化1
*/
function Parent4() {
this.name = 'parent4';
}
function Child4() {
Parent3.call(this);
this.name = 'child4';
}
Child4.prototype = Parent4.prototype;
Child4.prototype.constructor = Child4; // 直接引用,Parent4的原型对象的constructor也会变成Child4
/*
* 5、组合继承的优化2
*/
function Parent5() {
this.name = 'parent5';
}
function Child5() {
Parent3.call(this);
this.name = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype);
Child5.prototype.constructor = Child5;
什么是闭包,为什么要用它
闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以 访问到当前函数的局部变量。
闭包有两个常用的用途。
闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,我们可以通过在外部调用闭包函数,从而在外 部访问到函数内部的变量,可以使用这种方法来创建私有变量。
函数的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以 这个变量对象不会被回收。
其实闭包的本质就是作用域链的一个特殊的应用,只要了解了作用域链的创建过程,就能够理解闭包的实现原理。
数组和对象有哪些原生方法,列举一下
数组和字符串的转换方法:toString()、toLocalString()、join() 其中 join() 方法可以指定转换为字符串时的分隔符。
数组尾部操作的方法 pop() 和 push(),push 方法可以传入多个参数。
数组首部操作的方法 shift() 和 unshift() 重排序的方法 reverse() 和 sort(),sort() 方法可以传入一个函数来进行比较,传入前后两个值,如果返回值为正数,则交换两个参数的位置。
数组连接的方法 concat() ,返回的是拼接好的数组,不影响原数组。
数组截取办法 slice(),用于截取数组中的一部分返回,不影响原数组。
数组插入方法 splice(),影响原数组查找特定项的索引的方法,indexOf() 和 lastIndexOf() 迭代方法 every()、some()、filter()、map() 和 forEach() 方法
数组归并方法 reduce() 和 reduceRight() 方法
手写call、apply、bind、promise、防抖、节流、继承、柯里化
call
jsFunction.prototype.myCall = function(_this = window, ...rest) { const fn = Symbol(); _this[fn] = this; const res = _this[fn](...rest); delete _this[fn]; return res; }
apply
jsFunction.prototype.myApply = function(_this = window, args) { const fn = Symbol(); _this[fn] = this; const res = args ? _this[fn](...args) || _this[fn](); delete _this[fn]; return res; }
bind
jsFunction.prototype.myBind = function(_this = window, ...rest) { const fn = this; return function bind(...args) { if(this instanceof bind) { return new fn(...rest, ...args); } else { return fn.apply(_this, [...rest, ...args]); } } }
通信类
什么是同源策略及限制
同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。
- Cookie/localStorage IndexDB 无法读取
- DOM 无法获得
- Ajax 请求不能发送
前后端如何通信
- ajax
- websocket
jsvar ws = new WebSocket('ws://localhost:3001/'); ws.onopen = function () { // 这里用一个延时器模拟事件 ws.send('我来了'); }; // 这里接受服务器端发过来的消息 ws.onmessage = function (e) { console.log(e.data); var div = document.createElement('div'); div.innerText = e.data; msgBox.appendChild(div); }; ws.onclose = function () { console.log('关闭了'); }; ws.onerror = function () { console.log('发生了什么'); }; sendBtn.addEventListener('click', function (e) { var msg = msgInput.value; ws.send(msg); msgInput.value = ''; });
如何创建 ajax
- XMLHttpRequest 对象的工作流程
- 兼容性的处理
- 事件触发条件
- 事件触发顺序
jsvar xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTT'); xhr.onreadstatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { res = JSON.parse(xhr.resoponseText); } }; xhr.open('GET', url, true); //第三个参数默认true异步,false同步 xhr.send();
跨域通信的几种方式(跨域实际上是浏览器的限制)
JSONP
js//发起请求告诉服务器callback名称,服务器返回一个可执行的代码块 callback(data),即执行传入的方法名。
Hash url#后改变页面不刷新
js// iframe嵌入的页面 var b = document.getElmentByTagName('iframe'); b.src = b.src + '#' + 'data'; //b中的代码如下 window.onhashchange = function () { var data = window.location.hash; };
postMessage
js// 两个窗口 Bwindow.postMessage('data', 'http://B.com'); // 在窗口中监听 window.addEventListener('message', function (event) { console.log(event.origin); console.log(event.source); console.log(event.data); });
webSocket 不受同源策略限制
jsvar ws = new WebSocket('ws://localhost:3001/'); ws.onopen = function () { // 这里用一个延时器模拟事件 ws.send('我来了'); }; // 这里接受服务器端发过来的消息 ws.onmessage = function (e) { console.log(e.data); var div = document.createElement('div'); div.innerText = e.data; msgBox.appendChild(div); }; ws.onclose = function () { console.log('关闭了'); }; ws.onerror = function () { console.log('发生了什么'); }; sendBtn.addEventListener('click', function (e) { var msg = msgInput.value; ws.send(msg); msgInput.value = ''; });
CORS(Cross-Origin Resource Share)支持跨域通信的 ajax get post head
使用 CORS 的方式,CORS 是一个 W3C 标准,全称是"跨域资源共享"。CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,因此我们只需要在服务器端配置就行。浏览器将 CORS 请求分成两类:简单请求和非简单请求。对于简单请求,浏览器直接发出 CORS 请求。具体来说,就是会在头信息之中,增加一个 Origin 字段。Origin 字段用来说明本次请求来自哪个源。服务器根据这个值,决定是否同意这次请求。对于如果 Origin 指定的源,不在许可范围内,服务器会返回一个正常的 HTTP 回应。浏览器发现,这个回应的头信息没有包含 Access-Control-Allow-Origin 字段,就知道出错了,从而抛出一个错误,ajax 不会收到响应信息。如果成功的话会包含一些以 Access-Control- 开头的字段。 非简单请求,浏览器会先发出一次预检请求,来判断该域名是否在服务器的白名单中,如果收到肯定回复后才会发起请求。
jsfetch('/some/url', { methods: 'get', }) .then(function (response) {}) .catch(function (err) {});
HTTP 协议类
http 协议的主要特点
简单快速 灵活 无连接 无状态
http 报文的组成部分
- 请求报文 请求行+请求头+空行+请求体
- 响应报文 状态行+响应头+空行+响应体
http 方法
- get 获取资源
- post 传输资源
- patch 局部更新
- put 更新资源
- delete 删除资源
- head 获得报文首部
post 和 get 的区别
- get在浏览器回退时是无害的,而post会再次提交请求
- get产生的url地址可以被收藏,post不可以
- get请求会被浏览器主动缓存,post不会,除非手动设置
- get请求智能进行url编码,而post支持多种编码方式
- get请求参数会被完整保留在浏览器历史记录里,而post请求的参数不会被保留
- get请求在url中传送的参数有长度限制,post没有
- 对参数的数据类型,get只接收ASCALL字符,而post没有限制
- get比post更不安全,因为参数直接暴露在url上,所以不能用来传递敏感信息
- get参数通过url传递, post放在request body中
http 状态码
- 1xx 指示信息-表示请求已接收,继续处理
- 2xx 成功-表示请求已经被成功接收
- 3xx 重定向-要完成请求必须进行更进一步操作
- 4xx 客户端错误-请求有语法错误或请求无法实现
- 5xx 服务器错误-服务器未能实现合法的请求
- 200 OK: 客户端请求成功
- 206 Partial Content: 客户发送了一个带有 Range 头的 get 请求,服务器完成了它(音视频很大)
- 301 Moved Permanently: 所请求的页面已经转移至新的 url
- 302 Found: 所请求的页面已经临时转移至新的 url
- 304 Not Modified: 客户端有缓冲的文档并发出了一个条件性的请求,服务器告诉客户,原来缓冲的文档还可以继续使用
- 400 Bad Request: 客户端请求有语法错误,不能被服务器所理解
- 401 Unauthorized: 请求未经授权,这个状态码必须和 WWW-Authenticate 报头域一起使用
- 403 Forbidden: 对被请求页面的访问被禁止
- 404 Not Found: 请求的资源不存在
- 500 Internal Server Error: 服务器发生不可预期的错误原来缓冲的文档还可以继续使用
- 503 Server Unavailable: 请求未完成,服务器临时过载或宕机,一段时间后可能恢复正常
什么是持久连接
http 协议采用“请求-应答”模式,当使用普通模式,即非 Keep-Alive 模式时,每个请求/应答客户和服务器都要新建一个连接,完成后立即断开连接(http 协议为无连接的协议)。 当使用 Keep-Alive 模式(持久连接、连接重用)时,使客服端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接。
什么是管线化
使用持久连接的情况下
某个连接上消息的传递类似于
请求 1 -> 响应 1 -> 请求 2 -> 响应 2 -> 请求 3 -> 响应 3
某个连接上的消息变成类似这样
请求 1 -> 请求 2 -> 请求 3 -> 响应 1 -> 响应 2 -> 响应 3
- 管线化机制通过持久连接完成,仅HTTP/1.1支持此技术
- 只有get和head请求可以进行管线化,而post则有所限制
- 初次创建连接时不应启动管线机制,因为对方服务器不一定支持HTTP/1.1版本的协议
- 管线化不会影响响应到来的顺序
- HTTP/1.1要求服务器端支持管线化,但并不要求服务器端也对响应进行管线化处理,只是要求对管线化的请求不失败即可
- 由于上面提到的服务器端问题,开启管线化很可能并不会带带大幅度的性能提升,而且很多服务器端和代理程序对管线化的支持并不好,因此现代浏览器如Chrome和Firefox默认并未开启管线化支持
HTTP 1.0/1.1/2.0的区别
- HTTP 1.0
- HTTP协议是无状态的,即同一客户端每次请求都没有任何关系
- 消息结构包含请求头和请求体
- HTTP 1.1
- 引入了持久连接,即TCP连接默认不关闭,可以被多个请求复用
- 在同一个TCP连接里面,客户端可以同时发送多个请求
- 虽然允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的,服务器只有处理完一个请求才会接着处理下一个请求
- 新增了一些请求方法(如PUT、DELETE等) 、新增一些请求头和响应头
- HTTP2.0
- 采用二进制格式而非文本格式
- 完全多路复用,而非有序并阻塞的、秩序一个连接即可实现并行
- 使用报头压缩,降低开销
- 服务器推送
- HTTP 1.0
HTTPS
- 使用了SSL/TLS协议进行了加密处理,相对更安全
- 默认端口443
- 由于需要加密以及多次握手,实际性能会稍逊HTTP
安全类
- CSRF
- 基本概念
跨站请求伪造
- 攻击原理 必须在注册网址登录过
- 防御措施
- Token 验证
- Referer 验证 (页面来源)
- 隐藏令牌 和 token 差不多
- 基本概念
- XSS 跨域脚本攻击
- 评论区注入 js
- 防御措施
- 开启CSP(Content Security Policy内容安全策略)CSP
算法类
- 排序 快速排序、选择排序、希尔排序、冒泡排序
- 堆栈、队列、链表
- 堆栈:先进后出
- 队列: 先进先出
- 递归
- 波兰式和逆波兰式(数学运算)
渲染机制
什么是 DOCTYPE 及作用
DTD(document type definition,文档类型定义)是一系列的语法规则,用来定义 XML 或(X)HTML 的文件类型,浏览器会使用它来判断文档类型,决定使用何种协议来解析,以及切换浏览器模式 DOCTYPE 就是声名文档类型和 DTD 规范的
html<!-- html5 --> <!DOCTYPE html> <!-- html4.0 严格模式 包含所有HTML元素和属性,但不包括展示性和弃用的元素(如font) 传统模式 包含所有HTML元素和属性,包括展示性和弃用的元素(如font) -->
浏览器渲染过程
layout 计算每个元素的位置宽高
重排 Reflow
DOM 结构中每个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式来计算并根据计算结果将元素放到它该出现的位置,这个过程称之为 reflow
- 触发 Reflow
- 当你增加、删除、修改 DOM 节点是,会导致 Reflow 或 Repaint
- 当你移动 DOM 的位置,或是搞个动画的时候
- 元素尺寸改变——边距、填充、边框、宽度和高度
- 内容变化,比如用户在 input 框中输入文字
- 设置 style 属性的值
- 当你 Resize 窗口的时候(移动端没有这个问题),或是滚动的时候
- 计算 offsetWidth 和 offsetHeight 属性
- 当你修改网页的默认字体时
- 触发 Reflow
重绘 Repaint
页面内容画在页面上
- 触发 Repaint,页面有变化
- DOM 改动
- CSS 改动
- 触发 Repaint,页面有变化
使用
document.createDocumentFragment
减少- 如何减少回流(重排)?
- (1)使用 transform 替代 top
- (2)不要把节点的属性值放在一个循环里当成循环里的变量
- (3)不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
- (4)把 DOM 离线后修改。如:使用 documentFragment 对象在内存里操作 DOM
- (5)不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,然后修改 DOM 的 className。
浏览器渲染原理
- (1)首先解析收到的文档,根据文档定义构建一棵 DOM 树,DOM 树是由 DOM 元素及属性节点组成的。
- (2)然后对 CSS 进行解析,生成 CSSOM 规则树。
- (3)根据 DOM 树和 CSSOM 规则树构建渲染树。渲染树的节点被称为渲染对象,渲染对象是一个包含有颜色和大小等属性的矩形,渲染对象和 DOM 元素相对应,但这种对应关系不是一对一的,不可见的 DOM 元素不会被插入渲染树。还有一些 DOM 元素对应几个可见对象,它们一般是一些具有复杂结构的元素,无法用一个矩形来描述。
- (4)当渲染对象被创建并添加到树中,它们并没有位置和大小,所以当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
- (5)布局阶段结束后是绘制阶段,遍历渲染树并调用渲染对象的 paint 方法将它们的内容显示在屏幕上,绘制使用 UI 基础组件。值得注意的是,这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html 都解析完成之后再去构建和布局 render 树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
输入url后到页面成功出现之间发生了什么?(这里不考虑缓存) - 域名解析(DNS解析),获取到服务器ip地址 - 建立连接,TCP三次握手,如果是https还要先建立TLS连接 - 发送请求,获取html - 解析html生成DOM树,遇到link(请求资源不阻塞dom解析),遇到script(请求和执行都会阻塞dom解析),css解析会阻塞js执行(js中可能会获取和修改css属性) - 同步解析css生成CSSOM树 - DOM树和CSSOM树构建渲染树RenderTree - 根据渲染树计算节点位置和大小 reflow - 遍历渲染树调用paint方法绘制到页面上 - TCP四次挥手,断开连接
存储
请描述一下 COOKIES,SESSIONSTORAGE 和 LOCALSTORAGE 的区别?
SessionStorage, LocalStorage, Cookie 这三者都可以被用来在浏览器端存储数据,而且都是字符串类型的键值对。区别在于前两者属于 HTML5 WebStorage,创建它们的目的便于客户端存储数据。而 cookie 是网站为了标示用户身份而储存在用户本地终端上的数据(通常经过加密)。cookie 数据始终在同源(协议、主机、端口相同)的 http 请求中携带(即使不需要),会在浏览器和服务器间来回传递。
- 存储大小:
- cookie 数据大小不能超过4 k 。
- sessionStorage 和 localStorage 虽然也有存储大小的限制,但比 cookie 大得多,可以达到 5M 或更大。
- 有过期时间:
- localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据。
- sessionStorage 数据在页面会话结束时会被清除。页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话。在新标签或窗口打开一个页面时会在顶级浏览上下文中初始化一个新的会话。
- cookie 设置的 cookie 过期时间之前一直有效,即使窗口或浏览器关闭。
- 作用域:
- sessionStorage 只在同源的同窗口(或标签页)中共享数据,也就是只在当前会话中共享。
- localStorage 在所有同源窗口中都是共享的。
- cookie 在所有同源窗口中都是共享的。
- 存储大小:
JS 运行机制
js 是单线程,同一时间只能做一件事。同步任务执行完毕再执行异步任务
console.log('A');
while (true) {
// 一直在循环
}
console.log('B');
// 输出A
for (var i = 0; i < 4; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
webWorker 多线程
var loader = new Worker('loader.js');
loader.postMessage('fff');
loader.onmessage = function (e) {
var m = e.data;
console.log(m); // 输出 fff1585379972889
loader.terminate();
};
loader.onerror = function (err) {
console.log(err);
};
// loader.js
onmessage = function (e) {
postMessage(test(e.data));
};
function test(d) {
return d + Date.now();
}
- 什么是 Event Loop
- 异步任务
- setTimeout setInterval
- DOM 事件
- es6 的 promise
页面性能
提升页面性能的方法
资源压缩合并(文件合并、css 雪碧图、使用 base64),减少 HTTP 请求
非核心代码异步加载
- 方式
- 动态脚本加载
js 创建 script 节点
- defer
- async
- 动态脚本加载
- 区别 主要区别就是顺序
- defer 是在 HTML 解析完之后才会执行,如果是多个,按照加载的顺序依次执行
- async 是在加载完成后立即执行,如果是多个,执行顺序和加载顺序无关
- 方式
利用浏览器缓存-》缓存的分类-》缓存的原理
强缓存(不问一下服务器)
Expires Expires:Thu,21, 21 Jan 2017 23:39:02 GMT 判断客户端的时间是不是这个时间,这个时间之前不会请求
Chche-Control:max-age=3600 拿到资源后3600内不会再去请求
两者都有以后者为准协商缓存
Last-Modified if-Modified-since 请求时携带上次下发的时间,看是否有变化
Etag if-None-Match 服务器下发时给一个etag值,检测是否还可用原来的
使用 CDN
预解析 DNS
html<meta http-equiv="x-dns-prefetch-control" content="on" /> <link rel="dns-prefetch" href="//host_name_to_prefetch.com" />
前端性能优化主要是为了提高页面的加载速度,优化用户的访问体验。我认为可以从这些方面来进行优化。
- 第一个方面是页面的内容方面
- (1)通过文件合并、css 雪碧图、使用 base64 等方式来减少 HTTP 请求数,避免过多的请求造成等待的情况。
- (2)通过 DNS 缓存等机制来减少 DNS 的查询次数。
- (3)通过设置缓存策略,对常用不变的资源进行缓存。
- (4)使用延迟加载的方式,来减少页面首屏加载时需要请求的资源。延迟加载的资源当用户需要访问时,再去请求加载。
- (5)通过用户行为,对某些资源使用预加载的方式,来提高用户需要访问资源时的响应速度。
- 第二个方面是服务器方面
- (1)使用 CDN 服务,来提高用户对于资源请求时的响应速度。
- (2)服务器端启用 Gzip、Deflate 等方式对于传输的资源进行压缩,减小文件的体积。
- (3)尽可能减小 cookie 的大小,并且通过将静态资源分配到其他域名下,来避免对静态资源请求时携带不必要的 cookie
- 第三个方面是 CSS 和 JavaScript 方面
- (1)把样式表放在页面的 head 标签中,减少页面的首次渲染的时间。
- (2)避免使用 @import 标签。
- (3)尽量把 js 脚本放在页面底部或者使用 defer 或 async 属性,避免脚本的加载和执行阻塞页面的渲染。
- (4)通过对 JavaScript 和 CSS 的文件进行压缩,来减小文件的体积。
- 第一个方面是页面的内容方面
错误监控
分类
- 即时运行错误
- try..catch
- window.onerror
- 资源加载错误(不会冒泡,会捕获)
- object.onerror
- performance.getEntries()
- Error 事件捕获
window.addEventListener('error',function(){},true)
- 延伸:跨域的 js 运行错误可以捕获吗?错误提示什么?应该怎么处理?
- 可以捕获
- 1)在 script 标签增加 crossorigin 属性
- 2)设置 js 资源响应头 Access-Control-Allow-Origin: *
- 即时运行错误
上报错误的基本原理
- 采用 Ajax 通信的方式上报
- 利用 Image 对象上报
(new Image).src="上报地址?data=xxx"
框架
vue
1、双向绑定实现原理
- vue2
html<input type="text" id="input" v-model="text" /> <input type="text" id="input1" v-model="text" /> <p id="p" v-text="text"></p> <script> const data = { text: 'This is a test', }; const input = document.querySelector('#input'); const p = document.querySelector('#p'); p.innerText = data[p.getAttribute('v-text')]; input.addEventListener('input', function (e) { data[input.getAttribute('v-model')] = e.target.value; }); input1.addEventListener('input', function (e) { data[input1.getAttribute('v-model')] = e.target.value; }); let text = data.text; Object.defineProperty(data, 'text', { get() { return text; }, set(val) { text = val; p.innerText = data[p.getAttribute('v-text')]; input.value = data[p.getAttribute('v-text')]; input1.value = data[p.getAttribute('v-text')]; }, }); </script>
- vue3
html<input type="text" id="input" v-model="text" /> <input type="text" id="input1" v-model="text" /> <p id="p" v-text="text"></p> <script> const data = { text: 'This is a test', }; const input = document.querySelector('#input'); const p = document.querySelector('#p'); let proxyData = new Proxy(data, { get(...rest) { return Reflect.get(...rest); }, set(target, prop, value) { if (prop === 'text') { p.innerText = value; input.value = value; input1.value = value; } return Reflect.set(target, prop, value); }, }); p.innerText = proxyData[p.getAttribute('v-text')]; input.addEventListener('input', function (e) { proxyData[input.getAttribute('v-model')] = e.target.value; }); input1.addEventListener('input', function (e) { proxyData[input1.getAttribute('v-model')] = e.target.value; }); </script>
2、v-model语法糖
- vue2
html<input v-model="text"> <!-- 相当于 --> <input :value="text" @input="text=$event.target.value"> <!-- 自定义组件 --> <custom-input v-model="text" /> <!-- 相当于 --> <custom-input :value="text" @input="text=$event" /> <script> Vue.component('CustomInput', { props: ['value'], template: `<input :value="value" @input="$emit('input', $event.target.value)" >` }) </script>
- vue3 可绑定多个v-model
html<input v-model="text"> <!-- 相当于 --> <input :value="text" @input="text=$event.target.value"> <!-- 自定义组件 --> <custom-input v-model="text" v-model:age="age" /> <!-- 相当于 --> <custom-input :modelValue="text" :age="age" @update:model-value="text=$event" @update:age="age=$event" /> <script> Vue.component('CustomInput', { props: ['modelValue', 'age'], emits: ['update:modelValue', 'update:age'] template: `<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" > <input :value="age" @input="$emit('update:age', $event.target.value)" > ` }) </script>
3、hash和history的区别
此处涉及的是Vue Router,分为hash模式和 HTML5 history模式
hash模式————使用URL的hash来模拟一个完整的URL,于是当URL改变时,页面不会重新加载,
https://www.wstee.com/#test
history模式————利用
history.pushState
API来完成URL跳转而无需重新加载页面。该模式需要后端配合,否则用户在浏览器访问了URL,但是后台没有匹配就会返回404,https://www.wstee.com/test
vue2
jsconst router = new VueRouter({ // 默认 // mode: 'hash', mode: 'history', routes: [...] })
vue3
jsconst router = VueRouter.createRouter({ // hash模式 // history: VueRouter.createWebHashHistory(), // history模式 history: VueRouter.createWebHistory(), routes: [...] })
hash
hash属性是一个可读可写的字符串,该字符串是URL的锚部分(从#号开始的部分),可使用
location.hash
来获取hash,window.onhashchange
监听hash改变,可打开控制台查看network,hash改变时并没有发起请求html<div>当前的hash:<span id="hash"></span></div> <div id="view"> <div>页面1</div> <div>页面2</div> <div>页面3</div> <div>页面4</div> </div> <a href="#1">#1</a> <a href="#2">#2</a> <a href="#3">#3</a> <a href="#4">#4</a> <script> const oHash = document.querySelector('#hash'); let hash = location.hash; const views = document.querySelectorAll('#view div'); function hashChangeHandler(hash) { for (let view of views) { view.style.display = 'none'; } switch (hash) { case '': case '#1': views[0].style.display = 'block'; break; case '#2': views[1].style.display = 'block'; break; case '#3': views[2].style.display = 'block'; break; case '#4': views[3].style.display = 'block'; break; } } hashChangeHandler(hash); window.onhashchange = function (e) { hash = location.hash; oHash.innerText = hash; hashChangeHandler(hash); }; </script>
history
利用history的
pushState
和replaceState
来改变url,不会向后台发起请求,可使用popstate
来监听history的后退、前进和替换html<div>当前的url:<span id="url"></span></div> <div id="view"> <div>页面1</div> <div>页面2</div> <div>页面3</div> <div>页面4</div> </div> <a href="javascript:jumpPage(1);">/1</a> <a href="javascript:jumpPage(2);">/2</a> <a href="javascript:jumpPage(3);">/3</a> <a href="javascript:jumpPage(4);">/4</a> <script> const oUrl = document.querySelector('#url'); let url = location.pathname.replace(/.*\//, ''); console.log(url); const views = document.querySelectorAll('#view div'); function jumpPage(page) { history.pushState({}, page, page); url = location.pathname.replace(/.*\//, ''); historyChangeHandler(url); } window.onpopstate = function (e) { console.log(e); url = location.pathname.replace(/.*\//, ''); historyChangeHandler(url); }; function historyChangeHandler(url) { for (let view of views) { view.style.display = 'none'; } console.log(url); switch (url) { case '': case '1': views[0].style.display = 'block'; break; case '2': views[1].style.display = 'block'; break; case '3': views[2].style.display = 'block'; break; case '4': views[3].style.display = 'block'; break; } } </script>
业务能力
我做过什么业务
负责的业务有什么业绩
使用了什么技术方案
突破了什么技术难点
遇到了什么问题
最大的收获是什么
- 业务能力 团队协作能力 事物推动能力 带人能力 其他能力
职业竞争力
- 业务能力
- 思考能力
- 对同一件事可以从不同角度去思考,找到最优解
- 学习能力
- 不断学习新的业务和技术,沉淀、总结
- 无上限的付出
- 对于无法解决的问题可以熬夜、加班
职业规划
- 目标是什么
- 在业务上成为专家,在技术上成为行业大牛
- 近阶段的目标
- 不断地学习积累各方面的经验,以学习为主
- 长期目标
- 做几件很有价值的事情,如开源作品、技术框架等
- 方式方法
- 先完成业务上的主要问题,做到极致,然后逐步向目标靠拢