客户端 TypeScript,无需 ASP.NET、Angular 等






4.32/5 (9投票s)
客户端 TypeScript 以及 VS Code 和 Visual Studio 的调试试验。
目录
引言
现实情况是,外面几乎所有东西似乎都是半生不熟的,在炎热的夏日阳光下,堆满了牛你懂的。这就是我在移植我的 HTML 编辑器 到 TypeScript 时,尝试让客户端 TypeScript 正常调试的经历(请注意,这不是 TypeScript 的错!),使用的是 Visual Studio Code。
牛粪堆 #1:Visual Studio Code 中的 Chrome Debugger 扩展,如果将源文件放入子文件夹,就根本无法工作。这个问题至少自 2018 年以来就被报告过,但唯一可行的解决方案是,在 TypeScript 代码的入口点添加 debugger;
,然后重新应用所有断点。真是胡扯...
牛粪堆 #2:即使要能在 Visual Studio Code 中运行代码,您还需要某种构建过程来打包所有转译后的 TypeScript。什么鬼?
好的,关于堆 #1 和 #2,我未能找出任何其他解决方案。
牛粪堆 #3:Visual Studio 强烈希望您接受并使用 ASP.NET 或 .NET Core 解决方案中的 TypeScript。然而,我不想接受,只想一个纯客户端解决方案。唯一的方法是加载一个 TypeScript HTML 模板,经过大量搜索后,我找到了。
牛粪堆 #4:但它实际上并不能“开箱即用”。您需要 require.js。您需要将模块构建器设置为正确的设置。
牛粪堆 #5:但是,令人惊讶的是,它确实有效了。除了剩下需要弄清楚,好吧,我该如何区分开发和生产代码?我如何选择是打包一切还是不打包?为什么我唯一的选择是打包或不打包?
这让我重申了我的印象:为什么我们将软件开发工具留给生活在一个充满无底洞、超级蝙蝠和偶尔出现的 Wumpus 的巨大地下洞穴系统中的地精,他们显然需要大量的维生素 D,因为他们从未见过阳光,也没有机会了解“用户”(努力完成工作的开发人员)的真正需求。
所以,这是我穿越客户端 TypeScript 工具地下迷宫的旅程,并发现了通往光明的未使用隧道。希望它能为您节省一些时间,如果您决定进行类似的探险。我没有探索所有的狭窄角落和隧道,所以可能还有更短的路径。如果您找到了,请留下地图给其他人!
Visual Studio Code - 不要在家尝试
市面上有太多的“编辑器”了,但我选择的洞穴入口标记为“Visual Studio Code”。我是说,为什么不呢?我已经熟悉它了,而且我真的不想花时间去弄清楚如何导航其他入口,比如 WebStorm(30 天后需要支付入场费,除非您是奥术魔法的学生)、Atom,或 Sublime(几个月前我把它封起来了,因为它变得无法使用并面临崩溃),而且我怀疑那些需要您结交的各种巨魔才能完成比简单编辑更复杂的事情。我把这留给其他冒险者。我已经和 Visual Studio Code 酒馆里的一些巨魔朋友一起旅行过,我宁愿和熟人而不是陌生人一起旅行,所以决定了。
Gulp、WebPack、Grunt
所以,您首先需要某种打包器和转译器执行器,我选择了 Gulp。WebPack 似乎很受欢迎(至少在 StackOverflow 上一直被提及),但我被其多页面的“入门”指南和“首先,我们将稍微调整一下我们的目录结构...”吓退了。真的吗?您无法创建一个入门指南而不进行一些调整?好吧。Grunt - 几年前我曾用过它,而且,我不知道,我就是不怎么喜欢。所以,最后是Gulp,特别是当我找到一些不错的关于如何使用客户端 TypeScript 的示例时,而且我认为“流式”概念比 Grunt 更现代。
预设 1
首先,使用 npm init
创建一个 `package.json` 文件。从命令行。 多么复古。
我从我的 `projects` 文件夹开始,创建了一个名为 `tsvscdemo` 的目录,然后 `cd` 进入了它。
然后运行 `npm init`,它会提示输入各种信息,我唯一更改的默认设置是入口点,将其设置为 `./dist/main.js`。
接下来,安装各种包。我包含了 jquery,因为我想学习如何像使用jquery一样使用第三方包与 TypeScript。这有点棘手,因为如果您想在编辑器中获得智能感知,第三方供应商需要提供一个 sourcemap 文件。说到 sourcemap,不妨看看 http://definitelytyped.org/,“高质量 TypeScript 类型定义存储库。” 我认为这是一个值得注意的链接!
总之,这样做
npm install -g gulp-cli
npm install --save-dev typescript gulp gulp-typescript
npm install --save-dev browserify tsify vinyl-source-stream
npm install --save-dev watchify fancy-log
npm install --save-dev @types/jquery
npm install --save jquery
`browserify` 包是一个打包器,它将您已经转译成 `.js` 文件的单独的 TypeScript `.ts` 文件打包成一个 `.js` 文件,然后您的浏览器可以加载它。为什么要这样做?好问题。我想答案是,这意味着您不必为每个单独的 `.js` 文件在 HTML 中添加 `script` 标签。我不太确定我能想到更好的理由,但似乎每个人都在这样做,而没有太多解释,他们只是期望您接受。
`watchify` 包会监视您的 `.ts` 文件(以及其他您想要的任何文件)的变化,并自动执行转译器和打包器,并将生成的文件移到 `dist` 文件夹。很有用。
预设 2
如果您还没有安装,请安装 Visual Studio Code 的 Debugger for Chrome 扩展。
创建 launch JSON 文件
在您的 `project` 文件夹的根目录下创建 `launch.json` 文件,内容如下:
{
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Program",
"file": "${workspaceFolder}/dest/index.html",
}
]
}
这告诉 Visual Studio Code 如何启动“程序”。
创建 Gulp 脚本
同样在项目根目录下,创建 `gulpfile.js` 文件,其中包含转译和打包客户端应用程序的脚本。
var gulp = require('gulp');
// var sourcemaps = require('gulp-sourcemaps');
var browserify = require('browserify');
var source = require('vinyl-source-stream');
var tsify = require('tsify');
var fancy_log = require('fancy-log');
var paths = {
pages: [
'*.html',
'*.css'
]
};
var br = browserify({
basedir: '.',
debug: true, // Setting to false removes the source mapping data.
entries: [
// TS files to transpile and bundle.
'index.ts',
],
cache: {},
packageCache: {}
}).plugin(tsify);
gulp.task('copy-html', function () {
return gulp.src(paths.pages)
.pipe(gulp.dest('./dest'));
});
function bundle() {
return br
.bundle()
.on('error', fancy_log)
.pipe(source('bundle.js'))
.pipe(gulp.dest('./dest'));
}
gulp.task('default', gulp.series(gulp.parallel('copy-html'), bundle));
这里重要的部分是指定
- 需要打包的源文件在哪里 - `entries` 数组
- 目标文件夹为 `./dest`
- 需要在 `pages` 数组中移至目标的附加文件。
添加 tsconfig.json 文件
还有更多事情要做
{
"files": [
"index.ts"
],
"compilerOptions": {
"noImplicitAny": true,
"target": "es6",
"sourceMap": true,
"outDir": "dest"
}
}
我不确定这里是否需要指定 `files`,相对于 gulp 文件来说,这似乎是多余的。真正的噩梦是“target
”,我将其指定为 `es6`。这是我唯一能够让 `import` 语句工作的方式。除其他外,ECMA Script 6 支持箭头函数,我经常使用。总之,这是需要弄清楚的痛点之一。
创建 index.html
一个简单的 HTML 文件
<!DOCTYPE html>
<html>
<body>
<div>Hello World!</div>
<input id='name'/>
<script src="bundle.js"></script>
</body>
</html>
这里的关键点是它加载了 Gulp 生成的脚本。
一个简单的 index.ts 文件
import * as $ from "jquery";
$("#name").val("Marc Clifton");
console.log("Hello World");
console.log("Goodbye Cruel World!");
在这里,我们只是测试 jQuery 并添加几行日志以进行断点测试。
运行它!
从项目文件夹的根目录,运行 Gulp(从命令行)。您应该看到
在 Visual Studio Code 中注意被放置在 `dest` 文件夹中的文件。
按 **F5** 开始以调试模式运行应用程序 - Visual Studio Code 应该会自动为您创建“**在 localhost 上启动 Chrome**”调试配置。
您应该会看到:
演示页面已加载,TypeScript 文件已转译并打包,并且 jQuery 正在工作。
调试 - 失败!
现在在第 3 行设置一个断点。
再次按 **F5**,Chrome 应该会在断点处停止,允许您在 Visual Studio Code 中调试代码。
它没有工作!为什么?嗯,事实证明,因为我们输出到了 `dest` 文件夹,Visual Studio Code 和它与 Chrome 的交互之间存在一些问题,导致断点失败。
糟糕的解决方法
在 VSCode 论坛中获得“赞”的解决方法是在某个地方设置一个 debugger;
语句,通常在应用程序的开头,然后您可以设置断点或“重新应用所有断点”,如下所示(添加 debugger;
行后,不要忘记再次运行 Gulp 项目),然后按 **F5** 启动调试器,您会看到这个:
现在,在 Visual Studio Code 的左下角右键单击断点,然后从弹出菜单中选择“**重新应用所有断点**”。
或者,如果那不起作用,只需再次手动添加断点。按 **F5** 继续调试,VS Code / Chrome 现在将在断点处停止。
结论
我觉得这完全是一个不可接受的解决方法!为什么 VS Code 中仍然存在这个问题,我实在无法理解,如果您知道如何解决这个问题,请告诉我,我将更新这篇文章,删除我的抱怨!
使用 Visual Studio
本节依赖于以下两篇文章:
- Visual Studio 的 HTML 应用程序与 TypeScript 项目模板
- 使用 require.js (StackOverflow 帖子)
令我惊讶的是,微软没有提供创建客户端 TypeScript 应用程序的方法,但话说回来,也许我也不该惊讶,因为他们希望您更多地接受 ASP.NET 或 .NET Core。好吧,没问题,但我发现自己正在创建各种不需要服务器,当然也不需要 Angular 这样框架的小型 Web 应用程序。
使用 TypeScript 模板创建 Visual Studio 项目(HTML 应用程序)
第一步是在创建新项目时使用“**在线**”选项。
然后选择“**TypeScript 的 HTML 应用程序**”模板。
请参阅 Rich Newman 的帖子(链接在上面),他重新创建了这个模板,该模板存在于 VS2015 中,但在 VS2017 中被移除。
创建项目,使其具有以下文件夹组织,忽略 `notes.txt` 文件,那是我在进行一些工作时添加的笔记。
配置项目
为了使其正常工作,特别是关于 `import` 命令,需要,嗯,`require.js` 和 AMD 模块系统。
这里有一个链接,可以阅读更多关于 JavaScript 模块系统 的信息。唉。作为旁注:AMD(异步模块定义)由 RequireJS 实现。
设置 require.js
- 您可以在此处下载 require.js:https://requirejs.node.org.cn/docs/download.html
- 以及 `.d.ts` 文件(sourcemap 文件)在这里:https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/requirejs
- 尽管如我在上面链接的 StackOverflow 帖子所述,将 `index.d.ts` 保存为 `require.d.ts`。
- 将它们放在如上图所示的文件夹中。您可以根据需要添加其他库,如 jQuery 及其 `d.ts` 文件。
index.html 文件
<html lang="en">
<head>
<meta charset="utf-8" />
<title>TypeScript HTML App</title>
<link rel="stylesheet" href="app.css" type="text/css" />
<script data-main="app/AppConfig" type="text/javascript" src="lib/require.js"></script>
</head>
<body>
<h1>TypeScript HTML App</h1>
<div id="content"></div>
</body>
</html>
这里的关键点是,JavaScript 入口点被指定为 `AppConfig`。
AppConfig.ts 文件
有点小题大做,但这说明了 JavaScript 的启动过程。
import { AppMain } from "./AppMain"
require(['AppMain'],
(main: any) => {
var appMain = new AppMain();
appMain.run();
}
);
AppMain.ts 文件
import { Greeter } from "./classes/Greeter"
export class AppMain {
public run() {
let greeter = new Greeter();
greeter.greet();
}
};
所有这些只是实例化 `Greeter` 类并执行 `greet` 方法。嗯,它也导入了类。这是一个重要的点。
Greeter.ts 文件
export class Greeter {
constructor() {
}
greet() {
console.log("Hi!");
}
}
这很令人兴奋!
调试它!
在控制台日志行上设置一个断点。
按 **F5**,瞧,Chrome 在断点处停止,Visual Studio 也在断点行。
没有麻烦,没有混乱,没有 grunt,没有大量需要调整的配置文件。太棒了!
使用 Visual Studio 打包 - 陷阱
您可以在项目的 TypeScript 生成配置选项中将 JavaScript 打包成一个文件。
陷阱 #1
执行此操作后,您将无法再调试该应用程序!
陷阱 #2
此外,为了尽量减少干扰,我不得不手动将 `require.js` 复制到一个 `lib` 文件夹中,以及 `index.html`。我猜微软认为您可以编写一个生成后步骤来将非 JavaScript 文件(HTML、CSS、图像、图标等)复制到“分发”文件夹。以及执行像运行最小化器这样的操作。因此,开发人员仍然需要做大量工作来创建应用程序的“生产”文件夹,当然,前提是您想将所有 JavaScript 打包到一个文件中。创建单独的包(例如,用于应用程序的不同页面)远远超出了 Visual Studio 的支持范围,但同样可以通过生成后操作来处理。至少,我是这么认为的,因为我还没有完成使用我在这里学到的知识来创建生产就绪代码库的完整过程。
陷阱 #3
另外,打包需要我修改 `index.html` 文件以使用打包后的文件。
<script data-main="bundle" type="text/javascript" src="lib/require.js"></script>
有趣的是,TypeScript 文件中的所有路径引用,例如这个:`import { Greeter } from "./classes/Greeter"`,由于导入被转译成 JavaScript 的方式,所以它们都是无关紧要的。
define("AppMain", ["require", "exports", "classes/Greeter"]
“define
”究竟是什么?嗯,它是 AMD 规范的一部分,猜猜怎么着,AMD 是由 RequireJs 实现的。在此处阅读更多信息:here。
所以,从积极的方面来看,所有这些路径引用都不需要修改打包后的 JavaScript。
结论
看到 Visual Studio Code 的失败情况真是令人失望,我仍然抱有一丝希望,认为我当时做错了什么。讽刺的是,在 Visual Studio Code 中,打包后的文件只要输出到 *根* 文件夹就可以调试,但在 Visual Studio 中,打包后的文件似乎无法调试,即使 VS 生成了一个 *sourcemap* 文件。同样,我觉得我当时做错了什么,但我也觉得这些东西应该就能正常工作,而不必让我花费数小时的时间浏览 StackOverflow 来寻找可能的解决方案。
无论如何,我已经实现了我想要的目标:使用 TypeScript 创建客户端应用程序,并能够调试它们。对我来说,这意味着我可以在不购买 ASP.NET 或 .NET Core 的情况下编写后端。我可以在 TypeScript 中编写前端,然后在任何我想要的语言中编写后端——例如 Python。