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 是一个拥有优秀基因的前端组件库,是因为他遵循了前端组件最基本的代码素养:

  1. 编译后的代码全部符合基本 JS 规范,换个角度来说,使用 webpack 内置基本 js loader 就能完全解析。
  2. 将 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 语句:

  1. 进行项目/组件调试时,开启 development 模式。
  2. 进行项目编译时,开启 production 模式。
  3. 进行组件编译时,开启 production 模式,且利用 webpack-node-externals 插件忽略 node_modules。

可以想像,根据第三条,如果所有组件都按照这个模式输出代码,那么 webpack 对 node_modules 编译时,只需要将所有 require 代码进行合并,不需要执行任何 loader,也不需要压缩,不需要 TreeShaking,因为这些在组件代码编译时全部已经做好了,这种构建效率几乎达到最大。

实际案例

我们拿支持 typescriptsasscss-modulesworker-loader 的场景作为案例。

我们创建三个文件 entry.tsx entry.worker.tsentry.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 的生态,因此维护性和拓展性都很强。后续再加入新功能,再也不需要到处找 babelgulp 的插件了!

3 总结

本文从 webpack 为切入点,但其实还可以从 parcelgulp 为切入点,实现前端项目、组件构建体系的统一。

不过从可定制性来看,webpack 插件生态更完善,所以笔者选择了 webpack

留下一个思考题:你的项目、组件是如何构建的呢?是用了一套代码,还是两套呢?

讨论地址是:精读《如何编译前端项目与组件》 · Issue #125 · dt-fe/weekly

如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。

精读《如何编译前端项目与组件》相关推荐

  1. 【韩松】Deep Gradient Comression_一只神秘的大金毛_新浪博客

    <Deep Gradient Compression> 作者韩松,清华电子系本科,Stanford PhD,深鉴科技联合创始人.主要的研究方向是,神经网络模型压缩以及硬件架构加速. 论文链 ...

  2. 【韩松】Deep Gradient Comression

    <Deep Gradient Compression> 作者韩松,清华电子系本科,Stanford PhD,深鉴科技联合创始人.主要的研究方向是,神经网络模型压缩以及硬件架构加速. 论文链 ...

  3. [文献阅读] 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 ...

  4. 【翻译】Batch Normalization: Accelerating Deep Network Trainingby Reducing Internal Covariate Shift

    Batch Normalization: Accelerating Deep Network Trainingby Reducing Internal Covariate Shift Sergey I ...

  5. 模型加速--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 ...

  6. 论文笔记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 提出了特征 ...

  7. 端到端图像压缩《Asymmetric Gained Deep Image Compression With Continuous Rate Adaptation》

    Asymmetric Gained Deep Image Compression With Continuous Rate Adaptation 一 简介 二 内容 2.1 目前方法的缺陷 2.2 整 ...

  8. 深度学习视频压缩1—DVC: An End-to-end Deep Video Compression Framework

    本文是第一篇端到端使用神经网络来进行视频压缩的论文, github地址:GitHub - GuoLusjtu/DVC: DVC: An End-to-end Deep Video Compressio ...

  9. 【论文阅读】Deep Compositional Captioning: Describing Novel Object Categories without Paired Training Data

    [论文阅读]Deep Compositional Captioning: Describing Novel Object Categories without Paired Training Data ...

  10. CVPR 2018 TRACA:《Context-aware Deep Feature Compression for High-speed Visual Tracking》论文笔记

    理解出错之处望不吝指正. 本文的模型叫做TRACA.模型中使用多个expert auto-encoder,在预训练阶段,每个expert auto-encoder针对一个特定类进行训练:在tracki ...

最新文章

  1. Android TV开发总结(三)构建一个TV app的焦点控制及遇到的坑
  2. P4847 银河英雄传说V2 非旋treap
  3. LIBCMTD.lib与libcpmtd冲突的解决方法。
  4. 信息学奥赛一本通 1014:与圆相关的计算 | OpenJudge NOI 1.3 09
  5. 打印机显示服务器连接错误怎么回事,打印机处于错误状态是怎么回事 打印机处于错误状态如何解决【图文详解】...
  6. h5链接加上 vconsole_淘宝bp链接让你手动也可以像软件一样
  7. macOS 访达的隐藏小技巧
  8. 微雪树莓派PICO笔记——8-PIO(可编程输入输出接口)
  9. 共享单车骑行及锁车定位方式分析
  10. 关于阿里云云虚拟主机上传php论坛代码的那点事
  11. 阿里云大幅降低CDN价格网宿蓝汛跟不跟?
  12. RocketMQ实战2
  13. 3D游戏建模:游戏建模都要用到哪些软件?
  14. pytest所有命令行标志都可以通过运行`pytest --help`来获得
  15. 什么是现金流游戏?_富爸爸_新浪博客
  16. [STL]set存储pair并自定义排序
  17. REDIS缓存集群介绍
  18. 有多少域名被漏掉了?
  19. java 滑块验证码 开源,Java AWT生成滑动验证码
  20. SEO常用外链资源站整理分享

热门文章

  1. 关于libusb-win32开发的经验
  2. vba判断是否为数字的方法小集
  3. Git撤销之世上真有后悔药
  4. 搜狗开源最新NLP研究成果,打造业内最全机器阅读理解工具包SMRC
  5. 不用下载安装,你的机器人可以直接在浏览器里跳舞丨Jupyter-ROS
  6. 循环神经网络(RNN)和LSTM初学者指南 | 入门资料
  7. 刷paper利器!不想打开PDF,这个插件自动帮你转到介绍页
  8. FIIL邬宁:AI能锦上添花,但耳机成不了下一个智能音箱
  9. leetcode刷题笔记(3)(python)
  10. SQL server 2008 中的五个系统数据库详解