理解前端工程化

最初对前端的观感:眼花缭乱,各种各样的工具链以及其对应的配置文件、VS Code 插件,各种技术百家争鸣,选择众多。后来才理解前端不同于后端,后端代码的运行环境相对可控,而前端代码运行在用户设备上,所以需要兼容不同的环境,而很大一部分的工具、配置都是解决兼容性的问题 TL;DR 工程化的目的:降低开发成本提高开发效率 方式:解决前端三大件(HTML, CSS, JS)存在的问题并对其进行增强,JS 通过 Babel 而 CSS 可以通过 Sass, Less, PostCSS 等工具实现进行增强,并且保证输出向后兼容的的 JS 或 CSS 代码 模块化、包管理 分解聚合:拆分复杂任务,降低复杂度(分而治之) 模块化解决 问题:1. 全局污染 2. 依赖管理 等等 JS 模块化标准(常用):1. CommonJS (Node 标准)2. ES modules(JavaScript 官方标准模块化方案) 实现:1. 浏览器只支持 ESM 2. Node 和 构建工具 上面两种标准都支持 包 (package) 管理:npm(Node.js的标准包管理器),还有其他的 pnpm 和 yarn 等 JS 工具链 JS 语言本身一直都在繁荣地发展,经常出现新的 API 和 语言特性,但是用户的运行环境(浏览器、Node等)的版本可能是五花八门的,可能会导致报错、兼容性等的问题,所以最朴素的解决方式就是把 JS 都转换为向后兼容的老版本 JS 代码。前端的运行环境更多的是在用户这边,这点不同于后端能比较自由地调整服务器运行环境,这可能就是前端缝缝补补的原因吧,修补主要有的方式: 新 API:使用 Polyfill(填充物),为其实现缺少的 API,例如 core-js 库,就实现了 Array 的 flatMap 方法,这样在旧版本的 Node 环境,也可使用该方法 新语法:例如 Promise,对于这样的语法糖,就无法直接为其编写方法,需要转换代码,有点类似翻译,通过 regenerator 库可以将含有 Promise 的代码转换为向后兼容的代码 有很多这样的库用于解决某个特定的兼容性问题,一个个导入很麻烦,所以就有了 Babel,可以通过 Babel 的插件整合这些转换代码的库,需要安装对应的 Babel 插件依赖 并在 babel.config.js 中配置 plugins。这样还是很麻烦,所以我们可以直接使用 Babel 的预设,其中最常用的就是:@babel/preset-env,安装依赖以后完成以下的 babel.config.js 配置,即可开箱即用: ...

April 28, 2024

JavaScript 事件循环 动画演示

在前端代码中很经常看到使用 setTimeout(fn, 0),如下面代码所示,乍一看很多余,但是移除了可能会出现一些奇奇怪怪的问题。要解释这个就需要理解 事件循环(Event Loop),下面会通过一些例子和动画来辅助理解事件循环 setTimeout(() => { // 调用一些方法 }, 0) 为什么使用事件循环 JS 是单线程的(浏览器和 Node则是多线程的),为了避免 渲染主线程 阻塞,需要异步,事件循环 是异步的实现方式 浏览器在一个渲染主线程中运行一个页面中的所有 JavaScript 脚本,以及呈现布局,回流,和垃圾回收。为了避免 同步 的执行方式导致渲染主线程阻塞,使得页面卡死,所以浏览器采用异步的方式:渲染主线程将任务交给其他线程去处理,自身 立即结束 任务的执行,转而执行后续代码,当其他线程完成时,将事先传递的回调函数包装成任务,加入到对应的消息队列的末尾排队,等待渲染主线程调度执行 流程: 渲染主线程执行全局 JS,需要异步的任务放到对应的队列,如果是 setTimeout 则会有线程计时,到了指定时间会将任务放入 延时队列(并非立即执行) 渲染主线程为空时,按队列的优先级依次选择队列(最先执行微队列的任务),依次按顺序执行各个队列的任务 任务没有优先级,而消息队列有优先级,不同任务分属于不同队列:参考 W3C 规范。微队列优先级最高,接着是交互队列然后才是延时队列 常见队列: 微队列(microtask):⽤户存放需要最快执⾏的任务,优先级「最⾼」,通过 Promise.resolve().then() ⽴即把⼀个函数添加到微队列 交互队列:⽤于存放⽤户操作后产⽣的事件处理任务,优先级「⾼」 延时队列:⽤于存放计时器到达后的回调任务,优先级「中」 事件循环 下面例子来自于:《WEB前端大师课》,大块的文字描述相对没那么直观,所以用 Keynote 做了 gif 方便理解(如果有更好的做 gif 的方式可以留言告诉我) 1. JS阻碍页面渲染 JS 修改了 DOM 后,并不会马上显示在页面上,需要进行 绘制 后才会显示页面变更 <!DOCTYPE html> <html lang="en"> <head></head> <body> <h1>初始h1</h1> <button>change</button> <script> var h1 = document.querySelector('h1'); var btn = document.querySelector('button'); function delay(duration) { var start = Date.now(); while (Date.now() - start < duration) {} } btn.onclick = function () { h1.textContent = '修改h1 textContent'; delay(3000); }; </script> </body> </html> ...

April 17, 2024