【问题标题】:Filtering Markdown content in Jade / Node.js在 Jade / Node.js 中过滤 Markdown 内容
【发布时间】:2012-02-27 21:52:00
【问题描述】:

尊敬的黑客,

我希望在 Jade 模板中过滤一个充满 Markdown 的字符串。 我在变量中有 Markdown。

Jade 在 Markdown 中插入一个变量就好了:

这个:

var jade = require('jade');

var jade_string = [
    ':markdown',
    '    ## This is markdown!',
    '    * First',
    '    * #{var2}',
    '    * Third'
].join('\n');

var fn = jade.compile( jade_string, { pretty: true } );

console.log( fn( { var1: "First!", var2: "Second!" } ) );

产生这个:

<h2>This is markdown!</h2>

<ul>
<li>First</li>
<li>Second!</li>
<li>Third</li>
</ul>

但是,我所拥有的是变量内实际完整的 Markdown。 还有这个:

var jade = require('jade');

var jade_string = [
    'div.markedup',
    '    :markdown',
    '        \\#{var2}'
].join('\n');

var fn = jade.compile( jade_string, { pretty: true } );

var markdown = [
    '## I am marked!',
    '* One',
    '* Two'
].join('\n');

console.log( fn( { var1: "First!", var2: markdown } ) );

只提供这个:

<div class="markedup"><p>## I am marked!
* One
* Two</p>
</div>

因此,在我看来,Jade 过滤块 之前 执行任何变量 插值,然后在生成的 HTML 中插入变量。这是 如果你想用 Markdown 写你的 模板 很好,但这并不多 如果您想在 Markdown 中编写 内容,请提供帮助。

我知道我可以通过更多编程来解决这个问题,但我觉得我 一定是遗漏了什么。毕竟,将 Markdown 内容的 sn-ps 保存在 一个数据库并将生成的 HTML 片段填充到模板中看起来像 :markdown 过滤器最明显的用例。

在 Jade 中是否有“正常”的方式来做到这一点?

在此先感谢您的启发。

【问题讨论】:

    标签: node.js pug


    【解决方案1】:

    我认为答案更多的是编程,但我会向您展示我的工作。我使用自定义中间件,让我在获得最终 HTML 文档输出之前组合任意转换过程。因此,例如,我的 middleware.js 模块中有以下过滤器,我将依次解释。

    如此简单的视图只需使用普通的翡翠及其各种过滤器,用于 Markdown、javascript、coffeescript。某些视图(例如博客文章)需要更复杂的中间件链,如下所示。

    首先,根据请求,我创建包含此响应的核心内容的文件,并将其设置为res.viewPath 上的属性。这可以是原始 HTML 片段文件或降价文件。然后我通过一系列中间件转换发送响应。我使用res.htmlres.dom 来存储正在构建的响应的中间表示。

    这只是存储原始 HTML(只是一个没有头部或布局的文档正文片段)。

    html = function(req, res, next) {
      if (!/\.html$/.test(res.viewPath)) return next();
      return fs.readFile(res.viewPath, "utf8", function(error, htmlText) {
        res.html = htmlText;
        return next(error);
      });
    };
    

    这会将markdown文件转换为HTML(使用markdown-js模块)。

    markdownToHTML = function(req, res, next) {
      if (!/\.md$/.test(res.viewPath)) return next();
      return fs.readFile(res.viewPath, "utf8", function(error, markdownText) {
        res.html = markdown(markdownText);
        return next(error);
      });
    };
    

    我有一个子布局,它位于我的主布局中,但围绕着每篇博文。因此,我将博客文章包装在此处的子布局中。 (未显示的单独代码从 json 元数据文件生成 res.post 对象)。

    blogArticle = function(req, res, next) {
      var footerPath, post;
      post = res.post;
      footerPath = path.join(__dirname, "..", "templates", "blog_layout.jade");
      return fs.readFile(footerPath, "utf8", function(error, jadeText) {
        var footerFunc;
        if (error) return next(error);
        footerFunc = jade.compile(jadeText);
        res.html = footerFunc({
          post: post,
          body: res.html
        });
        return next();
      });
    };
    

    现在我将布局围绕主要内容 HTML。请注意,我可以在此处设置页面标题之类的内容,或者等到稍后,因为我可以在此之后通过 jsdom 操作响应。我做body: res.html || "",这样我可以渲染一个空布局,如果更方便的话,稍后插入正文。

    exports.layout = function(req, res, next) {
      var layoutPath;
      layoutPath = path.join(__dirname, "..", "templates", "layout.jade");
      return fs.readFile(layoutPath, "utf8", function(error, jadeText) {
        var layoutFunc, locals;
        layoutFunc = jade.compile(jadeText, {
          filename: layoutPath
        });
        locals = {
          config: config,
          title: "",
          body: res.html || ""
        };
        res.html = layoutFunc(locals);
        return next(error);
      });
    };
    

    这就是真正强大的东西的来源。我将 HTML 字符串转换为 jsdom 文档对象模型,该模型允许在服务器端进行基于 jQuery 的转换。下面的 toMarkup 函数只允许我在没有额外的 &lt;script&gt; 标记的情况下为我们的 jsdom 添加的内存中的 jquery 获取 HTML。

    exports.domify = function(req, res, next) {
      return jsdom.env(res.html, [jqueryPath], function(error, dom) {
        if (error) return next(error);
        res.dom = dom;
        dom.toMarkup = function() {
          this.window.$("script").last().remove();
          return this.window.document.doctype + this.window.document.innerHTML;
        };
        return next(error);
      });
    };
    

    所以这是我做的一个自定义转换。这可以用真正有效的 HTML 替换像 &lt;flickrshow href="http://flickr.com/example"/&gt; 这样的虚构 DSL 标签,否则这将是一个很大的讨厌的 &lt;object&gt; 样板,我将不得不在每篇博客文章中复制,如果 flickr 曾经更改过他们使用的样板标记,在许多单独的博客文章降价文件中修复它将是一种维护痛苦。他们目前使用的样板在 flickrshowTemplate 变量中,并包含一些小胡子占位符 {URLs}

    exports.flickr = function(req, res, next) {
      var $ = res.dom.window.$;
      $("flickrshow").each(function(index, elem) {
        var $elem, URLs;
        $elem = $(elem);
        URLs = $elem.attr("href");
        return $elem.replaceWith(flickrshowTemplate.replace(/\{URLs\}/g, URLs));
      });
      return next();
    };
    

    同样用于嵌入 youtube 视频。 &lt;youtube href="http://youtube.com/example"/&gt;.

    exports.youtube = function(req, res, next) {
      var $ = res.dom.window.$;
      $("youtube").each(function(index, elem) {
        var $elem, URL;
        $elem = $(elem);
        URL = $elem.attr("href");
        return $elem.replaceWith(youtubeTemplate.replace(/\{URL\}/, URL));
      });
      return next();
    };
    

    现在我可以根据需要更改标题,添加/删除 javascript 或样式表等。这里我在布局已经呈现后设置标题。

    postTitle = function(req, res, next) {
      var $;
      $ = res.dom.window.$;
      $("title").text(res.post.title + " | Peter Lyons");
      return next();
    };
    

    好的,是时候回到最终的 HTML 了。

    exports.undomify = function(req, res, next) {
      res.html = res.dom.toMarkup();
      return next();
    };
    

    现在我们发货了!

    exports.send = function(req, res) {
      return res.send(res.html);
    };
    

    为了将它们按顺序捆绑在一起并快速使用它,我们这样做

    postMiddleware = [
      loadPost,
      html,
      markdownToHTML,
      blogArticle,
      layout,
      domify,
      postTitle,
      flickr,
      youtube,
      undomify,
      send
    ]
    
    app.get("/your/uri", postMiddleware);
    

    简洁?不,干净吗?我想是这样。灵活的?极其。快如闪电?可能不会很快,因为我相信 jsdom 是您可以做的更重量级的事情之一,但我将其用作静态站点生成器,因此速度无关紧要。当然,如果它比相应的 Markdown 页面正文内容文件更新,那么在中间件链的开头和结尾添加另一个函数将最终的 HTML 写入静态文件并直接提供它是微不足道的。 Stackoverflowers,我很想听听关于这种方法的想法和建议!

    【讨论】:

    • 这很有趣,谢谢。我实际上还没有考虑过深入了解它,但如果我的“节点实验”通过原型阶段,这是一个很好的思考。性能问题(如果有的话)可以通过像 Jade 那样将整个东西编译成一个函数并缓存该函数来缓解。
    【解决方案2】:

    玉人说将变量传递给过滤器is not supported。我无法理解 Peter Lyons 的答案,所以我使用了这个:

    marked = require 'marked'
    marked.setOptions
      <my options>
    app.locals.md = marked
    

    然后在翡翠:

    != md(<markdown string>)
    

    又快又脏。可能不是最理想的,因为它每次都运行转换而不缓存结果(我认为),但至少它可以工作。

    (编辑)

    您还可以使用marked 在浏览器中呈现markdown,从您的服务器上卸载一些工作并加快加载速度。

    【讨论】:

      猜你喜欢
      • 2012-03-14
      • 2011-06-08
      • 2021-06-03
      • 2015-09-24
      • 2017-07-10
      • 1970-01-01
      • 1970-01-01
      • 2020-08-31
      • 2023-03-29
      相关资源
      最近更新 更多