主题
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代码量,避免阻塞渲染。
- 使用
async
和defer
属性异步加载脚本。 - 利用
Web Workers
进行后台线程处理,避免阻塞主线程。
使用骨架屏或占位符:
- 在内容加载完成之前,使用
骨架屏
或占位符展示页面结构,减少用户等待时的白屏时间。
- 在内容加载完成之前,使用
利用浏览器缓存:
- 合理使用
浏览器的缓存机制
,如localStorage、sessionStorage或IndexedDB等,缓存已加载过的资源或数据。
- 合理使用
减少DOM操作:
- 尽量
减少不必要的DOM操作
,尤其是大量或频繁的DOM操作,因为它们可能导致页面重绘或重排,影响性能。
- 尽量
利用CSS优化:
- 使用
CSS3动画
代替JavaScript动画,减少渲染阻塞。 - 避免使用复杂的CSS选择器,提高选择器的性能。
- 使用
使用性能分析工具:
- 使用Lighthouse、
Chrome DevTools
等性能分析工具,分析页面加载和渲染过程中的瓶颈,并针对性地进行优化。
- 使用Lighthouse、
持续监控和优化:
- 监控用户设备的性能数据,了解不同设备下的性能表现,并根据需要进行优化。
- 定期对网站进行
性能测试
和优化,确保性能持续稳定。
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:基于 esbuild
与 Rollup
,依靠浏览器自身 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 模块请求次数
。
- 模块化兼容: 如开头背景所写,现仍共存多种模块化标准代码,Vite 在预构建阶段将依赖中各种其他模块化规范(CommonJS、UMD)转换成
按需加载
- 服务器只在接受到 import 请求的时候,才会编译对应的文件,将 ESM 源码返回给浏览器,实现真正的按需加载。
缓存
- HTTP缓存: 充分利用 http 缓存做优化,依赖(不会变动的代码)部分用
max-age, immutable强缓存
,源码部分用304 协商缓存
,提升页面打开速度。 - 文件系统缓存: Vite 在预构建阶段,将构建后的依赖
缓存
到node_modules/.vite
,相关配置更改时,或手动控制时才会重新构建,以提升预构建速度。
- HTTP缓存: 充分利用 http 缓存做优化,依赖(不会变动的代码)部分用
重写模块路径
- 浏览器 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-loader
、css-loader
、less-loader
、sass-loader等 - 文件:
raw-loader
、file-loader
、url-loader
等 - 编译:
babel-loader
、ts-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
属性)。
- 在你的 webpack 配置文件中,通过
调试和优化:
- 在实际使用中,可能需要对 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 插件示例代码,这个插件会在 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 编译器的配置选项。
重命名文件: 将
.js
文件重命名为.ts
文件。TypeScript 编译器可以处理.ts
文件,并将其编译成.js
文件。更新
package.json
: 确保package.json
中的scripts
部分使用tsc
命令来编译 TypeScript 文件。例如:json"scripts": { "build": "tsc", "start": "tsc && node ." }
这里的
start
脚本首先编译 TypeScript 文件,然后使用 Node.js 运行编译后的 JavaScript 文件。逐步添加类型注解: 不要一次性为整个项目添加类型注解,这可能会非常耗时且容易出错。相反,你可以从入口文件开始,逐步为函数、变量和类添加类型注解。TypeScript 的类型推断功能可以帮助你自动推断出很多类型,因此你不需要为所有内容都显式地添加类型。
使用 TypeScript 的特性: 随着迁移的进行,你可以开始利用 TypeScript 提供的特性,如接口(Interface)、枚举(Enum)、泛型(Generics)等,来改进代码的结构和可维护性。
处理第三方库: 如果你的项目依赖了第三方库,并且这些库没有提供 TypeScript 定义文件(
.d.ts
),你可能需要安装相应的@types
包或使用其他方法来提供类型信息。例如,通过npm
安装@types/库名
。运行和测试: 在迁移过程中,不断运行和测试你的应用以确保没有引入新的错误。你可以使用现有的测试套件(如 Jest、Mocha 等)来确保功能没有改变。
配置 ESLint: 配置 ESLint 以支持 TypeScript,这有助于在编写代码时捕获潜在问题。你可以使用如
@typescript-eslint/parser
和@typescript-eslint/eslint-plugin
这样的包来增强 ESLint 对 TypeScript 的支持。持续集成和部署: 更新你的持续集成(CI)和部署流程,以确保新的 TypeScript 代码能够正确编译和部署。
文档和代码清理: 随着迁移的完成,更新项目文档以反映新的 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-prettier
和eslint-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