url渲染
1、进行DNS解析,域名到对应的ip地址,dns解析耗时,可用dns-prefetch预解析优化 <link rel="dns-prefetch" href="https://fonts.googleapis.com/">
2、TCP三次握手,建立TCP链接
- c -> s 发送询问
- s -> c 回复询问,并询问
- c -> s 回复询问
四次挥手
- 主 -> 被 关闭主动通道,只能被动接收
- 被 -> 主 收到关闭消息
- 被 -> 主 关闭主动通道
- 主 -> 被 收到关闭消息
https建立TLS加密
- 验证证书,确认加密方式
五层因特网协议栈
- 从应用层的发送http请求,到传输层通过三次握手建立tcp/ip连接,再到网络层的ip寻址,再到数据链路层的封装成帧,最后到物理层的利用物理介质传输
- http 状态码
3、浏览器请求数据
- 浏览器缓存机制
- 强制缓存
- 浏览器直接从缓存中读取文件,不需要发送请求
- http1.0 响应头 => expires 绝对时间,浏览器时区和服务器时区不一致时 => 缓存失效
- http1.1 响应头 => cache-control 相对时间
- 协商缓存
- 浏览期发送请求,服务器决定是否使用缓存
- Last-Modified/if-Modified-Since 时间粒度是秒
- Etag/If-None-Match 根据文件内容生成一个标记
- 获取html文件,构建DOM树
- 边下载边解析
- 下载css文件,构建style树
- 边下载边解析
- DOM树和style树组合成渲染树
- 下载js文件,运行js
- js运行会阻塞DOM树的渲染
4、 layout布局、绘制像素
- js运行会阻塞DOM树的渲染
queue
stack
promise
- promise 异步编程的一种解决方案,可以将异步回调写法变成链式调用写法,流程更加清晰,代码更加优雅
- 三个状态,两个过程,一些方法
- pending、fulfilled、rejected
- pending -> fulfilled == resolve
- pending -> rejected == reject
- then 接收两个回调,成功回调,失败回调
- catch 接收一个失败回调,也可以抛出代码错误
- Promise.all 接收一个promise数组,都成功时返回成功的数组,有一个失败,返回失败的错误
- Promise.race 返回第一个改变状态的结果
- Promise.allSettled 所有promise实例结束后才会返回,成功的返回value,失败的返回err
- Promise.any 只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
- all和race传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被then的第二个参数或者后面的catch捕获;但并不会影响数组中其它的异步任务的执行。
ata.filter(({id}) => {
return selectedIds.includes(id);
})
const ids = {};
selectedIds.forEach(id => ids[id] = 1);
data.filter(({id}) => {
return !!ids[id];
})
debounce
function debounce(fn, wait){
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, wait);
}
}
throttle
function (fn, delay) {
let prev = null;
let timer = null;
return function() {
const now = Date.now();
if(prev && now - prev > delay) {
clearTimeout(timer);
timer = sertTimeout(fn, delay);
} else {
fn.apply(this, arguments)
prev = Date.now();
}
}
}
quickSort
function quickSort(arr) {
if(arr.length <= 1) return arr;
const pivotIndex = Math.floor(arr.length / 2);
const pivot = arr.splice(pivotIndex, 1);
let left = []
let right = [];
for (let i = 0; i < arr.length; i++) {
if( pivot > arr[i]) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat(pivot, quickSort(right));
}
// 空间换时间 o(n2) => o(n)
d
react
react UNSAVE_
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
挂载阶段
- constructor
- static getDerivedStateFromProps(nextProps, prevState)
- render
- componentDidMount
更新阶段
- getDerivedStateFromProps
- shouldComponentUpdate(nextProps, nextState)
- render
- getSnapshotBeforeUpdate(prevProps, prevState)
- componentDidUpdate(prevProps, prevState, snapshot)
卸载阶段
-
componentWillUnmount
-
static getDerivedStateFromError
-
componentDidCatch
state -> setState
-
在合成事件和钩子函数中是异步的,在原生事件和setTimeout中是同步的
-
react setState之后
- react会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程
- react会根据最新的state构建一颗新的虚拟DOM树
- 使用diff算法得出新树和旧树的差异
- 根据差异对界面进行最小化的重新渲染
react fiber
- 基于浏览器的单线程调度算法
- 16之前使用的是递归,很难中断,16之后使用循环
- fiber 一种将递归diff,拆分成无数个小任务的算法,他随时能够中断,恢复。停止恢复的时机取决于当前帧内还有没有时间用于计算
time slice
- 时间分片基于可中断、重启的fiber架构,中断当前的渲染,执行紧急任务,保证页面的流畅运行
redux
- store
- state
- action
- action creator
- reducer
- dispatch
- 1、用户通过dispatch方法发出action
- 2、store自动调用reducer,接收action和当前的state,返回新的state
- 3、state改变,store调用监听函数,更新view
react-redux
- provider 从最外部封装整个应用,向connect模块传递store
- connect 连接react和redux
- 获取state
- 将state和action通过props的方式传入原组件
- 监听state变化
react-thunk
- 异步中间件
- 使dispatch可以传递一个函数
- 把redux流程外的逻辑塞到了流程内
script 标签中增加corssorigin属性
- 可以获得更多的错误信息
react hooks
- useState
- useEffect
- useRef
- useCallback
- useMome
- useContext
- useReducer
- 闭包问题,过时的闭包问题
- useEffet中拿不到最新的state
- 依赖中增加state
- 异步更新问题
- useState中使用函数方法
- useEffet中拿不到最新的state
super
- 子类调用super,传递this,使子类this获取父类中的属性和方法
TypeScript
-
微软开源,可选的 强类型的JavaScript的超集,支持最新的js语法,不在浏览器上直接运行,需要编译成js
-
为什么用TS
- 稳定性,ts的静态类型检查系统,能够提升代码的质量,提高系统的稳定性,减少线上bug
- 可读性, 类型定义加上IDE的智能提示,提升了代码的可读性
- 可维护性, 所有的调用方,都遵守类型定义,在长期的迭代中,增强了可维护性
diff
- .js \ .ts
- 支不支持ES6
- 弱类型 / 强类型
- 运行时暴露错误 / 编译时暴露错误
接口是什么?
类是什么?
- 创建对象的模版,具有逻辑实体,有属性和方法
- 封装、继承、多态、抽象
- 使用class创建
装饰器
- 一种特殊类型的声明,可用于类,方法,访问器,属性,参数
- 用@符号为前缀的函数,
- 以声明的方式将注释和元数据添加到现有的代码中
泛型
- 可重用组件方法的工具,创建处理多种数据类型的组件
- 定义时用标示,引用时用具体类型替换
interface和type的区别
基本类型
- number,string,boolen,null,undefined,symbol
类型断言
- 可以用来手动指定一个值的类型, 断定这个变量的类型是啥
- <类型>值 or 值 as 类型
类型推导
- ts根据一定的定义可以推导出变量的类型
类型保护
- 对于多类型的变量,当使用typeOf判断之后可以确定变量的类型
类型别名
- type 声明一个别名 类似接口
函数重载
- 重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
- 可以重复定义一个函数的类型
declare
- 声明一个变量的定义,编译完会删除
- 用于声明第三方库暴露的变量
闭包
- 当函数可以访问并记住所在的词法作用域时,就产生了闭包。
- 闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,内部函数访问外部函数的局部变量,利用闭包可以突破作用域链,将函数内部的变量和方法传递到外部。
- 闭包特性:
- (1)函数内再嵌套函数
- (2)内部函数可以引用外层的参数和变量
- (3)参数和变量不会被垃圾回收机制回收 (过时了)
面向对象oop
- 面向对象编程就是把需求抽象成一个对象,对这个对象进行分析,为其添加对应的特征属性和行为方法,我们将这个对象称为类
如何优雅的写出符合W3C的代码
- 合理的使用html标签,语义化标签,块标签,行内标签,行内块标签
- 规范化书写css顺序,几何形状类型,位置类型,样式类型,尽量少的嵌套css
- 尽量使用ES6等新语法书写js,代码简洁,易读
- 合理的书写注释
- 变量命名的规范化,语义化
前端安全
- XSS 跨· cross site scripting
- 类型
- 储存型 存到数据库-》用户获取数据-》执行 评论、商品评价
- 反射型 存到url上-》服务端获取-》返回给前端-》前端执行 跳转
- DOM型 存到url上-》前端获取参数-》执行
- 防御
- 字符转义,
- dengerouslySetInnerHTML/v-html
- cookie的httpOnly
- csp
- 验证码
- 字符转义,
- 类型
- CSRF 跨站请求伪造 cross site request forgevies
- 方式
- get请求 通过img
- post请求 通过自执行的form
- a链接 引导用户点击
- 防御
- 请求中添加token
- 双重cookie验证
- 同源检测
- 方式
webpack
- 对webpack的理解
- 是一个模块打包工具,管理项目中的模块依赖,进行合并,拆分,编译成静态资源
- loader和plugin的区别
- loader是转换器,本质是一个函数,对接收到的内容进行转换,对其他类型的资源进行转义等预处理工作,在rules中配置,比如ts-loader,scss-loader
- plugin是扩展器,是一个插件,对webpack的功能进行扩展,在plugins中配置,比如 html-webpacl-plugin,unlifyjs-webpack-plugin
- webpack热更新的原理
- 热更新又叫热替换,这个机制可以做到不刷新浏览器的情况下,用变更的模块替换旧的模块,达到视图的自动更新
- 核心就是通过webSocket建立服务端与客户端的链接,当服务端监听到文件变动时,向客户端推送消息,客户端拉取最新文件,进行增量更新
vue
vue.js是采用的数据劫持结合发布者-订阅者模式的方式,通过object.defineProperty()来劫持各个属性的setter/getter
在数据变动时,发布消息给订阅者,触发相应的监听回调
具体步骤:
1)需要observe(观察者)的数据对象进行遍历,包括子属性对象的属性,都加上setter和getter,这样的话,
给这个对象的某个值赋值,就会触发setter,那么就能监听到数据的变化
2)compile(解析)解析模版指令,将模版中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,
添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
3)watcher(订阅者)是observer和compile之间通信的桥梁,主要做的事情是
1>在实例化时往属性订阅器(dep)里面添加自己
2>自身必须有一个update()方法
3>待属性变动dep.notice()通知时,能够调用自身的update()方法,并触发compile中绑定的回调,
4)mvvm作为数据绑定的入口,整合observer,compile和watcher来监听自己的model数据变化,通过compile来解析编译模版,
最终利用watcher搭起observer和compile之间的通信桥梁,达到数据变化->更新视图:视图交互变化->数据model变更的双向绑定效果
-
渐进式框架
- vue和react一样都是一个渲染UI的js库,本身是不做路由管理、数据管理的,如果需要可以增加插件进行渐进式开发
-
vue的声明式渲染
- 声明一个组件的实例,绑定数据,方法,在html模版中进行插值绑定,实现数据与视图的绑定,不用操作DOM进行数据的初始化和更新
-
MVC/MVVM
- MVC 用户操作-》控制器controller-》数据模型model-》视图view-》用户操作
- MVVM 视图view 《-》视图模型viewModel 《-》 数据模型model
- VM就是基于controler上封装了一层,帮助用户实现了一些逻辑
-
computed和watch的区别
- computed计算属性,依赖其他属性值,当依赖属性变化时重新计算,有缓存
- watch监听,当监听属性变化时执行异步操作,比如请求数据
-
vue中跟数组赋值
- 使用vue重写的数组splice方法,
- 使用vm.set方法
-
vue生命周期
- beforeCreate 组件创建之前
- created 组件实例创建完成 可以请求数据
- beforeMount 组件挂载之前 可以请求数据
- mounted 组件挂载完成 可以请求数据
- beforeUpdate 更新之前
- updated 更新完成
- beforeDestory 卸载之前
- destoryed 卸载完成
- activited 组件激活时调用,keep-alive专属
- deactivited 组件失活时调用,keep-alive专属
-
对keep-alive的理解
- vue内置的一个组件,可以使被包含的组件保留状态,阻止被重复渲染
- 一般跟路由组件结合使用,用于缓存路由组件
- 两个属性include/exclude
- 两个方法 activited/deactivited
-
vue中组件通信
- props
- $emit
- 全局事件总线 b u s . bus. bus.emit/ b u s . bus. bus.on
- vuex
-
SSR的优缺点
- 服务端渲染优点
- 更好的SEO
- 更快的首屏渲染
- 服务端渲染的缺点
- 更多的开发条件限制
- 更多的服务器负载
- 服务端渲染优点
-
vue-rouder的路由模式
- hash 使用URL 哈希值作为路由,支持所有浏览器
- hash实现的原理
- url中的hash值,只是客户端的一种状态,不会向服务器发送
- hash值改变,都会在浏览器访问历史中增加一个记录,因此能通过浏览器按钮控制hash
- 可以通过 拼接hash的url,或者修改localtion.hash,改变hash值
- 通过hashchange事件监听hash的变化,从而实现路由跳转
- hash实现的原理
- history 依赖HTML5 history api和服务端配置
- history实现的原理
- HTML提供的两个API history.pushState(),history.replaceState()在不刷新浏览器的情况下,操作浏览器的历史记录
- pushState,replaceState实现url的变化
- 使用popState事件监听url的变化触发路由跳转
- history实现的原理
- 支持所有js运行环境,如nodejs服务端
- hash 使用URL 哈希值作为路由,支持所有浏览器
-
proxy与Object.definedPropProperty区别
- proxy监听对象的变化,可以直接监听数组的变化
- proxy返回的是新对象,直接操作对象达到目的,denfineProperty需要遍历属性操作
- proxy有兼容性问题
-
虚拟DOM的原理
- 用js对象模拟真实DOM树,对真实DOM进行抽象
- 使用diff算法对比两颗虚拟DOM树的差异
- 将差异的部分更新成真实的DOM
-
虚拟DOM的优缺点
- 优点
- 性能优化,保证性能下限,性能普世性,在不用手动优化的情况下,也能保持不错的性能
- 无需手动操作DOM
- 跨平台,与浏览器DOM无关,例如服务端渲染
- 缺点
- 首次渲染慢,初始渲染需要虚拟DOM计算,生成大量的真实DOM
- 无法进行极致优化
- 优点
-
虚拟DOM中key的作用
- key是虚拟DOM对象中列表数据的标示,在进行虚拟DOM的diff算法是,有很大作用
- 当列表数据发生变化时,通过key进行虚拟DOM的diff算法
- 当key没有变时
- item没有变,直接使用真实DOM
- item变了,对原来真实的DOM进行数据更新
- 当key变了
- 销毁原来的真实DOM
- 即使是数据没变
- 当key没有变时
- key为index的问题
- 添加/删除/排序 -》 产生没有必要的真实DOM更新 -》页面渲染没问题,效率低
- 如果item有输入框 -》 产生错误的真实DOM更新 -》页面渲染有问题
- 解决,使用唯一标示,比如id
-
vue3.0
- proxy -》 Object.defineProperty
- 模版,更改了作用域插槽,改成了函数的形式,避免了插槽的变化引起父组件的更新
- 对象式的组件声明方式,对typescript的结合更加友好
-
vue与react的比较
- react的jsx语法vue的.vue语法,都需要编译后使用
- 都使用了虚拟DOM
- 基本单位都是组件
- 都是渐进式框架
- react是单项数据流MVC模式,vue是双向数据绑定MVVM模式
-
发布-订阅模式(观察者模式?)
- 发布者
- 缓存的订阅者列表
- 发布消息,遍历缓存列表中的订阅者,调用订阅者的方法
- obj.list = [] obj.listen = function obj.trigger = function
-
函数柯里化
- 是一种函数的高阶用法,用于函数转换,可以将一个函数多次调用,保存之前调用的参数变量
- 具体原理就是返回多个包装器
-
如何理解 HTML 语义化
- 让人更易懂
- 让机器更易懂,利于SEO
- 在没有css时,能展现出基本的结构样式
-
script 标签中 defer 和 async 的区别
- script :会阻碍 HTML 解析,只有下载好并执行完脚本才会继续解析 HTML。
- async script :解析 HTML 过程中进行脚本的异步下载,下载成功立马执行,有可能会阻断 HTML 的解析。
- defer script:完全不会阻碍 HTML 的解析,解析完成之后再按照顺序执行脚本。
作用域链
函数内部可以直接读取全局变量
在函数外部自然无法读取函数内的局部变量。
对象访问变量时,先在自身作用域下查找,找不到时会一级一级地向上寻找所有父对象的变量。这就是作用域链
原型链
每个对象都有一个隐式原型__proto__,指向创建该对象的函数的原型prototype
访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。
闭包
函数作为返回值,函数作为参数传递。
闭包就是能够读取其他函数内部变量的函数
只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。