mip-bind

<mip-bind> 是以数据驱动页面更新的功能,开发者通过配置数据信息,并绑定在相应 DOM 上,就可以轻松做到数据变动后 DOM 元素随之变动的效果,可以通过示例 查看效果。

使用方法

设置数据

其中 <mip-data> 元素用于在页面中设置数据源,一个页面中可以指定多个 <mip-data>,最终数据会合并到一个数据源对象上。数据源的设置可以通过以下两种方式:

内嵌数据

内嵌数据是指直接将数据嵌入到 HTML 页面中,提供给标签或自定义组件使用。要求符合 JSON 格式,如:

  1. <mip-data>
  2. <script type="application/json">
  3. {
  4. "name": "张三",
  5. "age": 25,
  6. "job": {
  7. "desc": "互联网从业者",
  8. "location": "北京"
  9. }
  10. }
  11. </script>
  12. </mip-data>

异步数据

如果需要异步数据,则需指定 src 地址,请求回来的数据会自动合并到数据表里,如:

  1. <mip-data src="https://www.example.org/data"></mip-data>

注意:
src 需要是 https// 协议开头,否则在 HTTPS 环境下会无法正常加载

当使用这种方式获取异步数据时,请注意:需要开发者服务端配置 CORS 跨站访问,具体步骤如下:

  • 接收到请求后,判断请求头中的 origin 是否是允许的,其中需要允许的域名包括:https://mipcache.bdstatic.com、开发者的站点originhttps://站点域名转换的字符串.mipcdn.com 。站点域名转换的字符串是指开发者的站点origin通过一定的规则(点.转换为中横线-)转换的字符串,如下面代码中的origins数组所示:origins[1]为开发者的站点origin,origins[2]为转换后的 origin;
  • 如果 origin 在指定的列表中则设置 response header 中的 Access-Control-Allow-Origin 为请求接收到的 origin,以 Node.js 举例,如下所示:
  1. let origins = {
  2. 'https://mipcache.bdstatic.com': 1,
  3. 'https://www-mipengine-org.mipcdn.com': 1,
  4. 'https://www.mipengine.org': 1
  5. }
  6. app.get('/data', function (req, res) {
  7. let ori = req.headers.origin
  8. if (origins[ori]) {
  9. res.header('Access-Control-Allow-Origin', ori)
  10. res.json({})
  11. }
  12. })

全局共享数据

详情请阅读 可交互 MIP - 全局共享数据

绑定数据

目前绑定数据只支持两种功能:

绑定指令 m-bind

绑定元素属性信息。具体格式为 m-bind:attrs="value",即:将 attrs 属性值设置为 value 的值,value 为数据源中的属性名,多层数据可以以 . 连接,如:

  1. <!-- 绑定 placeholder 值 -->
  2. <mip-data>
  3. <script type="application/json">
  4. {
  5. "placeholder": "请输入内容"
  6. }
  7. </script>
  8. </mip-data>
  9. <mip-form url="https://www.mipengine.org/">
  10. <input m-bind:placeholder="placeholder">
  11. </mip-form>
  1. <!-- 变更样式 -->
  2. <style mip-custom>
  3. [data-clicked=true] {
  4. background: pink;
  5. }
  6. </style>
  7. <mip-data>
  8. <script type="application/json">
  9. {
  10. "clicked": false
  11. }
  12. </script>
  13. </mip-data>
  14. <span m-bind:data-clicked="clicked" on="tap:MIP.setData({clicked:!m.clicked})">来点我呀!</span>
  1. <!-- 切换 tab 功能实现 -->
  2. <style mip-custom>
  3. #content, .filter {
  4. text-align: center;
  5. padding-top: 10px;
  6. }
  7. #content span,
  8. #content mip-img,
  9. #content mip-video {
  10. display: none;
  11. }
  12. .first span,
  13. .second mip-img,
  14. .third mip-video {
  15. display: block !important;
  16. }
  17. </style>
  18. <mip-data>
  19. <script type="application/json">
  20. {
  21. "clickedClass": "second"
  22. }
  23. </script>
  24. </mip-data>
  25. <mip-vd-tabs>
  26. <section>
  27. <li>第一页</li>
  28. <li>第二页</li>
  29. <li>第三页</li>
  30. </section>
  31. <div class="filter">
  32. <span on="tap:MIP.setData({clickedClass:'first'})">文字</span>
  33. <span on="tap:MIP.setData({clickedClass:'second'})">图片</span>
  34. <span on="tap:MIP.setData({clickedClass:'third'})">视频</span>
  35. </div>
  36. </mip-vd-tabs>
  37. <div id="content" m-bind:class="clickedClass">
  38. <span>我是文案啦!</span>
  39. <mip-img layout="responsive" width="350" height="263" src="https://www.mipengine.org/static/img/sample_01.jpg"></mip-img>
  40. <mip-video poster="https://www.mipengine.org/static/img/sample_04.jpg" controls layout="responsive" width="640" height="360" src="https://gss0.bdstatic.com/-b1Caiqa0d9Bmcmop9aC2jh9h2w8e4_h7sED0YQ_t9iCPK/mda-gjkt21pkrsd8ae5y/mda-gjkt21pkrsd8ae5y.mp4"></mip-video>
  41. </div>
  42. <script src="https://c.mipcdn.com/static/v1/mip-vd-tabs/mip-vd-tabs.js"></script>

注意:
如果通过 m-bind 绑定的数据为空值,即 “” 时,则删除当前元素的该属性 attrs。

绑定指令 m-text

绑定元素 textContent。具体格式为 m-text=value,即:将元素的 textContent 设置为 value 的值,同样 value 为数据源中的属性名,多层数据可以以 . 连接,如:

  1. <mip-data>
  2. <script type="application/json">
  3. {
  4. "loc": "北京",
  5. "job": {
  6. "desc": "互联网从业者"
  7. }
  8. }
  9. </script>
  10. </mip-data>
  11. <p>坐标:<span m-text="loc"></span></p>
  12. <p>职位信息:<span m-text="job.desc"></span></p>

Class 与 Style 绑定

详情请阅读 可交互 MIP - Class 与 Style 绑定

修改数据

MIP 提供了全局方法 MIP.setData(data) 来让开发者修改数据,以完成通过数据驱动 DOM 元素更新的交互方案。重复数据会进行覆盖。

用法

这个方法接受一个对象作为参数,要设置的数据对象将按照层级被合并到总的数据源中,后设置的数据会覆盖前者(支持多层级的复杂对象的 deep 数据覆盖)。

首次设置数据源如:

  1. <mip-data>
  2. <script type="application/json">
  3. {
  4. "name": "张三",
  5. "age": 25
  6. }
  7. </script>
  8. </mip-data>
  9. <mip-data>
  10. <script type="application/json">
  11. {
  12. "detail": {
  13. "home": {
  14. "province": "广东",
  15. "city": "深圳"
  16. }
  17. }
  18. }
  19. </script>
  20. </mip-data>

此时页面维护的数据源为:

  1. {
  2. "name": "张三",
  3. "age": 25,
  4. "detail": {
  5. "home": {
  6. "province": "广东",
  7. "city": "深圳"
  8. }
  9. }
  10. }

如果此时在进行数据的设置:

  1. <div on="tap:MIP.setData({job:'互联网从业者',age:26,detail:{home:{city:'广州'}}})"></div>

此时数据源将变为:

  1. {
  2. "name": "张三",
  3. "age": 26,
  4. "detail": {
  5. "home": {
  6. "province": "广东",
  7. "city": "广州"
  8. }
  9. },
  10. "job": "互联网从业者"
  11. }

注意
在 mip-data 小节中提到:通过 mip-data 组件设置的数据要求符合标准的 JSON 格式。而 setData 方法虽然没有严格要求传 JSON 格式的对象,但是仅允许传 JSON 对象可以接受的数据,因此是不允许设置为 function 的。

调用 MIP.setData 方法修改数据既可以通过在 HTML 元素中加入事件来完成(方式是在元素中加入 on 属性监听事件以触发指定修改,如前面的示例所示);也可以在组件代码中直接调用 setData 方法。

后者没有特殊要求,只需要开发者自行确认逻辑和数据即可。而前者的使用,对开发者的要求有两点:

  1. 使用单引号来引用字符串;
  2. 要修改的数据的值,如果是一个变量 var,需要是通过 mip-data 组件设置的数据,并且通过 m.var 来获取

支持数据表达式

开发者使用 setData 方法时,支持使用数据表达式来修改数据。其中包含以下两种:

支持运算表达式解析

如:

  1. <mip-data>
  2. <script type="application/json">
  3. {
  4. "price": 20,
  5. "count": 2
  6. }
  7. </script>
  8. </mip-data>
  9. <div m-text="price"></div>
  10. <button on="tap:MIP.setData({price:'30'})">30</button>
  11. <button on="tap:MIP.setData({price:30*m.count})">30*m.count</button>
支持 DOM 元素解析

<mip-bind> 支持 DOM 元素解析,在设置的数据中,可通过 DOM 变量来表示当前事件触发的源 DOM 元素,并可通过其获取元素上的属性值等,如:

  1. <mip-data>
  2. <script type="application/json">
  3. {
  4. "price": 20
  5. }
  6. </script>
  7. </mip-data>
  8. <div>
  9. DOM.value*m.price = <span m-text="price"></span>
  10. </div>
  11. <mip-form url="https://www.mipengine.org/">
  12. <input type='text' on="change:MIP.setData({price:DOM.value*m.price})">
  13. </mip-form>

使用数据

在 HTML 页面中

在 HTML 页面中使用数据,可以通过 m-bindm-text 来绑定数据。

在组件中

[规范]
  1. 仅允许在 HTML 页面使用 m-bind 来绑定数据,以 props 的形式向组件传递数据。
  2. 组件内部 不允许 使用 m-bind 语法来绑定全局数据,也无法直接读取到全局数据 m,仅允许绑定通过 props获得的数据。
  3. 组件内部需要使用 props 预定义所需数据,并且显式指定一种数据类型。如果遵循了Vue语法指定了多种数据类型,MIP将默认取第一种。
  4. 如果组件内部实现存在自定义标签嵌套的情况,传递数据时 应按照 Vue 父子组件的写法来传递数据。

组件内部可以通过调用 MIP.setData 来修改全局数据,以此触发重新渲染。

如:

index.html:

  1. <mip-data>
  2. <script type="application/json">
  3. {
  4. "userInfo": {
  5. "name": "baidu"
  6. },
  7. "anotherObject": {
  8. "name": "sfe"
  9. },
  10. "list": [1, 2, 3],
  11. "num": 2,
  12. "msg": "info",
  13. "loading": false
  14. }
  15. </script>
  16. </mip-data>
  17. <mip-a
  18. m-bind:userinfo="userInfo"
  19. m-bind:another-object="anotherObject"
  20. m-bind:list="list"
  21. m-bind:num="num"
  22. m-bind:msg="msg"
  23. m-bind:loading="loading"></mip-a>

mip-a 组件内部:

  1. <template>
  2. <div>
  3. <mip-b :userinfo="userinfo"></mip-b>
  4. <p @click="changeData"></p>
  5. <p v-if="loading">{{msg}}</p>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. props: {
  11. userinfo: {
  12. type: Object,
  13. default () {
  14. return {}
  15. }
  16. },
  17. anotherObject: {
  18. type: Object,
  19. default () {
  20. return {}
  21. }
  22. },
  23. list: {
  24. type: Array
  25. },
  26. num: Number,
  27. loading: Boolean,
  28. msg: String
  29. },
  30. methods: {
  31. changeData() {
  32. MIP.setData({
  33. userInfo: {
  34. name: 'baidu2'
  35. }
  36. })
  37. }
  38. }
  39. }
  40. </script>

mip-b 组件内部:

  1. <template>
  2. <p>{{userinfo.name}}</p>
  3. </template>
  4. <script>
  5. export default {
  6. props: {
  7. userinfo: {
  8. type: Object,
  9. default () {
  10. return {}
  11. }
  12. }
  13. }
  14. }
  15. </script>

注:
请留意上面例子中 HTML 与 mip-a 组件实现两段代码里面的 userinfo 和 anotherObject 的绑定示例。由于 m-bind 的基本原理是将动态数据绑定到指定 attribute 上,而我们知道,HTML 标签的 attribute 要求小写(即使写了大写也会转换为小写),因此如果开发者想要在组件里使用驼峰命名变量,请使用短横线(kebab-case)的方式指定 attribute。

mip-script 组件中

mip-script 组件允许开发者编写自定义的 JavaScript 代码,作用类似于 script 标签。其具体用法和规范开发者将在后面的小节了解到。此处我们将借这个组件来向开发者介绍一个读取数据的 API:MIP.getData(value)value 为数据源中的属性名,多层数据可以以.连接。注意,getData 方法在组件中并不开放使用,请开发者遵循前面的在组件中使用数据的规范。

在使用 mip-script 自定义 JS 代码时,如有数据操作的需要,开发者可以通过 getData 方法读取和使用数据。下面的例子实现了一个简单的多选功能,最终输出选中的目录序号。例子中使用到的 setData 设置数据方法、watch 监控数据方法均在后面的小节会详细讲解。如:

  1. <mip-data>
  2. <script type="application/json">
  3. {
  4. "i": 0,
  5. "selectedStr": "",
  6. "selected": []
  7. }
  8. </script>
  9. </mip-data>
  10. <ul>
  11. <li on="tap:MIP.setData({i:1})">目录1</li>
  12. <li on="tap:MIP.setData({i:2})">目录2</li>
  13. <li on="tap:MIP.setData({i:3})">目录3</li>
  14. </ul>
  15. <p>selected: <span m-text="selectedStr"></span></p>
  16. <mip-script>
  17. MIP.watch('i', function (newVal) {
  18. let selected = MIP.getData('selected')
  19. let index = selected.indexOf(newVal)
  20. if (index) {
  21. selected.splice(index, 1)
  22. } else {
  23. selected.push(index)
  24. }
  25. MIP.setData('selectedStr', selected.join(','))
  26. })
  27. </mip-script>

观察数据

MIP 提供了全局方法 MIP.watch(value, cb) 供开发者注册观察数据的行为。

其中 value 为数据源中的属性名,多层数据可以以 . 连接,允许是单个字符串或字符串数组。
cb 为当被观察的属性 value 发生数据变化时要执行的回调函数,接收一个参数,为 newValue,即 value 变化后的值。

观察数据的前提,是这个数据已经通过 mip-data 组件设置过。假如是通过 mip-data 的异步方式获取数据的,还需要等待异步数据设置完成。 MIP 提供全局变量 mipDataPromises,这是一个获取异步数据的 Promise 对象数组,如果当前没有获取异步数据的任务或任务已完成,则该数组为空。若该数组不为空,同时开发者需要观察的数据即为异步数据时,为确保观察正常执行,需要把 MIP.watch 的执行放到 Promise 的回调函数中。

示例:

通过调用 watch 方法,监听 num 和 img.first 的数据变化,并在 num 变化时改变 img.first 的值,从而触发 title 的数据变化。

  1. <mip-data>
  2. <script type="application/json">
  3. {
  4. "num": 1,
  5. "title": "Initial num = 1",
  6. "img": {
  7. "first": ""
  8. }
  9. }
  10. </script>
  11. </mip-data>
  12. <p m-text="title"></p>
  13. <div>
  14. num = DOM.value = <span m-text="num"></span>
  15. </div>
  16. <input type='text' on="change:MIP.setData({num:DOM.value})">
  17. <mip-script >
  18. MIP.watch('num', newValue => {
  19. MIP.setData({
  20. img: {
  21. first: 'img.first changed due to num changed to' + newValue
  22. }
  23. })
  24. })
  25. MIP.watch('img.first', newVal => {
  26. MIP.setData({
  27. title: newVal
  28. })
  29. })
  30. MIP.watch(['num', 'img.first'], newVal => {
  31. console.log('multiple')
  32. })
  33. </mip-script>