MDN对于template的解释如下:
<template>
: 内容模板元素
HTML内容模板(<template>
)元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后可以(原文为 may be)在运行时使用JavaScript实例化。
将模板视为一个可存储在文档中以便后续使用的内容片段。虽然解析器在加载页面时确实会处理<template>
元素的内容,但这样做只是为了确保这些内容有效;但元素内容不会被渲染。
举个🌰
是不是看的有点迷糊,没关系,我们稍微写点代码。
如下,你们觉得真正在浏览器中run起来,文字是否可见呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>template</title>
</head>
<body>
<div>这是一个茶杯</div>
</body>
<script>
</script>
</html>
毫无疑问,文字可见
好,接下来我们使用template标签,看看是什么情况
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>template</title>
</head>
<body>
<template>
<div>这是一个茶杯_template</div>
</template>
</body>
<script>
</script>
</html>
大家不妨想下,现在文字是否可见呢?
结果很显然,当然是不可见啦。。。不然还怎么做对照实验(PS:手动狗头)?
那么为什么会出现这种情况呢?我们可以思考下,节点不可见,要么是没渲染出来,要么是样式问题。现在可以看到节点是渲染出来了,那我们去看下样式。
发现template的display属性已经被浏览器默认设为none,当然不可见啦。好,我们现在将它设为block,再思考下,可见吗?
如下,发现文字仍然不可见
查了下样式,发现是因为高度为0。好,继续设置高度100px。结果有点奇怪,发现文字仍然不可见,这很显然不符合常用标签的使用习惯。很显然,浏览器不希望我们按照常规标签去使用template。
那我们换种想法,通过操作JS的方式来显示这串文本。如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>template</title>
</head>
<body>
<template id="temp">
<div>这是一个茶杯_template</div>
</template>
</body>
<script>
const temp = document.getElementById('temp')
console.log('temp.innerText = ', temp.innerText)
console.log('temp.innerHTML = ', temp.innerHTML)
document.body.innerHTML = temp.innerHTML
</script>
</html>
很显然,这就已经显示出来了,DOM结构就比较符合我们的常用书写方式。
但这种使用 DOMNode 或者 DOMString 的操作,不太符合前端人的写法,而且也容易造成一些安全隐患,如XSS。
既然如此,我们回头去看下template的MDN文档, 发现了 content 这么一个属性。我们将其打印出来,得到的便是一个文档片段(fragment),如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>template</title>
</head>
<body>
<template id="temp">
<div>这是一个茶杯_template</div>
</template>
</body>
<script>
const temp = document.getElementById('temp')
console.log('temp.content = ', temp.content)
</script>
</html>
从MDN中,可以看到这么一句话:content属性包含了模板所表示的DOM树。既然是一颗DOM树,那么DOM常用的API自然也能使用啦(如:getElementById,querySelector...),有兴趣的小伙伴不妨验证下。
继续看MDN文档,发现用到了一个API:document.importNode。
该方法接收两个参数,第一个参数类型是HTMLNode(HTML节点,只能是单个节点,不能是类数组的写法),第二个参数类型是boolean(是否深度克隆,默认为true)。如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>template</title>
</head>
<body>
<div id="pvue"></div>
<template id="temp">
<span id="span">标题</span>
<div>这是一个茶杯_template</div>
</template>
</body>
<script>
const root = document.getElementById('pvue')
const temp = document.getElementById('temp')
const realNode = document.importNode(temp.content.querySelector('div'), true)
root.appendChild(realNode)
</script>
</html>
关于 template 标签介绍就暂时到这里了。关于他的一些特性,如下所示
- 它的内容在激活之前一直处于惰性状态。本质上,这些标记就是隐藏的 DOM,它们不会被渲染。
- 处于模板中的内容不会有副作用。脚本不会运行,图片不会加载,音频不会播放,…直到模板被使用。
- 模板能够被放置在任何位置,包括
<head>、<body> 或 <frameset>
,并且任何能够出现在以上元素中的内容都可以放到模板中。 注意,”任何位置”意味着<template>
能够安全的出现在 HTML 解析器不允许出现的位置…几乎可以作为任何内容模型的子节点。
初心
其实,最开始的本意是想将template和vue相结合起来,但随着对vue的理解,发现vue是将template编译成AST,再去做静态分析。所以后续和vue的对比,也就不会往下进行。
以后
但是,我设想了下,template和shadow dom确实是可以作为未来的组件化方向去研究。毕竟天然的隔离性摆在这儿,避免了classname的冗余。
引用
感谢张鑫旭大佬和其他我不认识的大佬