推荐:两年前端程序媛从0到18k的逆袭之路 | 2021年中总结
image.png求点赞+求分享+求评论,哦耶!!!(基本工资有一部分花在付费学习上)
(文末有彩蛋。。。)。一个朋友,面试的小哥,每天早晨六点起床学习,三本两年多经验拿了年包 50w
的 offer
。某大佬:
你看很多,校招24k
白菜价,谁又拿了大厂的offer。你会发现,他们不但学历高,而且都十分的努力。我个人挺佩服的。
学历低的想拿大的offer
,除了努力提升自己的技术,你别无选择。捷径有人能走,但是你想一想这个人是不是你。
Dear developers
6月徐来,年中总结,认识某人三本两年多经验拿了年包 50w 的 offer
,通过相识,清晰了职业规划,发展方向。2021
年的时间进度条也已过半,我们彼此或多或少经历了很多很多不平凡的事发生。下面面试了一些大厂,中厂,小厂的经验。
每个人都需要 有目标,有职业规划,有梦想,有向上的心。(当然,如果你没有,谁也不强求你)
image.png在这里感谢一下:冴羽大佬,字节强哥,浩月等大佬。
简历发给他们后,只修改一次到达他们的认同(不过我卡学历,除非某些方面非常优秀),我也发现了很多差距,所以接下来准备继续学习(卷)。
淘系:简历上,如有学历硬伤,过不了简历评估;简历上不能废话太多,你要是面试官你希望面试官问你哪些地方,你就往哪些地方写;项目上也只是写你做过的哪些事情,技术深度,实际的提升等;如果你有博客,文章网站等都可以写一写。
字节:简历上,项目经验不要大量写业务描述,需要的是一些技术方面的亮点;区分与其他人的优势,找个好的简历模板(这里推荐 木及简历 百度一下就有),如果你是5年以下经验的,如果一页写不完,要么是大牛,要么就是提炼不足,所以不管怎样,提炼到一页以内写完。
一些普通的,你只需要在技能栏里写如:精通Vue
技术栈即可,(面试官或hr就了解关于Vue项目你基本能掌握即可)。
简历:简洁,主题鲜明。
简历细节:
性名,年龄,工作经验,联系方式,学历。
岗位,薪资面议,技能,工作经历(倒叙书写)。
技能就别写普通的了,到一定技能可写如下(只是个示例):
1. 熟练掌握JavaScript,TypeScript,Node,CSS以及ES6的新特性
2. 擅长Vue技术栈,以及其生态,某框架及其实现原理,源码
3. 有良好的数据结构与算法,能熟练在项目中运用设计模式
4. 熟悉HTTP协议,网络原理,webpack,babel等等
项目描述,如技术栈Vue+Vuex+axios+element ui+echarts+es6
开发
(项目描述,时间点,遇到什么问题,用什么技术解决,取得了什么效果)
(在项目中扮演的角色等,开源能力,知识笔记,博客)
聊聊小程序,小游戏,H5,app端,PC
端(我做过)
多端开发能力如何
网站性能优化,了解浏览器实现原理
浏览器缓存,计算机基础知识,数据结构与算法
浏览器事件循环
前端工程化的:模块化,组件化,规范化(eslint
配置,文件,组件,函数,变量命,commit
等),自动化
项目目录,规范,开发流程
HTTP:服务器的认识,三次握手/四次挥手,一个url输入到浏览器产生界面发现了等
单例模式
闭包是什么
原型链是什么
promise
- api all,race,any
等,async/await
编程能力解决单点难题
架构能力解决复杂,大项目的顶层设计
工程能力解决大规模人员参与的项目协作问题
领域知识解决特殊业务的系列问题
问道: es6
的fill,includes,find,Object.keys()
问道:防抖,节流
问道:promise
,let/const
,axios
,git flow
,branch
分支等
问道: es5,es6
的继承
问道:自动化包含:单元测试,ui
测试,CI/CD
,容器化,DockerFile
,容器编排k8s
监控sentry
HTML5的新标签:header,sction,footer,aside,nav,main,article,figure,localStorage和sessionStorage,audio/video/svg等。
CSS3的:定位,static,relative,find,absoulte,sticky。
中小厂:
初级:一线:7-20K;二线:5-15k
中级:一线:15-25k;二线:12-23k
高级:一线:20-40k;二线:15-35k
大厂:
25-50k都有,看能力
初级要:
负责业务系统前端模块的设计与开发;
负责产品的需求分析,开发,测试,维护等各项工作;
承担PC端和移动端的前端HTML5的开发任务;
整体页面结构以及css样式层的设计,优化;
完成页面脚本程序编写,实现各类页面动态,交互效果;
能够理解后端架构,与后端工程师配合为项目提供最优化的技术解决方案。
中级要:
负责所在项目需求实现与开发;
完成系统细节技术设计,完成核心代码的编写;
确保需求实现,满足项目设计规范,软件编码规范以及性能要求;
测试,系统测试等;
积极沟通,以确保功能实现按时,按质交付;
积极参与阶段评审,满足项目过程质量需求,审核和指导开发人员。
已满足,走向高级开发(个人带领过小团队):
负责大型系统的web前端开发
参与技术选型,推进应用和开发工作,支撑平台架构设计与开发功能
提升系统的整体用户体验,推动前端技术的发展
为提升团队开发效率,提炼公共组件,创造实用工具
优化现有业务,开发流程
关注前端发展,应用行业新技术
团队管理
ES6 Module和Commonjs区别:
ES6 Module
静态引入,编译时引入
Commonjs
动态引入,执行时引入
只有ES6 Module
才能静态分析,实现Tree-Shaking
let apiList = require('../config/api.js')
if(isDev) {
// 动态也引入执行时引入
apiList = require('../config/api.js')
}
import apiList form '../config/api.js'
if(isDev) {
// 编译时报错,只能静态引入
import apiList from '../config/api_dev.js'
}
Scope Hosting
:
// hello.js
export default 'hello'
// main.js
import str from './hello.js'
console.log(str)
webpack
性能优化-产出代码
小图片base64
编码
bundle
加hash
懒加载
提供公共代码
使用CDN
加速
使用production
Scope Hosting
代码体积更小
创建函数作用域更小
代码可读性更好
babel
:前端开发环境必备工具,同webpack
,需要了解基本的配置和使用
环境搭建和基本配置:babel-polyfill,babel-runtime
环境搭建:.babelrc
配置:presets
和plugins
core-js
,regenerator
结合,如何按需引入babel-polyfill
,Babel 7.4
之后弃用babel-polyfill
,推荐直接使用core-js
和regenerator
。
babel-polyfill
按需引入:文件较大,只有一部分功能,无需全部引入,配置按需引入。
babel-polyfill
的问题?
会污染全局环境;
如果做一个独立的web
系统,则无碍;
如果做一个第三方Lib
,则会有问题;
babel-runtime
不会污染全局环境:
基本配置
高级配置
优化打包效率
优化产生代码
构建流程概述
babel
细分:
拆分配置和merge
;启动本地服务;处理ES6
;处理样式;处理图片;多入口;抽离css文件;抽离公共代码;懒加载;处理JSX;处理Vue;webpack优化构建速度:优化babel-loader
,IgnorePlugin
,noParse
,happyPacK
,ParalleIUglifyPlugin
。
前端为何打包构建,好处?
体积更小(Tree-Shaking
,压缩,合并),加载更快
编译高级语言或语法(TS,ES6+,模块化,SCSS
)
兼容性和错误检查(Polyfill,postcss,eslint
)
通过打包和构建,可以统一,高效的开发环境;统一的构建流程和产出标准,集成公司构建规范(提测,上线等)。
module, chunk, bundle的区别?
module
各个源码文件,webpack
中一切皆模块
chunk
多模块合并成的,如entry
,import()
,splitChunk
bundle
最终的输出文件
loader
和plugin
的区别:loader
模块转换器,如less-css
;plugin
扩展插件,如:HtmlWebpackPlugin
。
babel
和webpack
的区别?
babel
-js
新语法编译工具,不关心模块化。
webpack
-打包构建工具,是多个loader
,plugin
的集合
如何产生一个lib
?参考webpack.dll.js ouput library.
output: {
// lib的文件名
filename: 'lodash.js',
// 输出lib到dist目录下
path: disPath,
library: 'lodash',
},
为何Proxy
不能被Polyfill
?
如Class
可以用function
模拟
如Promise
可以用callback
来模拟
但Proxy
的功能用Object.defineProperty
无法模拟
babel-polyfill
和babel-runtime
的区别
babel-polyfill
会污染全局
babel-runtime
不会污染全局
产生第三方lib
要用babel-runtime
webpack
如何实现懒加载
import()
结合Vue React
异步组件
结合Vue-router``React-router
异步加载路由
webpack优化构建速度(可用于生产环境)
优化babel-loader
IgnorePlugin
noParse
happyPack
ParalleIUglifyPlugin
webpack
优化构建速度(不用于生产环境)
自动刷新
热更新
DIIPlugin
项目流程
项目分多人,多角色参与
项目分多阶段
项目需要计划和执行
需求分析:了解背景,质疑需求是否合理,需求是否闭环,开发难度如何,是否需要其他支持,不要急于给排期。技术方案设计:1,求简,不过渡设计;2,产出文档,复盘;3,找准设计重点,组件怎么设计;4,组内评审;5,和RD,CRD
沟通;6,发出会议结论。
完整项目流程:各个角色(需求分析),技术方案设计,开发,联调,测试,上线。
如何保证代码质量,开发,项目质量?
如何反馈排期
符合开发规范
写出开发文档
及时写单元测试
Mock API
Code Review
联调:1,和RD,CRD技术联调;2,让UE确定视觉效果;3,让PM确定产品功能。
加需求:走需求变更流程,按规定走,发起项目组和leader的评审,重新评估排期。
测试:提测发邮件,抄送项目组,测试问题要详细记录。
有问题及时沟通,QA和FE天生信息不对称,当面讨论,让QA帮你复现,需要特定设备才能复现。沟通,及时识别风险,及时汇报。
了解:CSS
盒模型,DOM
事件类,HTTP
协议类,原型链类,面向对象类,通信类,前端安全类,前端算法类;渲染机制类,JS运行机制,页面性能,错误监控,业务能力,团队协作能力,带人能力。
布局
浮动,绝对定位,flex-box
,表格布局,网格布局。
浮动解决方案:
div {min-height: 100px;}
.left {float: left; width: 300px; background: red;}
.right {float: right;..}
.center { background: yellow; }
绝对定位:absolute
flex box
表格布局
网格布局
基本模型,content,padding, border, margin
height-content height, width-content width
标准模型,IE模型的区别:
计算的宽度和高度不同
IE模型:它的宽和高是计算:padding+border
。
CSS3的属性,CSS如何设置这两种模型,box-sizing
:content-box
;(标准模型) box-sizing:border-box
(IE
模型)
JS如何设置获取盒模型的宽和高:dom.style.width/height,dom.currentStyle.width/height
兼容性更好一些:window.getComputeStyle(dom).width/height
;dom.getBoundingClientRect().width/height
BFC(边距重叠解决方案)块级格式化上下文:BFC的原理,BFC的渲染规则:
BFC这个元素的垂直方向的边距会发生重叠
BFC的区域不会与浮动元素的boxs重叠,清除浮动布局
BFC在页面上是独立的容器,外面的元素不会影响里面的元素,里面的元素也不会影响外面的元素
计算BFC高度的时候,浮动元素也会参与计算
如何创建BFC,给父元素添加:overflow: hidden;
,overflow:auto;
,flow
不为none
,设置浮动BFC
,position
:不是static/relative
使用BFC的场景:解决边距重叠问题,解决清除浮动。BFC的元素不会与float元素相互重叠。
DOM事件:1.DOM事件的级别;2.DOM事件模型;3.DOM事件流;4.描述DOM事件捕获的具体流程;5.Event对象的常用应用;6.自定义事件。
DOM事件类,事件级别:
DOM0 element.onclick = function(){}
DOM2 element.addEventListener('click',function(){}, false)
DOM3 element.addEventListener('keyup',function(){}, false)
DOM
事件模型:捕获,冒泡
事件流:浏览器在为这个当前页面与用户做交互的过程中,点击属标,传到页面上。
事件流:window
对象,捕获,目标元素(目标阶段)-》冒泡,window对象。
描述DOM事件捕获的具体流程:window-document-html-body-目标元素
。(document.documentElement
)
Event
对象的常用:
event.preventDefault()
阻止默认行为;event.stopPropagation()
阻止冒泡行为;event.stopImmediatePropagation()
;event.currentTarget
,当前所绑定的事件;event.target
。
自定义事件:
var eve = new Event('custome');
ev.addEventListener('custome',function(){
console.log('custome');
});
ev.dispatchEvent(eve);
window.addEvenListener('click', function(){}, true);// 捕获阶段触发
HTTP协议:
HTTP协议的主要特点
HTTP报文的组成部分
HTTP方法
POST和GET的区别
HTTP状态码
什么是持久连接
什么是管线化
HTTP报文的组成部分
请求报文:请求行,请求头,空行,请求体;响应报文:状态行,响应头,空行,响应体。请求行包含:http方法,页面地址,http协议以及版本;请求头包含:key-value值,告诉服务器端我要什么内容。
HTTP协议类的主要特点:简单快速,灵活,无连接,无状态。
HTTP协议类,HTTP方法:GET,获取资源,POST,传输资源,PUT,更新资源,DELETE,删除资源,HEAD,获得报文首部。
HTTP协议类:POST和GET的区别:1.GET在浏览器回退时是无害的,而POST会再次提交请求;2.GET产生的URL地址可以被收藏,而POST不可以;3.GET请求会被浏览器主动缓存,而POST不会,除非手动设置;4.GET请求只能进行url编码,而POST支持多种编码方式;5.GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会保留;6.GET请求在URL中传送的参数是有长度限制的,而POST是没有限制的;7.对参数的数据类型,GET只接受ASCII字符,而POST没有限制;8.GET比POST更不安全,因为参数直接暴露在URL中,所以不是用来传递敏感信息的;9.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
客户端请求有语法错误,不能被服务器所理解
401
请求未经授权,这个状态码必须和www-Authenticate
报头域一起使用
403
对被请求页面的访问被禁止
404
请求资源不存在
505
服务器发送不可预期的错误,原来缓冲的文档还可以继续使用
503
请求未完成,服务器临时过载或宕机,一段时间后可能恢复正常
持久连接:HTTP1.1版本 1.0不支持
HTTP协议采用“请求-应答”模式,当使用普通模式,即非keep-alive
模式时,每个请求/应答 客户和服务器都要新键一个连接,完成之后立即断开连接(HTTP协议为无连接的协议)。
当使用keep-alive
模式(又称为持久连接,连接重用)时,keep-alive
功能使客户端到服务器端的连接有效,当出现对服务器的后继请求时,keep-alive
功能避免了建立或重新建立连接。
管线化:
在使用非持久连接的情况下,某个连接上消息的传递类似于请求1-响应1
使用持久连接情况下:请求1-请求2-响应1-响应2
管线化:
管线化机制通过持久连接完成,仅仅HTTP1.1支持此技术
只有GET和HEAD请求可以进行管线化,而post则有限制
初次创建连接时不应启动管线机制,因为对方(服务器)不一定支持HTTP/1.1 版本的协议
管线化不影响响应到来的顺序,响应返回的顺序并未改变
HTTP/1.1要求服务器支持管线化,但并不要求服务端也对响应进行管理化处理,只要求对于管线化的请求不失败即可
由于上面提到的服务器端问题,开启管线化很可能并不会带来大幅度的性能提升,而且很多服务器端和代理程序对管线化的支持并不好,因此现代浏览器如Chrome和FireFox默认并未开启管线化支持
原型链类:创建对象几种方法,原型,构造函数,实例,原型链,instanceof的原理。
创建对象的几种方法:
// 字面量
var o1 = {name:'o1'};
var o2 = new Object({name:'01'});
// 通过构造函数
var m = function(){this.name='01'}
var m1 = new m();
// Object.create
var p = {name:'o3'};
var o3 = Object.create(p);
原型链:
构造函数,new,实例,构造函数-prototype-原型对象-__proto__
原型对象,原型对象constructor
构造函数,实例__proto__
到原型对象。
原型链类:实例对象proto
- instanceof - prototype
- 原型,实例对象 -constructor
- 构造函数。
new 运算符:
一个新对象被创建,它继承自foo.protoype
。
构造函数foo被执行,执行的时候,相应的传参会被传入,同时上下文(this
)会被指定为这个新实例,new foo等同于new.foo()
,只能用在不传递任何参数的情况。
如果构造函数返回了一个“对象”,那么这个对象会取代整个new出来的结果。如果构造函数没有返回对象,那么new出来的结果为步骤1创建的对象。
类与实例:类的声明;生成实例。
类与继承:如何实现继承;继承的几种方式。
类的声明:
function Animal() {
this.name = 'name';
}
// es6 class
class Animal2 {
constructor() {
this.name = name;
}
}
继承的本质原型链:
借助构造函数实现继承
function Parent1() {
this.name = 'parent';
}
function Child() {
Parent.call(this);
this.type = 'child';
}
缺点是获取不到父类构造函数prototype上的方法。
借助原型链实现继承
function Parent2() {
this.name = 'parent2';
}
function Child2() {
this.type = 'child2';
}
child2.prototype=new Parent2();
原型链继承方式的缺点:原型链中的原型对象,引用同一个对象,就是父类的实例对象。
第三种继承方式:组合方式
function Parent3() {
this.name = 'parent3';
this.play = [1,2,3];
}
function Child3() {
Parent3.call(this);
this.type = 'child3';
}
Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child4();
s3.play.push(4);
解决1,2不足,缺点就是父级的构造函数执行了两次。
第四种方式:结合继承的优化,继承父类的原型对象(原型式)
function Parent4() {
this.name = 'parent4';
this.play = [1,2,3];
}
function Child4() {
Parent4.call(this);
this.type = 'child4';
}
Child4.prototype = Parent4.prototype
// 数据类型:应用类型和值类型
// constructor指向
第5,组合继承方式
function Parent5() {
this.name = 'parent5';
this.play = [1,2,3];
}
function Child5() {
Parent5.call(this);
this.play = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype); // __proto__
Child5.prototype.constructor = Child5;
// 构造函数指向Child5
DNS:根DNS,顶级DNS,权威DNS,本地DNS,负载均衡
。
下层协议:可靠数据传输,四层与七层,TCP/IP,UNIX Domain Socket
。
代理:正向代理,反向代理,Proxy协议。
URI/URL
:协议名,查询参数,编码。
webSocket
:全双工,二进制帧,有状态。
编码:Base64,chunked,压缩(gzip,deflate)
抓包工具:Wireshark,tcpdump
HTTP/3:基于UDP,QUIC,gRPC
HTTP/2:HPACK,Server Push,SPDY
HTTPS:对称加密(AES,ChaCha
),非对称加密(RSA,DH
),摘要算法(SHA-2
),证书(X509,CA
),SSL/TLS
(SNI,OCSP,连接优化)。
CDN
(负载均衡,就近访问,Squid/Varaish/ATS
WAF
:应用层防护,访问控制,审计
TCP
三次握手,SYN,SYN.ACK,ACK
;TCP
四次握手关闭连接:FIN,ACK/FIN,ACK
什么是同源策略以及限制
前后端如何通信
如何创建Ajax
跨域通信的几种方式
什么是同源策略以及限制
同源策略限制从一个源加载的文档或脚本如何与另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。
Cookie, LocalStorage, IndexDB无法读取
DOM无法获取
AJAX请求不能发送
AJAX是同源下面的通信方式,WebSocket不限制同源策略,CORS支持跨源通信,也支持同源通信。
如何创建Ajax
XMLHttpRequest
对象的工作流程
兼容性处理
事件的触发条件
事件的触发顺序
Object.prototype.toString.call()
var util = {};
util.indexOf = function(array, item) {
for(var i=0; i
模拟ajax
的功能:
util.json = function(options) {
var opt = {
utl: '',
type: 'get',
data: {},
success: function() {}
error: function() {}
};
util.extend(opt, options);
if(opt.url) {
var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP');
var data = opt.data, url = opt.url,
type = opt.type.toUpperCase(), dataArr=[];
for(var k in data) {
dataArr.push(K+'='+data[k]);
}
if(type === 'GET') {
url = url + '?' + dataArr.join('&');
xhr.open(type, url.replace(/\?$/g, ''), true);
xhr.send();
}
if(type === 'POST'){
xhr.open(type, url, true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(dataArr.join('&'));
}
xhr.onload = function() {
if(xhr.status === 200 || xhr.status === 304) {
var res;
if(opt.success && opt.success instanceof Function) {
res = xhr.responseText;
if(typeof res === 'string') {
res = JSON.parse(res);
opt.success.call(xhr.res);
}
}
}else{
if(opt.error && opt.error instanceof Function) {
opt.error.call(xhr.res);
}
}
跨域通信的几种方式:
JSONP
Hash
PostMessage
WebSocket
CORS
jsonp
的原理:
util.jsonp = function(url, onsuccess, onerror, charset) {
var callbackName = util.getName('tt_player');
window[callbackName] = function() {
if(onSuccess && utils.isFunction(onSuccess)) {
onSuccess(arguments[0]);
}
};
var script = util.creatScript(url+'&callback='+callbaseName,charset);
script.onload = script.onreadystatechange = function() {
if(!script.readyState || /loader|complete/.test(script.readyState)){
script.onload = script.onreadystatechange = null;
// 移除该script的DOM对象
if(script.parentNode){
script.parentNode.removeChild(script);
}
// 删除函数或变量
window[callbackName] = null;
}
};
script.onerror = function() {
if(onerror && util.isFunction(onerror)) {
onerror();
}
};
// script标签的异步加载来实现的
使用webSocket
不受同源策略限制:
var ws = new WebSocket('wss://echo.websocket.org');
ws.onopen = function(evt) {
console.log('');
ws.send('');
};
ws.onmessage = function(evt) {
console.log('');
ws.close();
};
ws.onclose = function(evt) {
console.log('');
};
csrf, xss
-CSRF:通常称为跨站请求伪造
Cross-site-request forgery
缩写CSRF,攻击原理,防御措施。
CSRF
防御措施:1.token
验证;2.Referer
验证;3.隐藏令牌
CSRF
攻击原理:依赖用户点击登录,下发cookie,引诱点击,访问,指向的是网站A的api接口,特别是get类型。加token验证,注册成功以后,没有手动上传token,没有带token就避免了攻击;Referer
验证,指页面来源,是否来自我这个站点下的页面,是的话执行动作,不是就拦截。
XSS
-Cross-site scripting
跨域脚本攻击:XSS
原理,向页面注入脚本,防御措施,让脚本不能执行
XSS和CSRF区别
XSS
是向你页面注入JS
去执行,然后JS函数体里做它想做的事情
CSRF
是利用你本身的漏洞去帮助你主动执行那些接口,CSRF依赖于你这个用户要登录网站
算法:堆,栈,队列,链表;JS的数组本身就具备堆,栈,队列的特性,push, pop, shift, unshift
堆栈:先进后出,先进先出,unshift进,pop出.
渲染机制
什么是DOCTYPE
及作用
浏览器渲染过程
重排Reflow
重绘Repaint
布局Layout
DTD
(document type definition
,文档类型定义)是一系列的语法规则,用来定义XML或(X)HTML的文件类型。浏览器会使用它来判断文档类型,决定使用何种协议来解析,以及切换浏览模式。
DOCTYPE
是用来声明文档类型和DTD规范的,一个主要的用途便是文件的合法性验证,如果文件代码不合法,那么浏览器解析时便会出一些差错。
浏览器的渲染过程
HTML,HTML Parser,DOM Tree(DOM) (Style sheets-> CSS Parser-> Style Rules)->Attachment->Render Tree(Layout)->Painting->Display
Reflow
重排
定义:DOM结构种的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式来计算并根据计算结果将元素放到它该出现的位置,这个过程称为reflow。
触发reflow:
当你增加,删除,修改DOM节点时,会导致Reflow或Repaint。
当你移动DOM的位置,或是搞个动画的时候
当你修改css样式的时候
当你Resize窗口的时候(移动端没有这个问题),或是滚动的时候
当你修改网页的默认字体时
重绘Repaint
当各种盒子的位置,大小以及其他属性,例如颜色,字体大小等会确定下来后,浏览器于是把这些元素都各自的特性绘制了一遍,于是页面的内容出现了,这个过程称为repaint。
运行机制:JS的单线程,一个时间之内,JS只能干一件事;EventLoop事件循环。
任务队列:同步任务,异步任务。
页面性能
提升页面性能的方法:
资源压缩合并,减少HTTP请求
非核心代码异步加载,异步加载的方式,
利用浏览器缓存
使用CDN
预解析DNS
CDN-让网络快速达到服务端,把文件下载下来,尤其是当页面第一次打开的时候,浏览器缓存是起不到任何作用的,那么使用CDN是非常明显的。
预解析DNS,如果你的页面使用多个域名的时候,你的DNS解析是非常明显的。
页面中的所有a
标签,在一些高级浏览器里面是默认打开DNS
预解析的,也就是说你不用加meta
等这句话,也可以使用DNS
预解析的,但是如果你的页面是https
协议开头的,很多浏览器默认是关闭DNS
预解析的,需要强制打开DNS
预解析。
非核心代码异步加载
利用浏览器缓存
异步加载的方式:动态脚本加载;defer;async
异步加载的区别:
defer
是在HTML
解析完之后才会执行,如果是多个,按照加载的顺序依次执行。
async
是在加载完之后立即执行,如果是多个,执行顺序和加载顺序无关。
浏览器缓存:
缓存的分类:强缓存/协商缓存
// 强缓存
Expires thXXX
Cache-Control: max-age
// 协商缓存
Last-Modified/If-Modified-Since
Etag/If-None-Match
缓存,浏览器缓存说的就是你个资源文件在浏览器中存在的这个备份,或说是副本。
强缓存就是我问都不问不直接请求,直接拿过来就用了;协商缓存就是浏览器发现我本地有这个副本,但我又不确定用不用它,向服务器问一下,我这个文件要不要用,也就是协商和服务器协商一下,我这个能不能用,它是不是过期了,这个过程叫协商缓存。
强缓存:Expires
:过期时间,值表示绝对时间;Cache-Control:Cache-Control:max-age=3600
相对时间。
协商缓存:Last-Modified
上次修改的时间,If-Modified-Since
请求的时候我给服务器带的,Etag
解决:服务器下方的资源会给你Etag
值,If-None-Match
。
1.0 缺点:虽然我的时间变了,但是我的内容没有发生变化,内容没有发生变化,我完全可以从副本中拿。
1.1 过了强缓存的时间,浏览器再去向服务器请求,问它,这个资源我可不可以再用的时候,它会通过If-None-Match
,http
中会加一个这个If-None-Match
key值,然后会加一个value
,这个value
就是服务器下方的Etag
值。
缓存是为了提高页面性能优化的.
了解错误监控类:前端错误的分类,错误的捕获方式,上报错误的基本原理。前端错误的分类:即时运行错误,代码错误,资源加载错误。即时运行错误的捕获方式:try...catch...
,window.onerror
资源加载错误:object.onerror
,performance.getEntries()
,Error
事件
上报错误的基本原理:1. 采用ajax通信的方式上报;2. 利用Image对象上报。
业务能力
思考能力
学习能力
付出
Vuex是一个专为Vue服务,用于管理页面的数据状态,提供统一数据操作的生态系统。它集中于MVC模式中的Model层,规定所有的数据操作必须通过action-mutation-state
,change的流程来进行,再结合Vue
的数据视图双向绑定特性来实现页面的展示更新。
统一的页面状态管理以及操作处理,可以让复杂的组件交互变得简单清晰,同时可在调试模式下进行时光机般的倒退前进操作,查看数据改变过程,使code debug
更加方便。
Vue Components
-Dispath
-Actions
-commit
-Mutations
-Mutate
-State
-Render
。
Vue Components,Vue组件,HTML页面上,负责接收用户操作等交互行动,执行dispath方法触发对应action进行回应。
dispath,操作行为触发方法,是唯一能执行action的方法。
actions,操作行为处理方法,负责处理Vue Components接收到的所有交互行为,包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation操作。该模块提供了Promise的封装,以支持action的链式触发。
commit,状态改变提交操作的方法。对mutation进行提交,是唯一能执行mutation的方法。
mutations:状态改变操作方法,是Vuex修改state的唯一方法,其他修改方式在严格模式下将会报错,该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
state:页面状态管理容器对象。集中存储Vue Components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
getters:state对象读取方法。被包含中render中,Vue Components通过该方法读取全局state对象。
生命周期流程图
new Vue
新建Vue
实例,初始化(事件&生命周期),beforeCreate
,初始化(注入&校验),created
,是否指定'el'选项?是,是否指定'template'选项,是(将template编译到render函数中),否(将el外部的HTML作为template编译);beforeMount
,创建vm.$el
,并用替换el,mounted
,挂载完毕。(当data被修改时,beforeUpdate
,虚拟DOM
重新渲染并应用更新,updated
),当调用vm.$destory
函数时,beforeDestory
,解除绑定销毁子组件以及事件监听器,destoryed
。
生命周期创建前后,挂载前/后,更新前/后,销毁前/后。
beforeCreate
,在beforeCreate
生命周期执行时,data
和methods
中的数据还未初始化,所以此时不能使用data
中的数据和methods
中的方法。
created
,data
和methods
初始化完毕,此时可以使用methods
中的方法和data
中的数据。
beforeMount
,template
模板已经编译好,但还未挂载到页面,此时页面还是上一个状态。
mounted
,此时Vue实例初始化完成了,DOM挂载完毕,可以直接操作dom或者使用第三方dom库。
beforeUpdate
,此时data已更新,但还未同步页面。
updated
,data
和页面都已经更新完成。
beforeDestory
,Vue实例进入销毁阶段,但所有的data和methods,指令,过滤器等都处于可用状态。
destoryed
,此时组件已经被销毁,data,methods等都不可用。
v-cloak主要是用来避免页面加载时出现闪烁的问题,可以结合CSS的[v-cloak]{display:none}
方式解决这一问题。
组件之间,父子之间的通信方案:
通过事件总线bus,即通过发布订阅方式
vuex
父组件通过prop向组件传输数据
子组件绑定自定义事件,通过this.$emit(event.params)
来调用自定义事件
使用vue提供的parent children
&refs
方法来通信
组件的按需加载是项目优化的一个环节,也可以降低首屏渲染时间;使用()=>import()
;使用resolve=>require(['./componentA'], resolve)
keep-alive
是Vue
的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。chunk
是webpack
打包过程中的Modules
的集合,是打包过程中的概念。
JSONP:ajax请求受同源策略影响,不允许进行跨域请求,而script标签src属性中的链接却可以访问跨域的Js脚本,利用这个特性,服务端不再返回JSON格式的数据,而是返回一段调用某个函数的js代码,在src中进行进行了调用,这样实现了跨域。
斐波那契数列:
function f(n) {
if(n===0){
return 0;
}else if(n===1){
return 1;
}else{
var fn1 = 0;
var fn2 = 1;
var fnx = 0;
for(var i = 0; i< n-1; i++) {
var newFn = fn2;
fnx = fn1+fn2;
fn1 = fn2;
fn2 = fnx;
}
return fnx;
}
}
递归算法:
function fib(count) {
var count = parseInt(count);
if(isNaN(count) || count<=0) {
return 0;
}
function f(count) {
if(count<=2) {
return 1;
}
return f(count-1)+f(count-2);
}
return f(count);
}
求前20个数字:
var arr = [];
arr[1] = 1;
arr[2] = 2;
for(var i = 3; i<20; i++) {
arr[i] = arr[i-1] + arr[i-2];
}
for(var i = 1; i
Vue实现数据双向绑定的:
实现了一个监听器Observer,对数据对象进行遍历,包括了属性对象,利用Object.defineProperty()
属性都加上setter
和getter
。这样的话,给这个对象的某个值赋值,就会触发setter
,那么就监听到了数据变化。
实现了一个解析器Compile,解析Vue模板指令,将模板中的遍历都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
实现一个订阅者Watcher:Watch订阅者是Observer和Compile之间通信的桥梁,主要的任务是订阅Observer中的属性值变化的消息,当收到属性值变化的消息时,触发解析器Compile中的对应的更新函数。
实现一个订阅器Dep,订阅器采用发布-订阅设计模式,用来收集订阅者Watcher,对监听器Observer和订阅者watcher进行统一管理。
算法
栈,后进先出,push,pop
队列,先进先出,push,shift
链表,结点,对元素的内存地址一无所知(每一个结点的结构都包括了两部分的内容,数据域和指针域)
示例:
{
// 数据域
val: 1,
// 指针域,指向下一个结点
next: {
val: 2,
next:。。。
}
}
// 链表
function ListNode(val) {
this.val = val;
this.next = null;
}
二叉树:根结点,子结点,叶子结点;叶子结点高度未1,等树的高度;度表示一个结点开叉出去多少个子树,被记为结点的度;度为0,表示为叶子结点。
二叉树结构:1,它可以没有根结点作为一颗空树存在;2,如果它不是空树,那么必须由根结点,左子树和右子树组成且左右子树度是二叉树。3.二叉树不能被简单定义为每个结点的度度是2的树。
二叉树:在JS中,二叉树使用对象来定义,它的结构分为三块:
数据域
左侧子结点的引用
右侧子结点的引用
二叉树结点的构造函数:
function TreeNode(val) {
this.val = val;
this.left = this.right = null;
}
// 创建一个二叉树结点时
const node = new TreeNode(1);
二叉树遍历:1.先序遍历,2.中序遍历,3.后序遍历,4.层次遍历(按照顺序规则不同)
按照实现方式不同:递归遍历;迭代遍历(先中后序遍历,层次遍历)
创建一个由数据域,左右子树组成的结点,每一颗二叉树度应该由这三部分组成。
递归函数(递归式,递归边界)
// 先序遍历的遍历实现
// 所有遍历函数的入参都是根据结点对象
function preorder(root) {
// 递归边界,root为空
if(!root) {
return
}
// 输出当前遍历的结点值
console.log('当前遍历的结点值' + root.val);
// 递归遍历左子树:
preorder(root.left);
// 递归遍历右子树:
preorder(root.right);
}
// 中序遍历,左子树,根结点,右子树
function inorder(root) {
if(!root) {
return
}
// 递归遍历左子树
inorder(root.left);
// 输出当前遍历的结点值
console.log('当前遍历的结点是', root.val);
// 递归遍历右子树
inorder(root.right);
}
// 后序遍历:左子树,右子树,根结点
function postorder(root) {
if(!root) {
return
}
// 递归遍历左子树
postorder(root.left)
// 递归遍历右子树
postorder(root.right)
// 输出当前遍历的结点值
console.log('当前遍历的结点值是:'+root.val)
}
时间复杂度/空间复杂度
时间:算法对应的执行总次数的一个变化趋势
空间:内存增长的趋势
求两数求和问题
const twoSum = function(nums, target) {
// 这里我用对象来模拟map的能力
const diffs = {}
// 缓存数组的长度
const len = nums.length
// 遍历数组
for(let i = 0; i
合并两个有序数组:
const merge = function(nums1, m, nums2, n) {
// 初始化两个指针的指向,初始化num1尾部索引k
let i = m-1, j = n-1, k = m+n-1;
// 当两个数组都没遍历完时,指针同步移动
while(i>=0 && j>=0) {
// 取较大的值,从末尾往前填补
if(nums1[i] >= nums2[j]){
num1[k] = nums1[i]
i--;
k--;
}else{
num1[k] = nums2[j]
j--;
k--;
}
}
// nums2留下的情况,特殊处理一下
while(j>=0){
nums1[k] = num2[j];
j--;
k--;
}
};
字符串:反转字符串:
const str = 'huang';
// 定义反转后的字符串
const res = str.split('').reverse().join('');
console.log(res);
回文字符串:正着读和倒着读都一样
function isPalindrome(str) {
// 先反转字符串
const reversedstr = str.split('').reverse().join('');
return reversedstr === str
}
具有对称性:
function isPalindrome(str){
// 缓存字符串的长度
const len = str.length
// 遍历前半部分,判断和后半部分是否对称
for(let i = 0; i
链表的合并:
const mergeTwoLists = function(l1,l2) {
// 定义结点,确保链表可以被访问到
let head = new ListNode()
let cur = head; // cur这里就是咱们那根“针”
while(l1&&l2) {
// 如果l1的结点值较小
if(l1.val <= l2.val) {
// 先串起来l1的结点
cur.next = l1
// l1指针向前一步
l1 = l1.next
}else{
// l2较小时,串起l2的结点
cur.next = l2
// l2指针向前一步
l2 = l2.next
}
// "针"在串起一个结点后,也会往前一步
cur = cur.next
}
// 处理链表不等长的情况
cur.next = l1 !== null ? l1 : l2
// 返回起始点
return head.next
}
单例模式
class SingleDog{
show() {
console.log('我是单例对象')
}
static getInstance() {
// 判断是否已经new过这个实例
if(!SingleDog.instance) {
SingleDog.instance = new SingleDog()
}
return SingleDog.instance
}
}
闭包:
SingleDog.getInstance = (function() {
// 定义自有变量instance
let instance = null
return function() {
// 判断自由变量是否为null
if(!instance) {
instance = new SingleDog()
}
return instance
}
})()
Vue原理:
组件化和MVVM数据驱动视图
响应式原理
vdom和diff算法
模板编译/更新
组件渲染过程
前端路由
路由模式:hash/history;
路由配置:动态路由/懒加载
组件渲染过程:1. 初次渲染过程,2.更新过程,3.异步渲染过程。
解析模板为render函数(或开发环境vue-loader),触发响应式监听data属性,getter和setter,执行render函数生成vnode,path(elem, newNode);修改data,触发setter,更新执行render函数生成newNode;$nextTick
,汇总data的修改,一次性更新视图,减少DOM操作次数,提高性能。
路由模式:hash变化会触发网页跳转即浏览器的前进后退,hash变化不会刷新页面,SPA必需的特点:hash永远不会提交到server端。
history原理表现:用url规范的路由,但跳转时,刷新页面,history.pushState
,window.onpopstate
组件化:组件化历史,数据驱动视图,MVVM。响应式:Object.defineProperty
,监听对象(深度),监听数组,Object.defineProperty
的缺点,Vue.set, Vue.delete
,原生数组。
vdom和diff
:Vnode
结构,Snabbdom
度。模板编译:with
语法,模板编译为render
函数,执行render
函数生成vnode
。
Vue
组件如何通讯:父子组件props
和this.$emit
,自定义事件event.$on/event.$off/event.$emit/Vuex
。
双向数据绑定v-model
的实现原理:
input
元素的value=this.name
绑定input
事件:this.name = $event.target.value
date
更新触发:re-render
computed
的特点:缓存,data不变,提高性能。何时使用异步组件:加载大组件,异步加载。何时使用deforeDestory
,解绑自定义事件:event.$off
,清除定时器,解绑自定义事件。
vue
优化:
合理使用v-show
和v-if
合理使用computed
v-for
时加key
,以避免和v-if
同时使用(v-for
优先级更高一些,每次v-for
都需要使用v-if
,这些性能是一种消耗。)
自定义事件,DOM
事件及时销毁
合理使用异步组件,合理使用keep-alive
,data
层级不要太深
使用vue-loader
,在开发环境做编译(预编译),前端通用的性能优化,如图片懒加载,使用SSR。
什么是并发/并行
并发是指一个处理器同时处理多个任务。
并行是指多个处理器或者是多核处理器同时处理多个不同的任务。
并发是逻辑上的同时发生,并行是物理上的同时发生。
并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。
垃圾回收:1,引用计数垃圾收集;2,标记-清除。
找出数据中重复的元素:
function fun(arr) {
var a = arr.sort();
var result = [];
for(var i = 0; i < arr.length; i++) {
if(arr[i] === arr[i+1] && result.indexOf(arr[i]) == -1) {
result.push(arr[i]);
}
}
return result;
}
new
操作:
var obj = new Da();
var obj = {};
obj.__proto__ = Da.prototype;
Da.call(obj);
深入浅出防抖函数debounce
,实现原理:
防抖函数debounce
指的是某个函数在某段时间内,无论触发了多少次回调,都只执行一次。假如我们设置了一个等待时间3秒的函数,在这个3秒内如果遇到函数调用请求就重新计时3秒,直到新的3秒内没有函数调用请求,此时执行函数,不然就以此类推重新计时。
实现原理就是利用定时器,函数第一次执行时设定一个定时器,之后调用时发现已经设定过定时器就清空之前的定时器,并重新设定一个新的定时器,如果存在没有被清空的定时器,当定时器计时结束后触发函数执行。
判断:
instanceOf
Constructor
Object.prototype.toString.call()
继承我总结了:有1,原型链继承,2,借用构造函数继承,3,组合继承,4,原型式继承,5,寄生式继承,6,寄生组合式继承。es6中新增的class和extends语法,用来定义类和实现继承,底层采用了寄生组合式继承。
this
的指向问题:
属性事件的this,在标签内调用事件函数,谁调用this所在的函数,那么this就指向谁
直接在fn函数中写this(如果直接在fn函数中写this,那么this为将根据其上下文来决定,一般指向window
onclick
事件中的this
遍历:
Object.keys(obj)
Object.values(obj)
你懂得越多,考虑的就越全面。
❤️关注+点赞+收藏+评论+转发❤️,原创不易,鼓励笔者创作更好的文章