util.js 6.4 KB
Newer Older
1 2 3
import {
  isFn,
  noop,
4
  hasOwn,
5 6 7
  isPlainObject
} from 'uni-shared'

8 9 10 11 12 13 14 15 16 17 18
const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__']

export function initMocks (vm) {
  const mpInstance = vm.$mp[vm.mpType]
  MOCKS.forEach(mock => {
    if (hasOwn(mpInstance, mock)) {
      vm[mock] = mpInstance[mock]
    }
  })
}

19
export function initHooks (mpOptions, hooks) {
20 21
  hooks.forEach(hook => {
    mpOptions[hook] = function (args) {
22
      return this.$vm.__call_hook(hook, args)
23 24 25 26
    }
  })
}

fxy060608's avatar
fxy060608 已提交
27
export function getData (vueOptions, context) {
fxy060608's avatar
fxy060608 已提交
28 29
  let data = vueOptions.data || {}
  const methods = vueOptions.methods || {}
30 31 32

  if (typeof data === 'function') {
    try {
fxy060608's avatar
fxy060608 已提交
33
      data = data.call(context) // 支持 Vue.prototype 上挂的数据
34
    } catch (e) {
35 36 37
      if (process.env.VUE_APP_DEBUG) {
        console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。', data)
      }
38
    }
39 40 41 42 43
  } else {
    try {
      // 对 data 格式化
      data = JSON.parse(JSON.stringify(data))
    } catch (e) {}
44
  }
fxy060608's avatar
fxy060608 已提交
45

46 47 48 49 50 51 52
  Object.keys(methods).forEach(methodName => {
    if (!hasOwn(data, methodName)) {
      data[methodName] = methods[methodName]
    }
  })

  return data
53 54 55 56
}

const PROP_TYPES = [String, Number, Boolean, Object, Array, null]

fxy060608's avatar
fxy060608 已提交
57 58 59 60 61 62 63 64
function createObserver (name) {
  return function observer (newVal, oldVal) {
    if (this.$vm) {
      this.$vm[name] = newVal // 为了触发其他非 render watcher
    }
  }
}

65
export function getProperties (props) {
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
  const properties = {
    vueSlots: { // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots
      type: null,
      value: [],
      observer: function (newVal, oldVal) {
        const $slots = Object.create(null)
        newVal.forEach(slotName => {
          $slots[slotName] = true
        })
        this.setData({
          $slots
        })
      }
    }
  }
81 82
  if (Array.isArray(props)) { // ['title']
    props.forEach(key => {
fxy060608's avatar
fxy060608 已提交
83 84 85 86
      properties[key] = {
        type: null,
        observer: createObserver(key)
      }
87 88 89 90 91 92 93 94 95 96 97
    })
  } else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String}
    Object.keys(props).forEach(key => {
      const opts = props[key]
      if (isPlainObject(opts)) { // title:{type:String,default:''}
        let value = opts['default']
        if (isFn(value)) {
          value = value()
        }
        properties[key] = {
          type: PROP_TYPES.includes(opts.type) ? opts.type : null,
fxy060608's avatar
fxy060608 已提交
98 99
          value,
          observer: createObserver(key)
100 101
        }
      } else { // content:String
fxy060608's avatar
fxy060608 已提交
102 103 104 105
        properties[key] = {
          type: PROP_TYPES.includes(opts) ? opts : null,
          observer: createObserver(key)
        }
106 107 108 109 110 111 112 113 114 115 116 117
      }
    })
  }
  return properties
}

function wrapper (event) {
  event.stopPropagation = noop
  event.preventDefault = noop

  event.target = event.target || {}
  event.detail = event.detail || {}
fxy060608's avatar
fxy060608 已提交
118 119 120 121 122 123 124

  if (__PLATFORM__ === 'mp-baidu') { // mp-baidu,checked=>value
    if (hasOwn(event.detail, 'checked') && !hasOwn(event.detail, 'value')) {
      event.detail.value = event.detail.checked
    }
  }

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
  // TODO 又得兼容 mpvue 的 mp 对象
  event.mp = event
  event.target = Object.assign({}, event.target, event.detail)
  return event
}

function processEventArgs (event, args = [], isCustom) {
  if (isCustom && !args.length) { // 无参数,直接传入 detail 数组
    return event.detail
  }
  const ret = []
  args.forEach(arg => {
    if (arg === '$event') {
      ret.push(isCustom ? event.detail[0] : event)
    } else {
      ret.push(arg)
    }
  })

  return ret
}

const ONCE = '~'
const CUSTOM = '^'

150 151 152 153 154 155 156 157
function getTarget (obj, path) {
  const parts = path.split('.')
  if (parts.length === 1) {
    return obj[parts[0]]
  }
  return getTarget(obj[parts[0]], parts.slice(1).join('.'))
}

158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
export function handleEvent (event) {
  event = wrapper(event)

  // [['tap',[['handle',[1,2,a]],['handle1',[1,2,a]]]]]
  const eventOpts = (event.currentTarget || event.target).dataset.eventOpts
  if (!eventOpts) {
    return console.warn(`事件信息不存在`)
  }

  // [['handle',[1,2,a]],['handle1',[1,2,a]]]
  const eventType = event.type
  eventOpts.forEach(eventOpt => {
    let type = eventOpt[0]
    const eventsArray = eventOpt[1]

    const isCustom = type.charAt(0) === CUSTOM
    type = isCustom ? type.slice(1) : type
    const isOnce = type.charAt(0) === ONCE
    type = isOnce ? type.slice(1) : type

    if (eventsArray && eventType === type) {
      eventsArray.forEach(eventArray => {
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
        const methodName = eventArray[0]
        if (methodName === '$set') { // prop.sync
          const args = eventArray[1]
          if (args.length === 2) { // :title.sync="title"
            this.$vm[args[0]] = event.detail[0]
          } else if (args.length === 3) {
            this.$vm.$set(getTarget(this.$vm, args[0]), args[1], event.detail[0])
          }
        } else {
          const handler = this.$vm[methodName]
          if (!isFn(handler)) {
            throw new Error(` _vm.${methodName} is not a function`)
          }
          if (isOnce) {
            if (handler.once) {
              return
            }
            handler.once = true
198
          }
199
          handler.apply(this.$vm, processEventArgs(event, eventArray[1], isCustom))
200 201 202 203 204 205 206 207 208 209 210
        }
      })
    }
  })
}

export function initRefs (vm) {
  const mpInstance = vm.$mp[vm.mpType]
  Object.defineProperty(vm, '$refs', {
    get () {
      const $refs = Object.create(null)
fxy060608's avatar
fxy060608 已提交
211
      const components = mpInstance.selectAllComponents('.vue-ref')
212
      components.forEach(component => {
fxy060608's avatar
fxy060608 已提交
213 214
        const ref = component.dataset.ref
        $refs[ref] = component.$vm
215
      })
fxy060608's avatar
fxy060608 已提交
216
      const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for')
217
      forComponents.forEach(component => {
fxy060608's avatar
fxy060608 已提交
218 219 220
        const ref = component.dataset.ref
        if (!$refs[ref]) {
          $refs[ref] = []
221
        }
fxy060608's avatar
fxy060608 已提交
222
        $refs[ref].push(component.$vm)
223 224 225 226
      })
      return $refs
    }
  })
fxy060608's avatar
fxy060608 已提交
227 228 229 230 231 232 233 234 235 236 237 238 239 240
}

function baiduComponentDestroy ($vm) {
  $vm.$children.forEach(childVm => {
    childVm.$mp.component.detached()
  })
  $vm.$mp.component.detached()
}

export function baiduPageDestroy ($vm) {
  $vm.$destroy()
  $vm.$children.forEach(childVm => {
    baiduComponentDestroy(childVm)
  })
241
}