处理 HTML 包含的最简单方法

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。从 200 美元的免费信用额度开始!

令我非常惊讶的是,HTML 从未有任何方法可以包含其他 HTML 文件。而且,地平线上似乎也没有任何东西可以解决这个问题。我指的是直接的包含,就像获取一段 HTML 并将其直接放到另一个 HTML 中一样。例如,整个互联网的大部分用例,所有页面都包含一个页眉和页脚

...
<body>
   <include src="./header.html"></include>

   Content

   <include src="./footer.html"></include>
</body>
...

顺便说一句,那不是真的。我只是希望它是真的。

人们一直在寻求其他语言来为他们解决这个问题。从某种意义上说,它是 HTML 预处理。早在我们预处理 CSS 之前,我们就一直在使用工具来操作我们的 HTML。我们现在仍在这样做,因为包含的想法在世界上几乎每个网站上都很有用。

使用 PHP

你能改用 PHP 吗?

...
<body>
   <?php include "./header.html" ?>

   Content

   <?php include "./footer.html" ?>
</body>
...

这将在服务器级别执行包含操作,使对它的请求发生在服务器上的文件系统级别,因此它应该比客户端解决方案快得多。

使用 Gulp

什么比服务器端包含更快?如果包含在它甚至在服务器上之前就被预处理了。 Gulp 有各种各样的处理器可以做到这一点。一个是 gulp-file-include

它看起来像这样

...
<body>
   @@include('./header.html')

   Content

   @@include('./footer.html')
</body>
...

你会像这样处理它

var fileinclude = require('gulp-file-include'),
  gulp = require('gulp');
 
gulp.task('fileinclude', function() {
  gulp.src(['index.html'])
    .pipe(fileinclude({
      prefix: '@@',
      basepath: '@file'
    }))
    .pipe(gulp.dest('./'));
});

看起来这个特定的插件有一些花哨的功能,你可以在其中向包含项传递变量,从而可以创建小型数据驱动的组件。

使用 Grunt

这就是 grunt-bake 插件的作用。您将配置 Grunt 来处理您的 HTML

grunt.initConfig({
    bake: {
        your_target: {
            files: {
                "dist/index.html": "app/index.html",
            }
        }
    }
});

然后你的 HTML 可以使用这种特殊的语法进行包含

...
<body>
   <!--(bake header.html)-->

   Content

   <!--(bake footer.html)-->
</body>
...

使用 Handlebars

Handlebars 有部分

你注册它们

Handlebars.registerPartial('myPartial', '{{name}}')

然后使用它们

{{> myPartial }}

它还有一些花哨的功能,允许评估和传递数据。您仍然需要一个处理器来运行它,可能是类似 gulp-handlebars 的东西。

说到使用花括号的模板语言……Mustache 也有它们

使用 Pug

Pug 是一种 HTML 预处理器,它具有全新的 HTML 语法,更加简洁。 它有包含功能

...
body
   include ./header.html"

   p Content

   include ./footer.html"

   ...

然后你用类似 gulp-pug 的东西运行它。

使用 Nunjucks

我非常喜欢 Nunjucks! Nunjucks包含。你会像这样做到

...
<body>
   {% include "./header.html" %}

   Content

   {% include "./footer.html" %}
</body>
...

如果你把它放在一个名为index.njk的文件中,你可以用一个简单的 Node 脚本将其处理成 index.html,如下所示

const nunjucks = require("nunjucks");
const fs = require("fs");

fs.writeFile("index.html", nunjucks.render("index.njk"), function(err, data) {
  if (err) console.log(err);
  console.log("Compiled the Nunjucks, captain.");
});

或者用类似 gulp-nunjucks 的东西处理它。

11ty 内置了 Nunjucks,以及迄今为止提到的许多其他内容。如果你真的在构建一个小网站,这可能对你有帮助。

使用 Ajax

假设你有……

<body>
  
  <header></header>
  
  Content.
  
  <footer></footer>

</body>

您可以从各自的文件中获取页眉和页脚的内容,并将内容转储到其中。

fetch("./header.html")
  .then(response => {
    return response.text()
  })
  .then(data => {
    document.querySelector("header").innerHTML = data;
  });

fetch("./footer.html")
  .then(response => {
    return response.text()
  })
  .then(data => {
    document.querySelector("footer").innerHTML = data;
  });

说到 JavaScript……如果你使用任何 JavaScript 框架构建你的网站,通过组件构建是主要的工作,并且将你想要包含在其他文件中的部分分解出来应该不成问题。某种import Header from "./header.js";<Header />是在 React 领域中的范畴。

使用 iframe

你可以这样做

<body>
  
  <iframe src="./header.html"></iframe>
  
  Content.
  
  <iframe src="./footer.html"></iframe>
  
</body>

但是这些 iframe 中的内容不共享相同的 DOM,所以有点奇怪,更不用说加载速度慢且难以设置样式(因为 iframe 不知道其内容的高度)。

Scott Jehl 记录了一个很酷的想法:您可以让 iframe 将其自身的内容注入到父页面中,然后删除自身。

<body>
  
  <iframe src="header.html" onload="this.before((this.contentDocument.body||this.contentDocument).children[0]);this.remove()"></iframe>
  
  Content.
  
  <iframe src="footer.html" onload="this.before((this.contentDocument.body||this.contentDocument).children[0]);this.remove()"></iframe>
  
</body>

Kolya Korruptis 这样写道,如果你的 HTML 文件包含 body 的第一个子元素以外的内容,此适配将包含更多内容

<iframe src="included.html" onload="this.insertAdjacentHTML('afterend', (this.contentDocument.body||this.contentDocument).innerHTML);this.remove()"></iframe>

使用 Jekyll

Jekyll 是一个基于 Ruby 的静态网站生成器,具有 包含。您将包含文件保存在/_includes/文件夹中,然后

<body>
  {% include header.html %}
  
  Content.

  {% include footer.html %}
</body>

Jekyll 非常重要,所以我在此处特别指出它,但是有 大量的静态网站生成器,我敢打赌其中任何一个都可以进行包含。

使用 Sergey

好的,我还要指出一个 SSG,因为它很新并且非常专注。 Sergey 具有 Web Components 样式的格式

<body>
  <sergey-import src="header" />

  Content.

  <sergey-import src="footer" />
</body>

您将文件命名为header.htmlfooter.html,并将它们放在/includes/中,然后当您运行它提供的 npm 脚本时,它将生成一个包含已处理包含项的构建。

使用 Apache SSI

Apache 是一款超级常见的 Web 服务器,可以 进行包含。您需要像这样操作

<body>
		
  <!--#include file="./header.html" -->
  
  Content
  
  <!--#include file="./footer.html" -->
  
</body>

但是您需要正确的 Apache 配置才能允许这些操作。我尽力尝试创建一个可用的演示,但没有太多运气。

我尝试在 Apache 服务器上的文件夹中使用.htaccess,并启用我认为正确的内容

Options +Includes

AddType text/html .html
AddOutputFilter INCLUDES .html

我相信有一种方法可以使其正常工作,如果您确实做到了,那么它无需任何其他依赖项就非常棒。

使用 CodeKit

仅限 Mac,但 CodeKit 有一个它处理的名为 Kit 的特殊语言,其中 90% 的重点是 HTML 包含。它使用特殊的 HTML 注释

...
<body>
   <!-- @import "./header.html" -->

   Content

   <!-- @import "./footer.html" -->
</body>
...

使用 Dreamweaver

哈哈,开玩笑。但它确实是一件事DWT,宝贝。

我的天哪

有很多方法,不是吗?

就像我在开头说的,我非常惊讶 HTML 本身没有直接解决这个问题。并不是说我认为对于性能来说,让<include>语句在我们的代码中触发网络请求是一个好主意,但它似乎与平台一致。直接使用 ES6 导入而不进行打包并不总是好主意,但我们有它们。在 CSS 中@import CSS 并不总是好主意,但我们有它。如果平台有本机语法,也许其他工具会以此为基础,就像 JavaScript 打包器支持 ES6 导入格式一样。