# 简介

# 什么是DPL

DPL 是 DuerOS Presentation Language 的缩写,是为面向有屏音箱的技能开发者提供的一个新的解决方案,主要解决原有固定模板(DBP 模板)样式单一、灵活度低、不易扩展等问题,采用 DPL 方式开发可以让你能够轻松的创造展现丰富、交互灵活的 DuerOS 有屏端技能。

# 为什么要用DPL

  • 对于有屏端技能的开发者来说有如下难点:

    • 仅支持固定几种样式的模板,页面布局灵活度低;

    • 交互单一,云端交互受限等;

    • 基于协议或功能的调整依赖前端同学的开发与上线;

  • DPL 提供了一套完整的有屏端技能开发解决方案,使用 DPL 可以在开发技能中提供如下的优势:

    • 丰富的组件资源:DPL 以组件的形式定义页面布局与事件交互,你可以使用 文本、图片、Pager、视频、音频多种的组件来构建你的技能模板,并可通过在页面或组件中的事件触发或基于服务端下发的指令执行,完成所希望实现的完整交互逻辑;

    • 弹性的设计:DPL 中的内容和布局展现具有灵活性,你可以通过指令来定义和动态切换在不同交互阶段时,你所希望呈现在屏幕上的可视组件以及它们的不同展现内容,来满足所需的不同需求场景,并可非常方便的移植复用到其他拥有类似交互和呈现的其他 DPL 技能中;

    • 易用性:为了方便快速上手,我们提供了丰富的、已经在实际应用中稳定可用的 DPL documents 实例模板以及相关的组件实例模板,你可以通过简单的修改来构建属于你自己的技能。尽管 DPL 看起来是一个全新的语言类型,但是它也遵守了一般化的编程规范,语法和协议上也尽可能的兼顾了服务端开发人员与前端开发人员的理解习惯,因此很容易上手熟悉。

# 对比效果

  • 原始DBP模板

    纯文本

    上下图文

    左图右文

    左文右图

    纯图片

    横向列表

    纵向列表

  • DPL效果如:

自定义布局 1

自定义布局 2

带弹幕的视频

列表布局:游戏中心

文字冒险游戏

# DPL 组件基础介绍

DPL 组件(components)构成了整个 DPL 模板的展现,它是基于页面元素或由设备端提供的功能实例封装或组装而成模块片段。

  • DPL 中提供的组件

    • 基于不同类型的最小的模块片段;

    • 基于常用业务需求抽象,封装了功能和基于交互或行为可触发事件;

  • 合理的利用 DPL 组件搭建自己的业务的好处:

    • DPL 组件具备完整的局部功能,可以通过独立、自由的组合,快速的构建出自己的模板;

    • 开发者只需要关心组件的基于协议的数据输入与事件交互输出,不需要关心组件内的工作原理;

    • DPL 组件目前在线上可稳定运行,经历了业务需求的考验,符合产品功能逻辑;

    • DPL 组件经过了规范的设计,统一使用有助于保持交互一致性和视觉风格的统一;

    • 使用 DPL 组件可减低业务模板之间的耦合度,只要跟随组件内部的优化就能得到更好的性能提升;

    • 模块化的设计便于排查开发中遇到的问题

# DPL组件协议格式

{
    // 组件名称
    "type": "ComponentName",
   //  指定组件的唯一id,不指定则默认生成,如需在bot中响应指定组件的 command,则需手动指定一个唯一的id
    "componentId": "componentId", 
    // class样式类,从stylesheet中获取,端上渲染时绑定到组件上,相同属性值优先级低于styles中的内容
    "class": "className1 className2",
    // 组件属性对象:用来描述基于各组件功能所需的属性值集合
    "props": {
        // 依靠各组件需求的功能属性
        "prop": "value"
        ...
    },
    // 组件样式对象:用来描述基于各组件展现所需的样式值集合,会覆盖掉class类中描述相同的属性值
    "styles": {
        // 依靠各组件展示所需的样式属性
        "style": "value"
    },
    // 该组件的可嵌套子组件内容,部分组件支持使用子组件
    "items": [
        // 子组件类型,可递归
        {{Component}},
        ...
    ],
    // 组件内置事件,基于某些交互或操作可触发,如果绑定了对应事件的指令,在触发事件时指令将被执行
    "events": {
        // 组件事件名,基于组件类型分类
        "onEventName": [
            // dpl 指令,详情可参考指令部分内容
            {{COMMAND}},
            ...
        ]
    },
    // 槽位,仅标识该组件是作为其父组件中的某个特定的槽位内容进行使用,如果其父容器没有作为此名的槽位,会被丢弃掉
    "slot": "slotName",
    // 组件特殊标识,默认不填,当你需要强制刷新渲染该组件时,可以给该组件赋予一个新的 key 值
    "key": "uniqueKey"
}

# DPL组件协议描述

  • type
    • 组件的名称,如:Container、Image等
  • componentId
    • 自定义组件 id, 如果需要在 bot 中响应指定组件的 command,需要在组件设置一个在当前 DPL document 组件描述中唯一的 componentId 值,如果不设置,默认会生成一个随机且唯一的 componentId 值,bot 将无法根据 UserEvent 上报事件中的 componentId 明确触发事件的组件类型。
  • class
    • 定义样式表,从stylesheet中获取,端上渲染时绑定到组件上,相同属性值优先级低于styles中的内容
  • styles
    • 该组件的样式对象集合,优先级高于class,会覆盖掉class中定义的相同属性的值
  • props
    • 定义组件的属性,用来描述基于各组件功能所需的属性值集合
  • items
    • 表示该组件提供的子组件的槽位信息,用来在基于当前的组件展现形式时,提供局部的可动态灵活扩展能力,有默认子组件和具名子组件两种。
      • 默认,则直接描述子组件即可,不需要指定槽位信息,即不需要指定slot属性。
      • 具名,在子组件中则需要明确指定slot属性,如Container组件中提供了firstItem的槽位,则Container的子组件使用方式如:"slot": "firstItem" 。
  • events
    • 可以用来绑定相应的 DPL 指令,当对应组件事件的触发时,执行绑定的指令方法
  • methods
    • 组件内部提供的一些方法,可以通过指令来直接调用
  • key
    • 组件特殊标识,默认不填,当你需要强制刷新渲染该组件时,可以给该组件赋予一个新的 key 值

# DPL 指令介绍

DPL 指令(commands)是在设备端 DPL 模板上执行,可以用来控制页面交互或展现、调用组件能力的命令;

DPL 中提供的指令来源:

  • 通过在下发的 RenderDocument 页面渲染指令的描述数据中对应事件上提前绑定( UpdateComponent、AppendComponent 指令或任意其他指令中包含这两类指令的复合指令类型,因这两类指令中包含模板组件片段,需要通过在云端进行提前解析,无法通过这种方式有效执行);

  • 通过用户语音请求的返回;

  • 通过事件上报的返回;

    • 服务端自身的回调,需要对端上进一步下发指令控制页面交互或展现;
    • 通过事件触发执行预先绑定的指令,其中包含对服务端的请求,返回了新的指令;

# DPL 事件介绍

DPL 事件(events)是在页面模板渲染完成后,由设备端上基于页面交互逻辑或组件功能交互、指令执行产生的行为声明。

事件提供了设备端能够基于对 DPL 协议模板的描述,基于交互主动或被动的执行预设定的指令来完成开发者设定的行为动作的能力。

  • 页面模板事件:

    • 不隶属任何组件(如 onLoaded 事件,可以在整个页面初次渲染完成后触发);
  • 组件事件:

    • 基于组件自身具有的功能或交互逻辑,在对应的交互完成时触发,如 Video 组件,可在视频播发完成后触发 onEnd 事件;

# DPL支持的功能脑图:

DPL2.0脑图