对浏览器内核的理解?
- 由2部分组成,分别是渲染引擎和js引擎
- 渲染引擎:负责取得网页内容整理讯息,以及计算网页显示方式
- js引擎:解析和执行js来实现网页动态效果
h5新特性
- 新增了canvas video radio
- 本地离线存储 localstorage
- sessionStorage浏览器关闭后自动删除
- 新增一些语义化的标签article、footer、header、nav、section
- 表单控件calendar date time email url search
- 新的技术webworker websocket Geolocation
- IE8/IE7/IE6支持通过document.createElement方法产生的标签
- 可以利用这一特性让这些浏览器支持HTML5新标签
- 浏览器支持新标签后,还需要添加标签默认的样式
webpack
打包效率:
- 开发环境采用增量构建,启用热更新
- 开发环境不做无意义的工作例如提取css计算文件hash等
- 配置devtool
- 选择合适的loader
- 个别loader开启cache如babel-loader
- 第三方库采用引入方式
- 提取公共代码
- 优化构建时搜索路径 指明需要构建目录及不需要构建目录
- 模块引入需要的部分
loader:
- loader就是一个node模块,它输出一个函数。当某种资源需要这个loader转换时
这个函数会被调用。并且,这个函数可以通过提供给他的this上下文访问Loader API
webpack的一些plugin(插件),怎么使用webpack对项目进行哟花
- 构建优化
-
减少编译体积
ContextReplacementPugin,
IgnorePlugin
Babel-plugin-import
babel-plugin-transform-runtime -
并行编译
happypack,
thread-loader,
uglifyjsWebpackPlugin
开启并行 -
缓存
cache-loader,
hard-source-webpack-plugin,
uglifyjsWebpackPlugin开启缓存,
babel-loader开启缓存 -
预编译
dllWebpackPlugin && DllReferencePlugin
auto-dll-webpack-plugin
- 性能优化
-
减少编译体积
Tree-shaking,
Scope Hositing -
hash缓存
webpack-md5-plugin -
拆包
splitChunksPlugin
import()
require.ensure
webpack优化项目
- 对于webpack4.0,打包项目使用production模式,这样会自动开启代码压缩
- 使用ES6模块开启tree shaking,这个技术可以移除没有使用的代码
- 优化图片,对于小图可以使用base64的方式写入文件中
- 按照路由拆分代码,实现按需加载
优化打包速度
- 减少文件搜索范围
- 比如通过别名(abc as rua)
- loader的test,include&exclude
- webpack默认压缩并行
- happypack并发调用
- babel也可以缓存编译
Babel原理
- 本质上就是编译器:当代码转为字符串AST,对AST进行转变最后在生成新的代码
- 分为三步:此法分析生成token,语法分析生成AST,遍历AST,根据插件变换相应的节点,最后吧AST转换为代码
如何实现一个插件(封装类,以及封装类的调用)
- 通过插件apply函数传入compiler对象
- 通过compiler对象监听事件
apply (compiler) { const afterEmit = (compilation, cb) => { cb() setTimeout(function () { process.exit(0) }, 1000) } compiler.plugin('after-emit', afterEmit) } } module.exports = BuildEndPlugin
cdn
- 内容分发网络,基本思路是尽可能避开互联网上有可能影响数据传输速度的稳定性的瓶颈和环节,是内容传输的更快,更稳定
内存泄漏
-
定义:程序中已动态分配的堆内存由于某种原因未释放或者无法释放引发的各种问题
-
结果:变慢,崩溃,延迟大等
-
原因:
- 全局变量过渡使用
- dom情况时,还存在引用
- ie中使用闭包
- 定时器未清除
- 子元素存在引起的内存泄漏
-
避免策略
- 减少不必要的全局变量,或者什么周期较长的对下那个,及时对无用的数据进行垃圾回收
- 注意程序逻辑,避免死循环之类的
- 避免创建过多对象原则:不用了的东西 要及时归还
- 减少层级过多的引用
前后端路由差别
- 后端每次路由请求都是重新访问服务器
- 前端路由实际上只是js根据url来操作dom元素,根据每个页面需要的去服务端请求数据,返回数据后和模板进行组合
bind,call,apply的区别
- call和apply都是为了解决改变this的指向,作用都是相同的,传参方式有所差别call为字符串参数apply为数组参
- 第一个参数为this所绑定的对象
简单说下原型链
-
每个函数都有
prototype
属性,除了
FUnction.prototype.bind()
,该属性指向原型 -
每个对象都有_proto_
属性,指向创建该对象的构造函数的原型,其实这个属性指向了
[[prototype]]
但是
[[prototype]]
是内部属性,我们并不能访问到,所以使用
_proto_
来访问 -
对象可以通过
_proto_
来寻找不属于该对象的属性,
_proto_
将对象连接起来组成了原型链
数组降维
- 普通数组降维
[1, [2], 3].flatMap(v => v)
// -> [1, 2, 3]
- 如果想将一个多维数组彻底的降维,实现方法
const flattenDeep = (arr) => Array.isArray(arr)
? arr.reduce( (a, b) => [...a, ...flattenDeep(b)] , [])
: [arr]
flattenDeep([1, [[2], [3, [4]], 5]])
事件代理
- 如果一个节点中的子节点是动态生成的,那么子节点需要注册事件的话应该注册在父节点上
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
let ul = document.querySelector('#ul')
ul.addEventListener('click', (event) => {
console.log(event.target);
})
</script>
- 事件代理的方式相对于直接给目标注册事件来说,有2个优点
- 节省内存
- 不需要给子节点注销事件
浏览器缓存
- 浏览器缓存对于前端性能优化来说是个很重要的点,良好的缓存策略可以降低资源的重复加载提高网页整体的加载速度
主要分为2种:强缓存和协商缓存
-
强缓存:实现强缓存可以通过2中响应头实现,
Expires
和
Cache-Control
强缓存表示在缓存旗舰不需要请求,state code为200Expires: Wed, 22 Oct 2018 08:41:00 GMT
Expires是HTTP/1.0的产物,表示资源会在Expires: Wed, 22 Oct 2018 08:41:00 GMT后过期,需要再次请求,并且Expires受限于本地时间,如果修改了本地时间,可能造成缓存失效。
Cache-control: max-age=30
Cache-Control出现于HTTP/1.1,优先级高于Expires。该属性表示资源会在30s后过期,需要再次请求。
-
协商缓存:
- 如果缓存过期了,我们就可以使用协商缓存来解决问题。协商缓存需要请求,如果缓存有效返回304.
- 协商缓存需要客户端和服务端共同实现,和强缓存一样,也有2种实现方式
Last-Modified和If-Modified-Since
- Last-Modified表示本文件最后修改日期,If-Modified-Since会将Last-Modefied的值发给服务器,询问服务器在改日期后资源十分有更新,有更新的话会将新的资源发送回来。
- 但是如果在本地打开缓存文件,就会造成Last-Modified被修改,所以在HTTP/1.1出现了ETag
ETag和If-None-Match的区别
- ETag类似于文件指纹,If-None-Match会将ETag发送给服务器,询问该资源ETag十分变动,如果
选择合适的缓存策略
- 对于大部分的场景都可以使用强缓存配合协商缓存解决,但是在一些特殊的地方可能需要选择特殊的缓存策略
- 对于某些不需要缓存的资源,可以使用Cache-control:no-store,表示该资源不需要被缓存
- 对于频繁变动的资源,可以使用Cache-Control:no-cache并配合ETag使用,表示该资源已经被缓存,但是每次都会发送请求询问资源是否更新。
- 对于代码文件来说,通常使用Cache-Control:max-age=31536000并配合策略缓存使用,然后对文件进行指纹处理,一旦文件名变动就会立刻下载新的文件
浏览器性能问题
-
重绘和回流
- 重绘和回流是渲染步骤中的一小节,但是这2个步骤对于性能影响很大
- 重绘是当节点需要更改外观而不会影响布局的,比如改变color就称为重绘
- 回流是布局或者几何属性需要改变就称为回流。
- 回流必定会发生重绘,重绘不一定发生会回流,回流苏so需要的成本比重绘高得多,改变深层次的节点很可能导致父节点的一系列回流。
-
可能导致性能问题的几个动作
- 改变widnow的大小
- 改变字体
- 添加或者删除样式
- 文字改变
- 定位或者浮动
- 盒模型
-
重绘和回流和Event loop的关系
- 当Event loop执行完Microtasks后,会判断document是否需要更新。因为浏览器是60hz刷新率,每16ms才会更新一次
- 然后判断是否有resize或者scroll,有的话会去触发事件,所以resize和scroll事件也是至少16ms才会触发一次,并且自带节流功能
- 判断是否触发了media query
- 更新动画并且发送事件
- 判断是否有全屏操作事件
- 执行requestAnimationFrame回调
- 执行IntersectionObserver回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不太好
- 更新界面
- 以上是一帧中可能会做的事情,如果在一帧中有空闲时间,就回去执行requestIdleCallback回调
-
减少重绘和回流
- 用translate代替top
- 使用visibility替换display:none,前者只会引起重绘,后者会引发回流(改变了布局)
- 不要把DOM节点的属性值放到一个循环里当成循环的变量
for(let i = 0; i < 1000; i++) { // 获取 offsetTop 会导致回流,因为需要去获取正确的值 console.log(document.querySelector('.test').style.offsetTop) }
- 不要使用table布局,可能很小的改动会造成整个table的重新布局 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用requestAnimationFrame
- css选择符从右往做匹配查找,避免DOM深度过深
- 将频繁运行的动画变为图层,图层能够阻止改节点回流影响别的元素。比如video标签,浏览器会自动将改节点变为图层