精读《如何编译前端项目与组件》
1 引言
说到前端编译方案,也就是如何打包项目,如何编译组件,可选方案有很多,比如:
- 通过 webpack / parcel / gulp 构建项目。
- 通过 parcel / gulp / babel 构建组件。
如果你喜欢零配置的 parcel,那么项目和组件都可以拿它来编译。
如果你业务比较复杂,需要使用 webpack 做深度定制,那么常见组合是:项目 - webpack,组件 - gulp。
但项目与组件的编译存在异同点,不同构建工具支持的生态也存在异同点。
webpack parcel gulp 生态的区别
- babel 一般不会解析模块,也就是一般仅做代码预处理,而不会改变文件结构,也对 require、import 语句不敏感。
- webpack / parcel 主要就是解决模块化打包问题,因为浏览器还不支持(现在部分支持
type="module"
)。 - gulp 理论上可以将 babel、webpack、parcel 作为插件,但这是后来的事。历史上由于 gulp 是作为 grunt 的替代品出现,当时要解决的问题是处理浏览器兼容问题,打包 scss 或 less,做一些公共资源替换,雪碧图等,最后可以顺带合并到一个文件,但模块化功能远远比 webpack 弱,基本上只能合并,但不能 “理解模块概念”。
项目构建与组件构建的区别
项目构建的目的主要在于发布 CDN,所以大家一般不在乎构建脚本的通用性。换句话说,无论项目使用了怎样的构建方式,怎样理解 import
语句,甚至写出 require.context
等自定义语法,只要最终编译出符合浏览器规范的代码(考虑到兼容性)就足够。
组件构建的目的主要在于发布 NPM,除了 ESNext 规范会使用 Babel 编译成 ES3,大部分代码写的很收敛,甚至对 SASS 的使用都要与 Typescript 插件一起组合成复杂的 Gulp Task。
所以往往大家会对项目采取复杂的构建约束策略,而对组件的编译采取相对简单的办法,确保发布代码的通用性。
所以在大部分项目使用 webpack 支持 worker-loader 时,编写组件时发现这段代码不灵了。或者至少你得付出一些代价,因为组件的调试依然可以利用 webpack-dev-server,这时可以加上 worker-loader,但由于 gulp 没有靠谱的 worker 插件,你的组件可能需要将 Worker 引用部分原样输出,希望由引用它的项目做掉对 worker-loader 的支持。
其实这种心态是很危险的,不仅导致了组件不通用,甚至引发了各构建工具的 Tree Shaking 优化。原因就是构建组件的代码太原始,冗余的代码没有删除,甚至直接引用的 SASS 代码仍然保留,更危险的是带上了一些特殊 webpack loader 才支持的语法。
之所以说 Antd 是一个拥有优秀基因的前端组件库,是因为他遵循了前端组件最基本的代码素养:
- 编译后的代码全部符合基本 JS 规范,换个角度来说,使用 webpack 内置基本 js loader 就能完全解析。
- 将 css 代码抽离出来,这样不会强制项目对 node_modules 的代码应用 css-loader。
所以一个 靠谱的组件库 的产出文件,应该符合基本 ES 模块化规范,且不包括任何特殊语法。
但是这引发了一个新的问题:组件开发体验比项目差很多。
比如组件想使用雪碧图自动优化、想使用 worker-loader 方便快捷的调用多线程,想用自己的 css modules,甚至想把项目里一堆 PostCSS 快捷语法搬过来时怎么办?难道组件开发就不能获得与项目开发一样的体验吗?
要解决这个问题,笔者介绍一种基于 webpack 的通用构建方案,让本地调试、CDN 打包、ES6 -> ES3 转换 都使用统一套配置代码,同一套 loader。
2 精读
核心思想只有一句话:利用 webpack-node-externals 忽略 Webpack 对指向 node_modules 的 require 或 import 语句:
- 进行项目/组件调试时,开启
development
模式。 - 进行项目编译时,开启
production
模式。 - 进行组件编译时,开启
production
模式,且利用 webpack-node-externals 插件忽略 node_modules。
可以想像,根据第三条,如果所有组件都按照这个模式输出代码,那么 webpack 对 node_modules 编译时,只需要将所有 require
代码进行合并,不需要执行任何 loader,也不需要压缩,不需要 TreeShaking,因为这些在组件代码编译时全部已经做好了,这种构建效率几乎达到最大。
实际案例
我们拿支持 typescript
、sass
、css-modules
、worker-loader
的场景作为案例。
我们创建三个文件 entry.tsx
entry.worker.ts
与 entry.scss
:
entry.scss:
.container {border: 1px solid #ccc;
}.primary {color: blue;&:hover {color: green;}
}
entry.worker.ts:
import hello from "hello";const ctx: Worker = self as any;ctx.onmessage = event => {ctx.postMessage(hello());
};export default null as any;
entry.tsx:
import * as React from "react";
import styles from "./entry.scss";
import * as MyWorker from "./parser.worker";const worker = new MyWorker();export default () => (<div className={styles.container}><button className={styles.primary}>Click Me.</button></div>
);
在上面三个文件中,我们分别利用了 Typescript 编译、SCSS 编译、css-modules 解析、worker-loader 解析(利用 webpack 自动生成字符串代码并利用 Blob URL 方式载入,这样就不需要创建新文件也可以用 worker 了,也不会存在跨域问题)。
为了支持这几个特性对如上代码做调试、项目发布、组件发布,我们分别看下这三个场景该如何配置编译脚本。
本地调试
本地调试是不用区分组件与项目的。因为无论何种情况,都需要进行基本的项目编译,载入所有自定义 loader 并打成一个 bundle 包。
此时我们只要维护一份 webpack
配置即可:
const webpackConfig = {mode: "development",module: {rules: [{test: /\.worker\.tsx?$/,use: {loader: "worker-loader",options: {inline: true}},include: path.join(projectRootPath, "src")},{test: /\.tsx?$/,use: [["babel-loader",{plugins: [["babel-plugin-react-css-modules",{filetypes: {".scss": {syntax: "postcss-scss"}}}]]}],"ts-loader"],include: path.join(projectRootPath, "src")},{test: /\.scss$/,use: ["style-loader",["css-loader",{importLoaders: 1,modules: true}],"sass-loader"],include: path.join(projectRootPath, "src")}]}
};export default webpackConfig;
利用这个配置加上 webpack-dev-server
即可完成组件与项目的本地调试。
项目发布
项目发布时,需要将所有代码打入到一个 bundle 包,此时只需使用 webpack-cli
即可,对配置做如下修改:
export default {...webpackConfig,mode: "production"
};
组件发布
组件发布时,依然使用 webpack-cli
构建,但利用 webpack-node-externals
忽略对 node_modules
的解析。
import * as nodeExternals from "webpack-node-externals";export default {...webpackConfig,mode: "production",externals: [nodeExternals()]
};
此时编译的组件代码,包含了 Typescript 编译、SCSS 编译、css-modules 解析、worker-loader 解析,但所有 node_modules
代码都保持原样,比如下面的代码:
做了代码去重、按需加载、打包、压缩,但因为保持了 require
原样,因此大小只有源码体积。
同时上述三个场景都在复用 webpack 一套代码的基础上,利用了 webpack 的生态,因此维护性和拓展性都很强。后续再加入新功能,再也不需要到处找 babel
或 gulp
的插件了!
3 总结
本文从 webpack
为切入点,但其实还可以从 parcel
或 gulp
为切入点,实现前端项目、组件构建体系的统一。
不过从可定制性来看,webpack
插件生态更完善,所以笔者选择了 webpack
。
留下一个思考题:你的项目、组件是如何构建的呢?是用了一套代码,还是两套呢?
讨论地址是:精读《如何编译前端项目与组件》 · Issue #125 · dt-fe/weekly
如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。
精读《如何编译前端项目与组件》相关推荐
- 【韩松】Deep Gradient Comression_一只神秘的大金毛_新浪博客
<Deep Gradient Compression> 作者韩松,清华电子系本科,Stanford PhD,深鉴科技联合创始人.主要的研究方向是,神经网络模型压缩以及硬件架构加速. 论文链 ...
- 【韩松】Deep Gradient Comression
<Deep Gradient Compression> 作者韩松,清华电子系本科,Stanford PhD,深鉴科技联合创始人.主要的研究方向是,神经网络模型压缩以及硬件架构加速. 论文链 ...
- [文献阅读] Sparsity in Deep Learning: Pruning and growth for efficient inference and training in NN
文章目录 1. 前言 2. Overview of Sparsity in Deep Learning 2.1 Generalization 2.2 performance and model sto ...
- 【翻译】Batch Normalization: Accelerating Deep Network Trainingby Reducing Internal Covariate Shift
Batch Normalization: Accelerating Deep Network Trainingby Reducing Internal Covariate Shift Sergey I ...
- 模型加速--CLIP-Q: Deep Network Compression Learning by In-Parallel Pruning-Quantization
CLIP-Q: Deep Network Compression Learning by In-Parallel Pruning-Quantization CVPR2018 http://www.sf ...
- 论文笔记30 -- (视频压缩)【CVPR2021】FVC: A New Framework towards Deep Video Compression in Feature Space
<FVC: A New Framework towards Deep Video Compression in Feature Space> CVPR 2021 的一篇Oral 提出了特征 ...
- 端到端图像压缩《Asymmetric Gained Deep Image Compression With Continuous Rate Adaptation》
Asymmetric Gained Deep Image Compression With Continuous Rate Adaptation 一 简介 二 内容 2.1 目前方法的缺陷 2.2 整 ...
- 深度学习视频压缩1—DVC: An End-to-end Deep Video Compression Framework
本文是第一篇端到端使用神经网络来进行视频压缩的论文, github地址:GitHub - GuoLusjtu/DVC: DVC: An End-to-end Deep Video Compressio ...
- 【论文阅读】Deep Compositional Captioning: Describing Novel Object Categories without Paired Training Data
[论文阅读]Deep Compositional Captioning: Describing Novel Object Categories without Paired Training Data ...
- CVPR 2018 TRACA:《Context-aware Deep Feature Compression for High-speed Visual Tracking》论文笔记
理解出错之处望不吝指正. 本文的模型叫做TRACA.模型中使用多个expert auto-encoder,在预训练阶段,每个expert auto-encoder针对一个特定类进行训练:在tracki ...
最新文章
- Android TV开发总结(三)构建一个TV app的焦点控制及遇到的坑
- P4847 银河英雄传说V2 非旋treap
- LIBCMTD.lib与libcpmtd冲突的解决方法。
- 信息学奥赛一本通 1014:与圆相关的计算 | OpenJudge NOI 1.3 09
- 打印机显示服务器连接错误怎么回事,打印机处于错误状态是怎么回事 打印机处于错误状态如何解决【图文详解】...
- h5链接加上 vconsole_淘宝bp链接让你手动也可以像软件一样
- macOS 访达的隐藏小技巧
- 微雪树莓派PICO笔记——8-PIO(可编程输入输出接口)
- 共享单车骑行及锁车定位方式分析
- 关于阿里云云虚拟主机上传php论坛代码的那点事
- 阿里云大幅降低CDN价格网宿蓝汛跟不跟?
- RocketMQ实战2
- 3D游戏建模:游戏建模都要用到哪些软件?
- pytest所有命令行标志都可以通过运行`pytest --help`来获得
- 什么是现金流游戏?_富爸爸_新浪博客
- [STL]set存储pair并自定义排序
- REDIS缓存集群介绍
- 有多少域名被漏掉了?
- java 滑块验证码 开源,Java AWT生成滑动验证码
- SEO常用外链资源站整理分享