# nginx 怎么设置前端项目的缓存

  • html。协商缓存
location ~* \.html$ {
  expires -1;  # 禁用强缓存,浏览器每次请求都要和服务器协商
  add_header Cache-Control "no-cache";
  add_header ETag $upstream_http_etag;
  add_header Last-Modified $date_gmt;
}
  • 其他静态资源。文件名有 hash 值,所以强缓存
location ~* \.(css|js|jpg|jpeg|png|gif|svg|woff|woff2|ttf|eot|ico)$ {
  expires 30d;  # 缓存30天
  add_header Cache-Control "public, max-age=2592000, immutable";
}

# webpack 打包流程

  • 初始化阶段

    • 解析配置。读取 webpack.config.js 文件配置

    • 初始化编译器对象 compiler。根据配置文件创建 compiler 对象,是整个打包过程的控制器

    • 确定入口 entry。根据配置文件读入入口文件,作为构建依赖图的起点

  • 编译阶段

    • 从入口文件开始构建依赖图

    • 递归解析依赖。根据依赖关系,如 import 或 require 引入的模块,将这些依赖加入到依赖图

    • 加载模块并转化。通过 loader 对模块进行转化:比如 babel-lader 将 es6 代码转成 es5、通过 style-loader 和 css-loader 将 css 文件转成模块

    • 生成抽象语法树 ast。webpack 对模块代码生成一个抽象语法树,用于后续的代码分析和转换

    • 将模块代码编译成浏览器可执行的代码。经过解析和转化,最后得到 js 代码

  • 输出

    • 生成代码块 chunk。根据依赖图,webpack 可以将模块分割为多个 chunk,chunk 通常是一个多个打包后的文件

    • 优化和处理代码。webpack 可以根据 plugin 进一步处理代码,比如 ugly 化(UglifyJsPlugin)、提取 css 文件(MinCssExtractPlugin)、自动生成 html 文件(HtmlWebpackPlugin)

    • 文件写入磁盘。根据 outpath 配置,输入文件到指定路径

# npi i 和 mpm ci 的区别

  • 安装依据

    • npm install 依赖与 package.json package-lock.json

    • npm ci 严格根据 package-lock.json 安装

  • 是否删除 node_moudles

    • npm install 不会,增量安装

    • npm ci 会,重新安装

  • 安装速度

    • npm install 较慢,因为它会重新解析依赖

    • npm ci 快一点,严格根据package-lock.json安装指定版本

  • 适用场景

    • npm install 适合本地开发

    • npm ci 适合 ci/cd 流程,需要一致性的场景

  • 新增依赖

    • npm install 可以执行 npm install <package>,进而修改package.json package-lock.json文件

    • npm ci 不能执行新增依赖命令

# npm cache 是什么

  • npm install 字后会计算每个包的 sha1 值,然后将这个包和和 sha1 值的关联保存到 package-lock.json 文件里面,下次 npm install 的时候,会根据 sha1 值去找对应的包,找到了就不需要重新下载安装了

# webpack 的热更新实现

  • 文件更改的监控, 通过 webpack-dev-server 开发服务器 监听文件变化

    • 生成模块依赖树和对用的 moudle id,这个 id 在构建过程中跟踪和标记模块
  • 重新编译发生变化的模块, webpack 增量编译,只编译发生变化的模块和依赖模块,生成一个新的模块更新块

    • 监控文件变化:webpack 服务器和 webpack 通过 watch 机制,监听到文件变更就会重新编译该文件和其依赖,监听机制实际是监听文件内容对应的 hash 值是否变更

    • 生成新的更新块 chunk,记录了原本的 module id 和 新的内容,注意这里的新的 chunk 是增量编译出来的,同样为了确定 chunk 是否有变更,也有对应的 chunkhash

    • webpack 服务器向浏览器发送信息,告知哪个模块变更,并且提供新的模块

    • 浏览器根据 module id 进行代码替换

  • 模块替换和更新。 不刷新页面,只是动态替换变更的模块

    • css 模块,生成新的 style 标签进行替换

    • js 模块,直接替换旧的代码模块,并调用模块的 dispose 钩子来清理之前的状态

# webpack 的热更新实现 描述

  • 文件变更,webpack 的 watch 机制检测到文件变更

  • 增量编译, Webpack 只会重新编译发生变化的模块及其依赖模块。增量编译的结果会生成一个更新块(Update Chunk),其中包含变更的模块代码和对应的 Module ID(这是每个模块的唯一标识)。

  • 生成更新块 ,更新块会包含新编译的模块内容;每个模块依然有唯一的 Module ID,Webpack 会用这个 Module ID 来跟踪和标识模块;chunkhash 或者 hash 用于标识模块的内容变化,但在 HMR 中,真正起到核心作用的是 Module ID。

  • 通知客户端: Webpack Dev Server 通过 WebSocket 通信,将更新块的内容发送给客户端(浏览器)。

  • 客户端接收更新块, 客户端通过 Module ID 来确定发生变化的模块,直接替换掉缓存中已加载的模块代码。

# vite 的热更新

  • vite 监听文件变化

  • 更新模块缓存

  • 通过 websocket,通知浏览器 hmr runtime

  • 浏览器基于 esm 的模块机制 用新的模块代码进行替换

# tree shaking 原理

  • webpack 依赖 babel 和其他 loader,将代码转变成 esm 形式的代码

  • 基于 es6 模块的 import 和 export 的静态分析,可以在打包的时候移除未使用的方法

  • 标记可用代码,移除未使用的代码

# preload、prefetch 怎么理解

  • preload 提前加载关键资源、优先级高、和页面其他必要资源 CSS JS 一起加载

    • 比如汉字语言包,提前加载,从而实现加快首屏渲染
  • prefetch 提前加载可能需要用到的资源、优先级低、浏览器空闲时间加载

    • 比如英文语言包,预先加载,如果用户切换语言将不会再去临时加载英文语言包

# http2 相比较 http1.1 的改进

  • 多路复用, 多个请求/相应可以共用一个 tcp 连接并行传输

  • 头部压缩

  • 二进制分帧

  • 支持服务器推送

  • 单一持久化连接, 单一持久连接,减少连接数

  • 流量控制

  • 减少延迟

# 对于已经 import 的模块,但是没有实际使用,webpack 会打包进去吗

取决于 webpack 配置的优化手段

  • tree-shaking,专门用于消除死代码(dead code),基于 es6 的静态分析能力移除未使用的导出,所以 commanjs 的导入导出就没法移除了

  • uglifyJsPlugin,压缩 js 代码的插件,也会进行死代码消除,会删除没有使用的代码

  • sideEffect属性,可以在 package.json 里面标识指定文件,告诉 webpack 可以安全的被移除

# 打包过程中代码分割 code splitting 的原理是什么

  • 按需加载,基于**动态导入 import()**的语法实现,它会返回一个 Promise,从而实现调用的时候才去加载指定的模块

  • 依赖关系分析,基于 webpack 的 dp-graph 依赖图,webpack 会决定模块的打包和分割

  • 多入口打包,这种多页面应用,有不同的入口文件

  • 共享代码块,不同代码块共享相同的依赖的时候,webpack 可以将这些共享以来分离出来,打包成独立的’共享代码块‘

  • proload和prefetch,预加载 和 预取,也会生成代码分割,webpack 支持魔法注释来设置预加载和预取

    // 使用预取
    import(/* webpackPrefetch: true */ './moduleA.js');
    
    // 使用预加载
    import(/* webpackPreload: true */ './moduleB.js');