关于前端
1. 框架类:
1.1 vue
- 原理;
- v-key的作用(diff 算法):"就地复用"策略,高效的更新虚拟DOM;
- 组件传参;
- 组件按需加载:
// vue异步组件
component: require('@/pages/login')
// webpack 可结合 webpack 做一些加工处理
component: r => require.ensure([], () =>; r(require('@/components/home')), 'chunk')
// es6
component: () => import('@/pageslogin')
- 动态渲染图片;
- 数组数据响应方法;
- Methods、computed、watch 区别:methods 在调用时才会计算。computed 当依赖的数据变化时才会计算, 当数据没有变化时, 它会读取缓存数据。watch 会在属性变化后重新计算。
- data为什么是函数:为了达到数据隔离,由于组件可以复用,当组件被实例化后,属性也会被创建在原型链上,因此需要用data 返回一个可独立维护的数据拷贝;
- nextTick 实现方式:Promise.resolve(),渐弱:setTimeout();
- axios 原理:axios可用于浏览器和 node.js 中,它会根据当前环境选择实现方式。如果是浏览器环境中,会基于XMLHttpRequests实现axios。如果是node.js环境,就会基于node内置 http模块 实现axios;
- 路由实现方式及原理:hash模式,根据
window.location.hash
读取地址变化,监听 hashchange 事件来响应;history模式,通过H5新增的两个apiwindow.history.pushState
、window.history.replaceState
来实现路由切换,监听 popstate 来响应。参考:vue-router 原理;
1.2 react
- 事件绑定 this 丢失:绑定事件时,传递的不是字符串,而是一个方法,onClick 即为中间变量,render 方法执行过程会丢失原来的 this 引用;
- hook
1.3 小程序
- 压缩、组件传参(设计模式)、胶囊按钮的位置
- 双线程模型:
- 外部样式引用:externalClasses
- behavior:
- wxs 文件的功能
- 与vue双向邦定的区别
1.4 Webpack
-
Plugin
- webpack整体是一个插件架构,所有的功能都以插件的方式集成在构建流程中,通过发布订阅事件来触发各个插件执行。webpack核心使用Tapable来实现插件(plugins)的binding(绑定)和applying(应用)。
- Compiler 模块是 webpack 的支柱引擎,它通过 CLI 或 Node API 传递的所有选项,创建出一个 compilation 实例。它扩展(extend)自 Tapable 类,以便注册和调用插件。大多数面向用户的插件首先会在 Compiler 上注册。
-
配置项: entry、output、module( loader )、plugins、devServer、resolve、devtool、optimization。
-
多入口配置
- entry 选项配置多入口的目录,同时配置 HtmlWebpackPlugin 生成对应 html 模板。chunks 表示 html 文件的入口chunks;
- vendor 是指提取涉及 node_modules 中的公共模块,manifest 是对 vendor 模块做的缓存
2. CSS:
2.1 布局
- 左侧固定,右侧自适应宽度;
- 垂直水平居中;
2.2 样式实现
- 三角形、正方形;
2.3 异常的处理
- 移动端兼容行高 line-height (Android、IOS)
- margin 溢出
2.4 移动端布局兼容
- rem
2.5 盒模型、包裹性、BFC
3. JavaScript:
3.1 原始类型 => 装箱拆箱 => 对象构造 => new 操作符 => 箭头函数、普通函数区别 => 闭包 =>; 垃圾回收(标记清除)
3.2 数据类型的判断(typeof,constructor,instance of,Object.prototype.toString.call())
3.3 js线程 => 事件循环 => 异步执行顺序
3.4 浏览器绘制页面 => 重绘重排
3.5 面向对象 继承(原型继承、构造继承、组合继承等)
3.6 深拷贝(Date、reg类型)
const isObject = (target) => (typeof target === "object" || typeof target === "function") && target !== null;
function deepClone(target, map = new WeakMap()) {
if (map.get(target)) {
return target;
}
// 获取当前值的构造函数:获取它的类型
let constructor = target.constructor;
// 检测当前对象target是否与正则、日期格式对象匹配
if (/^(RegExp|Date)$/i.test(constructor.name)) {
// 创建一个新的特殊对象(正则类/日期类)的实例
return new constructor(target);
}
if (isObject(target)) {
map.set(target, true); // 为循环引用的对象做标记
const cloneTarget = Array.isArray(target) ? [] : {};
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop], map);
}
}
return cloneTarget;
} else {
return target;
}
}
3.7 URL 参数提取
function parseParam(url) {
const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
let paramsObj = {};
// 将 params 存到对象中
paramsArr.forEach(param => {
if (/=/.test(param)) { // 处理有 value 的参数
let [key, val] = param.split('='); // 分割 key 和 value
val = decodeURIComponent(val); // 解码
val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字
if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
paramsObj[key] = [].concat(paramsObj[key], val);
} else { // 如果对象没有这个 key,创建 key 并设置值
paramsObj[key] = val;
}
} else { // 处理没有 value 的参数
paramsObj[param] = true;
}
})
return paramsObj;
}
3.8 this 绑定(new操作符、bind等、上下文调用绑定、默认绑定window)
3.9 new 关键字实现
// new 会产生一个新对象
// 新对象需要能够访问到构造函数的属性,所以需要重新指定它的原型
// 构造函数可返回
function objectFactory() {
var obj = new Object()
Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
var ret = Constructor.apply(obj, arguments);
// ret || obj 这里这么写考虑了构造函数显示返回 null 的情况
return typeof ret === 'object' ? ret || obj : obj;
};
3.10 call、apply、bind 模拟实现
Function.prototype.call = function (context) {
var context = context || window;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args +')');
delete context.fn
return result;
}
3.11 防抖节流原理
3.12 跨域的处理方式: CORS、代理
3.13 require 、import 区别
- require是“运行时加载”,只有在代码真正运行的时候才会去加载对应需要的东西,所以不能做到编译时就把想加载的模块加载进来,即不能做到编译时静态化
- import是编译时调用,所以必须放在文件开头
3.14 JS编译解析的流程
- 语法分析,生成抽象语法树(AST)和执行上下文:
- 生成 AST 需要经过两个阶段:
- 第一阶段是分词(tokenize),又称为词法分析
- 第二阶段是解析(parse),又称为语法分析
- 生成 AST 需要经过两个阶段:
- 预编译,解释器 lgnition 生成字节码
- 解释执行,第一次执行,解释器 lgnition 通常会一行行执行代码,解释过程中,若发现一段代码被重复执行多次,即为热点代码,则 编译器 TurboFan 会把该段热点的字节码编译为机器码,再次执行这段被优化的代码时,只需要执行编译后的机器码就可以了,这样就大大提升了代码的执行效率。这就是 即时编译(JIT)
3.15 设计模式:
- 发布订阅 (Publish Subscribe Pattern) -- 多个订阅者对象同时监听某一个主题对象,这个主题对象状态变化时,会通知所有订阅者对象,使它们能够自动更新自己的状态
- 代理模式 (Proxy Pattern) -- 当对一个对象的访问不能直接引用时,可通过一个称之为 “代理” 的中间层来实现间接引用。通过引入代理对象来间接访问一个对象
3.16 面向对象的基本特征: 封装、继承、多态 => Class =>; prototype;
3.17 promise、promise.all、promise.finally 模拟实现、async/await
3.18 ES6 +
-
箭头函数与普通函数的区别
- 没有this,会捕获其所在的上下文的this值
- 不能作为构造函数,不能使用 new
- 不绑定arguments,用rest参数...解决
- 没有原型属性
- 不能当做Generator函数,不能使用yield关键字
-
解构
-
运算符(扩展运算符... 可选链?. 空值合并?? )
4. TypeScript:
5. Node.js:
- handleCount
- koa、中间件
6. HTTP:
- 超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求 - 响应协议,归属于应用层协议,主要解决如何包装数据。
- 三次握手、四次挥手
- TCP、UDP
- 安全:xss、csrf
- 跨域:cors(预检请求)
7. 浏览器:
7.1 浏览器的渲染原理
- 根据 html 文档,构建一棵 DOM 树,由元素及其附加属性组成。
- 对 CSS 解析,生成 CSSOM 规则树,样式规则。
- 根据 DOM 树和 CSSOM 规则树构建渲染树 Render Tree。由一些包含颜色和大小等属性的矩形组成。
- 根据 Render Tree 来布局,计算各节点的位置、大小,此过程也就是“重排”。
- 绘制。遍历render树,调用 Paint 方法使用 UI 基础组件绘制出来。这个过程并非是等 html 完全解析完再执行,而是解析完一部分就执行显示一部分。如下图,
JavaScript 的加载,解析,执行 都会阻塞文档的解析;
CSS 是并行下载,不会阻塞后面 JS 的下载,但会阻塞后面 JS 的执行;
CSS 不会阻塞 DOM 的解析,但会阻塞 DOM 渲染。
因此,script 标签被建议放在body 标签底部,CSS 被建议放在头部。
7.2 缓存机制
- 7.2.1 浏览器的缓存过程:
-
域名解析,DNS缓存:域名查找IP地址的过程就是dns解析,这个过程会对网络请求带来一定的损耗,浏览器第一次解析后会将其缓存起来,即 DNS缓存。
- 下次识别到相同地址时,会先查找本地缓存,优先使用本地缓存;
- DNS缓存不存在,读取系统的hosts文件查找对应映射关系,若存在,则域名解析到此完成;
- 本地hosts文件不存在映射关系,查找本地DNS服务器,若存在,域名到此解析完成;
- 本地DNS服务器未找到,向服务器发送请求解析IP。
-
内存缓存(memory缓存):缓存于本地内存中,页面关闭,内存释放;
- HTTP缓存(强缓存和协商缓存):硬盘缓存,最主要的缓存方式,可控且优化空间大的一个缓存部分,也是面试中常问的一个缓存环节;
- 服务端缓存(CDN缓存):CDN节点解决了跨运营商和跨地域访问的问题,访问延时大大降低。浏览器本地缓存的资源过期之后,会向服务器发起资源申请,优先转向 CDN 边缘节点请求资源,CDN 中的缓存可用,直接返回,不可用或者过期,CDN 边缘节点会向源服务器发出回源请求,从而来获取最新资源,并做缓存。
-
- 7.2.2 HTTP缓存的命中方式:
- 根据 http 中的 header 判断是否命中强缓存,若命中,则直接从本地缓存中获取资源,不发送服务器请求;
- 强缓存未命中时,浏览器发送服务端请求,通过请求头验证是否命中协商缓存,若命中,则返回请求,但不返回资源,而是通知浏览器从本地加载资源;
- 都未命中,直接从服务器加载资源。
- 7.2.3 强缓存:
- 利用 Expires(HTTP/1.0)、Cache-Control(HTTP/1.1)这些请求头参数,控制资源过期时间。到了HTTP/1.1,Expires已经被Cache-Control替代,所以,这两个字段同时存在时,Cache-Control优先级高于Expires。
- Cache-Control 主要取值:
- max-age:指定一个时间长度,在这个时间段内缓存是有效的,单位是s。
- s-maxage:同 max-age,覆盖 max-age、Expires,但仅适用于共享缓存,在私有缓存中被忽略。
- public:所有内容都将被缓存(客户端和代理服务器都可缓存)
- private:所有内容只有客户端可以缓存,Cache-Control的默认取值
- no-cache:在使用已缓存的数据前,发送带验证器的请求到服务器,不是字面意思上的不缓存。
- no-store:禁止缓存,所有内容都不会被缓存,既不使用强制缓存,也不使用协商缓存,每次请求都要向服务器重新获取数据。
- Cache-Control 主要取值:
参考 浏览器缓存命中策略。
- 使用时,在Web服务器返回的响应中添加
Expires
、Cache-Control
Header。 - 控制台显示为,状态码 Status: 200, size: (from cache)。
- 利用 Expires(HTTP/1.0)、Cache-Control(HTTP/1.1)这些请求头参数,控制资源过期时间。到了HTTP/1.1,Expires已经被Cache-Control替代,所以,这两个字段同时存在时,Cache-Control优先级高于Expires。
- 7.2.4 协商缓存:
- 某个资源的请求没有命中强缓存,会向服务器发送请求验证协商缓存是否命中,若命中,浏览器会收到304的响应,就会从缓存中加载资源。
- 大部分web服务器都默认开启协商缓存,同时启用 Last-Modified,If-Modified-Since (资源在服务器上最后修改时间)和 ETag、If-None-Match (资源的唯一标识)控制。
- 控制台显示为,状态码 Status: 304 Not Modified 。
/**
*
* ┏┓ ┏┓
* ┏┛┻━━━━━┛┻┓
* ┃ ┃
* ┃ ━ ┃
* ┃ ┳┛ ┗┳ ┃
* ┃ ┃
* ┃ ┻ ┃
* ┃ ┃
* ┗━┓ ┏━┛ Code is far away from bug with the animal protecting
* ┃ ┃ 神兽保佑,代码无bug
* ┃ ┃
* ┃ ┗━━━┓
* ┃ ┣┓
* ┃ ┏┛
* ┗┓┓┏━┳┓┏┛
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛
*
*/