前端开发掌握的知识点内容摘要:
HTML&CSS:浏览器内核、渲染原理、依赖管理、兼容性、CSS语法、层次关系,常用属性、布局、选择器、权重、CSS盒模型、Hack、CSS预处理器、CSS3动画
JavaScript: 数据类型、运算、对象、Function、继承、闭包、作用域、事件、Prototype、RegExp、JSON、Ajax、DOM、BOM、内存泄漏、跨域、异步请求、模板引擎、模块化、Flux、同构、算法、ES6、ES7、ES8特性、Nodejs、HTTP
框架和类库: ajax、jQuery、Bootstrap、axios、Vue、Vuex、React、Element-ui、Layui、Webpack,Antd,Antd pro,Umi
HTML&CSS&JavaScript:
1、css 实现图片自适应宽高
一般做法如下
1 <style> 2 div{width: 200px; height: 200px} 3 div img{width: 100%; height: 100%} 4 </style> 5 <div> 6 <img src="xxxx.png" /> 7 </div>
但是,如果外层元素的宽高和图片不一致时,会导致图片被拉伸或收缩,巨丑。所以可以让图片的部分填充整个外层元素
1 <style> 2 div{ 3 width: 200px; 4 height: 200px; 5 background: url('xxxx.png') no-repeat; 6 background-size: cover; 7 background-position: center center; 8 } 9 </style> 10 <div></div>
2、讲 flex,手写出 flex 常用的属性,并且讲出作用
flex-direction: row/row-reverse/column/column-reverse 决定主轴的方向(即项目的排列方向)
flex-wrap: wrap/nowrap/wrap-reverse 决定项目排列方式
flex-basis: number|auto|initial|inherit; 规定弹性项目的初始长度。
flex-grow: number|initial|inherit;规定在相同的容器中,项目相对于其余弹性项目的增长量。
flex-shrink: number|initial|inherit;规定在相同的容器中,项目相对于其余弹性项目的收缩量。
flex-flow <flex-direction>|<flex-wrap> 前两者简写形式,默认flex-flow:row nowrap;
justify-content: flex-start/flex-end/center/space-between/space-around 决定项目在主轴的对齐方式
* space-between:两端对齐,项目之间的间隔都相等。
* space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
align-items: flex-start/flex-end/center/baseline/stretch 定义项目在交叉轴上如何对齐
* baseline: 项目的第一行文字的基线对齐。
* stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。
align-content: flex-start/flex-end/center/space-between/space-around/stretch 定义多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
3、BFC 是什么
①何为BFC:BFC(Block Formatting Context)格式化上下文,是Web页面中盒模型布局的CSS渲染模式,指一个独立的渲染区域或者说是一个隔离的独立容器。
②形成BFC的条件:
1、浮动元素,float 除 none 以外的值;
2、定位元素,position(absolute,fixed);
3、display 为以下其中之一的值 inline-block,table-cell,table-caption;
4、overflow 除了 visible 以外的值(hidden,auto,scroll);
③BFC的特性
1.内部的Box会在垂直方向上一个接一个的放置。
2.垂直方向上的距离由margin决定
3.bfc的区域不会与float的元素区域重叠。
4.计算bfc的高度时,浮动元素也参与计算
5.bfc就是页面上的一个独立容器,容器里面的子元素不会影响外面元素。
详见https://www.cnblogs.com/chen-cong/p/7862832.html
Vue:
1、vue双向绑定原理
vue的数据双向绑定主要通过Object.defineProperty()方法来进行数据劫持以及发布者-订阅模式来实现的,vue实例化的时候会去遍历所有的属性,给这些属性添加get和set方法进行数据劫持;
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>forvue</title> 6 </head> 7 <body> 8 <input type="text" id="textInput"> 9 输入:<span id="textSpan"></span> 10 <script> 11 var obj = {}, 12 textInput = document.querySelector('#textInput'), 13 textSpan = document.querySelector('#textSpan'); 14 15 Object.defineProperty(obj, 'foo', { 16 set: function (newValue) { 17 textInput.value = newValue; 18 textSpan.innerHTML = newValue; 19 } 20 }); 21 22 textInput.addEventListener('keyup', function (e) { 23 obj.foo = e.target.value; 24 }); 25 26 </script> 27 </body> 28 </html>
使用Object.defineProperty()来定义属性的set函数,属性被赋值的时候,修改Input的value值以及span中的innerHTML;然后监听input的keyup事件,修改对象的属性值,即可实现这样的一个简单的数据双向绑定。
2、虚拟dom和真实dom
avaScript引擎不能直接操作真实DOM树。为了给JavaScript提供操作DOM树的能力,浏览器在全局对象window上为JavaScript封装了一个document对象,然后在该对象上提供了大量的DOM操作接口。操作真实DOM的代价往往是比较大的(这其中还涉及C++与JavaScript数据结构的转换问题)。再加上修改DOM经常导致页面重绘,所以一般来说,DOM操作越多,网页的性能就越差。当然了,虚拟DOM并不是解决DOM操作性能问题的唯一解决方案,Vue的响应式系统也是一种重要的解决方案。从某种程度上来说,Vue依靠响应式系统可以实现“精确定点更新”,即直接定位到哪个DOM节点需要更新,而不需要经过虚拟DOM的比对,不过“精确定点更新”的内存代价偏大,因此目前Vue采用了响应式系统和虚拟DOM结合的方式
虚拟DOM设计的核心就是用高效的js操作,来减少低性能的DOM操作,以此来提升网页性能。
react:
1、React就是这么使用虚拟DOM的。当我们使用jsx语法定义一个模板时,React会用它生成一个由JavaScript描述的虚拟DOM树,并将其保存在JavaScript内存中。这个虚拟DOM树还保留了我们在模板中定义的数据和视图的绑定关系,这为React自动根据数据变化更新视图提供了可能。随后当数据发生变化时,React重新生成一个虚拟DOM树,通过对比两个虚拟DOM树的差异,React就可以知道该如何高效地更新视图。接着它就会调用原生的DOM接口,去更新真实DOM。
实际操作中:
1、项目里面的前端鉴权是怎么实现的?
使用vuex保存全局状态,并做数据持久化
vuex里面面定义token变量来表示用户是否登录,初始值为 " "
1 import createPersistedState from 'vuex-persistedstate' 3 export default new Vuex.Store({ 4 state: { 5 token: '', 6 }, 7 mutations: { 8 setIsLogin(state, isLogin) { //登录成功调用 9 state.token = isLogin; 10 }, 11 FedLogOut(state) { //退出登陆执行 12 state.token='' 13 } 14 } 15 actions, 16 getters, 17 plugins:[createPersistedState({ //vuex数据固化到本地缓存,数据持久化 18 storage: window.localStorage 19 })] 20 });
配置request请求拦截器 request请求拦截器:发送请求前统一处理,如:设置请求头headers、应用的版本号、终端类型等。
1 service.interceptors.request.use( 2 config => { 3 if (store.state.token) { 4 // 为请求头对象,添加token验证的Authorization字段 5 config.headers.Authorization = store.state.token; 6 } 7 return config 8 }, 9 error => { 10 // do something with request error 11 console.log(error) // for debug 12 return Promise.reject(error) 13 } 14 )
esponse响应拦截器 response响应拦截器:有时候我们要根据响应的状态码来进行下一步操作,例如:由于当前的token过期,接口返回401未授权,那我们就要进行重新登录的操作。
1 service.interceptors.response.use( 2 response => { 3 Toast.clear() 4 const res = response.data 5 if (res.status && res.status !== 200) { 6 // 登录超时,token过期返回401,重新登录 7 if (res.status === 401) { 8 store.dispatch('FedLogOut').then(() => { 9 router.replace({ 10 path: '/login' 11 //登录成功后跳入浏览的当前页面 12 // query: {redirect: router.currentRoute.fullPath} 13 }) 14 }) 15 } 16 return Promise.reject(res || 'error') 17 } else { 18 return Promise.resolve(res) 19 } 20 }, 21 error => { 22 Toast.clear() 23 console.log('err' + error) // for debug 24 return Promise.reject(error) 25 } 26 )
2、手写函数防抖和函数节流
函数节流:不断触发一个函数后,执行第一次,只有大于设定的执行周期后才会执行第二次
1 /* 2 节流函数:fn:要被节流的函数,delay:规定的时间 3 */ 4 function throttle(fn,delay){ 5 // 记录上一次函数出发的时间 6 var lastTime = 0 7 return function(){ 8 // 记录当前函数触发的时间 9 var nowTime = new Date().getTime() 10 // 当当前时间减去上一次执行时间大于这个指定间隔时间才让他触发这个函数 11 if(nowTime - lastTime > delay){ 12 // 绑定this指向 13 fn.call(this) 14 //同步时间 15 lastTime = nowTime 16 } 17 } 18 }
函数防抖:不断触发一个函数,在规定时间内只让最后一次生效,前面都不生效
1 function debounce(fn,delay){ 2 var timer = null 3 // 清除上一次延时器 4 return function(){ 5 clearTimeout(timer) 6 // 重新设置一个新的延时器 7 timer = setTimeout(() => { 8 fn.call(this) 9 }, delay); 10 } 11 }
3、ES6语法比如 promise、class 等等
promise详见https://www.cnblogs.com/Blod/p/15801202.html
class详见https://www.cnblogs.com/Blod/p/15801399.html
4、浏览器渲染过程
浏览器渲染的过程主要包括以下五步:
-
浏览器将获取的HTML文档并解析成DOM树。(构建DOM树)
-
处理CSS标记,构成层叠样式表模型CSSOM(CSS Object Model)。(css解析)
-
将DOM和CSSOM合并为渲染树(rendering tree)将会被创建,代表一系列将被渲染的对象。(构建渲染树)
-
渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout。浏览器使用一种流式处理的方法,只需要一次pass绘制操作就可以布局所有的元素。(渲染树布局)
-
将渲染树的各个节点绘制到屏幕上,这一步被称为绘制painting.(渲染树绘制)
5、回流重绘
reflow(回流):当浏览器发现某个部分发生了变化从而影响了布局,这个时候就需要倒回去重新渲染,大家称这个回退的过程叫 reflow。 常见的reflow是一些会影响页面布局的操作,诸如Tab,隐藏等。reflow 会从 html 这个 root frame 开始递归往下,依次计算所有的结点几何尺寸和位置,以确认是渲染树的一部分发生变化还是整个渲染树。reflow几乎是无法避免的,因为只要用户进行交互操作,就势必会发生页面的一部分的重新渲染,且通常我们也无法预估浏览器到底会reflow哪一部分的代码,因为他们会相互影响。
repaint(重绘): repaint则是当我们改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸和位置没有发生改变。
需要注意的是,display:none 会触发 reflow,而visibility: hidden属性则并不算是不可见属性,它的语义是隐藏元素,但元素仍然占据着布局空间,它会被渲染成一个空框,这在我们上面有提到过。所以visibility:hidden 只会触发 repaint,因为没有发生位置变化。
我们不能避免reflow,但还是能通过一些操作来减少回流:
-
用transform做形变和位移.
-
通过绝对位移来脱离当前层叠上下文,形成新的Render Layer。
-
另外有些情况下,比如修改了元素的样式,浏览器并不会立刻reflow 或 repaint 一次,而是会把这样的操作积攒一批,然后做一次 reflow,这又叫异步 reflow 或增量异步 reflow。但是在有些情况下,比如resize 窗口,改变了页面默认的字体等。对于这些操作,浏览器会马上进行 reflow。
6,优化渲染效率
- CSS注意事项
使用CSS3动画造成页面的不流畅和卡顿问题,其潜在原因往往还是页面的回流和重绘,减少页面动画元素对其他元素的影响是提高性能的根本方向。
1、设置动画元素 position 样式为absolute或 fixed,可避免动画的进行对页面其它元素造成影响,导致其重绘和重排的发生;
2、避免使用margin,top,left,width,height等属性执行动画,用 transform 进行替代;
- JS注意事项
1、解决js同步加载问题
①、将js文件放在页面底部,即</body>标签之前。因为html文件默认是按照顺序从上到下依次加载的,这样就可以先渲染dom节点,再加载js
②、使用 H5 的async属性,用法和特点如下
1 <script src = "test.js" anysc></script> 2 //加载脚本时不阻塞页面渲染 3 //使用这个属性的脚本中不能调用document.write方法 4 //可以只写属性名,不写属性值。写法如上 5 //H5新增属性 6 //脚本在下载后立即执行,同时会在window的load事件之前执行,所以有可能出现脚本执行顺序被打乱的情况
③、使用HTML的defer属性,用法和特点如下(前三点和anysc相同)
1 <script src = "test.js" defer></script> 2 //加载脚本时不阻塞页面渲染 3 //使用这个属性的脚本中不能调用document.write方法 4 //可以只写属性名,不写属性值。写法如上 5 //H4属性 6 //脚本在页面解析完之后,按照原本的顺序执行,同时会在document的DOMContentLoaded之前执行
2、避免频繁操作DOM元素
①使用虚拟dom,createDocumentFragment()方法
②、设置DOM元素的display属性为none再操作该元素
③、复制DOM元素到内存中再对其进行操作
④、用局部变量缓存样式信息从而避免频繁获取DOM数据/5、合并多次DOM操作
- 其他
1、资源压缩与合并。
HTML代码压缩:压缩在文本中有意义,而在HTML中不需要的字符。比如,空格、制表符、换行符,还有一些其他意义的字符,如HTML注释也可以被压缩。
CSS代码压缩:删除无效的代码和css语义合并。
JS的压缩和紊乱:使用在线网站压缩、使用html-minifier工具、使用uglifyjs2进行压缩。
文件合并:将多个js/css小文件合并为一个文件,减少网络请求次数。
注:css压缩与js的压缩和紊乱比html压缩收益要大的多,同时css代码和js代码比html代码多的多。所以,css与js代码压缩非常有必要!
2、浏览器缓存
缓存作用:对于web应用来说,缓存是提升页面性能同时减少服务器压力的利器。
强缓存:不会向服务器发送请求,直接从缓存中读取资源,在Chrome控制台的network选项中可以看到该请求的状态码是 200,但是 size 的标识为 from dist cache 或者 from memory cache
response header:response header里的过期时间,浏览器再次加载该资源时,如果在有效时间内,则使用强缓存。
Last-Modified 和 If-Modified-Since:二者都是记录页面最后修改时间的 HTTP 头信息,Last-Modified 是由服务器往客户端发送的HTTP, If-Modified-Since 是客户端往服务器端发送的头。
再次请求本地缓存的 cache 页面时,客户端会通过 If-Modified-Since 头先将服务器端发过来的 Last-Modified 最后修改时间戳发送回去,这是为了让服务器端进行验证,通过这个时间戳判断客户端的页面是否是最新的。如果不是最新的,则返回新的内容,如果是最新的,则返回 304 告诉客户端其本地 cache 的页面时最新的,于是客户端就可以直接从本地加载页面了,这样在网络上传输的数据就会大大减少,同时也减轻了服务器端的负担。而在一些ajax应用中,要求回获取的数据永云是最新的,而不是读取缓存中的数据,做这样的设置是很有必要的。
3、CDN预解析
CDN服务提供商会有全国各个省份部署节点, 将网站静态资源部署到CDN后, 用户在访问页面时, CDN静态资源会从就近的CDN节点上加载资源. 当请求至达CDN节点后, 节点会判断资源是缓存是否有效, 若有效, 直接返回给用户, 若无效, 会从CDN服务器加载最新的资源返回给用户同时将资源保存一份到该CDN节点上, 以便后续的访问用户使用. 因此, 只在该地区有一个用户先加载了资源, 在CDN中建立了缓存, 该地区的其他用户都能受益。
4、DNS预解析
DNS 作为互联网的基础协议,其解析的速度似乎容易被网站优化人员忽视。现在大多数新浏览器已经针对DNS解析进行了优化,典型的一次DNS解析耗费20-120 毫秒,减少DNS解析时间和次数是个很好的优化方式。DNS Prefetching是具有此属性的域名不需要用户点击链接就在后台解析,而域名解析和内容载入是串行的网络操作,所以这个方式能减少用户的等待时间,提升用户体验。7、懒加载和预加载
懒加载也叫延迟加载:JS图片延迟加载,延迟加载图片或符合某些条件时才加载某些图片。
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。
懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
懒加载的意义及实现方式有:
意义:
懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。
实现方式:
1.第一种是纯粹的延迟加载,使用setTimeOut或setInterval进行加载延迟.
2.第二种是条件加载,符合某些条件,或触发了某些事件才开始异步下载。
3.第三种是可视区加载,即仅加载用户可以看到的区域,这个主要由监控滚动条来实现,一般会在距用户看到某图片前一定距离遍开始加载,这样能保证用户拉下时正好能看到图片。
预加载的意义及实现方式有:
意义:
预加载可以说是牺牲服务器前端性能,换取更好的用户体验,这样可以使用户的操作得到最快的反映。
实现方式:
1.用CSS和JavaScript实现预加载;
2.仅使用JavaScript实现预加载;
3.使用Ajax实现预加载。
常用的是new Image():
设置其src来实现预载,再使用onload方法回调预载完成事件。
只要浏览器把图片下载到本地,同样的src就会使用缓存,这是最基本也是最实用的预载方法。
当Image下载完图片头后,会得到宽和高,因此可以在预载前得到图片的大小(方法是用记时器轮循宽高变化)。
8、express框架
Express是一个基于Node平台的web应用开发框架,它提供了一系列的强大特性,帮助你创建各种web应用。
在终端中使用npm install express --save命令进行下载
express框架文档 https://www.expressjs.com.cn/
基本步骤(导入express)
1、导入express框架 let express = require ('express')
2、通过express函数,创建并返回一个web服务器对象 let app = express()
3、使用这个web服务器对象,开启一个web服务,并监听一个端口号 app.listen(8000,()=>{console.log('服务器已开启')})
中间件
1、定义
中间件就是一堆方法,可以接收客户端发来的请求、可以对请求做出响应,也可以将请求继续交给下一个中间件继续处理。
2、app.use中间件用法
app.use 匹配所有的请求方式,可以直接传入请求处理函数,代表接收所有的请求。所有的请求,都会先走use,作用是拦截器。
1 app.use((req,res,next)=>{ 2 //表示允许跨域请求 3 res.setHeader('Access-Control-Allow-Origin','*') 4 //next方法,表示继续往下执行 5 next() 6 })
实际开发中,我们用use中间件方法里面去判断用户的权限,从而确定该用户能否继续请求相关的接口。
跨域
1、定义
同源策略:协议名,主机名(域名或IP地址),端口号必须完全相同。违背同源策略就是跨域。
Ajax请求,必须遵循同源策略
2、解决跨域
设置响应头,允许跨域请求 respose.setHeader('Access-Control-Allow-Origin','*')
3、允许自定义请求头信息 res.setHeader('Access-Control-Allow-Origin','*')