omi/tutorial/pass-complex-type-props-to-...

4.8 KiB
Raw Blame History

给 web components 自定义元素传递复杂类型 props

先看一段来自 react readme 里的介绍:

react 基于组件构建管理自己状态的封装组件然后组合它们以生成复杂的UI。由于组件逻辑是用JavaScript而不是模板编写的所以您可以轻松地通过应用程序传递丰富的数据并将状态保存在DOM之外。

众所周知react 解决了 html 标签不能传递复杂 attribute 的问题,你只能传递 string 类型,否则只能使用 properties。使用 properties 就不能声明在 html 上,只能通过 javascript 后置处理。然而 Omi 框架有几种手段解决 web components 自定义元素传递复杂 attribute 的问题。如果你不想使用 Omi 替换整个前端框架,只是想使用其定义的组件作为其他框架的叶子节点,那么你必须好好读一读这篇文章。

定义自定义元素

define('my-ele', class extends WeElement {
  static propTypes = {
    user: Object
  }

  render(props) {
    return (
      <div>{props.user.name},{props.use.age}</div>
    )
  }
})

其中 propTypes 必须声明omi 内部就知道怎么解析传入的字符串。

使用方式一

<my-ele user="{ name: 'omi', age: 1 }"></my-ele>

直接传入 json 字符串omi 内部会将其 JSON.parse 转成 object 类型。

使用方式二

如果你不想写一大堆字符串在 html 标签上,可以使用下面的方式:

<script>
  Omi.$.user = { name: 'omi', age: 1 }
</script>

<my-ele user=":user"></my-ele>

当然也是支持 path:

<script>
  Omi.$.info = {
    list: [
      { name: 'dntzhang', age: 18 },
      { name: 'mali', age: 18 }
    ]
  }
</script>

<my-ele user=":info.list[0]"></my-ele>
<my-ele user=":info.list[1]"></my-ele>

→ 例子 codepen

传递 false 给自定义元素

HTML 有个设计缺陷,当该 attr 默认值是 true 的时候,你无法传递 false 设置元素 attr 为 false。比如:

define('my-element', class extends WeElement {
  static defaultProps = {
    show: true
  }

 static propTypes = {
    show: Boolean
  }

  render(props) {
    ...
    ...
  }
})

使用 Omi你可以通过 false 字符串或者 0 字符串搞定:

<my-element show="false"></my-element>

<my-element show="0"></my-element>

原理

可以看 → Omi 源码:

  case Boolean:
    if (val === 'false' || val === '0') {
      ele.props[key] = false
    } else {
      ele.props[key] = true
    }
    break
  case Array:
  case Object:
    if (val[0] === ':') {
      ele.props[key] = getValByPath(val.substr(1), Omi.$)
    } else {
      ele.props[key] = JSON.parse(val
        .replace(/(['"])?([a-zA-Z0-9_-]+)(['"])?:([^\/])/g, '"$2":$4')
        .replace(/'([\s\S]*?)'/g, '"$1"')
        .replace(/,(\s*})/g, '$1')
      )
    }
    break

这里可以看到omi 内部还会对 josn 格式进行处理,防止 JSON.parse 出错。当然 eval 和 new Function 可以转化错误格式的 json 字符串,但是考虑到安全性,还是使用JSON.parse

实战

定义自定义元素:

import { define, WeElement } from 'omi'

define('my-ele', class extends WeElement {
  static propTypes = {
    name: String
  }

  clickHandle = ()=>{
    //vue  and preact x
    this.fire('MyEvent', 'abc')
    //preact 8 and blow
    this.fire('myevent', 'abc')
  }

  render(props) {
    return (
      <div onClick={this.clickHandle}>{props.name} {props.user.name} {props.user.age} </div>
    )
  }
})

在 Vue 中使用

<script>
export default {
  created(){
    Omi.$.user = { name:'Omi' ,age: 1 }
  },
  methods: {
    myEvent: function(evt) {
      //output abc
      console.log(evt.detail)

      //更新自定义组件
      Omi.$.user.age = 2 //or this.$refs.myEle.props.user.age = 2
      this.$refs.myEle.update()
    }
  }
}
</script>

<template>
  <my-ele ref="myEle" name="Omi" user=":user" @MyEvent="myEvent" />
</template>

在 Preact 中使用

import { h, render } from 'preact';

Omi.$.user = { name:'Omi' ,age: 1 }

render((
  <div id="foo">
      <span>Hello, world!</span>
      <my-ele name="Omi" user=":user" onMyEvent={ e => console.log(e.detail)} />
  </div>
), document.body);

因为 preact 是最好的 web react 框架,而 react 不是,所以这里就不举 react 的例子了。

最后

Omi 对 attrs 和 props 进行了全映射, attr 就是 props在任意框架中都可以使用同样的方式使用 omi 定义的自定义元素。如果你有什么意见建议欢迎让我知道