最后更新: 2023/3/29 建立: 2019/5/14

(github) https://github.com/mirrortom/WebFilePublishVSIX

概述

2018年时想做一个使用C# razor语法写html页面工具.研究了asp.net的razor后没找到生成静态页面的办法,后来使用了三方的RazorEngine开源库,能够在asp.net环境之外,编译razor页面生成html.功能做成了VS插件,支持在VS2022Community版本使用.

项目改进

后来逐渐了解到使用razor的基础库编译razor生成html的方法,还有编写VS插件的方法,于是改进项目.新版本插件项目分成了2个项目,VS插件项目和实现功能的Windows服务项目.

插件项目没有使用原生的VSSDK开发,使用VisualStudio.Community.Toolkit这个VS扩展库.相比原生VSSDK,这个库极大的简化了开发,库对VSSDK进行了封装,用起来更简要了.XML配置文件有智能提示,并且关联到类,修改后对应的类值会更新.

razor方面,新开发了RazorService项目.使用Microsoft.AspNetCore.Razor.Language和Microsoft.CodeAnalysis.CSharp这2个基础库可以实现编译和运行razor文件输出html.

插件使用

在vs2022社区版安装这个插件,安装方法是找到插件项目的编译目录,运行WebFilePublishVSIX.vsix这个文件.按提示安装,过程中会要求关闭vs.

安装后以后打开VS,在"扩展"菜单下找到"Publish Web"菜单,里面的菜单是插件提供的.插件的功能就是将VS项目下的文件发布到输出目录.

主要功能

例如项目下有个index.cshtml页面,那么打开这个页面,点击菜单"发布当前编辑文件",插件会将文件编译/压缩,复制到输出目录.,这是插件的主要功能

插件会判断文件类型,对于js,css,jpg等web静态文件,则会压缩复制到发布目录.对于其它文件,会直接复制到发布目录.

发布文件 / 发布目录 / 发布项目

这3个命令是对于选中的不同对象.选中一个或者多个文件时,鼠标右键会显示"发布文件"菜单.选中文件夹时,显示"发布目录".选中项目时,显示发布项目.

目录的功能都是一样的,区别只是在于选择了不同范围内的文件.分别是选中的文件,选中的文件夹里的文件,和整个项目的文件.

菜单对应的快捷键:

  • 发布活动文件 (Alt + q + q)
  • 发布文件 (Alt + 1 + 1)
  • 发布目录 (Alt + 2 + 2)
  • 发布项目 (Alt + 3 + 3)

配置文件

发布选项由配置文件设定,位于项目根目录下的"publish.json",每次发布动作时,会读取这个文件.

首次发布时如果没有配置文件,插件程序会在项目根目录下生成默认的publish.json".

// 默认publish.json
{
    // 服务Ip地址 127.0.0.1
    "ip": "127.0.0.1",
    // 服务端口 50_015
    "port": 50015,
    // 源文件目录名(例如:src 相对路径,相对于项目根目录,这层目录不会发布,例如src/a/a.txt发布后是/a/a.txt)
    "sourceDir": "src",
    // 发布目录名(例如: 'dist' 或 'd:/pubdir' 相对路径或绝对路径)
    "distDir": "dist",
    // 对js,css,html压缩输出(0=不压缩,7=都压缩. 1=html,2=css,4=js)
    "miniOutput": 7,
    // 允许发布的文件扩展名,例: .html(.号开头)
    "allowExts": [ ".htm", ".html", ".cshtml", ".config", ".js", ".css", ".json", ".jpg", ".jpeg", ".gif", ".png", ".bmp", ".woff", ".woff2", ".ttf", ".svg", ".eot", ".otf" ],
    // 不允许发布的文件后缀名
    "denySuffix": [ "layout.cshtml", "part.cshtml", "part.js", "part.css", "min.js", "min.css" ],
    // 不允许发布的文件(例如:bundleconfig.json data/data.mdb 相对路径的文件名,相对于项目根目录)
    "denyFiles": [ "bundleconfig.json", "compilerconfig.json" ],
    // 不允许发布的目录(例如:cfg 相对路径,相对于项目根目录)
    "denyDirs": [],
    // razor搜索路径(相对路径,相对于项目根目录,razor页面引用的母版页部分页在此目录下查找
    "razorSearchDirs": [],
    // razor model数据,一个json键值对例如{ name : 'mirror' , ... },将作为dynamic对象作为Model传给cshtml文件,调用方法@Model.name
    "razorModel": {}
}
  

支持的razor功能

母版页,片段页,节,实体数据,这些基本的razor功能都已支持.

razor文件默认扩展名.cshtml,不支持其它扩展名.以下是一个母版页和主页的例子,展示了这些支持的功能.

// layout.cshtml
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link href="base.css" rel="stylesheet" />
    <title>@ViewBag.Title</title>
  </head>
  <body>
    // 这是套用该母版页的子页面的占位符
    @RenderBody()
    // 片段定义,第一个参数是片段名字.第二个参数:false子页面可以不实现,true必须实现 (可以引用多个)
    @RenderSection("Scripts", false)
  </body>
</html>

// index.cshtml
@{
  // 使用母板页.(母板页只能引用一个)
  // 路径可以是相对路径和绝对路径,扩展名可省略.
  Layout="layout.cshtml";
  // 支持ViewBag,这是一个Dynamic类型,对象是ExpandoObject
  ViewBag.Title = "页面标题";
  // 页面其它变量
  string val1 = "hello world";
}
<div>
  
  // 使用@Html.Raw()输出html字符串
  <div>@Html.Raw(html文本)</div>
  // C#代码
  @for (int i = 0; i < 10; i++)
  {
    <div>循环输出 @i</div>
    // 输出字符串,不带html标记的,可以用 @("")
    @("字符串")
  }

  // 输出模型数据,这个数据来自配置文件的razorModel:{}
  @Model.name
</div>

文件发布流程

选择文件后按下菜单,发布就开始了.

  • 检查文件是否选中,项目是否为活动项目
  • 检查publish.json
  • 计算文件路径,源文件路径和发布目标路径
  • 使用配置文件定义的规则,筛选符合发布要求的文件
  • 提交到服务,等待服务处理结果
    • 编译razor
    • 压缩css/js/html
    • 复制.按文件在项目中的目录结构复制到发布目录
  • 打印信息到"输出"窗口

参考项目和库

项目参考使用的其它项目和库.

RazorEngine

插件第一个版本的razor编译功能使用是就的这个库,属于比较早期的razor库了,在.net framework时代就已有了.实现了asp.net razor的大部分功能,支持Roslyn编译.

文档: https://antaris.github.io/RazorEngine/index.html

git: https://github.com/Antaris/RazorEngine

编译生成razor页面时,RazorEngine要使用RoslynCompiler工具编译.否则会出现编译卡死.在控制台程序中,默认的编译工具不会有问题,但做到插件中后出现卡死.这个VSIX项目最新使用了razorengine4.5版本,自带有Roslyn.

原因参考: https://github.com/Antaris/RazorEngine/issues/286 razorengine是虚拟机环境,cshtml模板文件会当成代码在这虚拟机里编译和运行,所以模板里可以写C#程序.

这些程序一定引用了其它库,比如System.dll这个基础的库.那么虚拟机要加载这些库来支持程序运行.razorengine默认会加载所有的库,从运行它的程序环境中.如果是在控制台里运行razorengine,那么这个控制台程序里可能没几个库,全部加载不会有速度慢的情况.

但是,如果是在VSIX的运行环境里运行,那么库很多,有的库还比较大,测试时发现加载的库多达680个.这个就是为什么在VSIX里运行很慢甚至卡死的原因,issue上的解决办法是为razorengine提供指定的库,而不是全部库.这个办法有效但测试时没有成功,在指定库时报错了.第2个办法是Roslyn,就是它了,简单方便,其中的原因没研究.

NUglify

C#版本的压缩库,除了js,css外,主要是能压缩html.插件使用这个库实现压缩功能.

git: https://github.com/xoofx/NUglify

RazorEngineCore

在.net平台运行的razor编译库,它使用的是新版本的RazorAPI,还有Roslyn编译器.项目的razor功能是参考这个项目实现.

git: https://github.com/adoconnection/RazorEngineCore

VisualStudio.Community.Toolkit

VS扩展开发社区工具包,使用这个开发VS扩展比较方便了,原生的VSSDK比较复杂.项目新版本使用的就是这个库.

git: https://github.com/VsixCommunity/Community.VisualStudio.Toolkit

有用的VS插件

BundlerMinifier

可以压缩js,将多个js合并成一个js.用于打包不错.

git: https://github.com/madskristensen/BundlerMinifier

一个碰到的坑记录 (2020年1月3日)

BundlerMinifier的配置文件bundleconfig.json一般在项目根目录下,右击它,选"Bundler & Minifier",弹出的菜单有一项"Produce Output Files",这个要勾选,否则"Update Bundles"这个菜单点击不了.

WebCompiler

这个插件很全面,能压缩js,css,html.还能处理css预编译语言scss,stylus

git: https://github.com/madskristensen/WebCompiler

ImageSprites

将图片合成一张图片,然后自动生成css的位置.做css裁剪时,再也不用量位置了

git: https://github.com/madskristensen/ImageSprites

ImageOptimizer

能将图片缩小.类似熊猫压缩图片那种功能

git: https://github.com/madskristensen/ImageOptimizer

Markdown Editor

markdown文档比较简洁,很多项目说明使用这种.git上的readme.md文件就是这语法的.形式上比html简洁,五分钟能上手.这个插件可以在编辑时对照效果.

git: https://github.com/madskristensen/MarkdownEditor