小笔记(工程、部署 篇)

73 阅读8分钟

笔记打算记录,自己整理好的面试题,
争取保持准确性和专业性,然后用直白的语言描述出来,
希望能做成一个API文档,让我日后翻到能直接对着复习就好,
如果也能帮助到你就最好了(我会不定时的更新的)

babel 如何将 ES6(更新版本) 转换为 ES5

总共分为三个步骤(Babel 7 以后,插件都进行了命名规范调整,均以 @babel/ 作为开头)

  1. parser解析:

将代码逐字进行解析,打散后组装成对象(AST树:abstract syntax tree),babel 7以前使用的是 babylon插件,新版本改名为 @babel/parser

  1. transformer转换:

根据config.json中配置的plugins, preset,转换成新的AST树。使用babel-traverse插件(新版本 @babel/traverse)遍历AST树,通过babel-transform插件进行转换。(后续版本 transform插件合并到 @babel/core 中)

  1. generator:

将AST树转换成可以识别的ES5语法代码,使用babel-generator插件完成。

git / svn 的区别

svn

  1. 集中式管理(如果代码服务器有问题,则无法正常提交)
  2. 分支是复制整个文件夹目录(分支变化会影响到全局)

git

  1. 去中心化,分布式的管理
  2. 支持单独的代码分支

ESM / CJS 的理解

ESM

  • 通过importexport来进行导入和导出
  • ESM模块是异步加载的
  • 只有实际被使用的代码才会加载,其他导出的资源如果未被使用会被过滤掉
  • 处于严格模式,防止混乱的全局变量
  • 必要时,只能以export default的形式引入CJS模块,否则会报错
//导出的多种方式
//1. 具体的命名导出 a.js
export const a = 'Value 1'; 
export const b = 'Value 2';
export function c() {
  return 'Value 3'
}

//2. 默认导出 b.js (一个文件中只允许有一个 default 认导出)
export default function d() {
  return 'abc'
}

//3. 支持混合导出 c.js
export default function e() {
  return 'abc'
}
export const f = 'abc';

//对应的导入方式
//1.默认导入
//2.具体命名导入 
//3.重命名导入(关键字:as)
//4.通配符全部导入(关键字:*)

CJS

  • 通过module.exportsrequire(资源路径)进行导出和导入操作
  • CJS模块是同步加载的,(异步加载的模块、模块的依赖关系)是需要项目运行后才能确定
  • node环境默认支持 CJS模块
  • 无法直接引入ESM模块
//导出的方式
//1. 直接使用 module.exports ,可以直接将一个值(函数、对象、类等)赋值给 module.exports
const func = () => {
  return 'abc'
}
module.exports = func;

//2. 使用 exports
exports.a = 'a';
exports.b = 'b';
exports.c = 'c';

//3. 使用 module.exports 进行对象赋值操作
module.exports = {
  name: 'abc',
  func: () => {
    return 'aa'
  }
}

//同一个js文件中,不建议同时使用 exports 和 module.exports
//同时使用的话,exports 导出的变量会全部失效
ESMCJS
资源加载异步加载(可结合promise链式使用,更适合移动端)同步加载(更适合服务端,会出现阻塞)
动态导入支持运行时使用import()动态导入没有API方法支持
静态分析项目未编译(静态),可直接获取资源的依赖关系需要项目编译运行后,才能计算出关系
全局变量提供__dirname__filename
资源导入形式导入的资源是使用引用的模式导入的资源是使用引用的模式
模式严格模式,无this关键字this指向当前模块

tree shaking 的理解

tree shaking是一种代码优化的概念,通过分析,剔除未被引用的 ES Module 导出值,从而减少打包文件的体积,提升应用性能

tree shaking(依赖 ES Module的模块特性),只分析函数importexport 变量,tree shaking原理:

  1. 分析静态文件代码,整理出代码importexport的依赖关系表
  2. 标记未使用的代码模块
  3. 去除无用代码,重新生成输出文件(实现代码瘦身)
  • webpack中,当在生产模式 mode: 'production' 的时候,会自动开启。如果需要更 准确 的 tree-shaking,还可以通过配置 sideEffects 字段,告诉工程哪些模块是不能够被剔除的。
{
  // 当前的 style.css文件,不能被 tree-shaking,剔除掉
  "sideEffects": ["./src/styles.css"]
}

rollup / webpack / vite 的理解

vite

  • 先启动不做全量打包,再按需加载依赖文件,利用浏览器 ESM 加载原理(加载 ESM 模块是通过 http 请求发送到浏览器),使用 middleware插件 对模块进行拦截,转换的过程使用 esbuild(go语言)执行
  • vite 当前版本使用 rollup 配合打包

优点:

  1. 因为 unbundle(不预先全局构建)的机制,资源文件利用 http 缓存,使得 vite 有快速响应。
  2. 使用 esbuild(go语言) 进行转换,go语言 比 nodejs 处理速度要快

缺点:

  1. unbundle 的机制 导致首屏、懒加载解析的时间变长。

webpack

  • 结合 loaderplugin,拥有更加全面的功能
  • 启动服务后,会对工程进行预打包,分析依赖关系,然后再进行页面呈现

缺点:

  1. 构建后的代码,进行了tree shaking的代码瘦身,但是代码中会有部分预设的代码在里面(cjs转换成ESM的工程函数),所以整体没有 rollup 打包后的精简

rollup

  • 利用 tree shaking 的机制,将各模块进行打包,构建后的代码比 webpack 要更精简
  • 更专注于构建打包,没有按需加载,没有HMR功能

webpack 常见的减小体积的方法

  • 利用插件 webpack-bundle-analyzer 进行分析,查看哪些模块占用了最多的空间,从而做针对性的优化。
// webpack 会自动打开一个可视化界面展示
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [new BundleAnalyzerPlugin()],
};
  • 利用插件动态加载css文件、剔除未使用的css文件(从而减小资源文件的体积)
// 动态加载
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 移除未使用的css
const PurgeCSSPlugin = require('purgecss-webpack-plugin');
  • 代码压缩(针对 js代码、css代码、html代码)
  1. js代码:terser-webpack-plugin
  2. css代码:css-minimizer-webpack-plugin
  3. html:html-webpack-plugin
// TerserWebpackPlugin (webpack 自带的工具,压缩 JS代码,移除注释,去掉 console.log)
module.exports = {
  mode: 'production' // 自动使用 TerserWebpackPlugin 进行压缩
};

// 对比与传统的 UglifyJS, terser支持更新 ①更新版本的js代码、②配置更多、③多线程压缩

// css代码的压缩
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

// html代码(利用 html-webpack-plugin 搭配内建 minify 选项)
const HtmlWebpackPlugin = require('html-webpack-plugin');
  • 引用第三方的库:例如用 lodash-es 代替 lodash,(更好配合 tree-shaking,从而减小文件大小)
  • 对静态文件进行优化:
    1. 例如使用插件压缩图片大小 image-webpack-loader
    2. 手动压缩图片资源大小 tinypng

webpack 常见的构建加速方法

  • 使用缓存 cache,新版本 webpack支持文件缓存

  • 启用多线程构建

// 使用 thread-loader 插件
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['thread-loader', 'babel-loader'],
        exclude: /node_modules/
      }
    ]
  }
};
  • 配置更优的 includeexclude (只处理需要构建的文件,例如 src
{
  test: /\.js$/,
  loader: 'babel-loader',
  include: path.resolve(__dirname, 'src'),
  exclude: /node_modules/
}
  • 利用 CDN,(将第三方库使用 cdn 进入)

  • 减少过多的 alias 配置,从而减少查找时间

  • 使用 source-map 时,选择更轻便的类型配合开发调试

  • 可选择 esbuild 替代 babel进行构建,(因为 go语言js语言 构建速度快)

gzip 压缩的理解

gzip 是一种压缩技术(算法),不是 Webpack 的插件,可以通过插件在构建阶段生成 .gz 文件

  • 在前端,gzip 压缩常用于服务器端,在将静态资源(如 JS、CSS、HTML)发送给浏览器之前进行压缩,以减小网络传输的体积,加快加载速度。
  • 浏览器能自动识别 .gz 文件并解压显示页面,不需要你手动干预

npm插件发布理解

项目工程中 package.json 文件最关键

npm工程部署指令

  • npm version patch:1.0.0 升级为 1.0.1
  • npm version minor:1.0.0 升级为 1.1.0
  • npm version major:1.0.0 升级为 2.0.0

npm / yarn / pnpm 的理解

3个依赖库安装工具,都能够对用于管理工程项目中的package,但是也有存在一些不足的地方

npm:

  1. 安装 package时,以串行的形式下载,安装的时间较长
  2. 每一个工程项目都会重新单独下载所需的 package,会导致开发的文件夹容量较大,和系统频繁 I/O
  3. 当依赖库有自己的依赖时,会因为文件夹嵌套过深,导致在 Windows 无法正常访问的问题
  4. 因为 ③ 衍生出,A依赖库自己的依赖是 1.0版本的Z,B依赖库自己依赖的是 2.0版本的Z,导致 node_modules 中出现依赖版本不一致的问题。

yarn:

在 npm 的基础上,优化了以下几个点:(仍保留了每个工程项目依赖库都会重新单独安装)

  1. 支持并行下载、离线缓存安装依赖库
  2. 支持依赖库 package-lock.json 的概念(npm5.0 开始支持)
  3. 引入依赖库扁平化的概念

依赖库扁平化带来的问题:

  1. 扁平算法复杂、耗时
  2. 扁平化后会出现未写入 package.json 的依赖库,也能被使用。(工程只引入了A依赖库,A依赖库自己依赖B,工程直接使用 B依赖库,虽然会报错,但是能正常使用)
  3. 多个不同的依赖库,各自所依赖的新依赖库版本不同时,扁平算法后会有不可预估的问题(文件路径有误、重复安装等)

pnpm:

名字是 performance npm,解决了npm、yarn重复安装,幽灵依赖的问题

  1. 一个工程项目中统一管理依赖库,安装后使用指针引用依赖库(减少 I/O,减少磁盘空间)