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