handlebars

Posted just小千

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了handlebars相关的知识,希望对你有一定的参考价值。

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

以上是关于handlebars的主要内容,如果未能解决你的问题,请参考以下文章

Handlebars的使用方法文档整理(Handlebars.js)

Handlebars模板引擎

如何将对象传递给 .handlebars 模板并在 javascript 代码块中使用它?

handlebars的简单使用

Handlebars.js 模板引擎

错误:Handlebars.js 中缺少帮助程序