data.js 5.0 KB
Newer Older
fxy060608's avatar
init v3  
fxy060608 已提交
1 2
import {
  guid,
fxy060608's avatar
fxy060608 已提交
3 4
  hasOwn,
  camelize
fxy060608's avatar
init v3  
fxy060608 已提交
5 6
} from 'uni-shared'

fxy060608's avatar
fxy060608 已提交
7 8 9
import {
  MOUNTED_DATA,
  UPDATED_DATA
fxy060608's avatar
fxy060608 已提交
10 11
} from '../../../constants'

fxy060608's avatar
init v3  
fxy060608 已提交
12 13 14 15 16
import {
  VDomSync
} from './vdom-sync'

import {
fxy060608's avatar
fxy060608 已提交
17
  V_IF,
18
  V_FOR,
fxy060608's avatar
fxy060608 已提交
19
  V_SHOW,
fxy060608's avatar
fxy060608 已提交
20 21
  V_ELSE_IF,
  B_CLASS,
fxy060608's avatar
fxy060608 已提交
22 23
  B_STYLE,
  S_CLASS
fxy060608's avatar
fxy060608 已提交
24
} from '../../constants'
fxy060608's avatar
init v3  
fxy060608 已提交
25 26 27 28 29

import {
  diff
} from './diff'

fxy060608's avatar
fxy060608 已提交
30 31
import parseComponentCreateOptions from './parse-component-create-options'

fxy060608's avatar
fxy060608 已提交
32
export function initData (Vue) {
fxy060608's avatar
init v3  
fxy060608 已提交
33 34
  Vue.prototype._$s = setData

fxy060608's avatar
fxy060608 已提交
35
  Vue.prototype._$setData = function setData (type, data) {
fxy060608's avatar
fxy060608 已提交
36 37 38 39 40 41
    this._$vd.push(
      type,
      this._$id,
      data,
      type === MOUNTED_DATA && parseComponentCreateOptions(this)
    )
fxy060608's avatar
fxy060608 已提交
42 43
  }

fxy060608's avatar
fxy060608 已提交
44
  Vue.prototype._$mounted = function mounted () {
fxy060608's avatar
fxy060608 已提交
45 46 47 48 49 50 51 52 53 54 55
    if (!this._$vd) {
      return
    }
    diff(this._$newData, this._$data, this._$vdMountedData)
    this._$data = JSON.parse(JSON.stringify(this._$newData))
    if (this.mpType === 'page') {
      // 页面 mounted 之后,第一次同步数据
      this._$vd.flush()
    }
  }

fxy060608's avatar
fxy060608 已提交
56
  Vue.prototype._$updated = function updated () {
fxy060608's avatar
fxy060608 已提交
57 58 59
    if (!this._$vd) {
      return
    }
fxy060608's avatar
fxy060608 已提交
60 61 62 63
    // TODO 自定义组件中的 slot 数据采集是在组件内部,导致所在 context 中无法获取到差量数据
    // 如何保证每个 vm 数据有变动,就加入 diff 中呢?
    // 每次变化,可能触发多次 beforeUpdate,updated
    // 子组件 updated 时,可能会增加父组件的 diffData,如 slot 等情况
fxy060608's avatar
fxy060608 已提交
64 65
    diff(this._$newData, this._$data, this._$vdUpdatedData)
    this._$data = JSON.parse(JSON.stringify(this._$newData))
fxy060608's avatar
fxy060608 已提交
66 67 68 69
    // setTimeout 一下再 nextTick( 直接 nextTick 的话,会紧接着该 updated 做 flush,导致父组件 updated 数据被丢弃)
    this._$vd.initialized && setTimeout(() => {
      this.$nextTick(this._$vd.flush.bind(this._$vd))
    }, 0)
fxy060608's avatar
init v3  
fxy060608 已提交
70 71 72
  }

  Object.defineProperty(Vue.prototype, '_$vd', {
fxy060608's avatar
fxy060608 已提交
73
    get () {
fxy060608's avatar
init v3  
fxy060608 已提交
74 75 76 77 78
      return this.$root._$vdomSync
    }
  })

  Vue.mixin({
fxy060608's avatar
fxy060608 已提交
79
    beforeCreate () {
fxy060608's avatar
init v3  
fxy060608 已提交
80 81 82 83 84 85 86
      if (this.$options.mpType) {
        this.mpType = this.$options.mpType
      }
      if (this.mpType === 'app') {
        return
      }
      if (this.mpType === 'page') {
87
        this._$vdomSync = new VDomSync(this.$options.pageId, this.$options.pagePath, this)
fxy060608's avatar
init v3  
fxy060608 已提交
88 89 90 91
      }
      if (this._$vd) {
        this._$id = guid()
        this._$vd.addVm(this)
fxy060608's avatar
fxy060608 已提交
92 93
        this._$vdMountedData = Object.create(null)
        this._$setData(MOUNTED_DATA, this._$vdMountedData)
fxy060608's avatar
init v3  
fxy060608 已提交
94 95 96 97
        this._$data = Object.create(null)
        this._$newData = Object.create(null)
      }
    },
fxy060608's avatar
fxy060608 已提交
98
    beforeUpdate () {
fxy060608's avatar
init v3  
fxy060608 已提交
99 100 101
      if (!this._$vd) {
        return
      }
fxy060608's avatar
fxy060608 已提交
102 103 104 105 106 107 108 109 110 111 112 113
      // 当已存在 _$vdMountedData 时,使用重置后的 _$vdMountedData
      const mountedData = this._$vd.find(MOUNTED_DATA, this._$id)
      if (mountedData) {
        this._$data = Object.create(null) // 清空已有数据
        this._$vdUpdatedData = mountedData[1][1] = Object.create(null)
        if (process.env.NODE_ENV !== 'production') {
          console.log('updated=>mounted:' + this._$id)
        }
      } else {
        this._$vdUpdatedData = Object.create(null)
        this._$setData(UPDATED_DATA, this._$vdUpdatedData)
      }
fxy060608's avatar
init v3  
fxy060608 已提交
114 115
      this._$newData = Object.create(null)
    },
fxy060608's avatar
fxy060608 已提交
116
    beforeDestroy () {
fxy060608's avatar
init v3  
fxy060608 已提交
117 118 119 120
      if (!this._$vd) {
        return
      }
      this._$vd.removeVm(this)
fxy060608's avatar
fxy060608 已提交
121
      this._$vdomSync && this._$vdomSync.destroy()
fxy060608's avatar
init v3  
fxy060608 已提交
122 123 124 125
    }
  })
}

fxy060608's avatar
fxy060608 已提交
126 127 128 129 130 131 132 133 134 135 136 137
function parseExternalClasses (clazz, vm) {
  const mpOptions = vm.$options.mpOptions
  if (mpOptions && Array.isArray(mpOptions.externalClasses)) {
    mpOptions.externalClasses.forEach(externalClass => {
      // 简单替换 externalClass
      const externalClassValue = vm[camelize(externalClass)]
      externalClassValue && (clazz = clazz.replace(externalClass, externalClassValue))
    })
  }
  return clazz
}

fxy060608's avatar
fxy060608 已提交
138
function setData (id, name, value) {
fxy060608's avatar
fxy060608 已提交
139 140
  switch (name) {
    case B_CLASS:
fxy060608's avatar
fxy060608 已提交
141 142 143 144
      value = parseExternalClasses(this._$stringifyClass(value), this)
      break
    case S_CLASS:
      value = parseExternalClasses(value, this)
fxy060608's avatar
fxy060608 已提交
145 146 147 148
      break
    case B_STYLE:
      value = this._$normalizeStyleBinding(value)
      break
149
    case V_IF:
fxy060608's avatar
fxy060608 已提交
150
    case V_SHOW:
fxy060608's avatar
fxy060608 已提交
151
    case V_ELSE_IF:
fxy060608's avatar
fxy060608 已提交
152
      value = value ? 1 : 0
fxy060608's avatar
fxy060608 已提交
153 154 155
      break
    case V_FOR:
      return setForData.call(this, id, value)
fxy060608's avatar
init v3  
fxy060608 已提交
156
  }
fxy060608's avatar
fxy060608 已提交
157 158 159 160 161 162
  // TODO 暂时先传递 dataset 至 view 层(理论上不需要)
  if (name.indexOf('a-data-') === 0) {
    try {
      value = JSON.stringify(value)
    } catch (e) {}
  }
fxy060608's avatar
init v3  
fxy060608 已提交
163

fxy060608's avatar
fxy060608 已提交
164
  return ((this._$newData[id] || (this._$newData[id] = {}))[name] = value)
fxy060608's avatar
init v3  
fxy060608 已提交
165 166
}

fxy060608's avatar
fxy060608 已提交
167
function setForData (id, value) {
fxy060608's avatar
fxy060608 已提交
168
  const diffData = this._$newData[id] || (this._$newData[id] = {})
fxy060608's avatar
fxy060608 已提交
169
  const vForData = diffData[V_FOR] || (diffData[V_FOR] = [])
fxy060608's avatar
fxy060608 已提交
170 171 172 173 174

  if (value.forItems) {
    return value.forItems
  }

fxy060608's avatar
init v3  
fxy060608 已提交
175 176 177 178 179 180 181 182 183 184 185
  const {
    forIndex,
    key
  } = value

  if (!hasOwn(value, 'keyIndex')) {
    vForData[forIndex] = key
  } else {
    (vForData[forIndex] || (vForData[forIndex] = {}))['k' + value.keyIndex] = key
  }
  return key
fxy060608's avatar
fxy060608 已提交
186
}