浏览器绘制
-
浏览器的进程模型
程序运行需要它自己的专属内存空间,可以把这块内存简单理解为进程,每个进程至少有一个进程,进程之间相互独立,即使需要通信,也需要双方统一
-
浏览器(浏览器进程、网络进程、渲染进程)
渲染进程:渲染进程启动后,会开启一个渲染主线程,主线程负责执行html css js;默认情况下浏览器会为每个标签开启一个渲染进程,以保证不同标签页之间相互不影响
-
渲染主线程如何工作?
-
解析Html
处理Html 并构建DOM树。解析过程中遇到css解析css,遇见js执行js,为了提高解析效率,浏览器再开始解析前,会启动一个预解析的线程,率先下载html中的外部css文件和js文件;
如果主线程解析到link位置,此时外部的css文件还没下载解析好,主线程不会等待,继续解析后续的html,这是因为下载和解析css的工作是在预解析线程中进行的,这就是css不会阻塞Html解析的根本原因
如果主线程解析到script位置,会停止解析Html,转而等待js文件下载好,js文件只会读取一遍,并将全局代码解析执行完成后,才能继续解析Html,这就是因为js代码的执行可能会修改当前的DOM树,所以DOM树的生成必须停止,这就是Js会阻塞HTML解析的根本原因-
优化script标签阻塞html渲染的方法:
-
将script放置于最上层:
这样会使得脚本加载并执行完成后才开始渲染Html内容。但是需要注意的是,由于脚本位于头部,所有的css样式表也必须先下载和应用到DOM元素上,然后再进行渲染。当网络比较慢时,可能导致首次渲染被延迟
-
将script放置于最下层:
脚本会在Html内容全部加载完成之前就开始执行,不会影响Html的渲染。由于脚本位于尾部,无法直接操作还未加载的DOM元素,需要等待DOMContentLoaded事件触发后才能获取到节点相关的节点
-
script添加async属性或者defer属性
使用async属性,脚本会异步加载和执行,在下载完成后立即执行,而不管其在文档中的位置;
使用defer属性,脚本会异步下载,但不会立即执行。它将在文档解析完成时按照它在文档中的顺序执行
-
-
-
计算样式
主线程会遍历得到的DOM树,依次为树中的每个节点计算出它最终的样式,称之为Computed Style在这一过程中,很多预设值会变成绝对值,比如rem变成px,这一步完成后,会得到一颗带有样式的DOM树
-
VUE 虚拟DOM:
基本工作原理:- 虚拟DOM树:当应用的状态发生变化时,框架会创建一个虚拟DOM树,一个轻量级的内存中的树形结构,与实际的DOM结构相对于。
- 对比和差异检测:框架会将前后两个虚拟DOM树进行比较,找出它们之间的差异
- 差异更新:框架会计算出需要进行更新的最小DOM操作集合,然后将这些操作应用到实际的DOM中,以反映最新的状态。这一次步骤通常包括插入、删除、更新DOM元素或属性,以及处理事件监听器等。
优点:
- 性能提升: 通过最小化实际DOM的操作,虚拟DOM可以显著提高页面渲染的性能
- 可跨平台:可以独立于底层平台,使前端框架能够在不同的浏览器和环境工作,而不必担心平台差异
- 简化复杂性:开发者可以更轻松地编写应用逻辑,不必担心手动管理DOM更新,从而减少错误和复杂性
-
-
处理图层
主线程会使用一套复杂的策略对整个布局树进行分层。
分层的好处在于,将来某一个层改变后,仅会对该层进行后续处理,从而提升效率。滚动条、折叠上下文等样式都会或多或少的影响分层结果。 -
绘制
主线程会为每个层单独产生绘制指令,用于描述这一层的内容该如何画出来
-
画
GPU硬件完成最终屏幕成像
-
渲染主线程执行任务
-
任务循环
- 渲染进程每次都从消息队列中取出一个任务进行执行
实际上没有微任务、宏任务之分;而是区分队列,队列执行有先后顺序之分
每个任务都有一个任务类型,同一个类型的任务必须在同一个队列,不同类型的任务可以分属于不同的队列。
在目前的chrome的实现中,至少包含下面的队列:
延时队列,用于存放计时器到达后的回调任务,优先级中;
交互队列,用户存放用户操作后产生的事件处理任务,优先级高;
微队列,用于存放需要最快执行的任务,优先级最高; Promise.resolve().then( funtion )、MutationObserver
VUE nextTick 通过Promise实现 等待下一次DOM更新刷新的工具方法;可以在状态改变后立即使用,以等待DOM更新完成。 - 最开始的时候,渲染主线程会进入一个无线循环,每一次循环会检查消息队列中是否还有任务存在。如果有,就会取出第一个任务执行,执行完一个后进入下一个循环;如果没有,则进入休眠状态
- 其他所有线程(包括其他进程和线程)可以随时向消息队列中添加任务。新任务会加到消息队列的末尾
- 在添加新任务时候,如果主线是休眠状态,则会将其唤醒以继续循环拿取任务
- 渲染进程每次都从消息队列中取出一个任务进行执行
-
异步
代码执行过程中,遇到一些无法立即处理的任务,setTimeout、setInterval 等,渲染主线程等待这些任务会导致长期处于阻塞状态,从而导致浏览器卡死
遇到延时任务:- 通知计时线程计时,当前任务结束
- 获取下一个任务执行
- 计时线程会在计时结束后将任务重新添加到队列末尾
异步方法:Promise、async/awit
浏览器垃圾回收
- 分代回收
V8使用分代回收策略,将对象分成不同的代,通常包括新生代喝老年代。新生代包含大多数新建的对象,而老年代包含多次回收仍然存活的对象。这种策略有助于提高性能,通常新生代的对象比老年代的对象更容易回收 - 标记-清除
这是一种更加复杂的垃圾回收策略,它将内存中的所有对象分为’可达’和’不可达’两组。垃圾回收期首先标记所有的可达对象,然后清除所有的不可达的对象。这个过程涉及两个阶段:标记阶段和清除阶段
标记阶段:从根对象开始,垃圾回收器递归遍历所有对象,并标记为可达的对象
清除对象:垃圾回收器清除所有的未标记的对象,即为不可达对象 - 增量回收
这种策略将垃圾回收操作分解为多个小步骤,以允许在应用程序执行期间执行垃圾回收操作,减少对应用的程序的中断 - 并行回收
允许多个线程同时进行垃圾回收操作 - 压缩
为了减少内存碎片,V8垃圾回收器还包括了内存压缩的步骤,将存活的对象移动到内存中的连续位置,以提高内存的使用效率
计算机网络
-
http
http1.0
HTTP1.0规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求,严重影响客户机和服务器的性能
http1.1
- HTTP支持持久连接,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟
- 还允许客户端不用等待上一次的请求结果返回,就可以发出下一次请求,但服务器必须按照接受到客户端请求的先后顺序依次返回
- 增加Host请求头字段。我们可以在一台web服务器上在同一个IP地址和端口号上使用不同的主机名来创建多个虚拟web站点
- 提供与身份认证、状态管理和Cache缓存等机制相关的请求和响应头
- 断点续传
http2.0
- 多路复用。允许同时通过单一的HTTP/2连接发起多重的请求-响应消息。容易去实现多流并行而不用依赖建立多个TCP连接。
- 二进制分帧。Http/2在应用层和传输层之间增加了一个二进制分帧层。在不改动HTTP/1的语义、方法、状态码、URI以及首部字段的情况下,解决了HTTP1的性能问题,改进传输性能,实现低延迟和高吞吐量
- 首部压缩。HTTP/2 使用了专门向首部压缩而涉及的HPACK算法
- 服务器推送。服务器推送是一种在客户端请求之前发送数据的机制。在HTTP/2中,服务器可以对客户端的一个请求发送多个响应
-
https
HTTPS工作原理:
- 服务器生成服务器公钥和服务器私钥,向CA认证机构申请证书。
- 浏览器向服务器发出请求网页
- 客户端支持的SSL/TLS协议的版本号
- Cipher Suite(密钥算法套件)
- 服务器响应,并发送证书以及服务器公钥
- 确认使用的加密通信协议版本,如果浏览器与服务器支持的版本不一样,服务器关闭加密通信
- 客户端检查证书是否是CA所颁发
- 客户端生成客户端公钥,客户端私钥,以及利用服务器公钥创建的会话密钥A
- 客户端向服务器发送客户端公钥,和会话密钥A
- 服务器通过服务器私钥还原密钥A,利用客户端公钥创建会话密钥B
- 服务器将会话密钥B发送给客户端,客户端通过客户端私钥还原会话密钥B
- 之后客户端向服务器发送数据会带上会话密钥A, 服务器向客户端发送数据带上会话密钥B
建立连接时: 公钥+私钥 进行非对称加密认证
后续数据传输:密钥A <=> 密钥B 对称加密
SSL(Secure Sockets Layer 安全套接字协议),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层与应用层之间对网络连接进行加密。
-
输入url的过程
- DNS解析
- 用户在浏览器输入URL,浏览器开始解析域名并查找DNS缓存,以查找域名对应的IP地址
- 如果域名对应的IP地址未在DNS缓存中找到,浏览器将查询操作传递给操作系统的DNS解析器,操作系统将查询操作传递给本地域名服务器
- 本地域名服务器在自己的DNS缓存中查找域名对应的IP地址,如果找到则返回,否则继续查询更高级别的DNS服务器,直到找到IP地址 - 建立TCP连接
- 浏览器使用解析后的IP地址,尝试与web服务器建立TCP连接
- 通过TCP三次握手,建立连接,确保浏览器和服务器之间可以相互通信
TCP三次握手
- 第一次握手(SYN):发送方首先向接收方发送一个SYN(同步)标志的TCP包,该包包含一个随机生成的初始化序列号(ISN)。这表示发送方希望建立一个连接,并且指定了一个用于数据传输的起始序号
- 第二次握手(SYN+ACK):接收方接收到发送方的SYN包后,它会回应一个带有SYN和ACK(确认)标志的TCP包。这个响应包不仅确认了接收到的SYN,还包含了接收方的初始序列号。这两个序列号表示了双方用于传输数据的初始顺序
- 第三次握手(ACK):最后,发送方接收到接收方的响应后,它会发送一个带有ACK标志的TCP包,表示对接收方的响应已经收到。至此,连接建立完成,双方可以开始进行数据传输。
-
发起HTTP请求
一旦建立了TCP连接,浏览器通过HTTP协议发送HTTP请求,包含请求的资源、请求头、浏览器类型等等 -
服务器处理请求
服务器接收到HTTP请求,开始处理请求
服务器可能需要查询数据库,生成动态内容,或者从文件系统中读取静态资源 -
服务器响应
-
浏览器渲染页面
-
断开连接
四次挥手
- 客户端发送终止请求(FIN)给服务器
- 服务器发送剩余数据给客户端,并发送终止请求(FIN)给客户端
- 客户端确认服务器的终止请求,发送确认(ACK)
- 服务器等待一段时间后,确认客户端的终止请求,发送确认(ACK)
- 连接终止完成
- DNS解析
浏览器缓存
- 浏览器加载资源时,根据响应头的Pragma和Cache-Control来识别强缓存和协商缓存; Pragma>Cache-Control>Expires
- 如果没有命中强缓存,浏览器一定会发送一个请求到服务器,通过last-modified和Etag验证资源是否命中协商缓存,如果命中,服务器会将这个请求返回,但是不会返回这个资源的数据,依然从缓存中读取资源
- 前两者都没有命中,直接从服务器加载资源
301永久重定向,302临时重定向
-
强缓存
- Expires:描述的绝对时间,由服务器返回。Expires受限于本地时间,如果修改了本地时间,可能会造成缓存失效
- Cach-Control:
- no-store: 禁止缓存。
- no-cache:需要重新验证缓存。客户端需要向服务器发送一个请求来确认缓存的有效性
- max-age=:指定资源在缓存中的最大存储时间
- public:可以被任何缓存,公共资源
- private:只能被浏览器缓存,不允许代理服务器缓存
-
协商缓存
如果协商缓存命中,响应头返回304,协商缓存是利用Last-Modified、if-Modified-Since 和ETag、if-None-Match这两对Header来管理的 - Last-Modified:表示本地文件最后修改日期 - ETag:资源变化会导致ETag变化,跟最后修改时间没有关系,ETag可以保证每一个资源是唯一的 - ETag优先级高于Last-Modified
-
Cookie
Cookie是一种存储在用户浏览器的小文件,用于存储网站的一些信息。服务器可以识别用户并保持会话。 是为了解决HTTP协议的无状态性问题。Http协议是一种无状态的协议,即服务器无法识别不同的用户或跟踪用户状态
-
localSotrage
5-10MB大小,永久保存,除非显式删除
-
sessionStorage
5-10MB大小,窗口关闭数据删除
-
IndexedDB
支持更大容量 > 10MB ,数据持久化,除非显示删除, 离线应用
网络安全
客户端安全
XSS(Cross Site Scripting)跨脚本攻击, 为了区分css,所以缩写为XSS,XSS攻击是往web页面插入恶意的JavaScript代码,当用户浏览器网页的时候,插入代码的代码被执行,从而达到攻击的目的。
XSS攻击要素:攻击者提交恶意代码; 浏览器执行恶意代码
XSS攻击防范
1. 将重要的cookies标记为HTTP ONLY
2. 只允许用户输入我们期望的数据
3. 对数据进行加密处理
4. 过滤或者移除特殊的html标签
5. 对特殊字符进行转移
服务器安全
CSRF跨站请求伪造。CSRF是通过伪装成来自受信任用户,在受信任的网站进行请求,盗取信息。是用受害者的身份向网站发送恶意请求
攻击要素:
1.攻击一般发起在第三方网站,被攻击的网站无法防止攻击发生
2.攻击者利用被攻击网站的登录凭证,冒充操作
3. 跨站请求可以是图片URL等
防范:
1. 表单里面增加Hash值
2. 验证码:每次用户的提交都珠海表单中填写一个图片上的随机字符串
3. 修改操作尽量用post
4. token
数据库安全
SQL注入攻击;应用程序向后台数据库传递SQL时,攻击者将SQL命令插入到web表单提交或输入域名或页面请求查询字符串,最终达到欺骗服务器恶意执行SQL命令
防范: 黑白名单校验、对数据表的访问权限授权
Vue(核心:数据劫持、组件化)
Vue响应式原理
响应式作为Vue的核心,使用数据劫持实现数据驱动试图。
- Observe:响应式原理的入口,进行响应式的绑定
- Dep: 依赖收集器,属性都会有一个Dep实例,方便发生变化时候能够找到对应的依赖(watcher)派发更新
- Watcher: 用于执行更新渲染
Object.defineProperty(obj,prop, des)
- 只能对已经存在的属性进行劫持,无法拦截新增的属性和删除的属性。Vue2需要借助Vue.set和Vue。delete来通知Vue响应式系统进行更新
- 劫持属于响应式级别的,每个属性都需要被劫持。
Proxy(target, handler)
- 对整个对象进行拦截和代理,可以拦截对象读取、赋值、删除等操作
- 无需在每个属性上进行劫持。
Vue生命周期
- 创建阶段
- beforeCreate: Vue实力初始化之后,数据观测之前调用。组件实例尚未初始化,数据和事件都还未准备(setup函数中的响应式数据和计算属性尚未准备好)
- created:组件实例创建完成后被立即调用。DOM节点尚未挂载(setup函数中的响应式数据和计算属性已经准备好了)
- 挂载阶段
- beforeMount(onBeforeMount):虚拟DOM已经被创建,尚未渲染到实际DOM
- mounted(onMounted):实例已经成功挂载到DOM中,可以执行DOM操作和访问DOM元素
- 更新阶段
- beforeUpdate(onBeforeUpdate):数据变化会触发更新渲染,但尚未更新到DOM
- updated(onUpdated):更新之后调用
- 销毁阶段
- beforeDestroy(onBeforeUnmount):实例摧毁之前调用
- destroyeed(onUnmount):实例摧毁之后调用
new Vue之后,发生了什么? 数据改变后,又发生了什么
- 首先做一些初始化操作,主要设置一些私有属性到实例中
- 运行声明周期钩子函数beforeCreate
- 进入注入流程:处理属性、computed、methods、data、provide、inject 最后使用代理模式讲他们挂载到实例中
- 运行生命周期钩子函数created
- 生成render函数:如果有配置,直接使用配置的render,如果没有,使用运行时编译器,把模板编译成render
- 运行生命周期钩子函数beforeMount
- 创建一个Watcher,传入一个函数updateComponent,该函数会运行render,把得到的vnode再传入_update函数执行;在执行render函数的过程中,会收集所有依赖,将来依赖变化时会重新运行updateComponent函数;在执行_update函数过程中,触发patch函数,由于目前没有旧树,因此直接为当前的虚拟dom树的每一个普通节点生成elm属性,即真实的dom;如果遇到创建一个组件的vnode,则会进入组件实例化流程,该流程和创建vue实例流程基本相同,最终会把创建号的实例挂载vnode的compoentInstance属性中,以便复用
- 运行生命周期钩子函数mounted
** 改变数据后: **
- 数据变化后,所有依赖该数据的Watcher均会重新运行,这里仅考虑updateComponent函数对应的Watcher
- Watcher会被调度器放到nextTick中运行,也就是微队列中,这样是为了避免多个依赖的数据同时改变后被多次执行
- 运行生命周期钩子函数beforeUpdate
- updateComponent函数重新执行;在执行render函数过程中,会去掉之前的依赖,重新收集所有的依赖,将来依赖变化时会重新运行updateComponent函数;在执行_update函数的过程中,触发patch函数,新旧树进行对比,进行创建、删除、移动、更新
- 运行生命周期钩子函数updated
Vue3 setup函数执行原理
setup(props, context) {
// 透传 Attributes(非响应式的对象,等价于 $attrs)
console.log(context.attrs)
// 插槽(非响应式的对象,等价于 $slots)
console.log(context.slots)
// 触发事件(函数,等价于 $emit)
console.log(context.emit)
// 暴露公共属性(函数)
console.log(context.expose)
/**
attrs 和 slots 都是有状态的对象,它们总是会随着组件自身的更新而更新。这意味着你应当避免解构它们,并始终通过 attrs.x 或 slots.x 的形式使用其中的属性。此外还需注意,和 props 不同,attrs 和 slots 的属性都不是响应式的。如果你想要基于 attrs 或 slots 的改变来执行副作用,那么你应该在 onBeforeUpdate 生命周期钩子中编写相关逻辑。
*/
/**
expose 函数用于显式地限制该组件暴露出的属性,当父组件通过模板引用访问该组件的实例时,将仅能访问 expose 函数暴露出的内容:
*/
// 让组件实例处于 “关闭状态”
// 即不向父组件暴露任何东西
expose()
const publicCount = ref(0)
const privateCount = ref(0)
// 有选择地暴露局部状态
expose({ count: publicCount })
}
// 写法
// 方式一:
export default {
props:[],
setup(props,ctx) {
const name = ref('张三');// 响应式数据
const myMethods = ()=>{ // 自定义方法
}
return {
name,
myMethods,
}
},
}
// 方式二:全新骨架,多了一个defineComponent
export default defineComponent({
props:[],
setup(props,ctx) {
....
return {...}
},
})
// 方式三: 单文件组件使用的组合式 API
<script setup>
interface Props {
name: string
}
// const { name, count = 100 } = defineProps<Props>(); // props方式一
const props = withDefaults(defineProps<Props>(), { // props方式二
name: 'hello',
})
const myMethods = ()=>{ // 自定义方法
}
</script>
- 初始化组件
- 创建组件实例:每个组件都独立存储相关实例信息,比如组件id、父组件实例、组件种类等信息
- 设置组件实例:频道当前组件实例是否是一个有状态组件,初始化(props、slots),执行setup处理函数
- 创建渲染上下文:instance.ctxx做一层proxy,对渲染上下文的instance.ctx属性的访问和修改,代理到对setupState、ctx、data、props中的数据的访问和修改
- 创建setup函数上下文:对setup函数参数context的处理,也就是初始化context里的四个属性
- 执行setup函数获取的结果:获取setup函数执行结果setupResult
- 处理setup函数执行结果:也就是处理setup函数执行结果、比如setup的返回值
- 完成组件实例设置:现实标准化模板或者渲染函数
setup为什么可以返回一个render函数?
在 Vue 3 中,setup 函数可以返回一个包含 render 函数的对象。这种设计是基于 Vue 3 中的新特性,即用于编写逻辑的 Composition API。
原理是,setup 函数被执行时会在内部创建一个响应式的上下文对象。在这个上下文对象中,我们可以定义响应式的状态、计算属性、方法等。同时,setup 函数可以返回一个对象,该对象可以包含 render 函数。
当 Vue 接收到组件的 setup 返回值后,会根据这个值来确定组件的行为。如果返回的对象中包含 render 函数,Vue 将会将其作为组件的渲染函数,用于生成组件的虚拟 DOM。
通过将组件的渲染逻辑与状态、计算属性等分离,便于组件的复用和测试。这种设计使得开发者可以更加自由地编写组件逻辑,同时更好地利用了 Vue 3 的 Composition API 提供的功能。
综上所述,Vue 3 中 setup 函数可以返回 render 函数的原理是基于 Composition API 的响应式上下文对象以及 Vue 内部对返回值的处理,用于将 render 函数作为组件的渲染函数。
diff算法
虚拟DOM
基本工作原理:
- 虚拟DOM树:当应用的状态发生变化时,框架会创建一个虚拟DOM树,一个轻量级的内存中的树形结构,与实际的DOM结构相对应
- 对比和差异检测:框架会将前后两个虚拟DOM树进行比较,找除它们之间的差异
- 差异更新:框架会计算出需要更新的最小DOM操作集合,然后将这些操作应用到实际的DOM中,以反映最新的状态。这一步骤通常包括插入、删除、更新DOM元素或属性,以及处理事件的监听器等
将前后两个虚拟DOM树进行对比,这个对比过程就是diff算法。
diff算法是一种通过同层的树节点进行比较的高效算法,避免了对树进行逐层搜索遍历,所以时间复杂度只有O(n)。
vue2diff算法核心:
- 首先进行新老节点头尾对比,头与头,尾与尾对比,寻找未移动的节点。
- 新老节点头尾对比完成后,进行交叉对比,头与尾,尾与头对比,这一步寻找移动后可以复用的节点。
- 然后再剩余新老节点对比寻找可复用节点,创建一个老节点keyToIndex的哈希表map记录key,然后继续遍历新节点索引通过key查找可以复用的旧的节点
- 节点遍历完成后,通过新老索引,进行移除多余老节点或者新增新节点的操作
vue3diff算法核心:
- 首先进行新老节点头尾对比,头与头,尾与尾对比,寻找未移动的节点
- 然后创建一个新节点在旧节点中的位置的映射表,这个映射表的元素如果不为空,表示可以复用
- 然后根据这个映射表计算出最长递增子序列,这个序列只用的节点代表可以原地复用。之后移动剩下的新节点到正确的位置即递增序列的间隙中
差异性
- vue2、vue3的diff算法实现差异的主要体现在:处理完收尾节点后,对剩余节点的处理方式
- vue2是通过对旧节点列表建立一个{key, oldVNode}的映射表,然后遍历新节点列表的剩余节点,根据newVNode.key在旧映射表中寻找可复用的节点,然后打补丁并且移动到正确的位置
- vue3 则是建立一个存储新节点数组中的剩余节点在旧节点数组上的索引的映射关系数组,建立完成这个数组后也即找到了可复用的节点,然后通过这个数组计算得到最长递增子序列,这个序列中的节点保持不动,然后将新节点数组中的剩余节点移动到正确的位置
Vuex
- state:公共数据
- mutations:使用它来修改数据
- getters:计算属性,获取新的值
- actions:发起异步请求
- modules:模块拆分
Webpack
- 读取配置文件:首先会读取项目中的配置文件,该配置文件包含了构建过程中的各种设置,入口、输出目录、加载器(loaders)、插件(plugins)
- 解析入口文件:根据配置文件定义的入口点(entry points)解析应用程序的依赖关系
- 解析依赖:构建一个依赖关系图
- 加载器处理:使用加载器来处理不同类型的资源文件,如css,字体等。允许开发人员在构建过程中转换这些资源文件,以便将它们整合到最终的输出文件中。用于处理模块中的资源文件,将他们转换为Webpack可以理解的模块js json
常见加载器Loader:- Babel Loader:es6+版本转换为es5
- css Loader、sass Loader 、vue loader
- 插件处理:执行各种任务,如代码压缩、资源优化、HTML、生成、热模块。插件可以根据需要自定义webpack构建过程。执行各个自定义构建任务和优化
常见插件Plugin:- HtmlWebpackPlugin、MiniCssExtractPlugin
- 生成输出文件
- 优化和压缩
- 输出到字典给目录
js
str.substring(start: Number, end: Number)
//截取字符串从start到end(不包含的字符串)
str.slice(start: Number, end: Number)
// 截取字符串从start到end(不包含,可为负数)的字符串
str.replace(searchValue:{staring, RegExp}, replaceValue: string)
// 将searchValue 符合条件的替换成replaceValue
str.split(splitter: string, limit?: Number)
// 将str根据splitter拆分成长度不大于limit的数组
str.repeat(count: Number)
// 将str重复指定的次数,返回重复后的字符串
arr.toString()
// 将转换成以逗号分隔的字符串 obj.toString()=> '[object Object]'
Array.from(iterabel: Iterabel<any>)
// 将可迭代的值转换为以逗号分隔的数组
arr.join(separator?: string)
// 将数组分隔成以separator连接的字符串 空则为,
arr.push()
// 数组尾部添加元素
arr.pop()
// 删除数组尾部元素,返回删除的元素
arr.unshift()
// 数组开头添加元素 返回新的长度
arr.shift()
// 数组开头删除元素 返回第一个元素
arr.sort(Function?: (a: any, b:any) => a-b :number )
// 默认对数组进行升序 b-a执行倒叙
arr.reverse()
// 颠倒数组中元素的顺序 会改变原数组
arr.slice(start: Number, end: Number)
// 返回原数组中指定下标到结束下标直接组成的新数组
arr.splice(start: number, deleteCount: number, ...items: any[])
// start开始操作的位置,deleteCount删除的数量, items进行插入的元素
arr.forEach(callbackfn:(item, index)=>{})
// 遍历数组 没有返回值 无法中断(除了throw)
arr.map(callbackfn: (item,index)=>{})
// 返回一个新数组 无法中断
for (const key in object) {
if (Object.hasOwnProperty.call(object, key)) {
const element = object[key];
}
}
// 遍历对象,数组 key值 如果迭代的对象的变量值是null或者undefined, for in不执行循环体
for (const iterator of object) {
}
// for..of适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象,因为没有迭代器对象
判断js的数据类型
- typeof:判断一个值的基本数据类型
- instanceof:用于检查对象是否属于某个类的实例 arr instanceof Array
ES6继承和ES5继承的区别
ES6:
- Class和extends关键字
- 通过构造函数constructor定义初始化逻辑,并通过super调用父类的构造函数
- 子类中通过super关键字调用父类的构造函数和普通方法
ES5原型链继承:
缺点:属性共享, 不能传递参数
防抖 节流
// 防抖
debounce(fn, wait){
let timer
return () => {
clearTimeout(timer)
timer = setTimeout(() => {
fn()
timer = null
}, wait);
}
},
// 节流
throttle(fn, wait){
let timer
return () => {
if(timer) return
timer = setTimeout(() => {
fn()
timer = null
}, wait);
}
},
this
- 默认绑定:指函数独立调用,不涉及其他绑定规则。非严格模式下,this指向window,严格模式下,this指向undefined
fun() // this->window - 隐式绑定:函数的调用是在某个对象上触发,即调用位置存在上下文对象,通俗点说就是 xxx.func(), 此时this指向xxx,但如果存在链式调用,例如xx.yy.zz.func,this永远指向最后调用它的那个对象。匿名函数: this指向window;setInterval、setTimeout: this指向window
隐式绑定容易丢失:
1. 使用另外一个变量作为函数别名,之后使用别人来执行函数
2. 将函数作为参数传递时被隐式赋值
a = 1
var obj = {
a: 2,
foo() {
console.log(this.a)
}
}
var foo = obj.foo;
obj.foo(); // 2
foo(); // 1
--------------------------------------
var obj = {
a: 1,
foo() {
console.log(this.a)
}
};
var a = 2;
var foo = obj.foo;
var obj2 = { a: 3, foo: obj.foo }
obj.foo(); // 1
foo(); // 2
obj2.foo(); // 3
-------------------------------------------
function foo() {
console.log(this.a)
}
function doFoo(fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo) // window 2
-------------------------------------------
function foo() {
console.log(this.a)
}
function doFoo(fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo }
var a = 2
var obj2 = { a: 3, doFoo }
obj2.doFoo(obj.foo) // {a: 3, doFoo: ƒ} 2
- 显示绑定:call(),apply(),bind() , 指向第一个参数
- new绑定: 指向函数this
- 箭头函数:没有自己的this,它的this指向外层作用域的this,且指向定义时候的this而非执行时候。
HTML5特性
- 语义元素:header、footer、nav、section
- 多媒体:audio、video
- canvas:允许通过js创建和操作图形
- websocket: 实时通信
- svg: 矢量图
css3
- 圆角、渐变、自定义属性var()、透明度
- 转换transform:rotate(30deg) 旋转、translate(x, y) 水平移动、scale(x, y) 缩放
画一条0.5px的线 height: 1rpx;trransform: scaleY(0.5) - 过度transition:过渡的css属性 进行的时间长度 快慢 过渡开始时间
- 动画animation :使用@keyframes规则和animation属性
animation: name 6s 动画运用的类型(匀速linear、加速度、减速度、贝塞尔曲线)
@keyframes name{}
原型链
js基础
- Array 对象方法
方法 | 描述 |
---|---|
concat() | array1.concat(array2,array3,…,arrayX) 连接两个或更多的数组,并返回结果 |
entries() | array.entries() 返回数组的可迭代对象 |
every() | array.every(function(currentValue,index?,arr?), thisValue?) 检测数组元素的每个元素是否都符合条件 |
fill() | array.fill(value, start?, end?) 您可以指定开始和结束填充的位置。如果未指定,则将填充所有元素。 |
filter() | array.filter(function(currentValue,index?,arr?), thisValue?) 创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素 |
find() | array.find(function(currentValue, index?, arr?), thisValue?) 返回符合传入测试(函数)条件的数组元素。 |
findIndex() | array.findIndex(function(currentValue, index?, arr?), thisValue?) 返回数组中通过测试的第一个元素的索引(作为函数提供) |
forEach() | array.forEach(function(currentValue, index?, arr?), thisValue?) 用于调用数组的每个元素,并将元素传递给回调函数 |
from() | Array.from(object, mapFunction, thisValue) 具有 length 属性或可迭代对象的任何对象返回 Array 对象 |
indexOf() | array.indexOf(item,start) 搜索数组中的元素,并返回它所在的位置 |
join() | array.join(separator) 把数组的所有元素放入一个字符串。 |
map() | 通过指定函数处理数组的每个元素,并返回处理后的数组。 |
pop() | 删除数组的最后一个元素并返回删除的元素。 |
push() | 向数组的末尾添加一个或更多元素,并返回新的长度。 |
reverse() | 反转数组的元素顺序。 |
shift() | 删除数组的第一个元素。 |
slice() | array.slice(start, end?) 可提取字符串的某个部分,并以新的字符串返回被提取的部分 |
some() | array.some(function(currentValue,index?,arr?),thisValue) 用于检测数组中的元素是否满足指定条件(函数提供) |
sort() | array.sort(sortfunction?) 用于对数组的元素进行排序 |
splice() | array.splice(index,howmany,item1,…,itemX?) 用于插入、删除或替换数组的元素 |
toString() | 方法可把数组转换为字符串,数组的所有值用逗号隔开,并返回结果。 |
unshift() | array.unshift(item1,item2, …, itemX) 可向数组的开头添加一个或更多元素,并返回新的长度。 |
valueOf() | 返回 Array 对象的原始值 |
- Boolean 对象方法
方法 | 描述 |
---|---|
toString() | 把布尔值转换为字符串,“true” 或者 “false” 并返回结果。 |
valueOf() | 可返回 Boolean 对象的原始值。 |
- Date对象方法
方法 | 描述 |
---|---|
getDate() | 从 Date 对象返回一个月中的某一天 (1 ~ 31)。 |
getDay() | 从 Date 对象返回一周中的某一天 (0 ~ 6)。 |
getFullYear() | 从 Date 对象以四位数字返回年份。 |
getHours() | 返回 Date 对象的小时 (0 ~ 23)。 |
getMinutes() | 返回 Date 对象的分钟 (0 ~ 59)。 |
getMonth() | 从 Date 对象返回月份 (0 ~ 11)。 |
getSeconds() | 返回 Date 对象的秒数 (0 ~ 59)。 |
getTime() | 返回 1970 年 1 月 1 日至今的毫秒数。 |
valueOf() | 返回 Date 对象的原始值。 |
- Math 对象属性
方法 | 描述 |
---|---|
ceil(x) | 对数进行上舍入。 |
floor(x) | 对 x 进行下舍入。 |
max(x,y,z,…,n) | 返回 x,y,z,…,n 中的最高值。 |
min(x,y,z,…,n) | 返回 x,y,z,…,n中的最低值。 |
random() | 返回 0 ~ 1 之间的随机数。 |
round(x) | 把数四舍五入为最接近的整数。 |
- Number 对象属性
方法 | 描述 |
---|---|
toFixed(x) | 把数字转换为字符串,结果的小数点后有指定位数的数字。 |
toString() | 把数字转换为字符串,使用指定的基数。 |
valueOf() | 返回一个 Number 对象的基本数字值。 |
- String 对象方法
方法 | 描述 |
---|---|
charAt() | 返回在指定位置的字符。 |
concat() | 连接两个或更多字符串,并返回新的字符串。 |
indexOf() | 返回某个指定的字符串值在字符串中首次出现的位置。 |
lastIndexOf() | 从后向前搜索字符串。 |
replace() | string.replace(searchvalue,newvalue) 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。 |
search() | 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。 |
slice() | string.slice(start,end?) 可提取字符串的某个部分,并以新的字符串返回被提取的部分 |
split() | string.split(separator?,limit?) 用于把一个字符串分割成字符串数组 |
substr() | string.substr(start,length?) 从起始索引号提取字符串中指定数目的字符。 |
substring() | string.substring(from, to) 用于提取字符串中介于两个指定下标之间的字符 |
trim() | 去除字符串两边的空白 |
valueOf() | 返回某个字符串对象的原始值。 |