├── 2024 └── test.md ├── .gitignore ├── Blogs ├── ECMAScript-2021-the-final-feature-set.md ├── how-to-build-a-cli-with-node-js.md ├── images │ ├── cn01.svg │ ├── cn02-snowpack.svg │ ├── cn03-vite.svg │ ├── cn04-wmr.svg │ └── cn05.png ├── javascript-what-is-the-meaning-of-this.md └── simpread-comparing-the-new-generation-of-build-tools.md ├── Docs └── 0000-vue3-ie11-support.md ├── README.md ├── React Conf 2018 ├── README.md ├── React Today and Tomorrow - Part I.md ├── React Today and Tomorrow - Part II.md ├── React Today and Tomorrow and 90% Cleaner React With Hooks - YouTube.srt ├── React Today and Tomorrow part1.srt ├── React Today and Tomorrow part1_zh_CN.srt ├── React Today and Tomorrow part2.srt ├── React Today and Tomorrow part2_zh_CN.srt └── React的今天和明天——第一部分.md ├── Vite-Open-Source-Friday ├── README.md ├── subtitle-en.srt └── subtitle-zh.srt └── crowdin.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.log 4 | explorations 5 | TODOs.md 6 | dist/*.gz 7 | dist/*.map 8 | dist/vue.common.min.js 9 | test/e2e/reports 10 | test/e2e/screenshots 11 | coverage 12 | RELEASE_NOTE*.md 13 | dist/*.js 14 | packages/vue-server-renderer/basic.js 15 | packages/vue-server-renderer/build.js 16 | packages/vue-server-renderer/server-plugin.js 17 | packages/vue-server-renderer/client-plugin.js 18 | packages/vue-template-compiler/build.js 19 | .vscode 20 | React Conf 2018/test.js 21 | React Conf 2018/test1.js 22 | Blogs/how-to-build-a-cli-with-node-js.md 23 | -------------------------------------------------------------------------------- /2024/test.md: -------------------------------------------------------------------------------- 1 | # test 2 | A highly customizable and feature-rich tabs component library for React, inspired by Google Chrome's sleek tab design. 3 | ## IntroDuction 4 | -------------------------------------------------------------------------------- /Blogs/ECMAScript-2021-the-final-feature-set.md: -------------------------------------------------------------------------------- 1 | # ECMAScript 2021: 最终功能集确定 2 | 3 | > * 原文地址:[ECMAScript 2021: the final feature set](https://2ality.com/2020/09/ecmascript-2021.html) 4 | > * 原文作者:[Axel Rauschmayer](http://dr-axel.de/) 5 | > * 本文永久链接:[https://github.com/Ivocin/Translation/Blogs/ECMAScript-2021-the-final-feature-set.md](https://github.com/Ivocin/Translation/blob/master/Blogs/ECMAScript-2021-the-final-feature-set.md) 6 | > * 翻译、校对:[Ivocin](https://github.com/Ivocin/) 7 | 8 | **更新于 2021-03-09:** 今天,[ES2021 候选提案](https://github.com/tc39/ecma262/releases/tag/es2021-candidate-2021-03) 发布了其最终功能集的版本。如果它能够在今年 6 月的 ECMA 大会上通过,就会成为官方的标准。本文描述了有哪些新的内容。 9 | 10 | ## 1、ECMAScript 2021 的编辑 11 | 12 | 这个版本的编辑是: 13 | 14 | * [Jordan Harband](https://twitter.com/ljharb) 15 | * [Shu-yu Guo](https://twitter.com/_shu) 16 | * [Michael Ficarra](https://twitter.com/smooshMap) 17 | * [Kevin Gibbons](https://twitter.com/bakkoting) 18 | 19 | ## 2、关于 ECMAScript 的版本说明 20 | 21 | 注意,自从 [TC39 进程](https://exploringjs.com/impatient-js/ch_history.html#tc39-process)制定以来,ECMAScript 版本的重要性就降低了很多。现在真正重要的是提案处于哪个阶段:一旦提案到了第 4 阶段,那么它就可以使用了。但是即使这样,你仍然需要检查你的引擎是否支持该功能。 22 | 23 | ## 3、ES2021 功能(第 4 阶段提案) 24 | 25 | * [`String.prototype.replaceAll`](https://exploringjs.com/impatient-js/ch_regexps.html#replace-replaceAll) (Peter Marshall, Jakob Gruber, Mathias Bynens) 26 | 27 | * [`Promise.any()`](https://exploringjs.com/impatient-js/ch_promises.html#Promise.any) (Mathias Bynens, Kevin Gibbons, Sergey Rubanov) 28 | 29 | * WeakRefs (Dean Tribble, Mark Miller, Till Schneidereit, Sathya Gunasekaran, Daniel Ehrenberg) [[proposal](https://github.com/tc39/proposal-weakrefs)] 30 | 31 | * [Logical assignment operators](https://exploringjs.com/impatient-js/ch_operators.html#logical-assignment-operators) (Justin Ridgewell, Hemanth HM) 32 | 33 | * Underscores (`_`) as separators in [number literals](https://exploringjs.com/impatient-js/ch_numbers.html#numeric-separator-number-literals) 以及 [bigint literals](https://exploringjs.com/impatient-js/ch_bigints.html#numeric-separator-bigint-literals) (Sam Goto, Rick Waldron) 34 | 35 | 36 | ## 4、常见问题 37 | 38 | ### 4.1 阶段的含义是什么? 39 | 40 | 阶段是指 “TC39 进程“的成熟阶段。更多信息可以查看“JavaScript for impatient programmers” 中的[“TC39 进程” 部分](https://exploringjs.com/impatient-js/ch_history.html#tc39-process)。 41 | 42 | ### 4.2 [我最喜欢的提案功能] 现在怎么样了? 43 | 44 | 如果你想查看不同的提案功能现在处于什么阶段,请查阅 [ ECMA-262 GitHub 仓库的 README 文件](https://github.com/tc39/ecma262/blob/master/README.md)。 45 | 46 | ### 4.3 有官方的 ECMAScript 功能列表吗?  47 | 48 | 当然,TC39 仓库列出了 [已完成提案](https://github.com/tc39/proposals/blob/master/finished-proposals.md) 以及它们是在哪个 ECMAScript 版本被引入的说明。 49 | 50 | ## 5、ES2021 的免费书籍 51 | 52 | 以下书籍包括了到 ECMAScript 2021 的 JavaScript,并且可以免费在线阅读: 53 | 54 | * [“JavaScript for impatient programmers”](https://exploringjs.com/impatient-js/) 55 | * [“Deep JavaScript”](https://exploringjs.com/deep-js/) -------------------------------------------------------------------------------- /Blogs/how-to-build-a-cli-with-node-js.md: -------------------------------------------------------------------------------- 1 | > * 原文地址:[How to build a CLI with Node.js](https://www.twilio.com/blog/how-to-build-a-cli-with-node-js) 2 | > * 原文作者:[DOMINIK KUNDEL](https://www.twilio.com/blog/author/dkundel) 3 | > * 本文永久链接:[https://github.com/Ivocin/Translation/Blogs/how-to-build-a-cli-with-node-js.md](https://github.com/Ivocin/Translation/Blogs/how-to-build-a-cli-with-node-js.md) 4 | > * 译者:[Ivocin](https://github.com/Ivocin/) 5 | > * 校对者: 6 | 7 | # 如何使用 Node.js 构建 CLI 8 | 9 | ![](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/atZ3n9vMFjjXDl_XxDtL_FCRSOt6EF0d8LnbMRCCJQUesM.width-808.png) 10 | 11 | Node.js 中内置的命令行界面(CLI)能够让你充分利用庞大的 Node.js 生态系统自动化执行重复性任务。感谢像 [`npm`](https://www.npmjs.com/) 和 [`yarn`](https://yarnpkg.com/) 这样的包管理器,它们可以在多个平台上轻松分发和使用。在本文中,我们将介绍为什么要编写 CLI、如何使用Node.js 来创建 CLI、一些实用的包以及如何发布你的新 CLI。 12 | 13 | ## 为什么使用 Node.js 创建 CLI 14 | 15 | Node.js 如此受欢迎的原因之一是其丰富的包生态系统,在 [`npm` 注册表](https://npmjs.com)中有超过 900,000 个包。通过使用 Node.js 编写 CLI,你可以利用这个生态系统,包括大量针对 CLI 的软件包。 其中: 16 | 17 | * [`inquirer`](http://npm.im/inquirer)、 [`enquirer`](http://npm.im/enquirer)  或者 `[prompts](https://npm.im/prompts)` :复杂的输入提示 18 | * [`email-prompt`](http://npm.im/email-prompt) :简化电子邮件输入提示 19 | * [`chalk`](http://npm.im/chalk) or `[kleur](https://npm.im/kleur)` :彩色输出 20 | * [`ora`](http://npm.im/ora):漂亮的加载状态动画 21 | * [`boxen`](http://npm.im/boxen):在输出周围绘制边框 22 | * [`stmux`](http://npm.im/stmux):提供 `tmux`(Terminal Multiplexing) 风格的 UI 23 | * [`listr`](http://npm.im/listr):进度列表 24 | * [`ink`](http://npm.im/ink):使用 React 构建 CLI 25 | * [`meow`](http://npm.im/meow) 或者 [`arg`](http://npm.im/arg):基础参数解析 26 | * [ `commander`](http://npm.im/commander) 和 [`yargs`](https://www.npmjs.com/package/yargs):复杂参数解析以及子命令支持 27 | * [`oclif`](https://oclif.io/) 由 Heroku 构建的可扩展 CLI 框架 (也可以选择 `[gluegun](https://infinitered.github.io/gluegun/#/)`) 28 | 29 | Additionally there are many convenient ways to consume CLIs published to `npm` from both `yarn` and `npm`. Take as an example `create-flex-plugin`, a CLI that you can use to bootstrap a plugin for [Twilio Flex](https://twilio.com/flex). You can install it as a global command: 30 | 此外,`yarn` 和 `npm` 都提供了许多方便的方法来使用发布到 `npm` 的 CLI。我们以 `create-flex-plugin` 为例,使用它可以创建 [Twilio Flex](https://twilio.com/flex) 插件。你可以将其安装为全局命令: 31 | 32 | ``` 33 | # 使用 npm: 34 | npm install -g create-flex-plugin 35 | # 使用 yarn: 36 | yarn global add create-flex-plugin 37 | # 安装成功后你就可以使用了: 38 | create-flex-plugin 39 | ``` 40 | 41 | 也可以将其作为项目的特定依赖: 42 | 43 | ``` 44 | # 使用 npm: 45 | npm install create-flex-plugin --save-dev 46 | # 使用 yarn: 47 | yarn add create-flex-plugin --dev 48 | # 安装成功后命令在 ./node_modules/.bin/create-flex-plugin 路径 49 | # 或者使用 npm 的 npx 命令: 50 | npx create-flex-plugin 51 | # 也可以使用 yarn: 52 | yarn create-flex-plugin 53 | ``` 54 | 55 | 事实上,即使没有提前安装 CLI,`npx` 命令也可以执行。只需运行 `npx create-flex-plugin` 命令,`npx` 会优先使用本地或全局安装的版本,如果找不到,它会将其下载到缓存中。 56 | 57 | 从 `npm` 6.1 版本开始,使用 `npm init` 和 `yarn` 支持使用名为`create-*` 的 CLI 来启动项目的方法。例如,对于我们的`create-flex-plugin`,我们真正需要调用的是: 58 | 59 | ``` 60 | # 使用 Node.js 61 | npm init flex-plugin 62 | # 使用 Yarn: 63 | yarn create flex-plugin 64 | ``` 65 | 66 | ## 创建你的第一个 CLI 67 | 68 | 如果你希望按照视频教程进行操作,[请查看我们在 YouTube 上的教程](https://www.youtube.com/watch?v=s2h28p4s-Xs). 69 | 70 | 现在我们介绍了你可能想要使用 Node.js 创建 CLI 的原因,现在让我们来构建一个 CLI。我们将在本教程中使用 `npm`,但是绝大部分都有相同作用的 `yarn` 命令。确保你在系统上安装了 [Node.js](https://nodejs.org/en/download/) 和 [`npm`](https://www.npmjs.com/)。 71 | 72 | 在本教程中,我们将创建一个 CLI,通过运行 `npm init @your-username/project`,创建一个你自己偏好的新项目。 73 | 74 | 通过如下命令创建一个新的 Node.js 项目: 75 | 76 | ``` 77 | mkdir create-project && cd create-project 78 | npm init --yes 79 | ``` 80 | 81 | 然后在项目的根目录中创建一个名为 `src/` 的目录,并在其中创建 `cli.js` 文件,代码如下: 82 | 83 | ``` 84 | export function cli(args) { 85 | console.log(args); 86 | } 87 | ``` 88 | 89 | 这将是我们稍后解析逻辑然后触发实际业务逻辑的部分。接下来,我们需要为 CLI 创建入口。在项目的根目录中创建一个新目录 `bin/`,并在其中创建一个名为 `create-project` 的新文件。将以下代码行放入其中: 90 | 91 | ``` 92 | #!/usr/bin/env node 93 | 94 | require = require('esm')(module /*, options*/); 95 | require('../src/cli').cli(process.argv); 96 | ``` 97 | 98 | 这个小代码片段一共做了两件事情。首先,我们引入了一个名为 `esm` 的模块,它可以让我们在其他文件中使用 `import`。这与构建 CLI 没有直接关系,但我们将在本教程中使用 [ES 模块](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import),`esm` 包允许我们这样做,而无需在没有支持的情况下转换 Node.js 版本。然后我们引入我们的 `cli.js` 文件并调用其暴露的 `cli` 函数,并传入 [`process.argv`](https://nodejs.org/api/process.html#process_process_argv) 参数,该参数是从命令行传递给该脚本的全部参数的数组。 99 | 100 | 在我们测试脚本之前,需要安装 `esm` 依赖,执行如下命令: 101 | 102 | ``` 103 | npm install esm 104 | ``` 105 | 106 | 我们还必须告知包管理器我们需要暴露的 CLI 脚本。通过在 `package.json` 中添加合适的条目来完成此操作。不要忘记更新 `description`、`name`、`keyword` 和 `main` 属性: 107 | 108 | ``` 109 | { 110 | "name": "@your_npm_username/create-project", 111 | "version": "1.0.0", 112 | "description": "A CLI to bootstrap my new projects", 113 | "main": "src/index.js", 114 | "bin": { 115 | "@your_npm_username/create-project": "bin/create-project", 116 | "create-project": "bin/create-project" 117 | }, 118 | "publishConfig": { 119 | "access": "public" 120 | }, 121 | "scripts": { 122 | "test": "echo \"Error: no test specified\" && exit 1" 123 | }, 124 | "keywords": [ 125 | "cli", 126 | "create-project" 127 | ], 128 | "author": "YOUR_AUTHOR", 129 | "license": "MIT", 130 | "dependencies": { 131 | "esm": "^3.2.18" 132 | } 133 | } 134 | ``` 135 | 136 | 我们来看 `bin` 这个属性,值为一个包含两个键值对的对象。它们定义了你的包管理器将要安装的 CLI 命令。在我们的例子中,我们为相同脚本注册了两条命令。一条命令使用我们自己用户名的 `npm` 作用域,另一条通用的 `create-project` 命令方便使用。 137 | 138 | 现在我们完成了这项工作,我们可以测试我们的脚本。要做到这一点,最简单的方法是使用 [`npm link`](https://docs.npmjs.com/cli/link.html) 命令。在项目内的终端中运行: 139 | 140 | ``` 141 | npm link 142 | ``` 143 | 144 | 这将当前项目链接到全局执行环境,因此在更新代码时无需重新运行它。运行 `npm link` 后,你就可以使用 CLI 命令了。试试运行: 145 | 146 | ``` 147 | create-project 148 | ``` 149 | 150 | 应该可以看到类似于如下的输出: 151 | 152 | ``` 153 | [ '/usr/local/Cellar/node/11.6.0/bin/node', 154 | '/Users/dkundel/dev/create-project/bin/create-project' ] 155 | ``` 156 | 157 | Note that both paths will be different for you depending on where your project lies and where you have Node.js installed. This array will be longer with every argument that you add to this. Try running: 158 | 159 | ``` 160 | create-project --yes 161 | ``` 162 | 163 | And the output should reflect the new argument: 164 | 165 | ``` 166 | [ '/usr/local/Cellar/node/11.6.0/bin/node', 167 | '/Users/dkundel/dev/create-project/bin/create-project', 168 | '--yes' ] 169 | ``` 170 | 171 | ## Parsing Arguments and Handling Input 172 | 173 | We are now ready to parse the arguments that are being passed to our script and we can start making sense of them. Our CLI will support one argument and a few options: 174 | 175 | * `[template]`: We'll support different templates out of the box. If this is not passed we'll prompt the user to select a template 176 | * `--git`: This will run `git init` to instantiate a new git project 177 | * `--install`: This will automatically install all the dependencies for the project 178 | * `--yes`: This will skip all prompts and go for default options 179 | 180 | For our project we'll use `inquirer` to prompt for missing values and the `arg` library to parse our CLI arguments. Install the missing dependencies by running: 181 | 182 | ``` 183 | npm install inquirer arg 184 | ``` 185 | 186 | Let's first write the logic that will parse our arguments into an `options` object that we can work with. Add the following code to your `cli.js`: 187 | 188 | ``` 189 | import arg from 'arg'; 190 | 191 | function parseArgumentsIntoOptions(rawArgs) { 192 | const args = arg( 193 | { 194 | '--git': Boolean, 195 | '--yes': Boolean, 196 | '--install': Boolean, 197 | '-g': '--git', 198 | '-y': '--yes', 199 | '-i': '--install', 200 | }, 201 | { 202 | argv: rawArgs.slice(2), 203 | } 204 | ); 205 | return { 206 | skipPrompts: args['--yes'] || false, 207 | git: args['--git'] || false, 208 | template: args._[0], 209 | runInstall: args['--install'] || false, 210 | }; 211 | } 212 | 213 | export function cli(args) { 214 | let options = parseArgumentsIntoOptions(args); 215 | console.log(options); 216 | } 217 | ``` 218 | 219 | Try running `create-project --yes` and you should see `skipPrompt` to turn to `true` or try passing another argument in like `create-project cli` and the `template` property should be set. 220 | 221 | Now that we are able to parse the CLI arguments, we'll need to add the functionality to prompt for the missing information as well as skip the prompt and resort to default arguments if the `--yes` flag is passed. Add the following code to your `cli.js` file: 222 | 223 | ``` 224 | import arg from 'arg'; 225 | import inquirer from 'inquirer'; 226 | 227 | function parseArgumentsIntoOptions(rawArgs) { 228 | // ... 229 | } 230 | 231 | async function promptForMissingOptions(options) { 232 | const defaultTemplate = 'JavaScript'; 233 | if (options.skipPrompts) { 234 | return { 235 | ...options, 236 | template: options.template || defaultTemplate, 237 | }; 238 | } 239 | 240 | const questions = []; 241 | if (!options.template) { 242 | questions.push({ 243 | type: 'list', 244 | name: 'template', 245 | message: 'Please choose which project template to use', 246 | choices: ['JavaScript', 'TypeScript'], 247 | default: defaultTemplate, 248 | }); 249 | } 250 | 251 | if (!options.git) { 252 | questions.push({ 253 | type: 'confirm', 254 | name: 'git', 255 | message: 'Initialize a git repository?', 256 | default: false, 257 | }); 258 | } 259 | 260 | const answers = await inquirer.prompt(questions); 261 | return { 262 | ...options, 263 | template: options.template || answers.template, 264 | git: options.git || answers.git, 265 | }; 266 | } 267 | 268 | export async function cli(args) { 269 | let options = parseArgumentsIntoOptions(args); 270 | options = await promptForMissingOptions(options); 271 | console.log(options); 272 | } 273 | ``` 274 | 275 | Save the file and run `create-project` and you should be prompted with a template selection prompt: 276 | 277 | ![](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/yfGSsiUKImPbvqn6_YlDO5TyLhCF9qT953-6KN4vStg5Wl.width-500.png) 278 | 279 | And afterwards you'll be prompted with a question whether you want to initialize `git`. Once you selected both you should see output like this printed: 280 | 281 | ``` 282 | { skipPrompts: false, 283 | git: false, 284 | template: 'JavaScript', 285 | runInstall: false } 286 | ``` 287 | 288 | Try to run the same command with `-y` and the prompts should be skipped. Instead you'll immediately see the determined options output. 289 | 290 | ![](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/JhCdQpSDKZzTbIW7stxZScvwr5ak8IZzjvlyLtPihPovb-.width-500.png) 291 | 292 | ## Writing the Logic 293 | 294 | Now that we are able to determine the respective options through prompts and command-line arguments, let's write the actual logic that will create our projects. Our CLI will write into an existing directory similar to `npm init` and it will copy all files from a `templates` directory in our project. We'll allow the target directory to be also modified via the options in case you want to re-use the same logic inside another project. 295 | 296 | Before we write the actual logic, create a `templates` directory in the root of our project and place two directories with the names `typescript` and `javascript` into it. Those are the lower-cased versions of the two values that we prompted the user to pick from. This post will use these names but feel free to use other names you'd like. Inside that directory place any `package.json` that you would like to use as the base of your project and any kind of files you want to have copied into your project. Our code will later simply copy those files into the new project. If you need some inspiration, you can check out my files at github.com/dkundel/create-project. 297 | 298 | In order to do recursive copying of the files we'll use a library called `ncp`. This library supports recursive copying cross-platform and even has a flag to force override existing files. Additionally we'll install `chalk` for colored output. To install the dependencies run: 299 | 300 | ``` 301 | npm install ncp chalk 302 | ``` 303 | 304 | We'll place all of our core logic into a `main.js` file inside the `src/` directory of our project. Create the new file and add the following code: 305 | 306 | ``` 307 | import chalk from 'chalk'; 308 | import fs from 'fs'; 309 | import ncp from 'ncp'; 310 | import path from 'path'; 311 | import { promisify } from 'util'; 312 | 313 | const access = promisify(fs.access); 314 | const copy = promisify(ncp); 315 | 316 | async function copyTemplateFiles(options) { 317 | return copy(options.templateDirectory, options.targetDirectory, { 318 | clobber: false, 319 | }); 320 | } 321 | 322 | export async function createProject(options) { 323 | options = { 324 | ...options, 325 | targetDirectory: options.targetDirectory || process.cwd(), 326 | }; 327 | 328 | const currentFileUrl = import.meta.url; 329 | const templateDir = path.resolve( 330 | new URL(currentFileUrl).pathname, 331 | '../../templates', 332 | options.template.toLowerCase() 333 | ); 334 | options.templateDirectory = templateDir; 335 | 336 | try { 337 | await access(templateDir, fs.constants.R_OK); 338 | } catch (err) { 339 | console.error('%s Invalid template name', chalk.red.bold('ERROR')); 340 | process.exit(1); 341 | } 342 | 343 | console.log('Copy project files'); 344 | await copyTemplateFiles(options); 345 | 346 | console.log('%s Project ready', chalk.green.bold('DONE')); 347 | return true; 348 | } 349 | ``` 350 | 351 | This code will export a new function called `createProject` that will first check if the specified template is indeed an available template, by checking the `read` access (`fs.constants.R_OK`) using [`fs.access`](https://nodejs.org/api/fs.html#fs_fs_access_path_mode_callback) and then copy the files into the target directory using `ncp`. Additionally we'll log some colored output saying `DONE Project ready` when we successfully copied the files. 352 | 353 | Afterwards update your `cli.js` to call the new `createProject` function: 354 | 355 | ``` 356 | import arg from 'arg'; 357 | import inquirer from 'inquirer'; 358 | import { createProject } from './main'; 359 | 360 | function parseArgumentsIntoOptions(rawArgs) { 361 | // ... 362 | } 363 | 364 | async function promptForMissingOptions(options) { 365 | // ... 366 | } 367 | 368 | export async function cli(args) { 369 | let options = parseArgumentsIntoOptions(args); 370 | options = await promptForMissingOptions(options); 371 | await createProject(options); 372 | } 373 | ``` 374 | 375 | To test our progress, create a new directory somewhere like `~/test-dir` on your system and run inside it the command using one of your templates. For example: 376 | 377 | ``` 378 | create-project typescript --git 379 | ``` 380 | 381 | You should see a confirmation that the project has been created and the files should be copied over to the directory. 382 | 383 | ![](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/hJhXfoE6BvWWHNFzomCs4YD5D4fLUWQqHmR-am2wzAGszS.width-500.png) 384 | 385 | Now there are two more steps we want our CLI to do. We want to optionally initialize `git` and install our dependencies. For this we'll use three more dependencies: 386 | 387 | * [`execa`](http://npm.im/execa) which allows us to easily run external commands like `git` 388 | * [`pkg-install`](http://npm.im/pkg-install) to trigger either `yarn install` or `npm install` depending on what the user uses 389 | * [`listr`](http://npm.im/listr) which let's us specify a list of tasks and gives the user a neat progress overview 390 | 391 | Install the dependencies by running: 392 | 393 | ``` 394 | npm install execa pkg-install listr 395 | ``` 396 | 397 | Afterwards update your `main.js` to contain the following code: 398 | 399 | ``` 400 | import chalk from 'chalk'; 401 | import fs from 'fs'; 402 | import ncp from 'ncp'; 403 | import path from 'path'; 404 | import { promisify } from 'util'; 405 | import execa from 'execa'; 406 | import Listr from 'listr'; 407 | import { projectInstall } from 'pkg-install'; 408 | 409 | const access = promisify(fs.access); 410 | const copy = promisify(ncp); 411 | 412 | async function copyTemplateFiles(options) { 413 | return copy(options.templateDirectory, options.targetDirectory, { 414 | clobber: false, 415 | }); 416 | } 417 | 418 | async function initGit(options) { 419 | const result = await execa('git', ['init'], { 420 | cwd: options.targetDirectory, 421 | }); 422 | if (result.failed) { 423 | return Promise.reject(new Error('Failed to initialize git')); 424 | } 425 | return; 426 | } 427 | 428 | export async function createProject(options) { 429 | options = { 430 | ...options, 431 | targetDirectory: options.targetDirectory || process.cwd() 432 | }; 433 | 434 | const templateDir = path.resolve( 435 | new URL(import.meta.url).pathname, 436 | '../../templates', 437 | options.template 438 | ); 439 | options.templateDirectory = templateDir; 440 | 441 | try { 442 | await access(templateDir, fs.constants.R_OK); 443 | } catch (err) { 444 | console.error('%s Invalid template name', chalk.red.bold('ERROR')); 445 | process.exit(1); 446 | } 447 | 448 | const tasks = new Listr([ 449 | { 450 | title: 'Copy project files', 451 | task: () => copyTemplateFiles(options), 452 | }, 453 | { 454 | title: 'Initialize git', 455 | task: () => initGit(options), 456 | enabled: () => options.git, 457 | }, 458 | { 459 | title: 'Install dependencies', 460 | task: () => 461 | projectInstall({ 462 | cwd: options.targetDirectory, 463 | }), 464 | skip: () => 465 | !options.runInstall 466 | ? 'Pass --install to automatically install dependencies' 467 | : undefined, 468 | }, 469 | ]); 470 | 471 | await tasks.run(); 472 | console.log('%s Project ready', chalk.green.bold('DONE')); 473 | return true; 474 | } 475 | ``` 476 | 477 | This will run `git init` whenever `--git` is passed or the user chooses `git` in the prompt and it will run `npm install` or `yarn` whenever the user passes `--install`, otherwise it will skip the task with a message informing the user to pass `--install` if they want automatic install. 478 | 479 | Give it a try by deleting your existing test folder first and creating a new one. Then run: 480 | 481 | ``` 482 | create-project typescript --git --install 483 | ``` 484 | 485 | You should see now both a `.git` folder in your folder indicating that `git` has been initialized and a `node_modules` folder with your dependencies that were specified in the `package.json` installed. 486 | 487 | ![](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/_vaH2-wgo0HxH7o6NVcQwlb-h7MihVzFVO_6MsTcw71qB8.width-500.png) 488 | 489 | Congratulations you got your first CLI ready to go! 490 | 491 | ![](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/original_images/Hlw2ROeuFERjiDODTmaGu6z7YtmcGN0yTWiPeRLRRr6EENm7HJ8s3laAGZGdd54NefTAJPut5nZCDe) 492 | 493 | If you want to make your code consumable as an actual module so that others can reuse your logic in their code, we'll have to add an `index.js` file to our `src/` directory that exposes the content from `main.js`: 494 | 495 | ``` 496 | require = require('esm')(module); 497 | require('../src/cli').cli(process.argv); 498 | ``` 499 | 500 | ## What's Next? 501 | 502 | Now that you have your CLI code ready there are a few ways you can go from here. If you just want to use this yourself and don't want to share it with the world you can just keep on going along the path of using `npm link`. In fact try running `npm init project` and it should trigger your code. 503 | 504 | If you want to share your templates with the world either push your code to GitHub and consume it from there or even better push it as a scoped package to the `npm` registry with [`npm publish`](https://docs.npmjs.com/cli/publish). Before you do so, you should make sure to add a `files` key in your `package.json` to specify which files should be published. 505 | 506 | ``` 507 | }, 508 | "files": [ 509 | "bin/", 510 | "src/", 511 | "templates/" 512 | ] 513 | } 514 | ``` 515 | 516 | If you want to check which files will be published, run `npm pack --dry-run` and check the output. Afterwards use `npm publish` to publish your CLI. You can find my project under [`@dkundel/create-project`](http://npm.im/@dkundel/create-project) or try run `npm init @dkundel/project`. 517 | 518 | There's also lots of functionality that you can add. In my case I added some additional dependencies that will create a `LICENSE`, `CODE_OF_CONDUCT.md` and `.gitignore` file for me. You can [find the source code for it on GitHub](http://github.com/dkundel/create-project) or check out some of the libraries mentioned above for some additional functionality. If you have a library I didn't list and you believe it should totally be in the list or if you want to show me your own CLI, feel free to send me a message! 519 | 520 | * Email: [dkundel@twilio.com](mailto:dkundel@twilio.com) 521 | * Twitter: [@dkundel](https://twitter.com/dkundel?lang=en) 522 | * GitHub: [dkundel](https://github.com/dkundel) 523 | * [dkundel.com](https://dkundel.com/) 524 | 525 | 526 | -------------------------------------------------------------------------------- /Blogs/images/cn01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Blogs/images/cn02-snowpack.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Blogs/images/cn03-vite.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Blogs/images/cn04-wmr.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Blogs/images/cn05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ivocin/Translation/937be71e288e70f61c9eeab77fb34a9bf78b3e60/Blogs/images/cn05.png -------------------------------------------------------------------------------- /Blogs/javascript-what-is-the-meaning-of-this.md: -------------------------------------------------------------------------------- 1 | # [译]JavaScript: 带你彻底搞懂 this 2 | 3 | > * 原文地址:[JavaScript: What is the meaning of this?](https://web.dev/javascript-this/) 4 | > * 原文作者:[Jake Archibald](https://web.dev/authors/jakearchibald/) 5 | > * 原文发布时间:2021-03-08 6 | > * 本文永久链接:[https://github.com/Ivocin/Translation/Blogs/javascript-what-is-the-meaning-of-this.md](https://github.com/Ivocin/Translation/blob/master/Blogs/javascript-what-is-the-meaning-of-this.md) 7 | > * 翻译、校对:[Ivocin](https://github.com/Ivocin/) 8 | 9 | 10 | 搞明白 JavaScript 中 `this` 的值有时候会很棘手,本文带你彻底搞懂 `this`。 11 | 12 | JavaScript 的 `this` 往往会成为许多笑话的笑柄,因为它相当复杂。然而,我发现很多开发人员为了避免处理 `this`,用了更加复杂和特定领域的处理。如果你对 `this` 还不熟悉,希望本文能帮助到你。下面进入我的 `this` 指南。 13 | 14 | 我将从最具体的情况开始,以最不具体的情况结束,本文的结构类似与一个大的 `if (…) … else if () … else if (…) …` 语句,所以你可以直接跳转到匹配你代码情况的章节。 15 | 16 | 17 | 18 | 1. [如果是箭头函数](#arrow-functions) 19 | 2. [否则,如果使用 `new` 调用函数/类](#new) 20 | 3. [否则, 函数被 `bind` 了 `this`](#bound) 21 | 4. [否则, 如果 `this` 在调用时设置](#call-apply) 22 | 5. [否则, 如果使用父对象(`parent.func()`) 调用函数](#object-member) 23 | 6. [否则, 如果函数或者其父作用域使用严格模式](#strict) 24 | 7. [否则](#otherwise) 25 | 26 | ## 如果是箭头函数: 27 | 28 | ```js 29 | const arrowFunction = () => { 30 | console.log(this); 31 | }; 32 | ``` 33 | 34 | 在这种情况下,`this` 的值**永远**与父作用域的 `this` 相同。 35 | 36 | ```js 37 | const outerThis = this; 38 | 39 | const arrowFunction = () => { 40 | // 永远输出 `true`: 41 | console.log(this === outerThis); 42 | }; 43 | ``` 44 | 45 | 箭头函数非常优秀,因为其内部 `this` 的值无法被改变,它与外部的 `this` **永远** 相同。 46 | 47 | ### 其他例子 48 | 49 | 使用箭头函数, `this` 的值**无法**被 [`bind`](#bound) 改变: 50 | 51 | ```js 52 | // 输出为 `true` - bind `this` 被忽略: 53 | arrowFunction.bind({foo: 'bar'})(); 54 | ``` 55 | 56 | 使用箭头函数,`this` 的值**无法**被 [`call` 或 `apply`](#call-apply) 改变: 57 | 58 | ```js 59 | // 输出为 `true` - call `this` 被忽略: 60 | arrowFunction.call({foo: 'bar'}); 61 | // 输出为 `true` - apply `this` 被忽略: 62 | arrowFunction.apply({foo: 'bar'}); 63 | ``` 64 | 65 | 使用箭头函数,`this` 的值**无法**通过将函数作为另一个对象的成员变量来调用改变: 66 | 67 | ```js 68 | const obj = {arrowFunction}; 69 | // 输出为 `true` - 父对象被忽略: 70 | obj.arrowFunction(); 71 | ``` 72 | 73 | 使用箭头函数,`this` 的值**无法**通过将函数作为构造函数来调用而改变: 74 | 75 | ```js 76 | // TypeError: arrowFunction is not a constructor 77 | new arrowFunction(); 78 | ``` 79 | 80 | ### “绑定” 实例方法 81 | 82 | 对于实例方法,如果想要确保 `this` 始终指向类实例,最好的方法是使用箭头函数和 [class fields](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields): 83 | 84 | ```js 85 | class Whatever { 86 | someMethod = () => { 87 | // 永远是 Whatever 的实例: 88 | console.log(this); 89 | }; 90 | } 91 | ``` 92 | 93 | 这个模式在将实例方法作为组件内的事件监听器时十分有用(如 React 组件或者 Web Components)。 94 | 95 | 上面的代码貌似打破了“`this` 的值**永远**与父作用域的 `this` 相同”的规则,但是如果你将 class fields 看作将对象设置到构造函数的语法糖,那么就好理解了: 96 | 97 | ```js 98 | class Whatever { 99 | someMethod = (() => { 100 | const outerThis = this; 101 | return () => { 102 | // 永远输出 `true`: 103 | console.log(this === outerThis); 104 | }; 105 | })(); 106 | } 107 | 108 | // …大致等于: 109 | 110 | class Whatever { 111 | constructor() { 112 | const outerThis = this; 113 | this.someMethod = () => { 114 | // Always logs `true`: 115 | console.log(this === outerThis); 116 | }; 117 | } 118 | } 119 | ``` 120 | 121 | 其他模式包括在构造函数中绑定现有函数,或在构造函数中对函数赋值。如果你由于某种原因不能使用 class fields,则在构造函数中对函数赋值是一种合理的选择: 122 | 123 | ```js 124 | class Whatever { 125 | constructor() { 126 | this.someMethod = () => { 127 | // … 128 | }; 129 | } 130 | } 131 | ``` 132 | 133 | ## 否则,如果使用 `new` 调用函数/类: 134 | 135 | ``` 136 | new Whatever(); 137 | ``` 138 | 139 | 上面代码会调用 `Whatever`(或者它的构造函数,如果它是类),并将 `this` 设置为 `Object.create(Whatever.prototype)` 的结果。 140 | 141 | ```js 142 | class MyClass { 143 | constructor() { 144 | console.log( 145 | this.constructor === Object.create(MyClass.prototype).constructor, 146 | ); 147 | } 148 | } 149 | 150 | // 输出为 `true`: 151 | new MyClass(); 152 | ``` 153 | 154 | 使用旧式的构造函数结果也一样: 155 | 156 | ```js 157 | function MyClass() { 158 | console.log( 159 | this.constructor === Object.create(MyClass.prototype).constructor, 160 | ); 161 | } 162 | 163 | // 输出 `true`: 164 | new MyClass(); 165 | ``` 166 | 167 | ### 其他例子 168 | 169 | 使用 `new` 调用,`this` 的值**无法**被 [`bind`](#bound) 改变: 170 | 171 | ```js 172 | const BoundMyClass = MyClass.bind({foo: 'bar'}); 173 | // 输出为 `true` - bind `this` 被忽略: 174 | new BoundMyClass(); 175 | ``` 176 | 177 | 使用 `new` 调用,`this` 的值**无法**通过将函数作为另一个对象的成员变量来调用改变: 178 | 179 | ```js 180 | const obj = {MyClass}; 181 | // 输出为 `true` - 父对象被忽略: 182 | new obj.MyClass(); 183 | ``` 184 | 185 | ## 否则, 函数被 `bind` 了 `this`: 186 | 187 | ```js 188 | function someFunction() { 189 | return this; 190 | } 191 | 192 | const boundObject = {hello: 'world'}; 193 | const boundFunction = someFunction.bind(boundObject); 194 | ``` 195 | 196 | 每当 `boundFunction` 被调用,它的 `this` 值就是通过 `bind` 传入的值(`boundObject`)。 197 | 198 | ```js 199 | // 输出 `false`: 200 | console.log(someFunction() === boundObject); 201 | // 输出 `true`: 202 | console.log(boundFunction() === boundObject); 203 | ``` 204 | 205 | ----- 206 | 207 | **Warning**: 避免使用 `bind` 将函数绑定到其外部的 `this`。使用[箭头函数](#arrow-functions)替代,因为这样 `this` 可以在函数声明就能清楚地看出来,而非在后续代码中看到。 208 | 不要使用 `bind` 设置 `this` 为与父对象无关的值;这通常是出乎意料的,这也是 `this` 获得如此糟糕名声的原因。考虑将值作为参数传递;它更加明确,并且可以使用箭头函数。 209 | 210 | 211 | 212 | ### 其他例子 213 | 214 | 使用 `bind` 调用函数,`this` 的值**无法**被 [`call` 或 `apply`](#call-apply) 改变: 215 | 216 | ```js 217 | // 输出为 `true` - call `this` 被忽略: 218 | console.log(boundFunction.call({foo: 'bar'}) === boundObject); 219 | // 输出为 `true` - apply `this` 被忽略: 220 | console.log(boundFunction.apply({foo: 'bar'}) === boundObject); 221 | ``` 222 | 223 | 使用 `bind` 调用函数,`this` 的值**无法**通过将函数作为另一个对象的成员变量来调用改变: 224 | 225 | ```js 226 | const obj = {boundFunction}; 227 | // Logs `true` - parent object is ignored: 228 | console.log(obj.boundFunction() === boundObject); 229 | ``` 230 | 231 | ## 否则, 如果 `this` 在调用时设置: 232 | 233 | ```js 234 | function someFunction() { 235 | return this; 236 | } 237 | 238 | const someObject = {hello: 'world'}; 239 | 240 | // 输出 `true`: 241 | console.log(someFunction.call(someObject) === someObject); 242 | // 输出 `true`: 243 | console.log(someFunction.apply(someObject) === someObject); 244 | ``` 245 | 246 | `this` 的值就是传递给 `call`/`apply` 的对象。 247 | 248 | 249 | ---- 250 | 251 | **警告**: 不要使用 `bind` 设置 `this` 为与父对象无关的值;这通常是出乎意料的,这也是 `this` 获得如此糟糕名声的原因。考虑将值作为参数传递;它更加明确,并且可以使用箭头函数。 252 | 253 | 不幸的是,`this` 可能会被如 DOM 事件监听器之类的函数设置为其他值,使用它会导致代码难以理解: 254 | 255 | 不要这样: 256 | 257 | ```js 258 | element.addEventListener('click', function (event) { 259 | // 输出 `element`, 因为 DOM 将 `this` 设置为 260 | // click 绑定的元素上 261 | console.log(this); 262 | }); 263 | ``` 264 | 265 | 我会避免在上述场景中使用 `this`,我会这样使用: 266 | 267 | ```js 268 | element.addEventListener('click', (event) => { 269 | // 理想情况, 从父作用域获得它: 270 | console.log(element); 271 | // 但是如果你不想这么做,可以从 event 对象获取它: 272 | console.log(event.currentTarget); 273 | }); 274 | ``` 275 | 276 | ## 否则, 如果使用父对象(`parent.func()`) 调用函数: 277 | 278 | ```js 279 | const obj = { 280 | someMethod() { 281 | return this; 282 | }, 283 | }; 284 | 285 | // 输出 `true`: 286 | console.log(obj.someMethod() === obj); 287 | ``` 288 | 289 | 在这种情况下,函数作为 `obj` 的成员变量被调用,所以 `this` 指向 `obj`。这是在调用时发生的,因此如果没有使用父对象调用,或者使用一个不同的父对象调用,该连接会断开: 290 | 291 | ```js 292 | const {someMethod} = obj; 293 | // 输出 `false`: 294 | console.log(someMethod() === obj); 295 | 296 | const anotherObj = {someMethod}; 297 | // 输出 `false`: 298 | console.log(anotherObj.someMethod() === obj); 299 | // 输出 `true`: 300 | console.log(anotherObj.someMethod() === anotherObj); 301 | ``` 302 | 303 | `someMethod() === obj` 为 `false`,因为 `someMethod` **不是** 作为 `obj` 的成员变量被调用的。尝试执行以下操作时,可能会遇到此陷阱: 304 | 305 | ```js 306 | const $ = document.querySelector; 307 | // TypeError: Illegal invocation 308 | const el = $('.some-element'); 309 | ``` 310 | 311 | 这个报错是因为 `querySelector` 实现会寻找它的 `this` 值,并期望其某种 DOM 节点,上述代码破坏了连接。为了正确实现上述功能,可以这样写: 312 | 313 | ```js 314 | const $ = document.querySelector.bind(document); 315 | // 或者: 316 | const $ = (...args) => document.querySelector(...args); 317 | ``` 318 | 319 | 有趣的事实:并不是所有的 API 都在其内部使用了 `this`。Console 方法(如 `console.log`)就改为了不使用 `this` 引用,因此 `log` 方法不需要绑定 `console`。 320 | 321 | --- 322 | 323 | **警告**: 不要使用 `bind` 设置 `this` 为与父对象无关的值;这通常是出乎意料的,这也是 `this` 获得如此糟糕名声的原因。考虑将值作为参数传递;它更加明确,并且可以使用箭头函数。 324 | 325 | ## 否则, 如果函数或者其父作用域使用严格模式: 326 | 327 | ```js 328 | function someFunction() { 329 | 'use strict'; 330 | return this; 331 | } 332 | 333 | // 输出 `true`: 334 | console.log(someFunction() === undefined); 335 | ``` 336 | 337 | 在这种情况下,`this` 的值是 `undefined`。如果父作用域处于[严格模式](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)(而且所有模块都处在严格模式),则不需要在函数内部使用 `'use strict'`。 338 | 339 | --- 340 | 341 | **警告**: 不要依赖这个。我的意思是,有更简单的方式来得到一个 `undefined` 值 😀。 342 | 343 | ## 否则: 344 | 345 | ```js 346 | function someFunction() { 347 | return this; 348 | } 349 | 350 | // 输出 `true`: 351 | console.log(someFunction() === globalThis); 352 | ``` 353 | 354 | 在这种情况下,`this` 的值与 `globalThis` 相同。 355 | 356 | --- 357 | 358 | 很多人(包括我)把 `globalThis` 称为 `global` 对象,但这不是 100% 技术正确的。在 [Mathias Bynens with the details](https://mathiasbynens.be/notes/globalthis#terminology) 中,有它为什么叫 `globalThis` 而不是 `global` 的原因。 359 | 360 | --- 361 | 362 | **警告**: 避免使用 `this` 指向 `global` 对象(对,我仍然这么叫它)。改为使用[`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis),它更加明确。 363 | 364 | 365 | ## 结语 366 | 367 | 好了,这就是我理解的 `this` 的全部了。如果有任何问题或者我有所遗漏,请[给我发推](https://twitter.com/jaffathecake)。 368 | 369 | 感谢 [Mathias Bynens](https://twitter.com/mathias), [Ingvar Stepanyan](https://twitter.com/RReverser), 和 [Thomas Steiner](https://twitter.com/tomayac) 的审阅。 370 | 371 | 372 | -------------------------------------------------------------------------------- /Blogs/simpread-comparing-the-new-generation-of-build-tools.md: -------------------------------------------------------------------------------- 1 | # Comparing the New Generation of Build Tools 2 | 3 | A bunch of new developer tools have landed in the past year and they are biting at the heels of the tools that have dominated front-end development over the last few years, including webpack, Babel, Rollup, Parcel, create-react-app. 4 | 5 | These new tools aren’t designed to perform the exact same function, and each has different things they’re trying to achieve and features to get there. Despite their differences, these tools do share a common goal: **improve the developer experience.** 6 | 7 | 8 | 9 | > #### Table of contents 10 | > 11 | > 1. [esbuild](#esbuild) 12 | > 2. [Snowpack](#snowpack) 13 | > 3. [Vite](#vite) 14 | > 4. [wmr](#wmr) 15 | > 5. [Feature comparison](#feature-comparison) 16 | > 6. [Wrapping up](#wrapping-up) 17 | 18 | * * * 19 | 20 | Specifically, I’d like to evaluate each one, outlining what they do, why we need them, and their use cases. I realize that comparisons aren’t always fair. Again, it’s not like any of the things we’re looking at in this article are direct competitors. In fact, Snowpack and Vite actually _use_ esbuild under the hood for certain tasks. Our goal is more to get a better view of the landscape of developer tools that run tasks to make our jobs easier. This way, we see what options are out there and how they stack up, so we can make the best choices when we need them. 21 | 22 | Of course, all of this will be colored by my experience using React and Preact. I’m more familiar with these ~frameworks~ libraries, but we’ll look at their support for other front-end frameworks too. 23 | 24 | There have been a whole lot of great articles, streams and podcasts about these new developer tools. There are a couple of ShopTalk Show episodes I’d recommend for more context: [Episode 454](https://shoptalkshow.com/454/) discusses Vite and [Episode 448](https://shoptalkshow.com/448/) features the creators of wmr and Snowpack. Something that stands out from these episodes is that a huge amount of work has gone into building these tools to modernize our developer environments. 25 | 26 | ### [](#why-are-these-tools-all-arriving-now)Why are these tools all arriving now? 27 | 28 | In part, I think these tools are arriving as a reaction to JavaScript tooling fatigue — something captured nicely in [this article about learning JavaScript back in 2016](https://hackernoon.com/how-it-feels-to-learn-javascript-in-2016-d3a717dd577f). They also fill a missing middle ground between writing a single vanilla JavaScript file, and having to download 200 megabytes of tooling dependencies before you’ve written a line of your own code. They come batteries-included without the dependency list, and are part of a trend of [collapsing layers](https://www.swyx.io/js-third-age/) in the JavaScript ecosystem. 29 | 30 | Snowpack, Vite, and wmr have all been enabled by [native JavaScript modules](https://css-tricks.com/life-with-esm/) in the browser. Back in 2018, [Firefox 60 was released](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/60#javascript) with ECMAScript 2015 modules enabled by default. Since then, all major browser engines have supported native JavaScript modules. Node.js also shipped with [native JavaScript modules](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V13.md#13.2.0) in November 2019. We’re still finding out what possibilities native JavaScript modules unlock today in 2021. 31 | 32 | ### [](#how-are-these-different-from-existing-tools)How are these different from existing tools? 33 | 34 | Whether we use webpack, Rollup, or Parcel for a development server, the tool bundles our entire codebase from our source code and a `node_modules` folder, runs these through build processes —  like Babel, TypeScript, or PostCSS — then pushes the bundled code to our browser. This all takes work, and can slow development servers to a crawl in larger codebases, even after all the work that’s gone into caching and optimizing. 35 | 36 | Snowpack, Vite, and wmr development servers don’t follow this model. Instead, they wait until the browser finds an import statement and makes an HTTP request for the module. Only after this request is made will the tool apply transforms to the requested module and any leaf nodes in the module’s import tree, then serve these to the browser. This speeds things up a lot as there’s less work in the process of pushing to a dev server. 37 | 38 | You’ll notice [esbuild](https://esbuild.github.io/) missing from this picture. It’s a _bundler_ first and foremost. It doesn’t side-step bundling the way the other tools do. Instead, esbuild processes code [extremely fast](https://esbuild.github.io/faq/#benchmark-details) by [avoiding expensive transformations, leveraging parallelization and using the Go language](https://esbuild.github.io/faq/#why-is-esbuild-fast). 39 | 40 | ### [](#the-experiment)The experiment 41 | 42 | I took one of the the example apps from the React docs and rebuilt it with each tool covered in this article. The project I went with was [Snap Shot](https://github.com/Yog9/SnapShot) by [Yogita Verma](https://yog9.github.io/portfolio/). Here’s a [link to the original repo](https://github.com/Yog9/SnapShot), and a [link to my repo with the four versions of Snap Shot](https://github.com/Elliotclyde/build-tool-test), each using a different build tool. We’ll compare the output of each build step later. Rebuilding this app allowed me to test out the developer experience of pulling some pretty standard React dependencies into the tools, including [React Router](https://reactrouter.com/) and [a](https://github.com/axios/axios)[xios](https://github.com/axios/axios). 43 | 44 | ### [](#comparable-features)Comparable features 45 | 46 | Before we get into the specifics of each individual tool, they _all_ support the following features out of the box (to varying degrees): 47 | 48 | * First-class support for native JavaScript modules 49 | * TypeScript compilation (but not type checking) 50 | * JSX 51 | * Plugin API for extensibility 52 | * A built-in development server 53 | * CSS bundling and support for CSS-in-JS libraries 54 | 55 | All of these tools can compile TypeScript into JavaScript, but will do so _even if there are type errors_. For proper type checking you would need to install TypeScript and run `tsc --noEmit` on your root JavaScript file, or alternatively, use editor plugins to watch for type errors. 56 | 57 | OK, let’s take look at each tool. 58 | 59 | ### [](#esbuild)esbuild 60 | 61 | ![](./images/cn01.svg) 62 | 63 | esbuild was created by [Evan Wallace](https://github.com/evanw) (CTO of [Figma](https://www.figma.com/)). Its main feature is that it provides a build step 10×-100× faster than Node-based bundlers (by their own benchmarks). It doesn’t provide many of the developer conveniences you might find in something like create-react-app. But there are more and more esbuild starters popping up that fills those gaps, including [create-react-app-esbuild](https://github.com/pradel/create-react-app-esbuild), [estrella](https://github.com/rsms/estrella) and [Snowpack](https://www.snowpack.dev/), which uses esbuild for its build step. 64 | 65 | esbuild is very new. It hasn’t yet reached a 1.0 version and isn’t quite ready for production use — but it’s not far off. It gives you intuitive JavaScript and command line APIs with smart defaults. 66 | 67 | #### [](#use-cases)Use cases 68 | 69 | esbuild is a complete game-changer in the bundler world. It’s going to be most useful in large codebases where the speed difference between esbuild and node bundlers gets multiplied. When esbuild hits 1.0 it’s going to be _very_ useful in big production sites, and will save teams a whole lot of time waiting for builds to complete. Unfortunately, big production sites will have to wait until esbuild becomes stable. In the meantime it’ll just be good to add some speed to your bundling in side projects. 70 | 71 | esbuild’s lightening fast speed will be a bonus for any kind of work that you’re doing. Less time spent waiting for builds to run is always going to be good for developer experience! This considered, if you’re prototyping quick applications you might want to start with something more high level than esbuild — otherwise, you’ll need to spend some time pulling in dependencies and configuring your environment before you get conveniences we expect in the JavaScript ecosystem. Also, if you want to minimize the size of your bundle as much as possible you may want to use Rollup and terser, which will produce slightly smaller bundle sizes. 72 | 73 | #### [](#setup)Setup 74 | 75 | I decided to start a React project in esbuild in a naïve way: npm installing esbuild, React and ReactDOM. I created a `src/app.jsx` file and a `dist/index.html` file. Then, I used the following command to compile the app into a `dist/bundle.js` file: 76 | 77 | ```shell 78 | ./node_modules/.bin/esbuild src/app.jsx --bundle --platform=browser --outfile=dist/bundle.js 79 | ``` 80 | 81 | When I hosted and opened `index.html` in the browser, I was met with the “white screen of death” and an “Uncaught ReferenceError: process is not defined” console error. [Both the docs and the CLI explain exactly what you need to do to prevent this](https://esbuild.github.io/getting-started/#bundling-for-the-browser) but it might be a bit of a “gotcha” for beginners, because it requires an extra argument when bundling React: 82 | 83 | ```shell 84 | --define:process.env.NODE_ENV=\"production\" 85 | ``` 86 | 87 | Or, if you’re including esbuild in npm scripts written like this to escape the quotes: 88 | 89 | ```shell 90 | --define:process.env.NODE_ENV=\\\"production\\\" 91 | ``` 92 | 93 | This `define` argument is needed for any library bundled for the browser that expects node environment variables. Vue 2.0 also expects these. You won’t have the same problem with Preact because it doesn’t expect any environment variables and ships ready for the browser by default. 94 | 95 | After I ran the command with the `define` argument, my “Hello world” React app was working perfectly. JSX works out of the box with `.jsx` files. That said, React needs to be manually imported and then JSX is converted to the `React.createElement`. However, there are ways to add [auto imports in JSX and/or configure JSX for](https://esbuild.github.io/content-types/#jsx) [P](https://esbuild.github.io/content-types/#jsx)[react](https://esbuild.github.io/content-types/#jsx). 96 | 97 | #### [](#usage)Usage 98 | 99 | esbuild provides a `--serve` option for a development server. This bypasses the filesystem and serves modules straight from memory, ensuring that the browser doesn’t pull older versions of modules. However, it doesn’t include live/hot reloading, so you will find yourself refreshing the browser after saving which isn’t an ideal experience. 100 | 101 | I decided to use the newly-released [**watch**](https://esbuild.github.io/api/#watch) [feature](https://esbuild.github.io/api/#watch).This tells esbuild to recompile code every time a source file is saved. But we still need a server to see our saved changes. We can pull in a development server package, such as Luke Jackson’s [servor](https://www.npmjs.com/package/servor): 102 | 103 | ```shell 104 | npm install servor --save-dev 105 | ``` 106 | 107 | Then we can use the esbuild Javascript API to start as server and run esbuild’s watch mode at the same time. Let’s create a file at the root of our project called `watch.js`: 108 | 109 | ```javascript 110 | // watch.js 111 | const esbuild = require("esbuild"); 112 | const servor = require("servor"); 113 | 114 | esbuild.build({ 115 | // pass any options to esbuild here... 116 | entryPoints: ["src/app.jsx"], 117 | outdir: "dist", 118 | define: { "process.env.NODE_ENV": '"production"' }, 119 | watch: true, 120 | }); 121 | 122 | async function serve(){ 123 | console.log("running server from: http://localhost:8080/"); 124 | await servor({ 125 | // pass any options to servor here... 126 | browser:true, 127 | root: "dist", 128 | port: 8080, 129 | }); 130 | } 131 | 132 | serve(); 133 | ``` 134 | 135 | Now run `node watch.js` in the command line. This gives us a nice dev server, though again, it doesn’t give us hot module replacement or fast refresh (i.e., your client-side state won’t be preserved). But this was enough for my testing needs. 136 | 137 | Even though we’re rebundling our entire application every time we save a file, we’d need to have a pretty massive application before esbuild slows down. After I set up this tooling, I was getting instant feedback from changes. My computer uses an intel i7 from 2012, so it certainly isn’t a top-of-the-line machine. 138 | 139 | If you need a preconfigured version of esbuild with live reload and some React defaults, you can clone [this repo](https://github.com/Elliotclyde/esbuild-react-starter). 140 | 141 | #### [](#supported-files)Supported files 142 | 143 | esbuild can import CSS in JavaScript if that’s your style. It will compile CSS into an output file with the same name as your main output JavaScript file. It can also bundle CSS `@import` statements by default. There is no support for [CSS Modules](https://css-tricks.com/css-modules-part-1-need/), but there are plans for it. 144 | 145 | There is a [growing community of plugins for esbuild](https://github.com/esbuild/community-plugins). For example, there are plugins available for [Vue single file components](https://github.com/few-far/esbuild-vue-plugin), and [Svelte components](https://github.com/EMH333/esbuild-svelte). 146 | 147 | esbuild works with JSON files and can bundle them into JavaScript modules without any configuration. 148 | 149 | It can also import images in JavaScript with the option to either convert them into data URLs or copying them into an output folder. This behavior isn’t enabled by default, but you can add the following in your esbuild config object to enable either option: 150 | 151 | ```javascript 152 | loader: { '.png': 'dataurl' } // Converts to data url in JS bundle 153 | loader: { '.png': 'file' } // Copies to output folder 154 | ``` 155 | 156 | Code splitting appears to be a work in progress, but is mostly there in the ESM output format, and it does look like it is a priority for the project. It’s also worth mentioning that tree-shaking is built into esbuild by default and can’t be turned off. 157 | 158 | #### [](#production-build)Production build 159 | 160 | Using the “[minify](https://esbuild.github.io/api/#minify)” and “[bundle](https://esbuild.github.io/api/#bundle)” options in your esbuild command won’t create a bundle quite as small as a [Rollup](https://rollupjs.org/)/[Terser](https://terser.org/) pipeline. This is because esbuild sacrifices some bundle size optimization to get through your code in as few passes as possible. However, the difference may be pretty negligible, and worth it for the increase in bundling speed, depending on your project. In my clone of the Snap Shot application, esbuild created a bundle of 177 KB which isn’t a lot more than the 165KB produced by Vite, which uses rollup and terser. 161 | 162 | #### [](#overall)Overall 163 | 164 |
esbuild
Templates for multiple front end frameworks
Hot module replacement development server
Streaming imports
Preconfigured production build 
Automatic PostCSS and preprocessor conversion
HTM transform
Rollup plugin support
Size on disk (default install)7.34 MB
165 | 166 | esbuild is an extremely powerful tool. But it might be difficult if you’re used to zero-config setups. If you need more, then you might want to take a look at the next tool, Snowpack, which uses esbuild. 167 | 168 | ### [](#snowpack)Snowpack 169 | 170 | ![](./images/cn02-snowpack.svg) 171 | 172 | Snowpack is a build tool by the creators of [Skypack](https://www.skypack.dev/) and [Pika](https://www.pika.dev/). It provides an awesome development server and was created with an [“unbundled development”](https://www.snowpack.dev/concepts/how-snowpack-works#unbundled-development) philosophy. To quote the documentation: “You should be able to use a bundler because you want to, and not because you need to.” 173 | 174 | By default, Snowpack’s build step doesn’t bundle files into a single package but provides unbundled esmodules that run in the browser. esbuild is actually included in there as a dependency, but the idea is to use JavaScript modules and only bundle with esbuild when it’s needed. 175 | 176 | Snowpack has some [pretty slick documentation](https://www.snowpack.dev/), including a [list of guides](https://www.snowpack.dev/guides) for using it with JavaScript frameworks, and a [bunch of templates for them](https://github.com/snowpackjs/snowpack/tree/main/create-snowpack-app). Some of the guides are still a work in progress, but others like [the one for React](https://www.snowpack.dev/tutorials/react) are nice and clear. It also looks like Snowpack treats [Svelte as a first-class citizen](https://www.snowpack.dev/tutorials/svelte/). I actually first heard about Snowpack from Rich Harris’s [“Futuristic Web Development”](https://www.youtube.com/watch?v=qSfdtmcZ4d0&t=636s) talk at Svelte Summit 2020. That said, the upcoming Svelte meta-framework [SvelteKit](https://svelte.dev/blog/whats-the-deal-with-sveltekit) was supposed to be powered by Snowpack but has since switched to Vite (which we’ll review next). 177 | 178 | #### [](#use-cases)Use cases 179 | 180 | Snowpack is a good choice if you want to double down on unbundled deployment. You may be writing source code with a small number of modules. This would mean you’re not creating a big request waterfall with an unbundled build. If you don’t need the added complexity and technical debt of bundling, then Snowpack is a great choice. A good use case would be if you’re incrementally adopting a front-end framework into a server-rendered or static application. You’d be pulling in as little tooling as possible from the node ecosystem but you’d still be getting the benefits of declarative frontend frameworks. 181 | 182 | Secondly, I’d argue that Snowpack is a great wrapper around esbuild. If you want to try out esbuild but also want a development server and pre-written templates for front-end frameworks, then you can’t go wrong with Snowpack. Enable esbuild in the build step of your Snowpack config and you’re good to go. 183 | 184 | As things currently stand, I’d argue that Snowpack wouldn’t be the best replacement for a zero configuration tool like create-react-app because you’ll need to pull in plugins and configure them yourself if you have a big application and need a super-fancy optimized production-ready build step. 185 | 186 | #### [](#setup)Setup 187 | 188 | Let’s start a project with Snowpack by jumping into the command line: 189 | 190 | ```shell 191 | mkdir snowpackproject 192 | cd snowpackproject 193 | npm init #fill with defaults 194 | npm install snowpack 195 | ``` 196 | 197 | Now, let’s add the following to `package.json`: 198 | 199 | ```javascript 200 | // package.json 201 | "scripts": { 202 | "start": "snowpack dev", 203 | "build": "snowpack build" 204 | }, 205 | ``` 206 | 207 | Next, we’ll create a configuration file: 208 | 209 | ```shell 210 | // Mac or Linux 211 | touch snowpack.config.js 212 | // Windows 213 | new-item snowpack.config.js 214 | ``` 215 | 216 | I think the most magical part of Snowpack comes when setting one innocent-looking key value pair in the configuration file. Paste this into the configuration file, for example: 217 | 218 | ```javascript 219 | // snowpack.config.js 220 | module.exports = { 221 | packageOptions: { 222 | "source": "remote", 223 | } 224 | }; 225 | ``` 226 | 227 | `source: remote` enables something called [**streaming imports**](https://www.snowpack.dev/guides/streaming-imports#how-streaming-imports-work). Streaming imports enable Snowpack to bypass npm installation by converting bare imports (e.g., `import React from 'react';`) into CDN imports from Skypack. 228 | 229 | Moving ahead, let’s make an `index.html` file: 230 | 231 | ```index.html 232 | 233 | 234 | 235 | 236 | > 237 | Snowpack streaming imports 238 | 239 | 240 |
241 | 242 | 243 | 244 | 245 | ``` 246 | 247 | And, finally, we’ll add an `app.jsx` file: 248 | 249 | ```jsx 250 | // app.jsx 251 | import React from 'react' 252 | import ReactDOM from 'react-dom' 253 | const App = ()=>{ 254 | return

Welcome to Snowpack streaming imports!

255 | } 256 | ReactDOM.render(,document.getElementById('root')); 0 257 | ``` 258 | 259 | Note that we didn’t npm install React or ReactDOM at any stage. But if we start up the Snowpack developer server like this: 260 | 261 | ```shell 262 | ./node_modules/.bin/snowpack dev 263 | ``` 264 | 265 | …our app still works! 266 | 267 | Instead of pulling from a `node_modules` folder, Snowpack pulls the npm package down from Skypack, a CDN that hosts the npm registry, and it’s is pre-optimized to work in the browser. Snowpack then serves it in a `./_snowpack/pkg` URL. 268 | 269 | #### [](#usage)Usage 270 | 271 | This is a big step away from Node/npm-based workflow. What we’re actually looking at is a new **CDN/JavaScript module-based workflow.** 272 | 273 | If, however, we our app as it is and run a production build, Snowpack throws an error. This is because it needs to know which versions of React and ReactDOM to use when building. You can fix this by writing to a `snowpack.deps.json` which can automatically be created by running the following: 274 | 275 | ```shell 276 | ./node_modules/.bin/snowpack add react 277 | ./node_modules/.bin/snowpack add react-dom 278 | ``` 279 | 280 | That won’t download the package from npm, but it will record the version of the packages used for Snowpack builds. 281 | 282 | One caveat is that we miss out on developer error messages, as Skypack will ship the production version of packages. 283 | 284 | Even if we aren’t using streaming imports, the Snowpack development server bundles each dependency from `node_modules` into one JavaScript file per dependency, converts those files to a native JavaScript module, then serves it to the browser. This means the browser can cache these scripts and only re-request them if they’ve changed. The development server automatically refreshes on save, but doesn’t preserve the client-side state. All dependencies from node seemed to work out of the box regardless of whether they were using legacy module formats or node APIs (such as the infamous `process.env` we had trouble with in esbuild). 285 | 286 | Preserving client-side state in React requires [react-refresh](https://www.skypack.dev/view/@snowpack/plugin-react-refresh), which requires a few Babel packages of its own as dependencies. These aren’t included by default, but are available using the more maximal React template. The template pulls in react-refresh, Prettier, Chai, and React Testing Library, for a total Node dependency package weighing in at 80 MB: 287 | 288 | ```shell 289 | npx create-snowpack-app my-react-project --template @snowpack/app-template-react 290 | ``` 291 | 292 | #### [](#supported-files)Supported files 293 | 294 | JSX is supported, but again, only with `.jsx` files by default. Snowpack automatically detects if whether React or Preact is being used, and decides accordingly which render function to use for the JSX transform. However, if we want to customize the JSX further than this, we’d need to pull in Babel via [their plugin](https://www.npmjs.com/package/@snowpack/plugin-babel). There is also a [Snowpack plugin available for Vue single file components](https://www.npmjs.com/package/@snowpack/plugin-vue) and, of course, for [Svelte components](https://github.com/snowpackjs/snowpack/tree/main/plugins/plugin-svelte). Further, Snowpack compiles TypeScript, but for type checking we need the [TypeScript plugin](https://www.npmjs.com/package/@snowpack/plugin-typescript). 295 | 296 | CSS can be imported into JavaScript and are tossed into the document `` at runtime. CSS modules are also supported out of the box for scoping as long as they have the `.module.css` extension. 297 | 298 | Imported JSON files will be cast into a JavaScript module with an object as a default export. Snowpack supports images and copies them into the production folder. Going along with its unbundled philosophy, Snowpack does not include images as data URLs in the bundle. 299 | 300 | #### [](#production-build)Production build 301 | 302 | The default `snowpack build` command basically copies the exact source file structure into an output folder. For files that compile to JavaScript (e.g. TypeScript, JSX, JSON, `.vue`, `.svelte`), it transforms each individual file into a separate browser-friendly JavaScript module. 303 | 304 | This works fine, but isn’t great for production, as it could cause a big waterfall of requests if the source code is split into a lot of files. In the Snap Shot application I ended up with 184KB of source files which would then request another 105 KB of dependencies from Skypack which made for a pretty huge waterfall. 305 | 306 | However, Snowpack pulls esbuild as a dependency and we can enable esbuild to bundle, minify and compile our code by adding an “optimize” object to the Snowpack config: 307 | 308 | ```javascript 309 | // snowpack.config.js 310 | module.exports = { 311 | optimize: { 312 | bundle: true, 313 | minify: true, 314 | target: 'es2018', 315 | }, 316 | }; 317 | ``` 318 | 319 | This runs the code using the optimization features provided by esbuild, so by just adding these options we could get the same build we had earlier on with esbuild. 320 | 321 | Since esbuild hasn’t reached 1.0 yet, Snowpack recommends using either the [webpack](https://www.npmjs.com/package/@snowpack/plugin-webpack) or [Rollup](https://github.com/ParamagicDev/snowpack-plugin-rollup-bundle) plugin for production builds, both of which need to be configured. 322 | 323 | #### [](#overall)Overall 324 | 325 | Snowpack provides a lightweight developer experience with a full-featured development server, detailed documentation, and easy-to-install templates. You are left to decide whether you want to bundle your application and how you want to do so. If you want a tool that provides both a dev server and a more opinionated build step, you might want to take a look at Vite, the next tool on our list. 326 | 327 |
Snowpack
Templates for multiple front end frameworks
Hot module replacement development server✅ (when using templates)
Streaming imports
Preconfigured production build 
Automatic PostCSS and preprocessor conversion
HTM transform
Rollup plugin support✅ (when using snowpack-plugin-rollup-bundle for build step)
Size on disk (default install)16 MB
328 | 329 | ### [](#vite)Vite 330 | 331 | ![](./images/cn03-vite.svg) 332 | 333 | Vite is developed by Vue creator (and [Hades speedrunner](https://mobile.twitter.com/youyuxi/status/1331084461652516864)) Evan You. Where esbuild concentrates on the build step and Snowpack concentrates on the development server, Vite provides both: a full development server _and_ an optimized build command using Rollup. 334 | 335 | #### [](#use-cases)Use cases 336 | 337 | If you want a serious create-react-app or Vue CLI competitor, Vite is the closest one in the bunch because it comes with batteries-included features. The lightening-fast development server and zero-config optimized production build mean you can get from zero to production without any configuration. Vite is a tool that could be used in both a tiny side-project or a big production application. A good use case for Vite would be any sizeable single page app. 338 | 339 | Why _wouldn’t_ you use Vite? Vite is an opinionated tool and you might disagree with its opinions. You might not want to use Rollup for your build (we’ve been talking about how fast esbuild is), or you might want your tooling to give you the full power of Babel, eslint and the ecosystem of webpack loaders out of the box. 340 | 341 | Also, you want zero-config server-side rendering meta-frameworks, you’d be better off staying with webpack-based frameworks, like Nuxt.js and Next.js until the story for Vite server-side rendering is more complete. 342 | 343 | #### [](#setup)Setup 344 | 345 | Vite has more opinionated defaults than esbuild and Snowpack. Its [documentation](https://vitejs.dev/) is clear and detailed. We get full support for Vue with Evan being the creator and all, so Vite is a definite happy path for Vue developers. That said, Vite can be used with any front-end framework and even provides a [list of templates](https://github.com/vitejs/vite/tree/main/packages/create-app) to get you started. 346 | 347 | #### [](#usage)Usage 348 | 349 | Vite’s development server is pretty powerful. Vite pre-bundles all of a project’s dependencies together into a single native JavaScript module with esbuild, then serves it up with a heavily cached HTTP header. This means no time is wasted on compiling, serving or requesting imported dependencies after the first page load. Vite also provides clear error messaging, printing the exact block of code and the line numbers to troubleshoot. Again with Vite, I didn’t have any issues pulling in dependencies that used node APIs or legacy formats. They all seemed to be shimmed into a browser-acceptable esmodule. 350 | 351 | Vite’s React and Vue templates both pull in plugins that enable hot module replacement. The Vue template pulls in a [Vue plugin for single file components](https://github.com/vitejs/vite/tree/main/packages/plugin-vue), and a [Vue plugin for JSX](https://github.com/vitejs/vite/blob/main/packages/plugin-vue-jsx/package.json). The React template pulls in the [react-refresh plugin](https://github.com/vitejs/vite/tree/main/packages/plugin-react-refresh). Either way, both will give you hot module replacement and client-side state preservation. Sure, they add a few more dependencies, including Babel packages, but, Babel isn’t actually necessary when using JSX in Vite. By default, JSX works the same way as esbuild — it converts to `React.createElement`. It won’t automatically import React, but its behavior can be configured. 352 | 353 | And while we’re at it, Vite doesn’t support streaming imports like Snowpack and wmr do. That means npm-installing dependencies as usual. 354 | 355 | One cool thing is that Vite includes [experimental support](https://vitejs.dev/guide/ssr.html) for server-side rendering. Pick your framework of choice and generate static HTML that ships directly to the client. At the moment, it looks like we need to construct this architecture on our own, but still, this looks like a good opportunity for meta-frameworks to be built on top of Vite. Evan You already has a work in progress called [VitePress](https://vitepress.vuejs.org/), a replacement for [VuePress](https://vuepress.vuejs.org/) with the benefits of using Vite. And Sveltekit has also [added Vite to its dependency list](https://svelte.dev/blog/sveltekit-beta). It looks like [CSS code splitting](https://vitejs.dev/guide/features.html#css-code-splitting) inclusion was part of the reason Sveltekit made the switch to Vite. 356 | 357 | #### [](#supported-files)Supported files 358 | 359 | For CSS, Vite provides the most features out of all of the tools that we are looking at. It supports bundling CSS imports as well as CSS modules. But we can also `npm install` PostCSS plugins and create a `postcss.config.js` file, and Vite will automatically start applying these transforms to CSS. 360 | 361 | We can install and use CSS preprocessors — simply `npm install` the preprocessor and rename the file to the right extension (e.g. `.filename.scss`) and Vite will start applying the corresponding preprocessor. And, as we said in the overview, Vite support CSS code-splitting. 362 | 363 | Image imports default to a public URL, but we’re also able load them into the bundle as strings by using a `?raw` parameter at the end of the URL string. 364 | 365 | JSON files can be imported in the source and converted into an esmodule exporting a single object. We can also provide a named import and Vite will look in the root field of the JSON file to find the import and treeshake the rest. 366 | 367 | #### [](#production-build)Production build 368 | 369 | Vite uses Rollup for a preconfigured production build with a bunch of optimizations. It intentionally provides a zero-config build which should be enough for most use cases. 370 | 371 | The build comes with the Rollup features we expect: bundling, minification and tree shaking. But we also get extras, like code-splitting dynamic imports and something called “asynchronous chunk loading” which is a fancy way to say that if we request a JavaScript module that imports another module, the build will be pre-optimized to load both at the same time (asynchronously). 372 | 373 | Running Vite’s default build with the Snap Shot app I ended up with one 5KB JavaScript file and one 160KB JavaScript file (for a grand total of 165KB) and all CSS in the project was automatically minified to a tiny 2.71KB file. 374 | 375 | #### [](#overall)Overall 376 | 377 | The opinionated nature of Vite makes it a serious competitor with our current tooling. A lot of work has been done to make the developer experience really seamless and make production-ready builds out of the box. 378 | 379 |
Vite
Templates for multiple front end frameworks
Hot module replacement development server✅ (when using templates)
Streaming imports
Preconfigured production build 
Automatic PostCSS and preprocessor conversion
HTM transform
Rollup plugin support✅ 
Size on disk (default install)17.1 MB
380 | 381 | ### [](#wmr)wmr 382 | 383 | ![](./images/cn04-wmr.svg) 384 | 385 | Like Vite, wmr is another opinionated build tool that provides both a development server and a build step. It was built by the creator of [Preact](https://preactjs.com/), [Jason Miller](https://twitter.com/_developit), so it’s definitely a happy path for Preact developers. Jason Miller explained the thinking behind wmr when [he appeared as a guest on the JS Party podcast](https://changelog.com/jsparty/158): 386 | 387 | > Preact is tiny and it’s really good if you want to do a lightweight project. Where is our tooling for that? We have a webpack-based tool that’s used in used in production by a bunch of high profile sites, but that’s the heavyweight tool. Where’s the prototyping tool? That was the one hand. The other hand is myself and a bunch of others who happened to be on the Preact team; We had been kind of on the sidelines of the bundler ecosystem for a little while, prodding people, trying to get consensus on a direction that we can move in to further this idea of writing modern code and shipping modern code. 388 | 389 | This tells us that wmr is all about writing and shipping modern code, enabling lighter tooling in a project. 390 | 391 | You might be wondering what wmr stands for? Nothing! The names “Web Modules Runtime” and “Wet Module Replacement” were floated, but it’s a fake abbreviation, similar to npm. 392 | 393 | wmr is built with the same ruthless bundle size purging as Preact, so it’s tiny — weighing a mere 2.6 MB — and contains exactly zero npm dependencies. Still, it manages to pack in a whole lot of really awesome features including a hot-module-replacing development server and an optimized production build. 394 | 395 | #### [](#use-cases)Use cases 396 | 397 | I would use wmr if I was looking to create a prototype using Preact as fast as possible. There’s no configuration and it only takes seconds to download. It feels like using a supercharged static file server. With TypeScript, an optimized-build step, and static HTML rendering, wmr offers everything needed to ship small-to-medium sized applications. Its small size is also great for quickly trying out a library or demoing an idea. 398 | 399 | wmr may not be the tool for you if you’re not using Preact, React or vanilla JavaScript. The Preact team has yet to provide templates for other frameworks. The documentation also isn’t as detailed as the other tools we’ve looked at. This means the further you stray from the happy path, the more you’ll dig into the source. So, I can’t recommend it if a lot of customization is needed. 400 | 401 | #### [](#setup)Setup 402 | 403 | If you’re using preact there is absolutely zero setup needed except for a quick npm install. To use React with wmr instead of Preact, there are currently two steps to take. First, alias `htm/preact` to `htm/react`, and `react` to `es-react` in your `package.json`: 404 | 405 | ```javascript 406 | "alias": { 407 | "htm/preact": "htm/react", 408 | "react": "es-react" 409 | }, 410 | ``` 411 | 412 | Then add imports from `es-react` into your components: 413 | 414 | ```javascript 415 | // ReactDOM only needed on root render 416 | import { React, ReactDOM,} from 'es-react'; 417 | ``` 418 | 419 | This means we aren’t actually using the normal React package you might be used to, but instead pulling in React from [es-react](https://github.com/lukejacksonn/es-react). This is because wmr relies on packages being compatible with native JavaScript modules. React does not use native modules by default, instead using an older style of module called [UMD modules](https://github.com/umdjs/umd). es-react is a package that pulls in React but provides exports that are compatible with the web platform. 420 | 421 | This illustrates wmr’s philosophy of using native primitives of the web platform as opposed to using tooling to sidestep and abstract it away. 422 | 423 | Another option could be to use Skypack imports in our application, which is also pre-optimized to work in the browser: 424 | 425 | ```javascript 426 | import React from 'https://cdn.skypack.dev/react'; 427 | import ReactDOM from 'https://cdn.skypack.dev/react-dom'; 428 | ``` 429 | 430 | wmr expects that you’re writing modern code which runs in the browser, which may mean that you’ll need to do some configuration if you pull in dependencies that use node APIs or legacy module systems. To get the Snap Shot app working I needed to dive into node modules and convert a library or two to use native JavaScript module syntax. This might slow you down if you are using older libraries. The Preact ecosystem is all optimized to run in the browser and shouldn’t require any massaging. This is another reason to stick with the Preact happy path in wmr. 431 | 432 | There are plugins for wmr. It exposes a plugin API that supports Rollup plugins for a build step. There are more and more wmr-specific examples on the docs, including a plugin that [minifies](https://github.com/preactjs/wmr/wiki/Configuration-Recipes#minifying-html) [HTML](https://github.com/preactjs/wmr/wiki/Configuration-Recipes#minifying-html), and one that features [filesystem](https://github.com/preactjs/wmr/wiki/Configuration-Recipes#filesystem-based-routing--page-component-loading)[–](https://github.com/preactjs/wmr/wiki/Configuration-Recipes#filesystem-based-routing--page-component-loading)[based routing](https://github.com/preactjs/wmr/wiki/Configuration-Recipes#filesystem-based-routing--page-component-loading). 433 | 434 | wmr supports different frameworks, but there aren’t any pre-built templates for them. And at first I found it rather difficult to configure the JSX transform. All that said, Jason has confirmed there are plans to make JSX more configurable and that wmr is intended to be framework-agnostic. JSX is planned to work out of the box in regular JavaScript files. 435 | 436 | #### [](#usage)Usage 437 | 438 | To get started you can either run this command in the command line: 439 | 440 | ```shell 441 | npm init wmr your-project-name 442 | ``` 443 | 444 | Or alternatively, you can run these commands to manually build up your application: 445 | 446 | ```shell 447 | npm init -y 448 | npm install wmr 449 | mkdir public 450 | touch public/index.html 451 | touch public/index.js 452 | ``` 453 | 454 | Then add an script import in the body of your `index.html` (again make sure to use `type="module"`): 455 | 456 | ```html 457 | 458 | ``` 459 | 460 | Now you can write a Preact hello world into your `index.js` file: 461 | 462 | ```javascript 463 | import { render } from 'preact'; 464 | render(

Hello World!

, document.body); 465 | ``` 466 | 467 | And finally start your development server: 468 | 469 | ```shell 470 | node_modules/.bin/wmr 471 | ``` 472 | 473 | Now we have a full hot module replacement development server which will immediately respond to any changes to our source-code. 474 | 475 | wmr uses a tool called [htm](https://github.com/developit/htm) when transforming JSX, which provides some awesome benefits. Let’s say we’re writing a counter using Preact in wmr and make a mistake: 476 | 477 | ```jsx 478 | import { render } from 'preact'; 479 | import { useState } from 'preact/hooks'; 480 | function App() { 481 | const [count,setCount] = useState(0) 482 | return <> 483 | // HIGHLIGHT 484 |

count: {count}

485 | 486 | } 487 | render(, document.body); 488 | ``` 489 | 490 | `count` is misspelled in the `onClick` handler function, so running this results in an error. Usually, we would have to rely on our tooling and a source map to gather information about where the bug is located, but wmr takes a different solution. With htm, this gets as close as you can get to native JSX in the browser by using tagged template literals. So, where writing React or Preact code usually looks like this: 491 | 492 | ```jsx 493 | I am JSX. I am not actually valid Javascript 494 | ``` 495 | 496 | …htm looks more like this: 497 | 498 | ```html 499 | html`<${MyComponent}>I am about as close as it gets to JSX as you can get while being able to run in the browser` 500 | ``` 501 | 502 | Now, if we’re debugging our code and open the “Sources” panel in DevTools, we should see a script that’s almost identical to what the source code looks like in the editor. 503 | 504 | ![](./images/cn05.png) 505 | 506 | This way, we can properly investigate where bugs are located in the browser without having to use sourcemaps. Sure, this specific example is pretty contrived, but you can see how this could be really useful because it means wmr doesn’t need source maps in your developer environment. 507 | 508 | wmr supports streaming imports by default, so bare imports will be pulled down from the npm registry. This is done through a complex process which examines all of the source in the npm package, removes all the tests and metadata, and converts it to a single native JavaScript import. Similar to Snowpack, it’s possible to build a complex app without using npm to install anything. In fact, wmr is the first tool to support this idea. 509 | 510 | #### [](#supported-files)Supported files 511 | 512 | As far as the other types of files wmr supports, CSS files can be imported in JavaScript, and CSS modules are supported as well. 513 | 514 | There isn’t any built-in support for either Vue single file components or Svelte components. However, wmr’s build step works with Rollup plugins and the development server can be configured with [Polka](https://github.com/lukeed/polka)/[Express](https://expressjs.com/) middleware, so it’s possible to use these to convert imports into Vue and Svelte components. In fact, I wrote a [small plugin for Vue Single file Components](https://github.com/Elliotclyde/wmr-vue-plugin) to show how this might be done. 515 | 516 | We can’t import images into JavaScript as data URLs in wmr without a plugin. Instead, we need to import them using a syntactically correct JavaScript method. So, if we have, say, a picture of a dog in our public folder we might include it in a Preact component like this: 517 | 518 | ```javascript 519 | function Dog() { 520 | return dog hanging out 521 | } 522 | ``` 523 | 524 | And once the build step runs, the image is copied and accessible from the distribution folder. There is hot module replacement for images in the development server, so changes with images are immediately reflected in the browser. 525 | 526 | One more note on file support: JSON can be imported, and is converted into a JavaScript object for use. But when actually building an application, we’d need the [Rollup JSON plugin](https://github.com/rollup/plugins/tree/master/packages/json). 527 | 528 | #### [](#production-build)Production build 529 | 530 | wmr provides a production build step that includes bundling, minification and tree-shaking without any additional dependencies. Having a look at the source of wmr, it looks like rollup and terser are used under the hood, and minified versions of these are included in the wmr package. The wmr bundle for the Snap Shot app was 164KB so it created a bundle just a tiny bit smaller than total size of the two JavaScript files created by Vite. 531 | 532 | There is also a way to configure wmr is such a way that it renders an application to static HTML and hydrates on the browser using [preact-iso](https://www.npmjs.com/package/preact-iso). This means wmr can be used as a meta-framework for Preact, similar to Next.js. 533 | 534 | #### [](#overall)Overall 535 | 536 | I love the experience of using wmr to prototype both React and Preact applications. It feels great to get started with a tool that is ridiculously small but provides developer conveniences that get close to matching heavyweight bundlers. 537 | 538 |
wmr
Templates for multiple front end frameworks
Hot module replacement development server
Streaming imports
Preconfigured production build 
Automatic PostCSS and preprocessor conversion
HTM transform
Rollup plugin support✅ 
Size on disk (default install)2.57 MB
539 | 540 | ### [](#feature-comparison)Feature comparison 541 | 542 | We just covered a lot ground! Rather than scroll up and down this article to compare results, I’ve compiled everything here to see how the tools stack up wen they’re side-by-side. I’ve even included additional comparisons for features that we didn’t explicitly covers. 543 | 544 | #### [](#use-cases)Use cases 545 | 546 |
ToolUse case
esbuildLarge codebases. Not ready for production yet.
SnowpackSmall applications that don’t need bundling or applications where you want to choose which bundler you use. Also good for incrementally adopting JavaScript frameworks in server-rendered apps. 
ViteReplacement for Vue CLI/Create-React-App for producing single page applications. This is the happy path for Vue.
wmrPrototypes. Good for small- to medium-size apps and can be used for either single page or server-rendered apps. This is the happy path for Preact.
547 | 548 | #### [](#setup)Setup 549 | 550 |
esbuildSnowpackVitewmr
Templates for multiple front end frameworks
Size on disk (default install)7.34 MB16 MB17.1 MB2.57 MB
Zero-config production build
HMR Development Server with zero config
Process.env handling for node packages
551 | 552 | #### [](#development-server)Development server 553 | 554 |
esbuildSnowpackVitewmr
Hot module replacement
CSS hot replacement
npm dependency pre-bundling
Browser error messaging
HTM tranform
555 | 556 | #### [](#production-build)Production build 557 | 558 |
esbuildSnowpackVitewmr
Output bundle size of Snap Shop app177 KB184 KB of multiple JavaScript files, plus 105 KB of Skypack CDN dependencies 165 KB (one 5 KB file and one 160 KB file)164 KB
Go-based bundling✅ when using esbuild is used in build step
Preconfigured production build
Asynchronous chunk loading
Rollup plugin support
559 | 560 | #### [](#other-features)Other features 561 | 562 |
esbuildSnowpackVitewmr
Streaming inputs
Server-side rendering✅ (experimental)
CSS Modules
Automatic PostCSS and preprocessor conversion
563 | 564 | ### [](#wrapping-up)Wrapping up 565 | 566 | I’m super excited about building JavaScript applications with any and all of the tools we just looked at. Whether we’re writing a small side project or a big production site, all these tools speed up feedback loops and increase productivity. They’ve opened up the gates to ask what’s necessary in the JavaScript ecosystem and whether we can start losing the cruft brought by legacy modules and browsers. These tools are going to lower the barrier-to-entry for new developers by providing a leaner, faster developer environment with fewer abstractions between the code that gets written and the code that runs in the browser. 567 | 568 | If you’re getting sick of waiting for your dependencies to download and your build steps to run, I’d recommend giving this new generation of tooling a try. 569 | 570 | #### [](#further-reading)Further reading 571 | 572 | * [“Comparisons with Other No-Bundler Solutions”](https://vitejs.dev/guide/comparisons.html) (Vite) 573 | * [“Let’s Learn esbuild! (with Sunil Pai)”](https://www.youtube.com/watch?v=KLdF1yu_bmI) (Jason Lengstorf) 574 | * [Through the pipeline: an exploration of frontend bundlers](https://dev.to/walpolea/through-the-pipeline-an-exploration-of-front-end-bundlers-ea1) (Andrew Walpole) 575 | 576 | #### [](#other-new-javascript-tooling-to-check-out)Other new JavaScript tooling to check out 577 | 578 | * [Rome](https://rome.tools/) – A full toolchain including linting, compiling, bundling, test-running and formatting 579 | * [SWC](https://swc.rs/) – A rust-based JavaScript/TypeScript compiler 580 | * [Deno](https://deno.land/) – A runtime for JavaScript and TypeScript (similar to Node.js) -------------------------------------------------------------------------------- /Docs/0000-vue3-ie11-support.md: -------------------------------------------------------------------------------- 1 | - 开始日期: 2021-04-02 2 | - 目标版本: 3.x 3 | - 参考 Issue: N/A 4 | - 实现 PR: N/A 5 | 6 | # 摘要 7 | 8 | - 移除 Vue 3 对 IE11 的支持。 9 | - 将精力转为把兼容特性向前移植到 Vue 2.7 版本中。 10 | 11 | # 动机 12 | 13 | 从 Vue 3 启动开发开始,一直到 2018 年底,我们一直被问到有关 IE11 支持的问题。很多用户都问过,Vue 3 是否会支持 IE11,我们原本都计划是先发布 Vue 3,让它稳定下来,然后在后续阶段增加对 IE11 的支持。在漫长的开发过程中,我们另外还做了兼容 IE11 的研究和实验,但是由于其复杂性以及手头大量的其他工作,这项工作的优先级就降低了。 14 | 15 | 当我们在 2021 年再来看这个问题时,浏览器和 JavaScript 的情况都发生了巨大变化。现在更多的开发者使用现代语言特性,更为重要的是,[微软自己开始积极推动用户远离 IE](https://techcommunity.microsoft.com/t5/windows-it-pro-blog/the-perils-of-using-internet-explorer-as-your-default-browser/ba-p/331732),并对 Edge 持续投入精力。它还在[自己的主要产品(如 Microsoft 365)中移除了对 IE11 的支持](https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666)。而就在几天前, [WordPress 也做出了移除 IE11 支持的决定](https://make.wordpress.org/core/2021/03/25/discussion-summary-dropping-support-for-ie11/)。IE11 的全球使用率已[下降至不足 1%](https://caniuse.com/usage-table)。当我们谈论面向公众的网站和应用时,IE11 的下滑趋势十分明显。 16 | 17 | 我们认为这是一个重新思考 Vue 3 支持 IE11 的好机会。 18 | 19 | ## Vue 3 中支持 IE11 的成本 20 | 21 | ### 行为不一致 22 | 23 | Vue 2 的响应式系统是基于 ES5 的 getter/setters。Vue 3 利用了 ES2015 的 Proxy 实现了一个更高性能、更完备的响应式系统,但无法在 IE11 中 polyfill 这一特性。这是我们最大的障碍,因为这意味着如果我们要支持 IE11,就必须发布两个不同行为的的版本:一个是基于 Proxy 的响应式系统,另一个则是基于和 Vue 2 类似的基于 ES5 的 getter/setters 特性的响应式系统。 24 | 25 | Vue 3 的基于 Proxy 的响应式系统提供了近乎完整的语言特性覆盖。它能够检测到许多在 ES5 中完全无法检测的操作,比如属性到添加或删除,数组的索引以及长度变化,`in` 操作符检查。基于 Proxy 版本的代码无法在 IE11 里运行。这不仅仅给我们带来了技术上的复杂性,同时也给开发者造成了持续的心智负担。 26 | 27 | 我们原本的计划是在支持 IE11 版本的开发中同时发布 Proxy 和 ES5 的两种响应式版本。当它在支持 Proxy 的开发环境中运行时,会检测并对不兼容 IE11 的一些用法做出警告。这虽然在理论上可行,但是带来了极大的复杂性,因为它需要将两种实现混合在一起,而且增加了开发和生产环境行为不一致的风险。 28 | 29 | ### 长期维护的负担 30 | 31 | 支持 IE11 也意味着我们必须对整个代码库中使用的语言特性做出考量,并为我们的发布版本找到合适的 poliyfill / 编译策略。每一个在 IE11 中无法被 polyfill 的新特性都会带来新的行为警告。一旦 Vue 3 承诺支持 IE11,直到下一个大版本发布之前都无法摆脱它了。 32 | 33 | ### 给库开发者带来复杂性 34 | 35 | 如果 Vue 本身能够完全处理掉这种复杂性,那么在某种程度上也是可以接受的。然而,在与社区成员讨论后,我们意识到,共存的两个响应式系统实现也不可避免地影响到了库作者。 36 | 37 | 通过在 Vue 3 中支持 IE11,本质上库作者也需要做同样的决定。库作者不得不考虑他们的库运行在哪种 Vue 3 版本上(可能还得支持 Vue 2)。如果他们决定支持 IE11,在编写库时,脑子里也必须时刻考虑 ES5 响应式系统的相关警告。 38 | 39 | ### 为 IE11 持续存在做贡献 40 | 41 | 没人愿意支持 IE11。它是一个停留在过去的行将就木的浏览器。未来 Web 生态向前发展的越远,我们为了支持 IE11 所要填补的缺口就越大。具有讽刺意味的是,如果 Vue 3 支持 IE11,那么就等于我们给它注入了新的生命力。基于我们的用户基础,移除对 IE11 的支持可能会加快其被淘汰的速度。 42 | 43 | ## 对于那些实在需要 IE11 支持的用户 44 | 45 | 我们也很清楚,对 IE11 的真正需求来源于那些无法升级的用户:金融机构、教育部门和那些依赖 IE11 的屏幕阅读器。如果你正在构建一个面向这些领域的应用,你可能别无选择。 46 | 47 | 如果你需要 IE11 支持,我们推荐使用 Vue 2 版本。与其为 Vue3 和未来的版本承担巨大的技术债,我们认为把工作重心放在让 Vue2 拥有向后兼容特性、确保两个大版本之间的拥有更加近似的开发体验这件事更有意义。 48 | 49 | 一些可以在 2.7 版本向后兼容的特性: 50 | 51 | - 把 [`@vue/composition-api` plugin](https://github.com/vuejs/composition-api) 合并进 Vue 2。这会让使用 Composition API 开发的库同时支持 Vue2 和 Vue3。 52 | - 单文件组件中的 [`