继承

继承能够创建可复用的模板,定义页面的骨架,然后被子模板填充,子模板又可以作为父模板被继承。

继承主要通过两个标签语法实现

  • <t:template> 定义要继承的父模板
  • <b:block> 在父模板中用来定义可以被填充的区域;在子模板中用来定义将内容填充到父模板的指定区域

定义父模板layout.vdt

  1. <div>
  2. <b:header>
  3. <div>父模板头部</div>
  4. </b:header>
  5. <div>
  6. <b:content>父模板内容</b:content>
  7. </div>
  8. </div>

模板加载

  • 后端渲染时,可以直接在在模板中使用require()方法来加载父模板

    1. var layout = require('./layout');
    2. <t:layout>
    3. <b:header>
    4. <div>子模板头部</div>
    5. </b:header>
    6. <b:content>
    7. {parent()}
    8. <div>子模板内容</div>
    9. </b:content>
    10. </t:layout>
  • 浏览器渲染时,有多种方法来加载父模板

    1. 你可以直接将父模板编译后传入子模板的render方法中,此时模板的加载任务在模板外部进行

      如果你采用纯前端编译运行,你的模板可以通过<script>标签定义在html中,类似这样

      1. <script type="text/vdt" id="parent_template">
      2. <div>
      3. <b:header>
      4. <div>父模板头部</div>
      5. </b:header>
      6. <div>
      7. <b:content>父模板内容</b:content>
      8. </div>
      9. </div>
      10. </script>

      由于模板加载的任务在模板外部进行,此时你的子模板无需处理父模板的加载,只需render时传入编译后的父模板即可

      1. <script type="text/vdt" id="child_template">
      2. <t:layout>
      3. <b:header>
      4. <div>子模板头部</div>
      5. </b:header>
      6. <b:content>
      7. {parent()}
      8. <div>子模板内容</div>
      9. </b:content>
      10. </t:layout>
      11. </script>

      然后在前端实时编译Vdt.compile(source)父模板,传入子模板中

      1. var layout = Vdt.compile(document.getElementById('parent_template').text()),
      2. vdt = Vdt(document.getElementById('child_template').text());
      3. // 在render方法中,传入编译好的父模板
      4. vdt.render({layout: layout});
    2. 大部分时候,我们都是将模板拆分成单独的文件,然后前端按需加载,此时我们需要一种方法来加载模板文件。
      当然你可以使用$.ajax({dataType: 'text'})的方式来异步加载模板,然后前端实时编译。
      但从性能上考虑,更推荐前端使用模块加载器RequireJs等工具来加载编译好的文件。

      • 后端编译的情况下,使用Vdt.middleware可以很方便地将Vdt实时编译成js文件
      • 前端编译的情况下,可以使用[gulp-vdt][1]插件将Vdt提前编译成js文件

        上述工具可以将模板编译成amd风格的模块,所以我们可以在模板中直接加载当前模板所需的依赖,
        将模板的依赖交给模板自己处理

        使用RequireJs加载模板的例子

        父模板layout.vdt定义不变,子模板child.vdt中处理模板加载,需要注意路径问题

        1. // 注意RequireJs加载文件路径配置
        2. var layout = require('/static/js/layout');
        3. <t:layout>
        4. <b:header>
        5. <div>子模板头部</div>
        6. </b:header>
        7. <b:content>
        8. {parent()}
        9. <div>子模板内容</div>
        10. </b:content>
        11. </t:layout>
        1. // 将模板的依赖交给模板自己处理,js中无需关心
        2. define(['/static/template/child'], function(childTemplate) {
        3. var vdt = Vdt(childTemplate);
        4. vdt.render();
        5. });
    3. 可以借助webpack打包工具,将所有依赖整合成单文件。Vdt提供了配套工具[vdt-loader][2]来加载Vdt模板文件

      webpack.config.js配置中添加

      1. module: {
      2. loaders: [
      3. test: /\.vdt$/,
      4. loader: 'vdt-loader'
      5. ]
      6. }

      此时在子模板中,直接加载依赖。

      1. // 加上vdt后缀
      2. var layout = require('./layout.vdt');
      3. <t:layout>
      4. <b:header>
      5. <div>子模板头部</div>
      6. </b:header>
      7. <b:content>
      8. {parent()}
      9. <div>子模板内容</div>
      10. </b:content>
      11. </t:layout>

      鉴于ES6和webpack的普及,这种方式是最推荐的做法,所以后面的例子中,我们都会采用这种写法,当然你也可以使用import语法加载依赖

继承标签语法

模板加载的问题解决后,我们来看看Vdt提供的两个实现继承的标签语法

  • <t:template> 定义要继承的父模板,其中template是父模板名称(非文件名)
  • <b:block> 定义往父模板哪个区域填充内容,其中block为父模板中定义的block名称。需要注意以下规则:

    • 如果不存在嵌套,并且该block在父模板中找不到对应block名,则该block将被忽略

      1. var layout = require('./layout.vdt');
      2. <t:layout>
      3. <b:name>由于父模板不存在名称为nameblock,所以该内容会被忽略</b:name>
      4. </t:layout>
    • 如果存在嵌套,并且被该block嵌套的block在父模板中找不到对应block名,则该block将被忽略

      1. var layout = require('./layout.vdt');
      2. <t:layout>
      3. <b:name>
      4. <b:content>
      5. 虽然父模板中存在content block,但不存在name block
      6. 所以该内容会被忽略
      7. </b:content>
      8. </b:name>
      9. </t:layout>
    • 如果父模板定义的block没有被子模板填充,则父模板中的内容将会直接输出

      1. var layout = require('./layout.vdt');
      2. // 没有填充父模板的任何block,则父模板中定义的内容都会输出
      3. <t:layout />

完整的继承例子渲染结果如下:

    1. // @file ./layout.vdt
    2. <div>
    3. <b:header>
    4. <div>父模板头部</div>
    5. </b:header>
    6. <div>
    7. <b:content>父模板内容</b:content>
    8. </div>
    9. </div>
    1. var layout = require('./layout.vdt');
    2. <t:layout>
    3. <b:header>
    4. <div>子模板头部</div>
    5. </b:header>
    6. <b:name>
    7. 该内容不会输出
    8. </b:name>
    9. </t:layout>

引用父级parent()

有时候,继承父模板后不一定要完全替换所有父模板中定义的内容,此时可以通过parent()方法来
引用当前block对应的父模板中的内容,你可以将该内容插在当前block的任意地方,若有必要,你可以调用该方法任意次

    1. // @file ./layout.vdt
    2. <div>
    3. <b:content>
    4. 父模板内容
    5. </b:content>
    6. </div>
    1. var layout = require('./layout.vdt')
    2. <t:layout>
    3. <b:content>
    4. {parent()}
    5. 前面插一份
    6. {parent()}
    7. 中间插一份
    8. 后面插一份
    9. {parent()}
    10. </b:content>
    11. </t:layout>

block嵌套

block可以任意嵌套,但在一个模板中要保证block名称不重复

    1. // @file ./layout.vdt
    2. <div>
    3. <b:body>
    4. <aside>
    5. <b:sidebar>边栏</b:sidebar>
    6. </aside>
    7. <article>
    8. <b:content />
    9. </article>
    10. </b:body>
    11. </div>
    1. // @file ./child.vdt
    2. var layout = require('./layout.vdt');
    3. <t:layout>
    4. <b:content>只修改content block</b:content>
    5. </t:layout>

带参数的block

block可以传递参数,我们可以在父模板中传递参数给子模板,子模板中接受参数后,可以根据不同的数据
渲染不同的结果。这在v-for渲染中很有用,我们可以动态每一次渲染的结果

  1. 首先我们需要在父模板中给block指定实参,通过args属性指定,该属性值是一个数组
  2. 然后在子模板中给block指定形参,通过params属性指定,该属性值是一个字符串
    1. // @file ./list.vdt
    2. <ul>
    3. <li v-for={data}>
    4. <b:item args={[value, key]}>{value.name}</b:item>
    5. </li>
    6. </ul>
    1. // @file ./child.vdt
    2. var list = require('./list.vdt');
    3. <t:list data={[
    4. {name: 'Javey', age: 18},
    5. {name: 'Tom', age: 20}
    6. ]}>
    7. <b:item params="item, index">
    8. {index + 1}: {parent()}, {item.age}
    9. </b:item>
    10. </t:list>

上例中,父模板的itemblock通过args传入value key当做实参,子模板通过parmas定义形参。可以
看到,我们依然可以通过parent()访问到父模板中定义的内容

继承后再继承

模板可以继承后再被继承,继承链可以任意长。例如上面定义的子模板可以当做父模板再被继承

    1. var child = require('./child.vdt');
    2. <t:child>
    3. <b:sidebar>
    4. 来自孙模板的边栏
    5. {parent()}
    6. </b:sidebar>
    7. <b:content>
    8. {parent()}
    9. 来自孙模板的内容
    10. </b:content>
    11. </t:child>

包含

包含也是实现模板复用的一个重要功能,Vdt中并没有提供include标签语法,但是通过前面的继承语法<t:template>
可以很方便地实现包含

    1. // @file ./list.vdt
    2. <table>
    3. <thead>
    4. <tr><th>ID</th><th>姓名</th></tr>
    5. </thead>
    6. <tbody>
    7. <td>0</td><td>Vdt</td>
    8. </tbody>
    9. </table>
    1. var list = require('./list.vdt');
    2. <div>
    3. <h1>用户列表</h1>
    4. list内容放在下面
    5. <t:list />
    6. </div>

传递数据

在使用<t:template>时,通过指定属性,可以传递数据给被包含的模板。在父模板中可以通过scope对象来获取属性值

    1. // @file ./list.vdt
    2. <table>
    3. <thead>
    4. <tr><th>ID</th><th>姓名</th></tr>
    5. </thead>
    6. <tbody>
    7. <tr v-for={scope.data}>
    8. <td>{key}</td><td>{value}</td>
    9. </tr>
    10. </tbody>
    11. </table>
    1. var list = require('./list.vdt');
    2. <div>
    3. <h1>用户列表</h1>
    4. <t:list data={['Syalvia', 'Shadow', 'Javey']} />
    5. </div>

局部继承

<t:template>可以在任意地方使用,不一定是根元素,所以可以很方便地实现局部继承

    1. // @file ./content.vdt
    2. <div>
    3. <h1>{title}</h1>
    4. <b:content>
    5. 被包含文件的内容
    6. </b:content>
    7. </div>
    1. var content = require('./content.vdt');
    2. <div>
    3. content模板放在下面,并且扩展它
    4. <t:content title="标题">
    5. <b:content>
    6. 包含文件内容
    7. {parent()}
    8. </b:content>
    9. </t:content>
    10. </div>