Skip to content

一个Vue createElement的简写形式 #6

@bravf

Description

@bravf

先看写法

render (h) {
  var me = this

  jsx.h = h

  var hr = jsx.__('hr')
  var rinput = jsx.__('r-input')
  var rselect = jsx.__('r-select')
  var roption = jsx.__('r-select-option')
  var rradio = jsx.__('r-radio')
  var rcheckbox = jsx.__('r-checkbox')

  var {div,h2,ul,li,input,h3,select,option} = jsx

  var node = 
  div('.box',
    h2('.title', {s_color:'red'}, '文章列表'),
    ul('.list',
      li({o_click:this.liClick}, '乒乓健儿大杀四方...'),
      li({
        c_foo: true,
        c_bar: false,
        s_color: 'blue',
        s_fontSize: '12px',
        a_id: 'foo',
        o_click: this.liClick
      }, '柯洁又输了....')
    ),
    hr(),
    div(
      h3('form input text'),
      input({a_type:'text', vmodel: [me, 'name']}),
      rinput({vmodel: [me, 'name']}),
      hr()
    ),
    div(
      h3('form input radio'),
      input({a_type:'radio', a_value:'boy', vmodel: [me, 'radio']}),
      input({a_type:'radio', a_value:'girl', vmodel: [me, 'radio']}),
      rradio({a_label:'boy', a_value:'boy', vmodel: [me, 'radio']}),
      rradio({a_label:'girl', a_value:'girl', vmodel: [me, 'radio']}),
      hr()
    ),
    div(
      h3('form input checkbox single'),
      input({a_type: 'checkbox', vmodel: [me, 'singleCheckbox']}),
      rcheckbox({vmodel: [me, 'singleCheckbox']})
    ),
    div(
      h3('form input checkbox multi'),
      input({a_type: 'checkbox', a_value: '呵呵', vmodel: [me, 'multiCheckbox']}),
      input({a_type: 'checkbox', a_value: '哈哈', vmodel: [me, 'multiCheckbox']}),
      rcheckbox({a_value: '呵呵', vmodel: [me, 'multiCheckbox']}),
      rcheckbox({a_value: '哈哈', vmodel: [me, 'multiCheckbox']}),
    ),
    div(
      h3('form select single'),
      select({vmodel: [me, 'singleSelect']},
        option({a_value:'a'}, 'one'),
        option({a_value:'b'}, 'two'),
        option({a_value:'c'}, 'three'),
      ),
      rselect({vmodel: [me, 'singleSelect']}, 
        roption({a_value:'a', a_label:'one'}),
        roption({a_value:'b', a_label:'two'}),
        roption({a_value:'c', a_label:'three'}),
      )
    ),
    div(
      h3('form multi single'),
      select({a_multiple:true, vmodel: [me, 'multiSelect']},
        option({a_value:'a'}, 'one'),
        option({a_value:'b'}, 'two'),
        option({a_value:'c'}, 'three'),
      ),
      rselect({vmodel: [me, 'multiSelect']}, 
        roption({a_value:'a', a_label:'one'}),
        roption({a_value:'b', a_label:'two'}),
        roption({a_value:'c', a_label:'three'}),
      )
    ),
  )

  return node
}

vue 提供了两种写组件的方式,template属性和render 方法,template写起来比较直观,但render控制力更强。直接写render的话,createElement不要太繁琐,虽然有jsx,又需要额外的编译,只能想办法简化写法了。具体辅助函数如下:

var jsx = {
  h () {},

  getProp (context, prop) {
    var props = prop.split('.'), i
    while (i = props.shift()){
      context = context[i]
    }
    return context
  },
  setProp (context, prop, value) {
    var props = prop.split('.'), i
    while (i = props.shift()){
      if (!props.length){
        break
      }
      context = context[i]
    }
    context[i] = value
  },

  _ (...params) {
    var node = {tag: 'div', props: {'class':{},style:{},attrs:{},props:{},domProps:{},on:{},nativeOn:{}}, children: []}
    var plen = params.length
    if (!plen){
      return node
    }

    if (plen > 1){
      var second = params[1]
      var i = 1

      if ( (typeof second === 'object') && (second.__proto__.constructor.name !== 'VNode') ){
        var table = {c:'class', s:'style', a:'attrs', p:'props', dp:'domProps', o:'on', no:'nativeOn'}

        for (var k in second){
          if (k.includes('_')){
            var [a, b] = k.split('_')
            var aa = table[a]

            node['props'][aa][b] = second[k]
          }
          else {
            node['props'][k] = second[k]
          }
        }
        
        i = 2
      }
      
      node.children.push(...params.slice(i))
    }

    var first = params[0]
    if (first.includes('.')){
      var [t, s] = first.split('.')
      node['tag'] = t
      
      s.split('+').forEach(i => {
        node['props']['class'][i.trim()] = true
      })
    }
    else {
      node['tag'] = first
    }

    //- 处理vmodel
    if ('vmodel' in node.props){
      var [context, model] = node.props['vmodel']
      var tag = node.tag
      var props = node.props

      var inputType = props['attrs']['type']
      var isInput = tag === 'input'
      var isText = (isInput && (inputType === 'text')) || (tag === 'textarea')
      var isRadio =  isInput && (inputType === 'radio')
      var isCheckbox = isInput && (inputType === 'checkbox')
      var isSelect = tag === 'select'
      //- 针对rainbow自定义vmodel的组件
      var isRRadio = tag === 'r-radio' || tag === 'r-checkbox'


      if (isText){
        props['domProps']['value'] = jsx.getProp(context, model)
        props['on']['input'] = e => {
          jsx.setProp(context, model, e.target.value)
        }
      }
      else if (isRadio){
        props['domProps']['checked'] = props['attrs']['value'] === jsx.getProp(context, model) ? true : false
        props['on']['change'] = e => {
          jsx.setProp(context, model, e.target.value)
        }
      }
      else if (isCheckbox){
        //- 如果model是array
        if (Array.isArray(jsx.getProp(context, model))){
          var value = props['attrs']['value']
          var isChecked = props['domProps']['checked'] = jsx.getProp(context, model).includes(value)

          props['on']['change'] = e => {
            if (isChecked){
              jsx.getProp(context, model).splice(jsx.getProp(context, model).indexOf(value), 1)
            }
            else {
              jsx.getProp(context, model).push(value)
            }
          }
        }
        else {
          props['domProps']['checked'] = jsx.getProp(context, model) === true ? true : false
          props['on']['change'] = e => {
            jsx.setProp(context, model, !props['domProps']['checked'])
          }
        }
      }
      else if (isSelect){
        //- 如果model是array
        if (Array.isArray(jsx.getProp(context, model))){
          //- 好像有点麻烦,需要反推option children
        }
        else {
          props['domProps']['value'] = jsx.getProp(context, model)
          props['on']['change'] = e => {
            jsx.setProp(context, model, e.target.value)
          }
        }
      }
      //- 假设其他都是自定义组件
      else {
        var modelProp = 'value'
        var modelEvent = 'input'

        if (isRRadio){
          modelProp = 'checkedValue'
        }

        props['props'][modelProp] = jsx.getProp(context, model)
        props['on']['input'] = val => {
          jsx.setProp(context, model, val)
        }
      }
    }

    return jsx.h(node.tag, node.props, node.children)
  },

  __ (tag) {
    return (...params) => {
      if ( (typeof params[0] === 'string') && (params[0][0] === '.') ){
        params[0] = tag + params[0]
      }
      else {
        params.unshift(tag)
      }
      
      return jsx._(...params)
    }
  }
}

'a,b,button,dd,div,dl,dt,em,form,i,iframe,img,input,label,li,ol,optgroup,option,p,select,span,table,th,thead,tr,td,ul,h1,h2,h3,h4,h5,h6'.split(',').forEach(tag=>{
  jsx[tag] = jsx.__(tag)
})

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions