面试经

168 阅读17分钟

手写代码

实现一个 deepClone 深拷贝

function deepClone(obj) {
    // 简单类型
    if (typeof obj!== 'object' || obj === null) {
        return obj;
    }
    // 数组
    if (Array.isArray(obj)) {
        return obj.map(item => deepClone(item));
    }
    // 对象
    const clonedObj = {};
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            clonedObj[key] = deepClone(obj[key]);
        }
    }
    return clonedObj;
}

obj.hasOwnProperty() 是 JavaScript 中用于检查对象自身属性(非继承属性)的方法。它的作用是判断一个属性是否直接属于该对象(而不是从原型链继承来的)

const person = {
  name: "张三",
  age: 25
};

console.log(person.hasOwnProperty("name")); // true(自身属性)
console.log(person.hasOwnProperty("age"));  // true(自身属性)
console.log(person.hasOwnProperty("toString")); // false(继承自 Object.prototype)

性能优化

代码层面优化

组件/路由懒加载

// 路由懒加载 (Vue 2/3 通用)
const Home = () => import(/* webpackChunkName: "home" */ './views/Home.vue');

// Vue 3 专用方式
import { defineAsyncComponent } from 'vue';
const AsyncComp = defineAsyncComponent(() => import('./components/AsyncComp.vue'));

第三方库按需引入

图片资源优化

<!-- 使用 WebP 格式 -->
<picture>
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="Fallback">
</picture>

<!-- 懒加载 -->
<img v-lazy="'/path/to/image.jpg'" alt="">

工具

  • 使用 image-webpack-loader 自动压缩图片
  • 配置 url-loader 对小图片转 base64

构建优化

Webpack 配置优化

// vue.config.js
module.exports = {
  chainWebpack: config => {
    // 分包策略
    config.optimization.splitChunks({
      chunks: 'all',
      maxSize: 244 * 1024, // 单文件最大244KB
      cacheGroups: {
        vendors: {
          name: 'chunk-vendors',
          test: /[\\/]node_modules[\\/]/,
          priority: 10,
          chunks: 'initial'
        }
      }
    });
    
    // 压缩配置
    config.optimization.minimizer('terser').tap(options => {
      options[0].terserOptions.compress.drop_console = true;
      return options;
    });
  }
}

Gzip 压缩

npm install compression-webpack-plugin -D


const CompressionPlugin = require('compression-webpack-plugin');

module.exports = {
  configureWebpack: {
    plugins: [
      new CompressionPlugin({
        test: /\.(js|css)$/,
        threshold: 10240 // 大于10KB的文件才压缩
      })
    ]
  }
}

webpack

image.png

模块打包

第三方库动态加载

// 点击按钮时加载库
async function loadECharts() {
  const { default: echarts } = await import('echarts')
  const chart = echarts.init(dom)
}

是什么
将项目中分散的代码文件(如 Vue 组件、工具函数、第三方库)合并为少数几个浏览器可高效加载的文件。

为什么需要

  • 浏览器无法直接理解 import/export 等模块语法
  • 减少 HTTP 请求数量
  • 处理模块间的依赖关系

代码分割(Code Splitting)

是什么
将打包生成的单一大型文件拆分为多个小块,按需加载。

为什么需要

  • 避免首屏加载无关代码
  • 提升页面响应速度
  • 充分利用浏览器缓存 典型场景
  1. 路由级分割:每个路由加载独立文件

    // Vue Router 配置
    const routes = [
      {
        path: '/dashboard',
        component: () => import('./views/Dashboard.vue') // 动态导入
      }
    ]
    
  2. 组件级分割

    // 异步加载组件
    const Modal = defineAsyncComponent(() => import('./Modal.vue'))
    
  3. 第三方库分割

  // webpack配置
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        lodash: {
          test: /[\/]node_modules[\/]lodash[\/]/,
          name: 'lodash'
        }
      }
    }
  }

Tree-shaking树摇(去掉没有引用的代码)

为什么需要

  • 移除未引用的模块/函数
  • 减小最终打包体积

生效条件

  1. 使用 ES Module 语法(import/export
  2. 生产模式(NODE_ENV=production)
  3. 第三方库提供 sideEffects 声明

示例

// utils.js
export function add(a, b) { return a + b }  // 被使用
export function subtract(a, b) { return a - b } // 未使用

// main.js
import { add } from './utils'
add(1, 2)

// 打包后:subtract 函数会被移除

优化案例

// package.json
{
  "sideEffects": [
    "*.css",  // 标记CSS文件有副作用(不能删除)
    "*.vue"
  ]
}

微信小程序篇

签名功能

1.通过 canvas转成 base64

2.lineto store 记录点到点的距离

自定义分享

onshareAppMessage

支付

wx.requestOrderPayment

安全篇

数据加密

MD5

npm install crypto-js

import CryptoJS from "crypto-js";

const plainText = "123456"; // 待加密字符串
const md5Hash = CryptoJS.MD5(plainText).toString(); // 计算 MD5

console.log(md5Hash); // 输出: "e10adc3949ba59abbe56e057f20f883e"

哈希函数:MD5、SHA-1、SHA-256

通常用于:密码,存储,验证完整性 哈希函数特性:

  1. 确定性,哈希函数总是返回相同的输出,且长度固定
  2. 不可逆,无法从输出推导回原始输入数据
  3. 计算高效

AES

基本特性:

  1. 对称加密,加密解密使用相同密钥
  2. 分组加密,将明文分成固定长度的块进行加密(AES块大小为128位)
  3. 密钥长度,支持128位、192位和256位三种密钥长度
  4. 安全性,目前尚未被成功攻破,被政府和企业广泛采用

通常用于:文件加密、数据传输加密(vpn、https)、数据库字段加密、密码存储

与后端的基本解密要素

  1. 相同的密钥:前后端必须使用完全相同的密钥
  2. 相同的加密模式:如ECB、CBC等
  3. 相同的填充方式:如PKCS7/PKCS5
  4. 相同的IV(如果使用CBC等模式)

AES有多种工作模式,常见的有:

  1. ECB(电子密码本)

    • 最简单的模式
    • 相同明文块加密后结果相同
    • 安全性较低,不建议用于加密大量数据
  2. CBC(密码分组链接)

    • 每个块加密前与前一个密文块异或
    • 需要初始化向量(IV)
    • 比ECB更安全,推荐使用
  3. CTR(计数器模式)

    • 将计数器加密后与明文异或
    • 可以并行计算
  4. GCM(伽罗瓦/计数器模式)

    • 提供加密和认证
    • 高性能,适合网络传输

JWT

JWT(JSON Web Token)是一种用于在网络应用环境中安全地传递信息的开放标准(RFC 7519)。它通常用于身份验证和信息交换,具有以下特点:

JWT 结构 JWT 由三部分组成,每部分之间用点(.)分隔:

  • Header(头部):包含了令牌的类型(通常是 "JWT")以及所使用的签名算法(如 HMAC SHA256 或 RSA)。
  • Payload(有效载荷):包含了实际传递的数据,可以是关于用户的信息,也可以是其他任何数据。这里面可以存储一些声明(Claims)。声明通常有三种类型:
    • Registered Claims:一些预定义的标准字段(如 iss(发布者)、exp(过期时间)等)。
    • Public Claims:可以自定义的字段,通常用于传递业务相关信息。
    • Private Claims:应用程序之间共享的自定义信息,通常不会在公开场合使用。
  • Signature(签名):使用头部中指定的算法(如 HMAC SHA256 或 RSA)以及一个密钥,对头部和有效载荷进行加密。这样可以确保 JWT 的数据在传输过程中没有被篡改。

JWT 格式 JWT 的格式为:

<Header>.<Payload>.<Signature>
  • HeaderPayload 都是经过 Base64Url 编码的 JSON 对象。
  • Signature 是通过对 Header 和 Payload 进行加密签名得到的。

假设我们有一个 JWT,结构如下:

  • Header

    {
      "alg": "HS256",
      "typ": "JWT"
    }
    
  • Payload

    {
      "user_id": 12345,
      "role": "admin",
      "exp": 1626363840
    }
    
  • Signature:通过算法 HS256 和密钥 your-256-bit-secret 签名得出。

JWT 最终的字符串可能像这样:

eyJhbGciOiAiSFMyNTYiLCJ0eXAiOiAiSldUIn0.eyJ1c2VyX2lkIjogMTIzNDU2LCJyb2xlIjogImFkbWluIiwgImV4cCI6IDE2MjYzNjM4NDB9.Gs7w7zJ9tIl0YPkjEdsYbn9Q-V65l5Dz-7hwqWnCSoA

水印

1. DOM 元素水印(基础方案)

2. Canvas 背景水印(推荐方案)

function createCanvasWatermark(text) {
  const canvas = document.createElement('canvas');
  canvas.width = 300;
  canvas.height = 150;
  const ctx = canvas.getContext('2d');
  
  ctx.font = '16px Arial';
  ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
  ctx.rotate(-0.3);
  ctx.fillText(text, 30, 80);
  
  const watermarkDiv = document.createElement('div');
  watermarkDiv.style.backgroundImage = `url(${canvas.toDataURL()})`;
  watermarkDiv.style.position = 'fixed';
  watermarkDiv.style.top = '0';
  watermarkDiv.style.left = '0';
  watermarkDiv.style.width = '100%';
  watermarkDiv.style.height = '100%';
  watermarkDiv.style.pointerEvents = 'none';
  document.body.appendChild(watermarkDiv);
}

优势:难以简单删除、支持复杂样式

3. SVG 水印(高清方案)

image.png 根据场景选择

-   管理后台:Canvas+MutationObserver
-   内容网站:SVG重复水印
-   高安全需求:WebWorker+动态内容

Q1:如果删除带水印的dom元素不就没水印了

可以利用 MutationObserver 监听 water的节点,如果被修改了...

VUE篇

面试官:为什么不用 index 做 key?

index 的弊端:节点内容没变的前提下,在前面添加了一项,导致后面项的 index 变化,页面会重新渲染。

Diff算法

  • 新旧虚拟DOM对比的时候,Diff 算法比较只会在同层级进行,不会跨层级比较。

  • 首先比较两个节点的类型,如果类型不同,则废弃旧节点并用新节点替代。

  • 对于相同类型的节点,进一步比较它们的属性。记录属性差异,以便生成相应的补丁。

  • 如果两个节点相同,继续递归比较它们的子节点,直到遍历完整个树。

  • 如果节点有唯一标识,可以通过这些标识来快速定位相同标识的节点。

  • 如果节点的相同,只是顺序变化,不会执行不必要的操作。

详情

双向数据绑定

vue2实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()  来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。

浅谈 Object.defineProperty

vue重写数组

image.png image.png 深入研究的话并不是劫持不到数组,而是只使用该 API 无法满足响应式系统的实现,比如 push 一个新的元素它是一个对象,那我们依然需要对该对象进行数据劫持,但现在我们连这个对象都拿不到,更别说劫持了

为了解决上面的问题, Vue2 没有选择对数组进行劫持而是选择了一个巧妙的方式:重写数组方法

首先明确一下需要对哪些方法进行重写,可以发现我们只需要针对于会修改自身数组的方法进行劫持,而像查找遍历的相关的方法正常使用就可以

数组修改自身的方法:push、pop、shift、unshift、splice、sort、reverse

源码其实很简单没多少行,就是最开始截图的部分,可以明确针对于劫持数组方法的调用会有三个操作:

  1. 调用原生的数组方法拿到结果,最后将其返回
  2. 针对于插入操作获取到插入的内容,对插入的内容进行数据劫持
  3. 通知依赖收集的函数执行

浅谈Proxy

image.png 可以看出这种 Proxy 代理方式要比 Object.defineProperty 省事的多,最主要的区别在于 Proxy 的最小操作单元是对象,而 Object.defineProperty 最小操作单元是对象属性

这就导致了 Vue2 需要针对于某个对象还需要进行属性遍历,针对于每个属性进行 Object.defineProperty,也导致了直接添加和删除对象属性无法被劫持到 归根究底如果你有去研究源码的话可以发现 Vue2 针对于数组从始至终都没有进行 defineReactive,只不过给它增加了一个 observer 对象罢了,当遇到一个 value 是数组时 Vue2 会进行遍历针对于每个元素执行 defineReactive 操作,唯独数组本身没有

然而 Vue3 能够实现这一点要归功于 Proxy API,针对于一个数组代理只需要在 getter 中根据你访问的属性增加额外的判断处理逻辑即可

组件中的name有什么好处

  • 可以通过名字找到对应的组件( 递归组件:组件自身调用自身 )

  • 可以通过 name 属性实现缓存功能(keep-alive

  • 可以通过 name 来识别组件(跨级组件通信时非常重要)

  • 使用 vue-devtools 调试工具里显示的组见名称是由 vue 中组件 name 决定的

keep-alive

keep-alive是 Vue 提供的一个内置组件,用来对组件进行缓存——在组件切换过程中将状态保留在内存中,防止重复渲染DOM。

使用includeexclude 来指定特定组件包含或排除

max可以定义组件最大的缓存个数,如果超过了这个个数的话,在下一个新实例创建之前,就会将以缓存组件中最久没有被访问到的实例销毁掉

如果为一个组件包裹了 keep-alive,那么它会多出两个生命周期:deactivated、activated。同时,beforeDestroy 和 destroyed 就不会再被触发了,因为组件不会被真正销毁。

当组件被换掉时,会被缓存到内存中、触发 deactivated 生命周期;当组件被切回来时,再去缓存里找这个组件、触发 activated钩子函数。

keep-alive在各个生命周期里都做了啥吧:

  • created:初始化一个cache、keys,前者用来存缓存组件的虚拟dom集合,后者用来存缓存组件的key集合
  • mounted:实时监听include、exclude这两个的变化,并执行相应操作
  • destroyed:删除掉所有缓存相关的东西

data中的数据为什么不是对象而是函数

JavaScript中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。

而在Vue中,更多的是想要复用组件,那就需要每个组件都有自己的数据,这样组件之间才不会相互干扰。

所以组件的数据不能写成对象的形式,而是要写成函数的形式。数据以函数返回值的形式定义,这样当每次复用组件的时候,就会返回一个新的data,也就是说每个组件都有自己的私有数据空间,它们各自维护自己的数据,不会干扰其他组件的正常运行。

vue性能优化

编码阶段:

  • SPA 页面采用keep-alive缓存组件
  • key保证唯一
  • 使用路由懒加载、异步组件
  • 防抖、节流
  • 第三方模块按需导入
  • 虚拟滚动
  • 图片懒加载

SEO优化

  • 预渲染
  • 服务端渲染SSR

打包优化

  • 压缩代码
  • Tree Shaking/Scope Hoisting
  • 使用cdn加载第三方模块
  • splitChunks抽离公共文件
  • sourceMap优化

用户体验

  • 骨架屏
  • 还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。

JS篇

1.作用域和作用域链

作用域是指变量的可访问性和可见性的范围。

作用域链是指在 JavaScript 中变量查找的一种机制。当代码中需要访问一个变量时,JavaScript 引擎会按照作用域链的顺序逐级向上查找该变量,直到找到为止。

2.原型,原型链?

原型在 Javascript 中,每个函数(构造函数)都有一个prototype 属性,它是一个对象,用于存储该类型所有实例共享的属性和方法。(这里指的是 Target.prototype就是原型)

原型链当访问一个对象的属性或方法时,若对象自身没有,则会通过__proto__逐层向上查找原型,直到找到链的顶端,这种链式结构称为原型链。

function Target(brand) {
  this.XX = XX
}
Target.prototype.XX = XX;
const target = new Target();

image.png

4.解释JavaScript中的变量提升是什么

变量提升是指在JavaScript中,变量和函数声明会在代码执行之前被提升到作用域的顶部。这意味着可以在声明之前使用变量和函数。

5.解释JavaScript中的事件冒泡和事件捕获

事件冒泡是指当一个事件在DOM树中触发时,它会从最内层的元素开始向外传播至最外层的元素。

事件捕获是指当一个事件在DOM树中触发时,它会从最外层的元素开始向内传播至最内层的元素。

创建一个 this中做了什么

事件循环(Event Loop)、微任务、宏任务

事件循环(Event Loop) :是JavaScript中处理异步操作的机制。事件循环不断地从任务队列中取出任务并执行,直到任务队列为空。事件循环由主线程和任务队列组成,主线程负责执行同步任务,异步任务会被放入任务队列中,等待主线程空闲时被执行。

微任务(Microtask)

  1. 微任务是一种异步任务,它的执行顺序在宏任务之后,在每个事件循环迭代中立即执行。
  2. 微任务通常比宏任务更高优先级,因此它们会在当前宏任务执行完毕后立即执行,而不会等待下一个宏任务。
  3. 常见的微任务包括Promise的thencatch方法、process.nextTick(Node.js中的微任务)等。
  4. 微任务通常用于执行一些高优先级的任务,比如更新UI,处理Promise的结果等。

宏任务(Macrotask)

  1. 宏任务是一种异步任务,它的执行顺序在微任务之后,通常在每个事件循环迭代中只执行一个。
  2. 宏任务包括整体的JavaScript代码、setTimeout、setInterval、requestAnimationFrame(浏览器中的动画帧任务)、I/O操作等。
  3. 宏任务的执行顺序会等待当前的微任务队列执行完毕后,然后从宏任务队列中选择下一个宏任务执行。

总结 : 微任务和宏任务是异步编程中用于控制任务执行顺序的两个关键概念。微任务通常比宏任务具有更高的优先级,因此它们会在当前宏任务执行完毕后立即执行。了解它们的执行顺序对于理解事件循环和编写可预测的异步代码非常重要。在浏览器环境中,宏任务和微任务的常见示例包括setTimeout、Promise、MutationObserver等。在Node.js环境中,宏任务和微任务的示例包括setImmediate、process.nextTick等。

8.什么是柯里化(Currying)?请给出一个柯里化的示例。

柯里化是一种将接受多个参数的函数转换为接受一个参数并返回一个新函数的过程。

function add(a) {
  return function(b) {
    return a + b;
  }
}

var add5 = add(5);
console.log(add5(3)); // 输出:8

9.闭包

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以访问到当前函数的局部变量。

闭包有两个常用的用途;

  • 闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
  • 闭包的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收。

11.promise,async await

Promise 和 async/await 是 JavaScript 中用于处理异步操作的两种不同的机制,它们都旨在让异步代码更加可读和易于管理。

Promise

  1. Promise 是一种异步编程模式,它提供了一种更结构化的方式来处理异步操作。一个 Promise 表示一个异步操作的最终完成或失败,以及它的结果值或失败原因。
  2. Promise 有三个状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。一旦 Promise 进入 fulfilled 或 rejected 状态,它就不会再改变状态。
  3. Promise 使用 .then() 方法来注册回调函数,当异步操作成功时执行 then() 方法的第一个回调函数,当异步操作失败时执行第二个回调函数。
  4. Promise 链(Promise chaining)允许你按顺序执行一系列的异步操作,每个操作都返回一个 Promise,这样可以更好地控制异步代码的流程。
function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Data fetched successfully");
        }, 1000);
    });
}

fetchData()
    .then((result) => {
        console.log(result);
    })
    .catch((error) => {
        console.error(error);
    });

async/await

  1. async/await 是基于 Promise 的语法糖,它提供了一种更像同步代码的方式来处理异步操作。async 函数返回一个 Promise,而 await 关键字用于暂停函数的执行,等待一个 Promise 的解决。
  2. async 函数内部可以使用 await 关键字来等待异步操作的结果,这样可以在代码中像编写同步代码一样使用异步操作。
  3. async 函数的执行会在遇到第一个 await 表达式时暂停,然后等待该表达式的 Promise 解决。之后,async 函数会继续执行,直到遇到下一个 await 表达式或函数结束。

11.模块化

<script type="module"></script>
  • require 是 CommonJS 模块系统的一部分,主要用于 Node.js 中。
  • ES6 引入了 import 和 export 语法,用来替代 require

结论:模块化思想解决了全局污染,依赖混乱,数据安全

new做了什么

  1. 创建一个新的空对象。
  2. 将新对象的 __proto__ 指向构造函数的 prototype 属性。
  3. 调用构造函数,并将构造函数内部的 this 绑定到新对象上。
  4. 默认返回新对象,除非构造函数显式返回一个对象。

网络篇

1.简述 TCP 连接的过程

TCP 协议通过三次握手建立可靠的点对点连接,具体过程是:

首先服务器进入监听状态,然后即可处理连接

第一次握手:建立连接时,客户端发送 syn 包到服务器,并进入 SYN_SENT 状态,等待服务器确认。在发送的包中还会包含一个初始序列号 seq。此次握手的含义是客户端希望与服务器建立连接。

第二次握手:服务器收到 syn 包,然后回应给客户端一个 SYN+ACK 包,此时服务器进入 SYN_RCVD 状态。此次握手的含义是服务端回应客户端,表示已收到并同意客户端的连接请求。

第三次握手:客户端收到服务器的 SYN 包后,向服务器再次发送 ACK 包,并进入 ESTAB_LISHED 状态。

最后,服务端收到客户端的 ACK 包,于是也进入 ESTAB_LISHED 状态,至此,连接建立完成。

TCP三次握手,第一次握手客户端向服务端发起请求,含义是客户端希望与服务器建立连接,第二次握手服务端收到请求回应,含义是服务端回应客户端,表示已收到并同意客户端的连接请求,第三次握手客户端向服务端发送包,服务端收到后改变状态,连接建立完成。

2.文件上传如何做断点续传

客户端将文件的二进制内容进行分片,每片数据按顺序进行序号标识,上传每片数据时同时附带其序号。服务器接收到每片数据时,将其保存成一个临时文件,并记录每个文件的 hash 和序号。

若上传中止,将来再次上传时,可以向服务器索要已上传的分片序号,客户端仅需上传剩余分片即可。

当全部分片上传完成后,服务器按照分片的顺序组装成完整的文件,并删除分片文件。

3.webSocket 协议是什么,能简述一下吗?

websocket 协议 HTML5 带来的新协议,相对于 http,它是一个持久连接的协议,它利用 http 协议完成握手,然后通过 TCP 连接通道发送消息,使用 websocket 协议可以实现服务器主动推送消息。

首先,客户端若要发起 websocket 连接,首先必须向服务器发送 http 请求以完成握手,请求行中的 path 需要使用ws:开头的地址,请求头中要分别加入upgrade、connection、Sec-WebSocket-Key、Sec-WebSocket-Version标记

然后,服务器收到请求后,发现这是一个 websocket 协议的握手请求,于是响应行中包含Switching Protocols,同时响应头中包含upgrade、connection、Sec-WebSocket-Accept标记

当客户端收到响应后即可完成握手,随后使用建立的 TCP 连接直接发送和接收消息。

4.在浏览器地址栏输入地址,并按下回车键后,发生了哪些事情?

  • 浏览器根据url地址查找本地缓存,根据缓存规则看是否命中缓存,若命中缓存则直接使用缓存,不再发出请求

  • 通过DNS解析找到服务器的IP地址

  • 浏览器向服务器发出建立TCP连接的申请,完成三次握手后,连接通道建立

  • 浏览器开始从上到下解析HTML,若遇到外部资源链接,则进一步请求资源

  • 解析过程中生成DOM树、CSSOM树,然后一边生成,一边把二者合并为渲染树(rendering tree),随后对渲染树中的每个节点计算位置和大小(reflow),最后把每个节点利用GPU绘制到屏幕(repaint)

  • 在解析过程中还会触发一系列的事件,当DOM树完成后会触发DOMContentLoaded事件,当所有资源加载完毕后会触发load事件

5.列举优化网络性能方法

  1. 优化打包体积

利用一些工具压缩、混淆最终打包代码,减少包体积

  1. 压缩

现代浏览器普遍支持压缩格式,因此服务端的各种文件可以压缩后再响应给客户端,只要解压时间小于优化的传输时间,压缩就是可行的

  1. CDN

利用 CDN 可以大幅缩减静态资源的访问时间,特别是对于公共库的访问,可以使用知名的 CDN 资源,这样可以实现跨越站点的缓存

  1. 缓存

对于除 HTML 外的所有静态资源均可以开启协商缓存,利用构建工具打包产生的文件 hash 值来置换缓存

  1. http2

开启 http2 后,利用其多路复用、头部压缩等特点,充分利用带宽传递大量的文件数据

  1. 雪碧图

对于不使用 HTTP2 的场景,可以将多个图片合并为雪碧图,以达到减少文件的目的

  1. defer、async

通过 defer 和 async 属性,可以让页面尽早加载 js 文件

  1. prefetch、preload

通过 prefetch 属性,可以让页面在空闲时预先下载其他页面可能要用到的资源

通过 preload 属性,可以让页面预先下载本页面可能要用到的资源

TS

为什么要学习 js

1.不清不楚的数据类型

2.有漏洞的逻辑

3.访问不存在的属性

4.低级的拼写错误