事件机制
事件触发三阶段
- window 向下往事件目标传播,遇到注册的捕获事件会触发
- 传播到事件目标触发注册的事件
- 从事件目标向上往 window 冒泡,遇到注册的冒泡事件会触发
大致就是:事件捕获阶段、处于目标阶段、事件冒泡阶段
事件注册
addEventListener
第三个参数,可以是 Boolean、Object。默认 false,true 是捕获阶段触发,false 是冒泡阶段触发。如果是对象,captrue 和 Boolean 一样,once 是只执行一次,passive 表示不会调用 propagation?
阻止冒泡:event.stopPropagation()
on
重复定义会覆盖...
事件委托
<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>
网络请求
跨域
协议、域名、端口号,有一个不同,就会跨域。跨域不是浏览器没有发出请求,也不是服务器没返回响应,而是浏览器拦截了响应
跨域主要是防止:CSRF 攻击(Cross-site request forgery):跨站请求伪造。如果没有跨域,假如网站已经是登录态,就可以通过请求获取网站信息。
ajax
function _ajax() {
var xhr = new XMLHttpRequest()
xhr.open('get', _url + '/xhr')
xhr.send(null)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
console.log('xhr', xhr)
console.log('xhr', JSON.parse(xhr.response))
}
}
}
jsonp
缺点是只能 get,原理就是利用 script 标签不存在跨域。
// client
son.addEventListener(
'click',
function (e) {
e.stopPropagation()
var _script = document.createElement('script')
_script.src = 'http://localhost:3003/jsonp?say=fnSayRes'
document.body.appendChild(_script)
})
function fnSayRes(res) {
console.log('jsonp.res:', res)
}
// server
const Koa = require('koa')
const app = new Koa()
app.use(async (ctx) => {
const { request } = ctx
if (request.url.startsWith('/jsonp')) {
ctx.body = `${request.query.say}('12233')`
}
})
app.listen(3003, () => {
console.log('koa.3003 ..........')
})
CORS
设置 Access-Control-Allow-Origin
postMessage
发送方用postMessage给接受方一个消息,然后接收方监听message事件,就是事件驱动
存储
cookie
服务器生成,每次请求都在header中,4kb
localStorage
sessionStorage
indeDB
Service Worker
浏览器渲染
html 加载发生了什么
加载页面时,浏览器把 html 解析成 Dom 树,css 文件生成 CSSOM 以后,再将这两棵树结合成 render 树。 渲染树只会包括:显示的节点、这些节点的样式信息。然后根据渲染树来布局,也叫做回流。
回流
render 树种一些因为元素的尺寸、布局,隐藏等改变需要重建,就是回流。
回流必定引起重绘,布局、几何属性改变就回流
重绘
render 树中元素更新属性,包括外观如颜色背景等,叫做重绘
与 Event Loop 相关
回流、重绘和 Event Loop 有关,下次宏任务执行之前,会判断是否还有微任务,清空微任务后,就会判断浏览器器是否需要更新。
浏览器是 60hz 的,16.6ms 才更新一次。所以 scroll、resize 事件,最少 16.6ms 执行一次。
判断是否触发了媒体查询,更新动画并且发送事件,是否有全屏操作事件?,执行 requestAnimationFrame 回调,IntersectionObserver,更新界面,有空闲的话,执行 requestIdleCallback,都是这一帧做的
减少回流、重绘
- 使用 visibility 替换 display,前者占空间,不会回流,只会重绘,后者会引起回流(改变了布局)
- 不要使用 table,删掉一个可能会引起整个布局的回流
- css 选择符,避免层级过多
- 尽量多的使用 requestAnimationFrame 做动画
安全问题
XSS
攻击者将可执行的代码注入到网页中,通过转义字符,将引号、尖括号、斜杠转义和加白名单
CSP
建立白名单,就是告诉浏览器可以加载和执行哪些外部资源。
// 只允许加载本站资源
Content-Security-Policy: default-src ‘self’
// 只允许加载Https协议的图片
Content-Security-Policy: img-src https://*
// 允许加载任何资源
Content-Security-Policy: child-src 'none'
CSRF
中文名:跨站请求伪造。原理就是造出一个后端请求地址,让用户去点击,如果用户是在登录的状态下点击,就会泄露信息,通过一段操作让后端误以为是用户。
如何防御:
- get 请求不做数据修改
- 不让第三方获取到 cookie
- 阻止第三方请求
- 请求时附带验证码、token
SameSite
对 cookie 设置 SameSite,该属性表示 cookie 不随跨域请求发送
Referer
验证 Referer,看是否是第三方发起的
Token
服务端随机 Token,每次发送带上,验证有效性
点击劫持
将恶意网站的链接,设置透明,然后诱导客户去点
X-FRAME-OPTIONS,防止 iframe 嵌套点击
通过 js,判断是否有子域,有的话隐藏
中间人攻击
就是攻击者同时和客户端、服务端取得连接,让对方以为是安全的,实际上整个通讯过程都被控制了
解决方法:加一个安全通道,https 就行
性能优化
图片优化
100 _ 100 的图片是 10000 个像素点,假如每个像素点用 rgba 存储,那就是每个像素是 4 个通道。每个通道 1 个字节,图片大小就是:10000 _ 4 / 1024 kb = 39kb
那么优化图片就是:减少像素点,减少每个像素表示的颜色
使用 svg,使用 webp 格式图片,因为webp 使用了更好的图像数据压缩算法
DNS 与解析
预先获取域名的 ip
<link rel="dns-prefetch" href="//yuchengkai.cn">
webpack 性能优化
优化 loader
babel-loader,只检索.js 结尾的,忽略 node_moduels。加上缓存
module.exports = { module: {
rules: [ {
// js 文件才使用 babel test: /\.js$/,
loader: 'babel-loader',
// 只在 src 文件夹下查找 include: [resolve('src')], // 不会去查找的路径
exclude: /node_modules/ }
] }
}
loader: 'babel-loader?cacheDirectory=true'
HappyPack
受限于node单线程,webpack打包也是单线程。在执行loader时,长时间编译,会导致等待。HappyPack可以将loader同步执行转化成并行,充分利用资源加快打包效率
plugins: [
new HappyPack({
id: 'happybabel',
loaders: ['babel-loader?cacheDirectory'], // 开启 4 个线程
threads: 4
})
]
DllPlugin
将特定的类库提前打包然后引入,只有当类库版本更新才会重新打包。也实现了将公共代码抽离成单独文件的优化方案
// 单独配置在一个文件中
// webpack.dll.conf.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: {
// 想统一打包的类库
vendor: ['react']
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].dll.js',
library: '[name]-[hash]'
},
plugins: [
new webpack.DllPlugin({
// name 必须和 output.library 一致
name: '[name]-[hash]',
// 该属性需要与 DllReferencePlugin 中一致
context: __dirname,
path: path.join(__dirname, 'dist', '[name]- manifest.json ')
})
]
}
// webpack.conf.js
module.exports = {
// ...省略其他配置
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
// manifest 就是之前打包出来的 json 文件
manifest: require('./dist/vendor-manifest.json'),
})
]
}
代码压缩
现在只需设置mode:production。就自动压缩了,配置删掉console
resolve.alias:配置别名,会搜索的更快些
减少打包体积
按需加载:为了首屏加载更快,将每个路由页面打包成一个文件,loadsh这种大型类库也可以这样
Scope Hoisting
分析模块之前的依赖关系,尽可能把打包出来的模块合并到一个函数中
module.exports = {
optimization: {
concatenateModules: true
}
}
// test.js
export const a = 1
// index.js
import { a } from './test.js'
[
/* 0 */
function (module, exports, require) {
//...
},
/* 1 */
function (module, exports, require) {
//...
}
]
变成
[
/* 0 */
function (module, exports, require) {
//...
}
]
Tree Sharking
删除项目中未被引用到代码,Webpack 4 production会自动开启
本文使用 mdnice 排版