handlebars
开发一个页面组件,原本想着本人纯手工撸,通过大大的领导发现有个叫 handleBars 的模版工具,能够缩小很多开发工作量,于是试了一下,这货色有点老,也有坑,但总体用下来比本人纯手写款式还是香很多。
Handlebars 是一种简略的模版语言,应用模版和输出对象来生成 HTML 或者其余文本格式,是个带有嵌入式的 handlebars 表达式,就相似{{表达式}},在执行模版时,这些表达式就会被输出对象中的值所替换
1. 装置
1. 应用 Handlebars 最快的形式就是 cdn
<script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js"></script>
<script>
// compile the template
var template = Handlebars.compile("Handlebars <b>{{doesWhat}}</b>");
// execute the compiled template and print the output to the console
console.log(template({ doesWhat: "rocks!"}));
</script>
2. 应用 npm 装置
(1)npm install handlebars
应用 webpack 进行打包,还须要额定安装包(handlebars-loader 和 handlebars-webpack-plugin)进行配置
const HtmlWebpackPlugin = require("html-webpack-plugin");
const HandlebarsWebpackPlugin = require("handlebars-webpack-plugin");
const path = require("path");
module.exports = {
mode: "development",
entry: "./src/index.ts",
output: {
filename: "handlebars-demo.js",
libraryTarget: "umd",
globalObject: "this",
library: "Demo",
libraryExport: "default",
},
module: {
rules: [
{
enforce: "pre",
test: /\.jsx?$/,
exclude: /node_modules/,
loader: "eslint-loader",
options: {
fix: true,
formatter: "eslint-friendly-formatter",
},
},
{
test: /\.hbs$/,
loader: "handlebars-loader",
exclude: /node_modules/,
options: {
partialDirs: [path.join(__dirname, "../src/generatedpartial", "partials"), // partials 的门路,注册所有该门路下的代码模块
],
helperDirs: [path.join(__dirname, "../src/generatedpartial", "helper"),// helper 的门路,注册所有该门路下的代码模块
],
},
},
],
},
resolve: {extensions: [".ts", ".tsx", ".js"],
alias: {"@": path.resolve(__dirname, "../src/"),
},
},
plugins: [
new HtmlWebpackPlugin({
template: "src/index.html",
// the output file name
filename: "index.html",
// inject: "head",
}),
new HandlebarsWebpackPlugin({
htmlWebpackPlugin: {
enabled: true, // register all partials from html-webpack-plugin, defaults to `false`
prefix: "html", // where to look for htmlWebpackPlugin output. default is "html"
HtmlWebpackPlugin, // optionally: pass in HtmlWebpackPlugin if it cannot be resolved
},
entry: path.join(process.cwd(), "src", "generatedpartial", "*.hbs"), // 会将 generatedpartial 目录下的 hbs 文件输入为 html
output: path.join(process.cwd(), "dist", "[name].html"),
partials: [path.join(process.cwd(), "src", "partials", "*", "*.hbs")], // 将所有的 partials 进行转换
}),
],
};
hbs 次要蕴含 helper 和 partials 两大模块,具体模块用法在接下来具体解说。
(2)操作模版时在对应的 js 文件中将 hbs 文件 require 引入就好
const headerTpl = require("./generatedpartial/head.hbs");
const data={
title:[{
label: "demo1",
type: "",
key: "demo1",
}],
srcAddr:'',
destAddr:'',
orderStatus:'',
demo1:'这是一个 demo'
};
const basicMsgWrap = document.createElement("div");
basicMsgWrap.classList.add("basic-msg");
basicMsgWrap.innerHTML = headerTpl(data); // data 对应的是须要传入的对象
// data 外面须要蕴含 head.hbs 中的字段
/* head.hbs */
<div class="basic-msg__main">
{{#each title}}
{{>msg parameter=(lookup .. key)}}
{{/each}}
<div class="msg__value">
<span id="src-short">{{srcAddr}}</span> — <span id="dest-short">
{{destAddr}}
</span>
</div>
</div>
<div class="basic-msg__status">
<span class="basic-msg__status__text">
{{orderStatus}}
</span>
</div>
hbs 文件看起来和 html 文件相似,然而外面多了一些特有的语法,具体的语法接下来具体解说。
2. 语法详解
(1)partials,代码片段
代码片段顾名思义就是一个某段代码,在编写模版的时候会呈现一些可复用的模块,此时就须要应用块助手,实现编写一次模块,可在多个中央重复使用的目标。
应用代码片段用{{> 代码片段名称 param=data}}
,param 用于传参
// 注册代码片段
Handlebars.registerPartial('myPartial', '{{prefix}}');
// 应用注册的代码片段
{{> myPartial}}
head.hbs 中的 msg 就是已写好的一个代码片段,因为我我的项目用 webpack 打包,注册代码片段的过程 webpack 曾经帮忙做了,在代码中就不须要手动进行 registerPartial
(2)helper,块助手
块助手实质上就是一些辅助函数,通过块助手代码来调用模板上下文,以此实现在模板中解决渲染逻辑,使模板更加通用化。
// 注册块助手代码
Handlebars.registerHelper("noop", function(options) {return options.fn(this);
});
// 自定义的块助手代码 equal.js
// this 对应的就是上下文参数,option 是默认的参数,option 蕴含一个 option.fn 函数
// 这个函数的意义是 v1 等于 v2 时才返回上下文,否则不返回任何值
module.exports = function (v1, v2,option) {if (v1 === v2) {return option.fn(this);
}
};
// 应用 equal
// 次数如果传入的 title 等于操作历史时才会走入 equal 两头的逻辑
{{#equal title '操作历史'}}
{{>history data=(lookup ../this content)}}
{{/equal}}
和代码片段一样,用 webpack 打包之后咱们不须要用 registerHelper 进行注册,而是间接 exports 对应函数就好(目前是一个函数一个 js 模块)
应用块助手用 {{# 块助手名称 参数 1 参数 2 ...}} 代码块{{/ 块助手名称}}
,块助手代码必须闭环,否则会报错,满足逻辑的代码块将被渲染
(3)内置助手代码
Handlebars 定义了几个内置的助手代码,方便使用,接下来简略介绍一下
-
if
依据条件渲染代码块,如果参数返回 false,undefined,null,””,0 或者 [] 时,该代码块将不会被渲染
{{#if 0 === 1}}<div> 我永远不会被渲染 </div>{{/if}}
-
unless
和 #if 相同,当表达式返回 false 时渲染代码块
{{#unless 0 === 1}}<div> 我会被渲染 </div>{{/unless}}
-
each
遍历列表,能够应用 this 来援用被迭代的元素
{{#each title}}{{this.key}}{{/each}}
假如 title 是个对象数组,此时渲染进去的就是每个对象中 key 的值,若 title 是 head.hbs 中的 title,此时
this.key === 'demo1'
在 each 中还提供了一个 else,该代码块只会在列表为空时显示
{{#each paragraphs}}<p>{{this}}</p>{{else}}<p class="empty">No content</p> {{/each}}
当遍历
each
中的我的项目时,你能够抉择通过{{@index}}
援用以后循环的索引。对于对象迭代,能够应用
{{@key}}
援用以后的键名,通过@first
和@last
变量记录迭代的第一项和最初一项,要拜访父级的索引,能够应用{{@../index}}
-
with
更改上下文表达式,就是说 #with 前面的参数就能够做为#with 内容中的 this,这个对于多重嵌套的模板非常好用,嵌套档次很深的时候,就不再须要
a.b.c.d
这种模式进行数据援用`{{#with city as | city |}}
{{#with city.location as | loc |}}{{city.name}}: {{loc.north}} {{loc.east}}
{{/with}}
{{/with}}`其中 as 用于给对应的参数取别名,取别名之后就能够应用别名进行援用了
each 也能够应用{{else}}
-
lookup
lookup 容许应用变量进行动静的参数解析,它的应用办法跟下面的 #不一样,是间接应用,相似于函数,动静的参数解析在进行数组索引时非常有用
const data={ title:[{ label: "demo1", type: "", key: "demo1", },{ label: "demo2", type: "", key: "demo2", }], srcAddr:'', destAddr:'', orderStatus:'', demo1:'这是一个 demo', demo2:'this is demo' }; // hbs 代码局部 {{#each title}} {{>msg parameter=(lookup .. key)}} {{/each}}
此时 parameter 传回的是父级对应的 key 值的字段对应的值,也就是 ’ 这是一个 demo’ 和 ’this is demo’。
- log
容许在执行模板时记录上下文的状态
(4)补充语法
{{{外面的内容原样输入}}}
3. 模板语言的原理(本身了解)
模板语言分为预设解决模块和模板编辑模块两个局部,预设解决模块是预设一些模板的解决逻辑,当零碎遇到这些逻辑时,就会进行相应的解决,比方在解决 handlebars 时,遇到 {{}}
时,零碎就会将双括号内的代码辨认为非凡逻辑,进行对应的解决。
一个简略的 demo,实现字符串的替换(ps: 正则真的很重要)
const mapping = (data)=>({
title:'test',
key:'testKey',
}[data])
const demo = text =>{if(text){return text.replace(/\{\{(.*?)\}\}/g, (match, key) => mapping(key)}
}
demo('{{title}}&{{key}}') // test&testKey