一个朋友,面试的小哥,每天早晨六点起床学习,三本两年多经验拿了年包 50woffer。某大佬:

  • 你看很多,校招24k白菜价,谁又拿了大厂的offer。你会发现,他们不但学历高,而且都十分的努力。我个人挺佩服的。

  • 学历低的想拿大的offer,除了努力提升自己的技术,你别无选择。捷径有人能走,但是你想一想这个人是不是你。

1.Dear developers

6月徐来,年中总结,认识某人三本两年多经验拿了年包 50w 的 offer,通过相识,清晰了职业规划,发展方向。2021年的时间进度条也已过半,我们彼此或多或少经历了很多很多不平凡的事发生。下面面试了一些大厂,中厂,小厂的经验。

每个人都需要 有目标,有职业规划,有梦想,有向上的心。(当然,如果你没有,谁也不强求你)


2.简历总结

淘系:简历上,如有学历硬伤,过不了简历评估;简历上不能废话太多,你要是面试官你希望面试官问你哪些地方,你就往哪些地方写;项目上也只是写你做过的哪些事情,技术深度,实际的提升等;如果你有博客,文章网站等都可以写一写。

字节:简历上,项目经验不要大量写业务描述,需要的是一些技术方面的亮点;区分与其他人的优势,找个好的简历模板(这里推荐 木及简历 百度一下就有),如果你是5年以下经验的,如果一页写不完,要么是大牛,要么就是提炼不足,所以不管怎样,提炼到一页以内写完。

一些普通的,你只需要在技能栏里写如:精通Vue技术栈即可,(面试官或hr就了解关于Vue项目你基本能掌握即可)。

image.png

image.png

简历:简洁,主题鲜明。

简历细节:

  1. 性名,年龄,工作经验,联系方式,学历。

  2. 岗位,薪资面议,技能,工作经历(倒叙书写)。

  3. 技能就别写普通的了,到一定技能可写如下(只是个示例):

1. 熟练掌握JavaScript,TypeScript,Node,CSS以及ES6的新特性
2. 擅长Vue技术栈,以及其生态,某框架及其实现原理,源码
3. 有良好的数据结构与算法,能熟练在项目中运用设计模式
4. 熟悉HTTP协议,网络原理,webpack,babel等等
  1. 项目描述,如技术栈Vue+Vuex+axios+element ui+echarts+es6开发

  • (项目描述,时间点,遇到什么问题,用什么技术解决,取得了什么效果)

  • (在项目中扮演的角色等,开源能力,知识笔记,博客)

3.细聊

  • 聊聊小程序,小游戏,H5,app端,PC端(我做过)

  • 多端开发能力如何

  • 网站性能优化,了解浏览器实现原理

  • 浏览器缓存,计算机基础知识,数据结构与算法

  • 浏览器事件循环

  • 前端工程化的:模块化,组件化,规范化(eslint配置,文件,组件,函数,变量命,commit等),自动化

  • 项目目录,规范,开发流程

  • HTTP:服务器的认识,三次握手/四次挥手,一个url输入到浏览器产生界面发现了等

  • 单例模式

  • 闭包是什么

  • 原型链是什么

  • promise - api all,race,any等,async/await

  1. 编程能力解决单点难题

  2. 架构能力解决复杂,大项目的顶层设计

  3. 工程能力解决大规模人员参与的项目协作问题

  4. 领域知识解决特殊业务的系列问题

  • 问道: es6fill,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。

4.工资只是个大概估算(大概而已,别杠)

中小厂:

  • 初级:一线:7-20K;二线:5-15k

  • 中级:一线:15-25k;二线:12-23k

  • 高级:一线:20-40k;二线:15-35k

大厂:

  • 25-50k都有,看能力

image.png

水平(也只是个大概)

初级要:

  1. 负责业务系统前端模块的设计与开发;

  2. 负责产品的需求分析,开发,测试,维护等各项工作;

  3. 承担PC端和移动端的前端HTML5的开发任务;

  4. 整体页面结构以及css样式层的设计,优化;

  5. 完成页面脚本程序编写,实现各类页面动态,交互效果;

  6. 能够理解后端架构,与后端工程师配合为项目提供最优化的技术解决方案。

中级要:

  1. 负责所在项目需求实现与开发;

  2. 完成系统细节技术设计,完成核心代码的编写;

  3. 确保需求实现,满足项目设计规范,软件编码规范以及性能要求;

  4. 测试,系统测试等;

  5. 积极沟通,以确保功能实现按时,按质交付;

  6. 积极参与阶段评审,满足项目过程质量需求,审核和指导开发人员。

已满足,走向高级开发(个人带领过小团队):

  1. 负责大型系统的web前端开发

  2. 参与技术选型,推进应用和开发工作,支撑平台架构设计与开发功能

  3. 提升系统的整体用户体验,推动前端技术的发展

  4. 为提升团队开发效率,提炼公共组件,创造实用工具

  5. 优化现有业务,开发流程

  6. 关注前端发展,应用行业新技术

  7. 团队管理

笔记

模块一:

ES6 Module和Commonjs区别:

  1. ES6 Module静态引入,编译时引入

  2. Commonjs动态引入,执行时引入

  3. 只有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性能优化-产出代码

  1. 小图片base64编码

  2. bundlehash

  3. 懒加载

  4. 提供公共代码

  5. 使用CDN加速

  6. 使用production

  7. Scope Hosting

  • 代码体积更小

  • 创建函数作用域更小

  • 代码可读性更好

babel:前端开发环境必备工具,同webpack,需要了解基本的配置和使用

环境搭建和基本配置:babel-polyfill,babel-runtime

环境搭建:.babelrc配置:presetsplugins

core-js,regenerator结合,如何按需引入babel-polyfillBabel 7.4之后弃用babel-polyfill,推荐直接使用core-jsregenerator

babel-polyfill按需引入:文件较大,只有一部分功能,无需全部引入,配置按需引入。

babel-polyfill的问题?

  1. 会污染全局环境;

  2. 如果做一个独立的web系统,则无碍;

  3. 如果做一个第三方Lib,则会有问题;

babel-runtime不会污染全局环境:

  1. 基本配置

  2. 高级配置

  3. 优化打包效率

  4. 优化产生代码

  5. 构建流程概述

  6. babel

细分:

拆分配置和merge;启动本地服务;处理ES6;处理样式;处理图片;多入口;抽离css文件;抽离公共代码;懒加载;处理JSX;处理Vue;webpack优化构建速度:优化babel-loaderIgnorePluginnoParsehappyPacKParalleIUglifyPlugin

前端为何打包构建,好处?

  1. 体积更小(Tree-Shaking,压缩,合并),加载更快

  2. 编译高级语言或语法(TS,ES6+,模块化,SCSS

  3. 兼容性和错误检查(Polyfill,postcss,eslint

通过打包和构建,可以统一,高效的开发环境;统一的构建流程和产出标准,集成公司构建规范(提测,上线等)。

模块二:

module, chunk, bundle的区别?

  1. module各个源码文件,webpack中一切皆模块

  2. chunk多模块合并成的,如entryimport()splitChunk

  3. bundle最终的输出文件

loaderplugin的区别:loader模块转换器,如less-cssplugin扩展插件,如:HtmlWebpackPlugin

babelwebpack的区别?

  1. babel-js新语法编译工具,不关心模块化。

  2. webpack-打包构建工具,是多个loaderplugin的集合

如何产生一个lib?参考webpack.dll.js ouput library.

output: {// lib的文件名filename: 'lodash.js',// 输出lib到dist目录下path: disPath,library: 'lodash',
},

为何Proxy不能被Polyfill?

  1. Class可以用function模拟

  2. Promise可以用callback来模拟

  3. Proxy的功能用Object.defineProperty无法模拟

babel-polyfillbabel-runtime的区别

  1. babel-polyfill会污染全局

  2. babel-runtime不会污染全局

  3. 产生第三方lib要用babel-runtime

webpack如何实现懒加载

  • import()

  • 结合Vue React异步组件

  • 结合Vue-router``React-router异步加载路由

webpack优化构建速度(可用于生产环境)

  • 优化babel-loader

  • IgnorePlugin

  • noParse

  • happyPack

  • ParalleIUglifyPlugin

webpack优化构建速度(不用于生产环境)

  • 自动刷新

  • 热更新

  • DIIPlugin

项目流程

  1. 项目分多人,多角色参与

  2. 项目分多阶段

  3. 项目需要计划和执行

需求分析:了解背景,质疑需求是否合理,需求是否闭环,开发难度如何,是否需要其他支持,不要急于给排期。技术方案设计:1,求简,不过渡设计;2,产出文档,复盘;3,找准设计重点,组件怎么设计;4,组内评审;5,和RD,CRD沟通;6,发出会议结论。

完整项目流程:各个角色(需求分析),技术方案设计,开发,联调,测试,上线。

如何保证代码质量,开发,项目质量?

  1. 如何反馈排期

  2. 符合开发规范

  3. 写出开发文档

  4. 及时写单元测试

  5. Mock API

  6. Code Review

联调:1,和RD,CRD技术联调;2,让UE确定视觉效果;3,让PM确定产品功能。

加需求:走需求变更流程,按规定走,发起项目组和leader的评审,重新评估排期。

测试:提测发邮件,抄送项目组,测试问题要详细记录。

有问题及时沟通,QA和FE天生信息不对称,当面讨论,让QA帮你复现,需要特定设备才能复现。沟通,及时识别风险,及时汇报。

模块三:

了解:CSS盒模型,DOM事件类,HTTP协议类,原型链类,面向对象类,通信类,前端安全类,前端算法类;渲染机制类,JS运行机制,页面性能,错误监控,业务能力,团队协作能力,带人能力。

布局

浮动,绝对定位,flex-box,表格布局,网格布局。

浮动解决方案:
<div class="left"></div>
<div class="right"></div>
<div class="center"></div>
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模型的区别:

  1. 计算的宽度和高度不同

IE模型:它的宽和高是计算:padding+border

CSS3的属性,CSS如何设置这两种模型,box-sizing:content-box;(标准模型) box-sizing:border-boxIE模型)

JS如何设置获取盒模型的宽和高:dom.style.width/height,dom.currentStyle.width/height

兼容性更好一些:window.getComputeStyle(dom).width/heightdom.getBoundingClientRect().width/height

BFC(边距重叠解决方案)块级格式化上下文:BFC的原理,BFC的渲染规则:

  1. BFC这个元素的垂直方向的边距会发生重叠

  2. BFC的区域不会与浮动元素的boxs重叠,清除浮动布局

  3. BFC在页面上是独立的容器,外面的元素不会影响里面的元素,里面的元素也不会影响外面的元素

  4. 计算BFC高度的时候,浮动元素也会参与计算

如何创建BFC,给父元素添加:overflow: hidden;,overflow:auto;flow不为none,设置浮动BFCposition:不是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协议:

  1. HTTP协议的主要特点

  2. HTTP报文的组成部分

  3. HTTP方法

  4. POST和GET的区别

  5. HTTP状态码

  6. 什么是持久连接

  7. 什么是管线化

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-响应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 运算符:

  1. 一个新对象被创建,它继承自foo.protoype

  2. 构造函数foo被执行,执行的时候,相应的传参会被传入,同时上下文(this)会被指定为这个新实例,new foo等同于new.foo(),只能用在不传递任何参数的情况。

  3. 如果构造函数返回了一个“对象”,那么这个对象会取代整个new出来的结果。如果构造函数没有返回对象,那么new出来的结果为步骤1创建的对象。

类与实例:类的声明;生成实例。

类与继承:如何实现继承;继承的几种方式。

类的声明:

function Animal() {this.name = 'name';
}// es6 class
class Animal2 {constructor() {this.name = name;}
}

继承的本质原型链:

  1. 借助构造函数实现继承

function Parent1() {this.name = 'parent';
}
function Child() {Parent.call(this);this.type = 'child';
}

缺点是获取不到父类构造函数prototype上的方法。

  1. 借助原型链实现继承

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,ACKTCP四次握手关闭连接:FIN,ACK/FIN,ACK

模块五:

  1. 什么是同源策略以及限制

  2. 前后端如何通信

  3. 如何创建Ajax

  4. 跨域通信的几种方式

什么是同源策略以及限制

同源策略限制从一个源加载的文档或脚本如何与另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。

  1. Cookie, LocalStorage, IndexDB无法读取

  2. DOM无法获取

  3. AJAX请求不能发送

AJAX是同源下面的通信方式,WebSocket不限制同源策略,CORS支持跨源通信,也支持同源通信。

如何创建Ajax

  1. XMLHttpRequest对象的工作流程

  2. 兼容性处理

  3. 事件的触发条件

  4. 事件的触发顺序

  • Object.prototype.toString.call()

var util = {};
util.indexOf = function(array, item) {for(var i=0; i<array.length; i++) {if(array[i] === item) {return i;}}return -1;
};
// 判断是否为函数
util.isFunction = function(source) {return '[Object Function]` === Object.prototype.toString.call(source)
}
// 判断是不是ie
util.isIE = function() {var myNav = navigator.userAgent.toLowerCase();return (myNav.indexOf('msie') !== -1) ? parseInt(myNav.split('mise')[1]) : false;
};

模拟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标签的异步加载来实现的
<script src="http://xxx.com/?data = name & callback = 'jsonp'charset = 'utf-8'></script>
<script>jsonp({data:{}})
</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区别

  1. XSS是向你页面注入JS去执行,然后JS函数体里做它想做的事情

  2. CSRF是利用你本身的漏洞去帮助你主动执行那些接口,CSRF依赖于你这个用户要登录网站

算法:堆,栈,队列,链表;JS的数组本身就具备堆,栈,队列的特性,push, pop, shift, unshift

堆栈:先进后出,先进先出,unshift进,pop出.

渲染机制

  1. 什么是DOCTYPE及作用

  2. 浏览器渲染过程

  3. 重排Reflow

  4. 重绘Repaint

  5. 布局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:

  1. 当你增加,删除,修改DOM节点时,会导致Reflow或Repaint。

  2. 当你移动DOM的位置,或是搞个动画的时候

  3. 当你修改css样式的时候

  4. 当你Resize窗口的时候(移动端没有这个问题),或是滚动的时候

  5. 当你修改网页的默认字体时

重绘Repaint

当各种盒子的位置,大小以及其他属性,例如颜色,字体大小等会确定下来后,浏览器于是把这些元素都各自的特性绘制了一遍,于是页面的内容出现了,这个过程称为repaint。

运行机制:JS的单线程,一个时间之内,JS只能干一件事;EventLoop事件循环。

任务队列:同步任务,异步任务。

页面性能

提升页面性能的方法:

  1. 资源压缩合并,减少HTTP请求

  2. 非核心代码异步加载,异步加载的方式,

  3. 利用浏览器缓存

  4. 使用CDN

  5. 预解析DNS

<meta http-equiv='x-dns-prefetch-control` content='on'>
<link rel = 'dns-prefetch' href="//host_name_to_prefetch.com'>

CDN-让网络快速达到服务端,把文件下载下来,尤其是当页面第一次打开的时候,浏览器缓存是起不到任何作用的,那么使用CDN是非常明显的。

预解析DNS,如果你的页面使用多个域名的时候,你的DNS解析是非常明显的。

  • 页面中的所有a标签,在一些高级浏览器里面是默认打开DNS预解析的,也就是说你不用加meta等这句话,也可以使用DNS预解析的,但是如果你的页面是https协议开头的,很多浏览器默认是关闭DNS预解析的,需要强制打开DNS预解析。

  1. 非核心代码异步加载

  2. 利用浏览器缓存

异步加载的方式:动态脚本加载;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-Matchhttp中会加一个这个If-None-Matchkey值,然后会加一个value,这个value就是服务器下方的Etag值。

缓存是为了提高页面性能优化的.

了解错误监控类:前端错误的分类,错误的捕获方式,上报错误的基本原理。前端错误的分类:即时运行错误,代码错误,资源加载错误。即时运行错误的捕获方式:try...catch...window.onerror

资源加载错误:object.onerrorperformance.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

  1. Vue Components,Vue组件,HTML页面上,负责接收用户操作等交互行动,执行dispath方法触发对应action进行回应。

  2. dispath,操作行为触发方法,是唯一能执行action的方法。

  3. actions,操作行为处理方法,负责处理Vue Components接收到的所有交互行为,包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation操作。该模块提供了Promise的封装,以支持action的链式触发。

  4. commit,状态改变提交操作的方法。对mutation进行提交,是唯一能执行mutation的方法。

  5. mutations:状态改变操作方法,是Vuex修改state的唯一方法,其他修改方式在严格模式下将会报错,该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。

  6. state:页面状态管理容器对象。集中存储Vue Components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。

  7. 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

生命周期创建前后,挂载前/后,更新前/后,销毁前/后。

  1. beforeCreate,在beforeCreate生命周期执行时,datamethods中的数据还未初始化,所以此时不能使用data中的数据和methods中的方法。

  2. createddatamethods初始化完毕,此时可以使用methods中的方法和data中的数据。

  3. beforeMounttemplate模板已经编译好,但还未挂载到页面,此时页面还是上一个状态。

  4. mounted,此时Vue实例初始化完成了,DOM挂载完毕,可以直接操作dom或者使用第三方dom库。

  5. beforeUpdate,此时data已更新,但还未同步页面。

  6. updateddata和页面都已经更新完成。

  7. beforeDestory,Vue实例进入销毁阶段,但所有的data和methods,指令,过滤器等都处于可用状态。

  8. 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-aliveVue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。chunkwebpack打包过程中的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<arr.length; i++) {console.log(arr[i]);
}

模块八:

Vue实现数据双向绑定的:

实现了一个监听器Observer,对数据对象进行遍历,包括了属性对象,利用Object.defineProperty()属性都加上settergetter。这样的话,给这个对象的某个值赋值,就会触发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中,二叉树使用对象来定义,它的结构分为三块:

  1. 数据域

  2. 左侧子结点的引用

  3. 右侧子结点的引用

二叉树结点的构造函数:

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<len; i++) {// 判断当前对应的target差值是否存在if(diffs[target-nums[i]] !== undefined) {// 若有对应差值,那么答案getreturn [diffs[target-nums[i]], i]}// 若没有对应差值,则记录当前值diffs[nums[i]=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<len/2; i++) {if(str[i] !== str[len-i-1]){return false}}return true
}

链表的合并:

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() {// 定义自有变量instancelet instance = nullreturn function() {// 判断自由变量是否为nullif(!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和diffVnode结构,Snabbdom度。模板编译:with语法,模板编译为render函数,执行render函数生成vnode

Vue组件如何通讯:父子组件propsthis.$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-showv-if

  • 合理使用computed

  • v-for时加key,以避免和v-if同时使用(v-for优先级更高一些,每次v-for都需要使用v-if,这些性能是一种消耗。)

  • 自定义事件,DOM事件及时销毁

  • 合理使用异步组件,合理使用keep-alivedata层级不要太深

  • 使用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)

你懂得越多,考虑的就越全面。

一个月面试大厂,中厂,小厂的总结|2021 年中总结相关推荐

  1. 【面试招聘】去不了大厂实习,小厂实习去吗?

    各位读者们,我是千与千寻,大家好,最近马上就要秋招正式批了,祝大家都能找到满意的offer! 但是有不少读者说面试频频失利,想进大厂,但是面试不顺利,小厂感觉又不愿意去. 一.第一份实习 我结合自己曾 ...

  2. 应届生是选大厂offer还是小厂offer?

    PS:文中对大厂的定义是BATTMD等等有一定规模和知名度的公司以及各种独角兽 看到这篇文章,如果你确实在纠结"该选大厂offer还是小厂offer",那先恭喜你,因为这说明你至少 ...

  3. 用一个月面试了 大厂,中厂,小厂的总结|2021 年中总结

    推荐:两年前端程序媛从0到18k的逆袭之路 | 2021年中总结 image.png 求点赞+求分享+求评论,哦耶!!!(基本工资有一部分花在付费学习上) (文末有彩蛋...).一个朋友,面试的小哥, ...

  4. 【JAVA大厂面试必问】大厂面试八股文整理, 中厂小厂也爱问的八股文!

    一天看一点, 迟早进大厂! 秋招面试经验总结, 百分之八十都有用, 不做无用功! 文章目录 一天看一点, 迟早进大厂! 秋招面试经验总结, 百分之八十都有用, 不做无用功! 编译和解释的区别 Stri ...

  5. 都讨论大厂面试,当我小厂面试请喝茶的?

    前言 当你点进来的时候,我只能说声抱歉,因为我根本给不了你什么大厂面试方面的建议,我只是IT行业芸芸众生中的一粒小渣渣,但俗话讲有缘千里来相会,无缘对面不相逢,既然你我有缘,居士冒着被键盘侠喷出翔的风 ...

  6. 互联网公司大厂中厂小厂分别指哪些公司?

    大厂是指bat,这可不是蝙蝠的意思而是百度,阿里巴巴,腾讯的首字母. 现在华为,今日头条等也在跻身大厂行列. BAT已经成为中国最大的三家互联网公司.中国互联网发展了20年,现在形成了三足鼎立的格局, ...

  7. 【Java面试】大厂裁员,小厂倒闭,如何搞定面试官Java SPI是什么?有什么用?

    "Java SPI是什么?有什么用?" 这是阿里p6面试过程中,第二面的时候遇到的一个真实的问题. 如果你不理解SPI,建议你看完整篇文章. 大家好,我是Mic,一个工作了14年的 ...

  8. 那一年,我从计算机专业毕业了,要去大厂还是去小厂?

    三个我身上的小故事,让你感受下大厂和小厂有什么区别: 第一个故事: 我毕业那年,我去大厂面试,当然我也去小厂面试,从面试过程中,你很容易感受出大厂和小厂的区别.大厂面试的时候,笔试环节要考很多知识,涉 ...

  9. 互联网不只是有大厂,其实小厂也很香

    相信稍微跟编程有点熟悉的人都知道互联网大厂吧.在过去的一些文章里面不少人都会推荐大家去互联网大厂?小厂似乎没啥好说的. 似乎在大家眼里小厂就意味着门槛低.钱少.事多,技术落后,学不到东西,还加班. 直 ...

最新文章

  1. MySQL知识梳理与命令操作
  2. ubuntu查看python版本-切换Ubuntu默认python版本的两种方法
  3. php中文网数据库的搭建,【后端开发】php数据库中文乱码
  4. 微前端之qiankun
  5. android确认密码代码,Android自定义View实现验证码or密码输入框
  6. oracle 中文脚本,ORACLE常用脚本
  7. Thymeleaf视图
  8. Ucloud香港1h1g云服务器低至126元一年而且可开3年限时
  9. java 资深_Java架构师之路:从Java码农到资深架构师
  10. 外虚内实是什么意思_广东潮州“茶”文化浓厚,为什么“工夫茶”常被谬传“功夫茶”?...
  11. Ubuntu MySQL 配置 ip binding
  12. java设置绝对布局_浅谈Java绝对布局 原创
  13. 从个人经历出发,说说如何0基础学Java
  14. [软件工程] 形式化说明技术
  15. java+nanomsg(jnanomsg)
  16. shopex PHP Notice,解决最新shopex乱码问题
  17. 【虾神白话空间统计】笔记:置信度、零假设、PZ值、随机分布
  18. c语言 教学设计,C语言教案
  19. Lambda表达式和方法引用综合案例(获取年龄最大的两个用户的 姓)
  20. 创建facebook_我如何重新创建Facebook的微交互以进行功能发现

热门文章

  1. python判断一个字符串是不是ip地址
  2. 公司创业板上市与普通员工有关?
  3. 【校招VIP】Java Spring之spring boot
  4. 使用 ERD Online元数据管理平台,轻松创建和共享企业元数据
  5. PHP 门面设计模式在laravel中的应用
  6. 【ospf路由计算(一类LSA-router、二类LSA-Network、三类LSA-sum-Net)】-20211228-30
  7. 51单片机 | 并行I/O口扩展实例(74LS244/74LS373/4071)
  8. win7无线局域网_存储卡具备WiFi有多方便?东芝 FlashAir 无线存储卡上手体验
  9. Unity通过鼠标点击生成网格模型
  10. python qt gui与数据可视化编程 kindle_Kindle电子书 – D3.js数据可视化实战手册 azw3...