篆体字网 > 知识库

chinaitlab

来源:篆体字网 2024-01-05 05:12:21 作者:篆字君

直接在浏览器上使用TypeScript脚本语言中国IT实验室收集整理2012-10-22 12:42:23佚名已经有很多社区的文章在介绍 Typescript 这个新的语言,这是理所当然的 - 因为它解决了很多JavaScript的问题,尽管处于起步阶段,却已经显示了巨大的潜力。Typescript 要求你必须预编译来生成JavaScript 代码,现在也可以直接在浏览器上动态编译,但你必须引用 Typescript.js 这个 JS 编译器,这个编译器有 250Kb 大小,这可能是一颗很难咽下的药丸。好在,通过 ASP.NET 的瑞士军刀 —— Web API ,我们可以实现对 Typescript 动态编译为 Javascript。思路为了不用再每次修改 Typescript 后都要手工编译,我们将透过 Web API 来为我们完成这项工作,使用的是定制的 MediaTypeFormatter.你所需要做的就是通过一个特定的预先配置好的 Web API 路由/控制器来引用这个 JS 编译器脚本(Typescript.js),然后让 Web API 管道通过 MediaTypeFormatter 来完成这项重任务。路由和控制器我们在 HTML 中引用编译后的 js 文件如下:为了实现这个目的,新建一个典型的 MVC4, Web API 项目。需要一个定制的路由和一个简单的控制器:config.Routes.MapHttpRoute(name: "DynamicScripts",routeTemplate: "dynamic/{controller}/{name}.{ext}",defaults: new { name = RouteParameter.Optional, ext = RouteParameter.Optional },constraints: new { controller = "Scripts" });public class ScriptsController : ApiController{public string Get(string name){return name;}}这个路由可以让我们传递一个 name 参数和一个扩展(用于匹配所需的 filename.js),然后控制器简单的将文件对应的请求重定向到 formatter 中。插件为了让上述思路可行,我们需要命令行的 Typescript 编译器,可从官方网站 上获取,选择中间那个 (Plugins),推荐 Visual Studio 2012,但 2010 也没关系,我们只关心命令行工具而已。一旦安装成功,你会找到一个名为 TCS.exe 的可执行文件,默认位于 C:\Program Files (x86)\Microsoft SDKs\TypeScript\0.8.0.0\. 将所有编译器文件拷贝到解决方案目录下的 TS 文件夹。格式化器/编译器注意下面的代码应根据你特定的需求进行调整(包括持久化机制、错误处理等等):public class TypeScriptMediaTypeFormatter : MediaTypeFormatter{private static readonly ObjectCache Cache = MemoryCache.Default;public TypeScriptMediaTypeFormatter(){this.AddUriPathExtensionMapping("js", "text/html");}public override void SetDefaultContentHeaders(Type type, System.Net.Http.Headers.HttpContentHeaders headers, System.Net.Http.Headers.MediaTypeHeaderValue mediaType){headers.ContentType = new MediaTypeHeaderValue("application/javascript");}public override bool CanReadType(Type type){return false;}public override bool CanWriteType(Type type){return type == typeof(string);}public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext) {//TODO}}在我们开始写数据到流前,所有代码的执行都是很耗资源的,注意我们设置了一些默认值,我们添加了 UriPathExtensionMapping 这样格式化器就可以处理所有 .js 请求。所有的输出的内容 content-type 将是 application/javascript ,告诉浏览器这些都是 js 文件。也支持请求 text/html, 某些浏览器可能会这样。我们只支持序列化(单向的格式化,非反序列化),而且只接受字符串。WriteToStreamAsyncMediaTypeFormatter 方法根据如下流程将数据写到流中:1.文件名(Typescript 文件)2.检查 TS 文件是否存在3.检查缓存,如果相应的文件已存在则使用MD5 checksum 来确保文件没有改动4.如果没改动则直接从缓存中返回内容5.如果改动了,或者缓存文件不存在则使用 tcs.exe 编译并返回 JS6.将第5步生成的 JS 和 MD5 checksum 内容保存到缓存中以便继续使用public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext){var serverPath = HttpContext.Current.Server.MapPath("~/tsc");var filepath = Path.Combine(serverPath, value.ToString() + ".ts");var jsfilepath = Path.Combine(serverPath, value.ToString() + ".js");var tcs = new TaskCompletionSource();if (File.Exists(filepath)){string cachedItem = CheckCache(filepath, value as string);if (cachedItem != null){using (var writer = new StreamWriter(writeStream)){writer.Write(cachedItem);}}else{var typescriptCompiler = new ProcessStartInfo{UseShellExecute = false,RedirectStandardError = true,FileName = Path.Combine(serverPath, "tsc.exe"),Arguments = string.Format(""{0}"", filepath)};var process = Process.Start(typescriptCompiler);var result = process.StandardError.ReadToEnd();process.WaitForExit();if (string.IsNullOrEmpty(result)){using (var filestream = new FileStream(jsfilepath, FileMode.Open, FileAccess.Read)){filestream.CopyTo(writeStream);var fileinfo = new Dictionary();fileinfo.Add("md5", ComputeMD5(filepath));using (var reader = new StreamReader(filestream)){filestream.Position = 0;var filecontent = reader.ReadToEnd();fileinfo.Add("content", filecontent);}Cache.Set(value as string, fileinfo, DateTime.Now.AddDays(30));}}else{throw new InvalidOperationException("Compiler error: " + result);}}}tcs.SetResult(null);return tcs.Task;}private string CheckCache(string filepath, string filename){var md5 = ComputeMD5(filepath);var itemFromCache = Cache.Get(filename) as Dictionary;if (itemFromCache != null){if (itemFromCache["md5"] == md5){return itemFromCache["content"];}}return null;}private string ComputeMD5(string filename){var sb = new StringBuilder();using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read)){var md5 = new MD5CryptoServiceProvider();var bytes = md5.ComputeHash(file);for (int i = 0; i< bytes.Length; i++){sb.Append(bytes[i].ToString("x2"));}}return sb.ToString();}这样做的好处是文件修改时我们只需要编译一次就可以重复使用,这里选用的是直接在内存中缓存,你也可以使用其他方式,例如返回磁盘中的 js 文件之类的。原则上,我们是通过 MD5 checksum 来确定文件是否修改。注意这里使用了 TCS 进程的 StandardError 属性来判断编译器运行成功运行,如果编译过程中发生任何错误,该属性将会包含详细的错误信息,否则就是空的。测试假设有如下的 TS 文件,名为 demo.ts:class Person {constructor(public name) { }sing(text) {return this.name + " sings " + text;}}我在 HTML 页面中引用如下,使用相同的名称,只是将 ts 扩展名改为 js,这样该请求就会触发控制器调用编译方法:现在 Web API 将即时编译 demo.ts 并生成所需的 js 输出到浏览器,而我们并没有手工去编译它:而且 JS 内容是缓存的,以后再次刷新页面无需重新编译 ts 文件。我的调用结果:如果修改了 Typescript 代码:If I change the Typescript code to something else – i.e. let’s modify the sing method:class Person {constructor ( public name ) { }sing ( text ) {return this . name + " sings " + text + " and it's embarassing." ;}}我不需要重新编译,只需要刷新页面,因为 MD5 checksum 不匹配,因此 TS 会自动重新编译并生成新的 JS :总结我们前面提到的,Typescript 可使用纯 JavaScript 来编译生成 JavaScript,但因为 JS 编译器本身有 250Kb 大小,因此采用了这种方法来避免编译器的加载变得让人无法接受。当然,如果你使用的是其他的 Web 开发技术,也可以参考这个思路来实现。