Skip to content
sh
npm config set production false
npm i -D webpack
npm -v
npm update
npm config list
npm config set registry https://registry.npm.taobao.org
npm config list / npm config get registery
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm -v

js
const inBrowser = typeof window !== 'undefined'
const UA = inBrowser && window.navigator.userAgent.toLowerCase()
const isIE = UA && /msie|trident/.test(UA)
const isIE9 = UA && UA.indexOf('msie 9.0') > 0
const isEdge = UA && UA.indexOf('edge/') > 0
const isAndroid = (UA && UA.indexOf('android') > 0)
const isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA))
const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge
const isFF = UA && UA.match(/firefox\/(\d+)/)

(R << 16 | G << 8 | B).toString(16)
什么是MVVM?

MVVM(Model-View-ViewModel)是一种软件架构设计模式,就是定义了一个Observer观察者。ViewModel 是连接 View 和 Model 的桥梁。

  • ViewModel能够观察到数据的变化,并对视图对应的内容进行更新。
  • ViewModel能够监听到视图的变化,并能够通知数据发生变化。
白屏性能优化 ?

前端白屏性能优化是前端开发中的重要一环,它涉及到多个方面,包括代码优化、资源加载优化、渲染优化等。以下是一些建议和实践,可以帮助你优化前端白屏问题:

  • 代码拆分和懒加载:

    • 使用代码拆分(code splitting)将大型代码库拆分为较小的块,这样用户只加载他们当前需要的部分。
    • 懒加载(lazy loading)可以延迟加载非关键资源,直到它们真正需要时才加载。
  • 优化资源加载:

    • 压缩图片、CSS和JavaScript文件,减少传输时间。
    • 使用CDN(内容分发网络)加速资源加载。
    • 利用 HTTP 缓存机制,避免重复加载未变更的资源。
  • 使用预渲染或服务端渲染:

    • 预渲染可以生成静态HTML文件,减少页面首次加载时的渲染时间。
    • 服务端渲染(SSR)将渲染过程放在服务器端执行,然后将渲染好的HTML发送给客户端,可以加快首屏渲染速度。
  • 优化JavaScript执行:

    • 尽量减少JavaScript代码量,避免阻塞渲染。
    • 使用asyncdefer属性异步加载脚本。
    • 利用Web Workers进行后台线程处理,避免阻塞主线程。
  • 使用骨架屏或占位符:

    • 在内容加载完成之前,使用骨架屏或占位符展示页面结构,减少用户等待时的白屏时间。
  • 利用浏览器缓存:

    • 合理使用浏览器的缓存机制,如localStorage、sessionStorage或IndexedDB等,缓存已加载过的资源或数据。
  • 减少DOM操作:

    • 尽量减少不必要的DOM操作,尤其是大量或频繁的DOM操作,因为它们可能导致页面重绘或重排,影响性能。
  • 利用CSS优化:

    • 使用CSS3动画代替JavaScript动画,减少渲染阻塞。
    • 避免使用复杂的CSS选择器,提高选择器的性能。
  • 使用性能分析工具:

    • 使用Lighthouse、Chrome DevTools等性能分析工具,分析页面加载和渲染过程中的瓶颈,并针对性地进行优化。
  • 持续监控和优化:

    • 监控用户设备的性能数据,了解不同设备下的性能表现,并根据需要进行优化。
    • 定期对网站进行性能测试和优化,确保性能持续稳定。
SSR是什么?

SSR(Server-Side Rendering,服务器端渲染)是一种将应用程序的界面在服务器上进行预先渲染并以 HTML 形式发送到客户端的技术。与传统的客户端渲染(CSR)相比,SSR 在服务器端生成完整的 HTML 页面,然后将其发送到浏览器,以提供更好的性能和搜索引擎优化。

优势:

  • 更快的首次渲染:由于服务器在响应请求时已经生成了完整的 HTML 页面,所以用户打开页面时可以立即看到内容,无需等待 JavaScript 下载和执行。
  • 更好的搜索引擎优化(SEO):搜索引擎爬虫能够抓取到完整的 HTML 页面,并且页面内容可直接被搜索引擎索引。
  • 更好的用户体验:页面内容在服务器端渲染完成后即可展示,减少了白屏时间和加载等待。
说下 Vite 的原理

共存的模块化标准:

  • CommonJS:现主要用于Node.js(Node@13.2.0开始支持直接使用ES Module)
  • AMD:require.js 依赖前置
  • CMD:sea.js 就近执行
  • ES Module:ES语言规范

当前工程化的痛点:

  • 缓慢的服务启动
  • 缓慢的HMR热更新

Vite:基于 esbuildRollup,依靠浏览器自身 ESM 编译功能, 实现极致开发体验的新一代构建工具!

  • 依赖: 指开发不会变动的部分(npm包、UI组件库),esbuild进行预构建。
  • 源码: 浏览器不能直接执行的非 js 代码(.jsx、.css、.vue等),vite只在浏览器请求相关源码的时候进行转换,以提供ESM源码。

实现原理:

  • ESbuild 编译

    • esbuild 使用 go 编写,cpu 密集下更具性能优势,编译速度更快
  • 依赖预构建

    • 模块化兼容: 如开头背景所写,现仍共存多种模块化标准代码,Vite 在预构建阶段将依赖中各种其他模块化规范(CommonJS、UMD)转换成 ESM,以提供给浏览器。
    • 性能优化: npm 包中大量的 ESM 代码,大量的 import 请求,会造成网络拥塞。Vite 使用 esbuild,将有大量内部模块的 ESM 关系转换成单个模块,以减少 import 模块请求次数
  • 按需加载

    • 服务器只在接受到 import 请求的时候,才会编译对应的文件,将 ESM 源码返回给浏览器,实现真正的按需加载。
  • 缓存

    • HTTP缓存: 充分利用 http 缓存做优化,依赖(不会变动的代码)部分用max-age, immutable强缓存,源码部分用 304 协商缓存,提升页面打开速度。
    • 文件系统缓存: Vite 在预构建阶段,将构建后的依赖缓存node_modules/.vite ,相关配置更改时,或手动控制时才会重新构建,以提升预构建速度。
  • 重写模块路径

    • 浏览器 import 只能引入相对/绝对路径,而开发代码经常使用 npm 包名直接引入 node_module 中的模块,需要做路径转换后交给浏览器。
    • es-module-lexer 扫描 import 语法
    • magic-string 重写模块的引入路径

优势:

  • 非常快
  • 高度集成,开箱即用
  • 基于 ESM 急速热更新,无需打包编译
  • 基于 esbuild 的依赖预处理,比 Webpack 等 node 编写的编译器快几个数量级
  • 兼容 Rollup 庞大的插件机制,插件开发更简洁
  • 不与 Vue 绑定,支持 React 等其他框架,独立的构建工具
  • 内置 SSR 支持
  • 天然支持TS

不足:

  • Vue 仍为第一优先支持,量身定做的编译插件,对 React 的支持不如 Vue 强大。
  • 虽然已经推出2.0正式版,已经可以用于正式线上生产,但目前市场上实践少。
  • 生产环境集成 Rollup 打包,与开发环境最终执行的代码不一致

官方插件:

  • @vitejs/plugin-vue 提供 Vue3 单文件组件支持
  • @vitejs/plugin-vue-jsx 提供 Vue3 JSX 支持(专用的 Babel 转换插件)
  • @vitejs/plugin-react 提供完整的 React 支持
  • @vitejs/plugin-legacy 为打包后的文件提供传统浏览器兼容性支持
相比于npm和yarn,pnpm的优势是什么?

pnpm对比npm/yarn的优点:

  • 更快速的依赖下载
  • 更高效的利用磁盘空间
  • 更优秀的依赖管理

软硬连接,这是操作系统提供的机制,硬连接就是同一个文件的不同引用,而软链接是新建一个文件,文件内容指向另一个路径。当然,这俩链接使用起来是差不多的。

AST 语法树是什么?

AST是抽象语法树(Abstract Syntax Tree)的缩写,它是一种用于表示程序源代码结构的树状数据结构。AST 可以将源代码解析为一个由节点组成的树形结构,每个节点代表着代码中的一个特定语法结构或语义概念。

以下是常见的使用情况:

  • 解析和验证:通过解析源代码,将其转换为 AST 之后,可以对代码进行验证静态分析。这包括检查语法错误、类型错误、变量引用等,并发现潜在的问题或优化机会。
  • 优化和转换:AST 可以用于执行各种优化操作,例如消除冗余代码、提取共享表达式、内联函数调用等。它还能够进行代码转换,例如将ES6代码转换为ES5兼容的代码、将模板编译为渲染函数等。
  • 生成代码:从 AST 中可以再次生成目标代码,如JavaScript、HTML、CSS等。这使得可以将源代码翻译为其他语言、在不同平台上执行代码等。

通过使用AST,开发人员可以更好地理解和分析代码的结构,从而进行静态分析优化转换等操作。它也为很多编程工具提供了基础,如编译器、静态代码分析工具和IDE等。

微前端中的应用隔离是什么,一般是怎么实现的?
  • 技术栈无关: 主框架不限制接入应用的技术栈,微应用具备完全自主权
  • 独立开发、独立部署: 微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
  • 增量升级: 在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
  • 独立运行时: 每个微应用之间状态隔离,运行时状态不共享

CSS隔离:

  • 当主应用和微应用同屏渲染时,就可能会有一些样式会相互污染,如果要彻底隔离 CSS 污染,可以采用CSS Module或者命名空间的方式,给每个微应用模块以特定前缀,即可保证不会互相干扰,可以采用 webpack 的 postcss 插件,在打包时添加特定的前缀。
  • 而对于微应用与微应用之间的 CSS 隔离就非常简单,在每次应用加载时,将该应用所有的 link 和 style 内容进行标记。在应用卸载后,同步卸载页面上对应的 link 和 style 即可。

JavaScript隔离:

  • 采用沙箱机制(SandBox)。沙箱机制的核心是让局部的 JavaScript 运行时,对外部对象的访问和修改处在可控的范围内,即无论内部怎么运行,都不会影响外部的对象。通常在 Node.js 端可以采用 vm 模块,而对于浏览器,则需要结合 with 关键字和 window.Proxy 对象来实现浏览器端的沙箱
RESTful 接口规范是什么 ?

RESTful 接口规范是一种设计 Web 服务接口的风格和规范,遵循 REST(Representational State Transfer)架构。它的设计原则包括以下几点:

  • 资源(Resources):将系统中的所有事物视为资源,每个资源都有一个唯一的标识符(通常是 URL),用于对其进行操作。

  • 统一接口(Uniform Interface):接口设计应该简单一致,包括以下几个方面:

    • 使用标准的 HTTP 方法(GET、POST、PUT、DELETE 等)来对资源进行操作。
    • 使用标准的 HTTP 状态码(如 200、404、500)来表示请求结果。
    • 使用资源的 URL 来唯一标识资源。
    • 使用适当的 MIME 类型(如 JSON、XML)来传输数据。
  • 状态无关(Stateless):每个请求都应该包含足够的信息,服务器不需要保存客户端的状态。这样可以使系统更加简单、可伸缩性更好。

  • 客户端 - 服务器分离(Client-Server Separation):客户端和服务器之间的交互应该通过标准化的接口进行,使得客户端和服务器可以独立地进行演化。

  • 可缓存性(Cacheability):对于经常不变的数据,应该使用合适的缓存机制,提高系统的性能和可伸缩性。

  • 按需代码(Code on Demand)(可选):服务器可以向客户端传输可执行代码,以提供更丰富的功能。

遵循 RESTful 接口规范能够使得系统具有良好的可维护性、可伸缩性和性能,并且更容易与其他系统进行集成。

大文件断点续传 ?
  • 将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;
  • 初始化一个分片上传任务,返回本次分片上传唯一标识
  • 按照一定的策略(串行或并行)发送各个分片数据块;
  • 发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合并成得到原始文件
JSBridge是什么?

JSBridge的原理主要基于双向通信的通道机制,它连接了 Native(原生代码)和H5(HTML5),使得两者能够互相调用对方的功能。具体来说,JSBridge通过在原生代码中嵌入JavaScript引擎,以及在JavaScript环境中提供对原生代码的访问接口,实现了两者之间的通信。

JSBridge是给 JavaScript 提供调用 Native 功能的接口,让混合开发中的前端部分可以方便地使用 Native 的功能(例如:地址位置、摄像头)。

实际上,JSBridge 就像其名称中的 Bridge 的意义一样,是 Native 和非 Native 之间的桥梁,它的核心是构建 Native 和非 Native 间消息通信的通道,而且这个通信的通道是双向的。

什么是单点登录,以及如何进行实现?

单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。SSO 一般都需要一个独立的认证中心(passport),子系统的登录均得通过 passport,子系统本身将不参与登录操作。

实现:

  • 同域名下的单点登录(cookie 的 domin 属性设置为当前域的父域,并且父域的 cookie 会被子域所共享。Cookie 的 domain 属性设置为父域的域名(主域名),同时将 Cookie 的 path 属性设置为根路径,将 Session ID(或 Token)保存到父域中。这样所有的子域应用就都可以访问到这个 Cookie。)

  • 不同域名下的单点登录(可以选择将 Session ID (或 Token )保存到浏览器的 LocalStorage 中,让前端在每次向后端发送请求时,主动将 LocalStorage 的数据传递给服务端)

说一说 loader和 plugin 的区别 ?

loader主要用于处理非JavaScript文件,将其转换为webpack能够处理的模块。这包括将各种文件类型(如ES6/ES7、SCSS/Sass/less等)转换为webpack可以理解的JavaScript模块,或者将资源打包成JS文件。loader的作用范围相对较小,它是对每个文件进行单独处理的。例如,babel-loader可以将ES6/ES7代码转换为ES5代码,而css-loader则可以处理CSS文件。loader的使用需要在webpack配置文件的module.rules属性中进行配置,并且需要通过npm安装相应的loader。

而plugin则是一种用于扩展webpack功能的函数,它可以加入自定义的构建行为,使webpack能够执行更广泛的任务。plugin的功能十分丰富,包括但不限于优化打包结果(如压缩代码、去除无用代码等)、注入环境变量、自动生成页面、生成版本号、引入第三方库、拆分代码以及多页应用打包等。与loader不同,plugin并不直接处理文件内容,而是在webpack构建过程的特定时机执行相应的任务。plugin的使用需要在webpack配置文件的plugins属性中进行配置。

总的来说,loader和plugin在webpack中各自扮演着重要的角色。loader主要关注文件的转换和处理,使webpack能够处理各种非JavaScript文件;而plugin则主要关注扩展webpack的功能,以满足更复杂的构建需求。两者相互协作,共同构建出高效、灵活的webpack打包方案。

webpack 构建流程是怎样的 ?

Webpack的构建流程是一个有序且连贯的过程,从初始化配置开始,到最终的输出结束。以下是详细的步骤:

  • 初始化流程:

    • 从配置文件(如webpack.config.js)和Shell语句中读取与合并参数。
    • 初始化 compiler 对象,并注册所有配置的插件。
    • 插件监听 Webpack 构建生命周期的事件节点,做出相应的反应。
  • 编译构建流程:

    • 确定入口:从配置文件指定的 entry 入口开始解析文件,构建 AST(抽象语法树),找出依赖,并开始递归
    • 编译模块:在递归过程中,根据文件类型和 loader 配置,调用相应的 loader 对文件进行转换。这包括将各种文件类型(如ES6/ES7、CSS、图片等)转换为 Webpack 能够处理的模块。然后,继续找出该模块依赖的其他模块,并递归执行本步骤,直到所有入口依赖的文件都经过了处理。
  • 输出流程:

    • 对编译后的模块进行组合,生成一个或多个代码块(chunk)。
    • 将这些 chunk 转换成文件,并输出到文件系统。这通常包括压缩代码、优化输出等操作。

在整个构建流程中,Webpack的插件机制发挥了重要作用。插件可以监听并参与到Webpack构建的各个阶段,从而改变或增强构建行为。这使得Webpack具有高度的灵活性和扩展性,能够应对各种复杂的构建需求。

编写 webpack loader 的思路

常见的loader:

  • 样式:style-loadercss-loaderless-loader、sass-loader等
  • 文件:raw-loaderfile-loaderurl-loader
  • 编译:babel-loaderts-loader
  • 校验测试:eslint-loader

编写一个 webpack loader 的思路主要包括以下几个步骤:

  • 确定需求:

    • 首先,明确 loader 需要处理哪种类型的文件或数据。例如,你可能想要编写一个处理特定格式文本文件的 loader,或者一个转换图像格式的 loader。
    • 确定 loader 需要执行的具体转换或操作。
  • 安装必要的依赖:

    • 根据你的 loader 的功能,你可能需要安装一些 npm 包作为依赖项。
    • 确保这些依赖项在你的 loader 中被正确引用和使用。
  • 创建 loader 文件:

    • 创建一个新的 JavaScript 文件,用于实现你的 loader。
    • 通常,loader 的名称应该遵循 xxx-loader 的命名规范,其中 xxx 是你的 loader 的名称或功能描述。
  • 编写 loader 代码:

    • 在 loader 文件中,你需要导出一个函数,这个函数将接收源文件内容作为输入。
    • 在函数内部,执行你需要的转换或操作。这可能包括解析源文件、修改内容、生成新的内容等。
    • 最后,函数应该返回转换后的内容。
  • 处理异步操作:

    • 如果你的 loader 需要执行异步操作(例如,读取外部文件或发起网络请求),你可以使用异步函数或 Promise。
    • 确保你的 loader 能够正确处理异步操作,并返回正确的结果。
  • 处理错误:

    • 在 loader 的实现中,要考虑可能出现的错误情况,并相应地处理它们。
    • 你可以使用 try-catch 语句来捕获和处理错误,或者通过异步函数抛出错误。
  • 测试 loader:

    • 编写测试用例来验证你的 loader 的功能是否正确。
    • 可以使用 Jest 或其他测试框架来编写测试代码。
  • 发布 loader:

    • 一旦你的 loader 完成并经过测试,你可以将其发布到 npm 上,供其他人使用。
    • 在发布之前,确保你的 loader 遵循了 webpack 的最佳实践和命名规范。
  • 在 webpack 配置中使用 loader:

    • 在你的 webpack 配置文件中,通过 module.rules 属性来配置你的 loader。
    • 指定需要应用 loader 的文件类型(通过 test 属性)以及 loader 的名称(通过 use 属性)。
  • 调试和优化:

    • 在实际使用中,可能需要对 loader 进行调试和优化,以确保其性能和稳定性。
    • 可以使用 webpack 的 --progress--profile 标志来跟踪构建进度和性能瓶颈。
编写 webpack plugin 的思路

编写一个 webpack plugin 的思路主要包括以下几个步骤:

  • 明确需求:

    • 首先,确定你想要通过插件实现的功能。这可能包括优化构建过程、添加自定义的钩子、处理资源、生成额外的文件等。
    • 深入理解 webpack 的构建流程和插件机制,以便知道在何时何地插入你的插件逻辑。
  • 创建插件文件:

    • 创建一个新的 JavaScript 文件,用于实现你的插件。
    • 通常,插件的名称应该遵循 xxx-webpack-plugin 的命名规范,其中 xxx 是你的插件的功能描述。
  • 实现 Plugin 类:

    • 编写一个继承自 webpack.Plugin 的类,并在其中实现你的插件逻辑。
    • 在构造函数中,可以接收插件的配置参数,并保存为类的实例属性。
  • 应用钩子函数:

    • webpack 提供了一系列的钩子(hooks),允许插件在构建流程的特定时机插入逻辑。
    • 在你的 Plugin 类中,通过覆盖 webpack 提供的钩子函数(如 apply 方法),来添加你的自定义逻辑。
    • apply 方法中,你可以通过 compiler 对象访问 webpack 的编译实例,并监听相应的事件或钩子。
  • 处理异步操作:

    • 如果你的插件需要执行异步操作,确保正确处理异步逻辑。
    • 可以使用 Promise、async/await 或回调函数来处理异步操作,并确保在适当的时候调用 webpack 提供的回调函数。
  • 编写文档和示例:

    • 为你的插件编写清晰的文档,说明插件的用途、配置选项和使用方法。
    • 提供示例代码,展示如何在实际项目中使用你的插件。
  • 测试插件:

    • 编写测试用例来验证你的插件的功能是否正确。
    • 可以使用 Jest 或其他测试框架来编写测试代码,并模拟 webpack 的构建环境。
  • 发布插件:

    • 一旦你的插件完成并经过测试,你可以将其发布到 npm 上,供其他人使用。
    • 在发布之前,确保你的插件遵循了 webpack 的最佳实践和命名规范。
  • 使用插件:

    • 在你的 webpack 配置文件中,通过 plugins 属性来配置你的插件。
    • 创建一个插件实例,并将其添加到 plugins 数组中。
  • 调试和优化:

    • 在实际使用中,可能需要对插件进行调试和优化,以确保其性能和稳定性。
    • 可以使用 webpack 的日志输出和调试工具来跟踪插件的执行情况。

下面是一个简单的 webpack 插件示例代码,这个插件会在 webpack 构建完成后输出一条自定义的消息:

js
// 自定义插件文件:test-webpack-plugin.js
const { Compiler } = require('webpack');

class TestWebpackPlugin {
  constructor(options) {
    // 初始化插件,配置参数options是可选的
    this.options = options;
  }

  apply(compiler) {
    // 在compiler对象上挂载一个webpack事件钩子
    compiler.hooks.emit.tapAsync('TestWebpackPlugin', (compilation, callback) => {
      // 插件逻辑代码,这里是在构建输出资源到磁盘之前执行
      console.log('TestWebpackPlugin: 正在构建项目...');

      // 模拟异步操作,例如读取文件、网络请求等
      setTimeout(() => {
        console.log('TestWebpackPlugin: 构建完成!');
        console.log('TestWebpackPlugin 输出自定义信息:', this.options.message);

        // 异步操作完成后,调用callback通知webpack
        callback();
      }, 1000);
    });

    // 也可以监听其他钩子,如done钩子会在构建结束后执行
    compiler.hooks.done.tap('TestWebpackPluginDoneHook', (stats) => {
      console.log('TestWebpackPlugin: 所有构建任务完成!');
    });
  }
}

module.exports = TestWebpackPlugin;

要使用这个插件,你需要在你的 webpack.config.js 文件中配置它:

js
// webpack.config.js
const TestWebpackPlugin = require('./test-webpack-plugin'); // 引入你的插件

module.exports = {
  // ... 其他webpack配置 ...
  plugins: [
    // 实例化插件并传入配置参数
    new TestWebpackPlugin({ message: 'Hello, Webpack!' })
  ]
};

在这个例子中,TestWebpackPlugin 在构建输出资源到磁盘之前(emit 钩子)和构建结束后(done 钩子)各执行了一次。

请注意,插件可以监听 webpack 提供的各种钩子,并在这些钩子的回调函数中执行自定义逻辑。这个示例中的插件非常简单,只是为了展示如何编写一个基本的 webpack 插件。在实际开发中,插件可能会涉及更复杂的逻辑,比如修改构建输出、处理特定的文件类型、优化资源等。

介绍一下 webpack

Webpack是一个现代JavaScript应用程序的静态模块打包器(module bundler),也是目前最为流行的JavaScript打包工具之一。它的主要作用是将项目中的所有模块(包括JavaScript、CSS、图片等)视为一个整体,通过依赖关系将它们打包成一个或多个静态资源文件。

Webpack的核心特性包括:

  • 模块打包:通过递归检查每个js模块的依赖,构建一个依赖关系图,然后将整个应用程序打包成一个或多个bundle。
  • 依赖管理:能够分析模块之间的依赖关系,根据配置的入口文件找出所有依赖的模块,并将其整合到打包结果中。
  • 文件转换:虽然 Webpack 本身只能处理 JavaScript 模块,但通过加载器(Loader)的使用,可以将其他类型的文件(如CSS、LESS、图片等)转换为有效的模块,使其能够被打包到最终的结果中。
  • 代码拆分:支持将代码拆分成多个模块,按需加载,这有助于实现按需加载和提升应用性能。
  • 插件系统:提供了丰富的插件系统,可以通过插件实现各种功能的扩展,例如压缩代码、自动生成HTML文件等。
webpack externals 深入理解

如果我们想引用一个库,但是又不想让webpack打包,并且又不影响我们在程序中以CMD、AMD或者window/global全局等方式进行使用,那就可以通过配置externals。

json
externals:{
  react:'React',
  jquery:'jQuery'
}

我们开发了一个自己的库,里面引用了lodash这个包,经过webpack打包的时候,发现如果把这个lodash包打入进去,打包文件就会非常大。那么我们就可以externals的方式引入。也就是说,自己的库本身不打包这个lodash,需要用户环境提供。
项目从 js 迁移到 ts
  • 安装 TypeScript*: 在项目的根目录下运行以下命令安装 TypeScript 及其定义文件:
    bash
    npm install --save-dev typescript
    npx tsc --init
    tsc --init 命令会生成一个 tsconfig.json 文件,这个文件包含了 TypeScript 编译器的配置选项。
  1. 重命名文件: 将 .js 文件重命名为 .ts 文件。TypeScript 编译器可以处理 .ts 文件,并将其编译成 .js 文件。

  2. 更新 package.json: 确保 package.json 中的 scripts 部分使用 tsc 命令来编译 TypeScript 文件。例如:

    json
    "scripts": {
      "build": "tsc",
      "start": "tsc && node ."
    }

    这里的 start 脚本首先编译 TypeScript 文件,然后使用 Node.js 运行编译后的 JavaScript 文件。

  3. 逐步添加类型注解: 不要一次性为整个项目添加类型注解,这可能会非常耗时且容易出错。相反,你可以从入口文件开始,逐步为函数、变量和类添加类型注解。TypeScript 的类型推断功能可以帮助你自动推断出很多类型,因此你不需要为所有内容都显式地添加类型。

  4. 使用 TypeScript 的特性: 随着迁移的进行,你可以开始利用 TypeScript 提供的特性,如接口(Interface)、枚举(Enum)、泛型(Generics)等,来改进代码的结构和可维护性。

  5. 处理第三方库: 如果你的项目依赖了第三方库,并且这些库没有提供 TypeScript 定义文件(.d.ts),你可能需要安装相应的 @types 包或使用其他方法来提供类型信息。例如,通过 npm 安装 @types/库名

  6. 运行和测试: 在迁移过程中,不断运行和测试你的应用以确保没有引入新的错误。你可以使用现有的测试套件(如 Jest、Mocha 等)来确保功能没有改变。

  7. 配置 ESLint: 配置 ESLint 以支持 TypeScript,这有助于在编写代码时捕获潜在问题。你可以使用如 @typescript-eslint/parser@typescript-eslint/eslint-plugin 这样的包来增强 ESLint 对 TypeScript 的支持。

  8. 持续集成和部署: 更新你的持续集成(CI)和部署流程,以确保新的 TypeScript 代码能够正确编译和部署。

  9. 文档和代码清理: 随着迁移的完成,更新项目文档以反映新的 TypeScript 实践,并清理任何不再需要的旧 JavaScript 代码或配置。

taro 的实现原理是怎么样的?

Taro 是一个多端统一开发框架,可以使用一套代码编译成微信小程序、支付宝小程序、百度智能小程序、字节跳动小程序、QQ 小程序、快应用、H5 等多个平台的应用。

Taro 的实现原理主要基于以下几个方面:

  • JSX 转换:Taro 使用 Babel 插件将类似 HTML 的语法转换为 React 组件。在编译过程中,Taro 还会对 JSX 语法进行优化和压缩,以避免生成不必要的代码。

  • 多端适配:Taro 通过封装原生 API 和提供不同的 Polyfill 实现多端适配。例如,在微信小程序中,Taro 封装了 wx 对象,使得可以使用类似 React Native 的组件化开发方式;在 H5 中,Taro 则提供了针对浏览器的 Polyfill。

  • 跨端样式处理:Taro 通过CSS Modules技术和 PostCSS 插件来处理 CSS 样式。在编译过程中,Taro 会将样式文件转换为 JavaScript 对象,并按需导入到组件中。同时,Taro 提供了 @import 指令或 scss 语法等方式来支持复杂的样式表达。

  • 构建系统:Taro 使用 webpack 构建工具来打包编译后的代码,并提供了一系列开箱即用的插件、规则和配置项,例如自动化导入组件、静态资源压缩、TypeScript 支持等。

  • 运行时性能优化:Taro 在运行时对代码进行了一些优化,例如使用字典树实现 JSX 解析、避免使用内置事件监听器、减少对原生 API 的调用等方式来优化性能。

Taro 利用 Babel、React、Webpack 等技术,通过封装原生 API 和提供不同的 Polyfill 实现了多端适配,同时也支持复杂的样式表达和自动化导入组件等特性。这些技术的应用使得 Taro 框架在性能、可维护性、跨平台等方面都表现出色。

Taro 2.x 和 Taro 3 的最大区别可以总结为以下几个方面:

  • 编译方式:Taro 2.x 使用 Gulp 构建工具进行编译,而 Taro 3 改为使用 Webpack 进行构建。这使得 Taro 3 在编译速度、可扩展性、构建配置等方面有了更好的表现。

  • React 版本升级:Taro 2.x 使用的是 React 16 版本,而 Taro 3 升级到了 React 17 版本。React 17 引入了一些新特性,例如以初始渲染器为基础的事件处理、重新设计的事件系统等,从而提高了性能和稳定性。

  • API 改进:Taro 3 对 API 进行了改进,并引入了新的特性。例如,在 JSX 中可以使用 class 关键字来定义 CSS 样式;增加 useReady 钩子函数在小程序生命周期 onReady 被触发时执行;引入了快应用和 H5 等新平台的支持等。

  • 插件机制:Taro 3 引入了插件机制,使得开发者可以通过插件实现更多的功能和特性,例如对 TypeScript 支持的扩展、国际化支持等。

  • 性能优化:Taro 3 在性能方面进行了优化,例如使用虚拟 DOM 进行局部更新,减少对原生 API 的调用等。同时,Taro 3 可以根据平台的不同生成更小的代码包。

Taro 3 引入了一些新特性和优化,并提高了性能、可扩展性和稳定性。

提交代码,自动检测并格式化

在前端开发中,实现提交代码时自动格式化代码的功能,通常可以通过结合代码编辑器、Git钩子(hooks)以及代码格式化工具来实现。以下是一个基本的实现流程:

  • 安装代码格式化工具:常用的代码格式化工具有Prettier、ESLint等。这些工具可以根据你的配置规则来格式化代码。
bash
npm install --save-dev --save-exact prettier

npm install eslint --save-dev
  • 配置格式化工具:在项目的根目录下创建一个配置文件(如.prettierrc.eslintrc),并定义你的代码格式化规则。例如,Prettier的配置文件可能如下所示:
json
{
   "singleQuote": true,
   "semi": false,
   "trailingComma": "es5"
}
  • 对于ESLint,你还需要安装一个格式化插件,比如eslint-plugin-prettiereslint-config-prettier,以确保ESLint和Prettier能够协同工作。
  • 使用Git钩子实现提交时自动格式化:如果你想在每次提交代码时自动格式化,你可以使用Git的pre-commit钩子。你可以编写一个脚本,在每次提交前运行格式化工具,并检查是否有文件被修改。如果有文件被修改,你可以提示开发者重新格式化并提交。以下是一个简单的pre-commit钩子示例,使用Prettier自动格式化:
bash
#!/bin/sh
# pre-commit hook to run prettier

# Stash unstaged changes, we don't want to commit them yet
git stash --keep-index --quiet || exit 0

# Run prettier on all staged files
npx prettier --write $(git diff --cached --name-only --diff-filter=ACM | grep '\.js$')

# Check if prettier made changes
if ! git diff-index --quiet HEAD --; then
   echo "Prettier found changes, please add them to your commit"
   exit 1
fi

# Restore unstaged changes
git stash pop --quiet

将上述脚本保存为.git/hooks/pre-commit,并确保它有执行权限(chmod +x .git/hooks/pre-commit)。这样,每次尝试提交时,如果Prettier检测到需要格式化的更改,它将自动格式化这些文件,并提示你重新提交。

webpack dll ?

Webpack DLL(动态链接库)的原理主要基于Webpack的模块化和插件化功能,以及动态链接库(DLL)的概念。它通过将第三方库和代码分离出来,单独打包成一个或多个文件,然后在主项目中引用这些文件,实现代码的复用和优化。

具体来说,Webpack DLL的实现原理包括以下几个方面:

  • 分离第三方库:Webpack通过 DLLPlugin 插件,将第三方库和代码从主项目中分离出来,单独打包成一个或多个文件。这些文件通常包括库代码本身和库的依赖关系等信息。
  • 生成映射文件:在打包过程中,Webpack还会生成一个映射文件(通常是JSON格式),记录库文件和主项目之间的依赖关系。这个文件对于后续的代码引用和加载非常重要。
  • 引用第三方库:在主项目中,通过 DllReferencePlugin 插件,Webpack将生成的映射文件读入,并根据映射关系将第三方库映射到主项目的相关依赖上。这样,主项目在运行时就可以正确地加载和使用这些库了。
  • 动态链接和共享:DLL的概念在这里起到了关键作用。动态链接库是一种可以在多个程序之间共享的代码和数据的库。通过Webpack DLL的处理,这些库文件在编译时不会被包含到可执行文件中,而是在运行时被动态加载到内存中。这种方式不仅节省了内存空间,还提高了代码的复用性和灵活性。

总的来说,Webpack DLL的原理是通过将第三方库和代码分离、打包和映射,实现了代码的复用和优化,提高了应用的性能和可维护性。同时,结合动态链接库的概念,进一步提升了代码的共享和灵活性。

single-spa ?

Single-spa 是一个用于构建前端微服务架构的 JavaScript 框架,它将多个单页面应用聚合为一个整体应用。以下是关于 Single-spa 的详细介绍:

single-spa 借鉴了组件生命周期的思想,它为应用设置了针对路由的生命周期。当应用匹配路由/处于激活状态时,应用会把自身的内容挂载到页面上;反之则卸载。典型的 single-spa 由 html 页面、应用注册脚本、应用脚本自身构成。

  • 应用注册内容包含:
  • name 应用名;
  • loadingFunction 应用脚本加载函数;
  • activityFunction 应用激活态判断函数。
  • single-spa 又约定应用脚本包含以下生命周期:
  • load 当应用匹配路由时就会加载脚本(非函数,只是一种状态)
  • bootstrap 引导函数(对接 html,应用内容首次挂载到页面前调用)
  • mount 挂载函数
  • unmount 卸载函数(须移除事件绑定等内容)
  • unload 非必要(unload 之后会重新启动 bootstrap 流程;借助 unload 可实现热更新)
  • 生命周期函数获得参数包含:
  • name 应用名
  • singleSpa 实例
  • mountParcel 手动挂载函数
  • customProps 自定义信息;它必须返回 Promise 或其本身为
  • async 函数

优点: Single-spa的优点主要体现在以下几个方面:

  • 微前端架构支持
  • 技术栈无关性
  • 独立部署和升级
  • 代码共享和复用
  • 性能优化
  • 易于扩展
js
{
  name: string;// 应用名
  parcels: {},// 包
  status: NOT_LOADED | LOADING_SOURCE_CODE | NOT_BOOTSTRAPPED | BOOTSTRAPPING | NOT_MOUNTED | 
    MOUNTING | UPDATING | LOAD_ERROR | MOUNTED | UNMOUNTING | SKIP_BECAUSE_BROKEN;// 应用状态
  customProps: { [key: string]: any };// 自定义属性
  loadImpl: string | (url: string) => Promise;// 应用模块位置或应用脚本加载函数
  activeWhen: (location: string) => boolean;// 应用激活状态判断函数
  bootstrap: (props: { customProps, name, mountParcel, singleSpa }) => Promise;// 引导函数
  mount: (props: { customProps, name, mountParcel, singleSpa }) => Promise;// 挂载函数
  unmount: (props: { customProps, name, mountParcel, singleSpa }) => Promise;// 卸载函数
  unload: (props: { customProps, name, mountParcel, singleSpa }) => Promise;// 卸载脚本函数
  timeouts: { bootstrap, mount, unmount, unload };// 超时设置
  loadErrorTime: null,// 
  devtools: { overlays },// 设置 window.__SINGLE_SPA_DEVTOOLS__ 的调试环境中使用
}
GQL

GraphQL(Graph Query Language)是一种用于 API 的查询语言,由 Facebook 开发并开源。它允许客户端指定它们想要从服务器获取的确切数据,而不是像传统的 RESTful API 那样,客户端必须从预定义的端点获取数据,并可能收到大量不需要的数据。

GraphQL 的主要特点包括:

  • 灵活的数据获取:客户端可以精确地指定它所需要的数据字段,而不是获取整个资源或资源的子集。
  • 单一端点:与传统的 RESTful API 相比,GraphQL 通常只使用一个端点来处理所有请求。
  • 类型化:GraphQL 有一个严格的类型系统,这有助于确保数据的完整性和准确性。
  • 自描述:GraphQL 模式(schema)是自我描述的,客户端可以使用它来理解和查询 API 的能力。
  • 减少网络往返:由于客户端可以一次请求多个资源或资源的多个字段,因此可以减少网络请求的数量,从而提高性能。

GraphQL 常用于构建需要高度定制化数据获取的应用程序,特别是那些具有复杂数据模型或需要高度交互性的应用程序。

在使用 GraphQL 时,你通常会定义一个 schema,它描述了你的数据模型以及可以执行的查询和操作。然后,你可以使用 GraphQL 查询语言来编写查询,这些查询会被发送到服务器,服务器会执行这些查询并返回结果。

打包体积优化分析
  • webpack-bundle-analyzer

Powered by VitePress.