前言
正常渲染拿到数据后渲染,三步走:格式化数据、编译模板、渲染数据
如下例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小米商城</title>
</head>
<style>
* {
margin: 0;
padding: 0;
}
body,
html {
background-color: whitesmoke;
font-size: 12px;
}
ul {
list-style: none;
}
li {
display: inline-block;
padding: 0 10px;
background: white;
}
h3 {
text-align: center;
}
p {
text-align: center;
}
.title {
font-size: 16px;
font-weight: 400;
color: #333;
line-height: 42px;
}
.desc {
color: #b0b0b0;
}
.price {
color: #444;
line-height: 58px;
}
span {
color: red;
}
</style>
<body>
<ul id="product-list">
<!-- <li>
<img src="img/1.webp" alt="">
<h3 class="title">小米10青春版5G</h3>
<p class="desc">50倍潜望式驾驶员/轻薄5G手机</p>
<p class="price"><span>2099</span>元起</p>
</li>
<li>
<img src="img/2.webp" alt="">
<h3 class="title">小米10</h3>
<p class="desc">骁龙865/1亿预期相机</p>
<p class="price"><span>3799</span>元起</p>
</li>
<li>
<img src="img/3.webp" alt="">
<h3 class="title">Redmi K30 Pro</h3>
<p class="desc">双模5G,骁龙865,投放全面屏</p>
<p class="price"><span>2699</span>元起</p>
</li>
<li>
<img src="img/4.webp" alt="">
<h3 class="title">Redmi K30 Pro准版</h3>
<p class="desc">双模5G,骁龙865,投放全面屏</p>
<p class="price"><span>3499</span>元起</p>
</li> -->
</ul>
<script>
const productData = {
title: '小米手机',
list: [
{
productImgUrl: 'img/1.webp',
productCUID: '088722478239',
productTitle: 'PTD16-小米10青春版5G',
productDesc: '50倍潜望式驾驶员/轻薄5G手机',
productPrice: 209000,
productCount: 999
}, {
productImgUrl: 'img/2.webp',
productCUID: '088789478639',
productTitle: 'PTD16-小米10', //
productDesc: '骁龙865/1亿预期相机',
productPrice: 379000, //3790.50 一般不会直接传代小数的数字,因为前端的计算是有问题的,一般数据是乘百发送,或者发送字符串自己转换
productCount: 999
}, {
productImgUrl: 'img/3.webp',
productCUID: '088789478339',
productTitle: 'PTD16-Redmi K30 Pro',
productDesc: '双模5G,骁龙865,投放全面屏',
productPrice: 269900,
productCount: 0
}, {
productImgUrl: 'img/4.webp',
productCUID: '088789471239',
productTitle: 'PTD16-Redmi K30 Pro准版',
productDesc: '双模5G,骁龙865,投放全面屏',
productPrice: 349900,
productCount: 99
}]
};
let oUl = document.querySelector('#product-list');
class TempRender {
constructor(data = {}, parentEle = '') {
this.parentEle = document.querySelector(parentEle);
this.originalData = data;
this.list = this.formatData(this.originalData.list)
this.templateHtml = this.getTemplate(this.data);
this.renderHtml();
}
//格式化数据
formatData(list = []) {
if (list.length === 0) {
return [];
}
list = list.map(({ productImgUrl, productTitle, productDesc, productPrice }) => {
productPrice = productPrice / 100;
productTitle = productTitle.replace(/PTD16-/gi, '');
productImgUrl = productImgUrl ?? 'img/1.webp'; //?? 是||的进阶版,如果没有值的话设置初始值
productTitle = productTitle ?? '暂无信息';
productDesc = productDesc ?? '暂无信息';
productPrice = productPrice ?? '暂无信息';
return {
productImgUrl,
productTitle,
productDesc,
productPrice
}
})
return list;
}
//编译模板
getTemplate() {
let template = '';
for (let i = 0, len = this.list.length; i < len; i++) {
let item = this.list[i];
template += `
<li>
<img src="${item?.productImgUrl }" alt="">
<h3 class="title">${item?.productTitle}</h3>
<p class="desc">${item?.productDesc}</p>
<p class="price"><span>${item?.productPrice}</span>元起</p>
</li>
`;
}
return template;
}
// 渲染数据
renderHtml() {
this.parentEle.innerHTML += this.templateHtml;
}
}
new TempRender(productData, '#product-list');
</script>
</body>
</html>
在该业务流程中模板是写死的,变量结构都是写死的,复用性为0,想着把模板当参数传进来,为了效率就借助模板引擎
模板引擎的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小米商城</title>
</head>
<style>
* {
margin: 0;
padding: 0;
}
body,
html {
background-color: whitesmoke;
font-size: 12px;
}
ul {
list-style: none;
}
li {
display: inline-block;
padding: 0 10px;
background: white;
}
h3 {
text-align: center;
}
p {
text-align: center;
}
.title {
font-size: 16px;
font-weight: 400;
color: #333;
line-height: 42px;
}
.desc {
color: #b0b0b0;
}
.price {
color: #444;
line-height: 58px;
}
span {
color: red;
}
</style>
<body>
<ul id="product-list">
</ul>
<script src="js/template-web.js"></script>
<script type="text/template" id="testTemp">
<h1>{{title}}</h1>
{{each list}}
<li>
{{set price = $value.productPrice}}
<img src="{{$value.productImgUrl}}" alt="">
<h3 class="title">{{$value.productTitle}} {{$index}}</h3>
<p class="desc">{{$value.productDesc}}</p>
<p class="price"><span>{{price}}</span>元起</p>
<!-- <i style="color:red">{{$value.productCount == 0?'无货':'有货'}}</i> -->
{{if $value.productCount == 0}}
<i style="color:red">无货</i>
{{/if}}
{{if $value.productCount != 0}}
<i style="color:green">有货</i>
{{/if}}
</li>
{{/each}}
</script>
<script>
const productData = {
title: '小米手机',
list: [
{
productImgUrl: 'img/1.webp',
productCUID: '088722478239',
productTitle: 'PTD16-小米10青春版5G',
productDesc: '50倍潜望式驾驶员/轻薄5G手机',
productPrice: 209000,
productCount: 999
}, {
productImgUrl: 'img/2.webp',
productCUID: '088789478639',
productTitle: 'PTD16-小米10', //
productDesc: '骁龙865/1亿预期相机',
productPrice: 379000, //3790.50 一般不会直接传代小数的数字,因为前端的计算是有问题的
productCount: 999 //一般数据是乘百发送,或者发送字符串自己转换
}, {
productImgUrl: 'img/3.webp',
productCUID: '088789478339',
productTitle: 'PTD16-Redmi K30 Pro',
productDesc: '双模5G,骁龙865,投放全面屏',
productPrice: 269900,
productCount: 0
}, {
productImgUrl: 'img/4.webp',
productCUID: '088789471239',
productTitle: 'PTD16-Redmi K30 Pro准版',
productDesc: '双模5G,骁龙865,投放全面屏',
productPrice: 349900,
productCount: 99
}]
};
let oUl = document.querySelector('#product-list');
let templateHtml = template('testTemp', formatData(productData));
oUl.innerHTML = templateHtml;
function formatData(data) {
let list = data.list;
data.list = list.map(({ productImgUrl, productTitle, productDesc, productPrice, productCount }) => {
productPrice = productPrice / 100;
productTitle = productTitle.replace(/PTD16-/gi, '');
productImgUrl = productImgUrl || 'img/1.webp';
productTitle = productTitle ?? '暂无信息';
productDesc = productDesc || '暂无信息';
productPrice = productPrice || '暂无信息';
return {
productImgUrl,
productTitle,
productDesc,
productPrice,
productCount
}
})
return data;
}
</script>
</body>
</html>
常见模板工具
jade.js doT.js tpl.js art-template.js handlebars
模板引擎工具的主要用法,就是给数据给模板,然后返回结果.
学习模板引擎主要学习模板引擎的语法
art-template:号称是性能最快的模板引擎
地址: http://aui.github.io/art-template/
handlebars:
地址: https://handlebarsjs.com/
模板语法
模板引擎本质上是通过特定标识符号和语句嵌入HTML文本中 , 通过输入数据 的到 嵌套好数据的HTML文本内容用于生产页面 数据+模板 = 结构内容 , 其中模板语法为核心 下面用art-template语法演示
<ul class="clearfix">
{{each target}}
<li> 序号: {{$index}} 姓名 :{{$value.name}} 年龄: {{$value.age}}</li>
{{/each}}
</ul>
渲染方法
模板工具会提供模板渲染方法, 调用传参 数据 与 模板 返回生成的结构内容结果字符串
let html = template('testTemp', { target: [{ name: '赫赫', age: 11 }, { name: '豆丁', age: 6 }, { name: '西瓜', age: 5 }, { name: '莉莉莎', age: 8 }, { name: '杰克', age: 22 }, { name: '奥米拉', age: 3 }] })
document.body.innerHTML = html;
效果
原生模板实现
原理就是正则替换内容
原理步骤:
-
正则替换 规定标识符内容
普通标签
<%= %> 内容替换成变量的值
<% %> 内容替换成js代码
-
动态生成渲染函数 new Function(函数字符串)
-
通过正则格式化模板内容
-
拼接返回格式化模板内容代码字符串
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>商品</title>
</head>
<body>
<script type="text/template" id="temp">
<h2><%= data.title %></h2>
<ul>
<% for(let i=0; i < data.list.length; i++) { %>
<li data-index=<%=i%>><%= data.list[i] %>
<% if(data.list[i].length>3) {%>
<%= '哈哈哈' %>
<% } %>
</li>
<% } %>
</ul>
</script>
<script>
function getTempScript(id) {
let script = document.querySelector(`#${id}[type="text/template"]`);
return `${script.innerHTML}`
}
function compile(id) {
let template = getTempScript(id);
let evalExp = /<%=(.+?)%>/g,
expr = /<%([\s\S]+?)%>/g;
template = template.replace(evalExp, '`) \n echo($1) \n echo(`').replace(expr, '`) \n $1 \n echo(`');
template = 'echo(`'+template +'`)';
let funcStr = `
let html = '';
function echo(content) {
html += content;
}
${template}
return html;
`
return funcStr;
}
let funStr = compile('temp');
let comp1 = new Function('data',funStr);
document.body.innerHTML += comp1({ title: '标题', list: ['糖醋鱼', '香辣蟹', '红烧排骨'] })
</script>
</body>
</html>