@mpxjs/webpack-plugin 2.9.17 → 2.9.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -46,6 +46,7 @@ const { matchCondition } = require('./utils/match-condition')
46
46
  const processDefs = require('./utils/process-defs')
47
47
  const config = require('./config')
48
48
  const hash = require('hash-sum')
49
+ const nativeLoaderPath = normalize.lib('native-loader')
49
50
  const wxssLoaderPath = normalize.lib('wxss/index')
50
51
  const wxmlLoaderPath = normalize.lib('wxml/loader')
51
52
  const wxsLoaderPath = normalize.lib('wxs/loader')
@@ -196,7 +197,7 @@ class MpxWebpackPlugin {
196
197
 
197
198
  static nativeLoader (options = {}) {
198
199
  return {
199
- loader: normalize.lib('native-loader'),
200
+ loader: nativeLoaderPath,
200
201
  options
201
202
  }
202
203
  }
@@ -251,6 +252,10 @@ class MpxWebpackPlugin {
251
252
  return addQuery(request, { isComponent: true })
252
253
  }
253
254
 
255
+ static getNativeEntry (request) {
256
+ return `!!${nativeLoaderPath}!${request}`
257
+ }
258
+
254
259
  static getPluginEntry (request) {
255
260
  return addQuery(request, {
256
261
  mpx: true,
package/lib/loader.js CHANGED
@@ -50,7 +50,6 @@ module.exports = function (content) {
50
50
  const localSrcMode = queryObj.mode
51
51
  const srcMode = localSrcMode || globalSrcMode
52
52
  const autoScope = matchCondition(resourcePath, mpx.autoScopeRules)
53
- const isApp = !(pagesMap[resourcePath] || componentsMap[resourcePath])
54
53
 
55
54
  const emitWarning = (msg) => {
56
55
  this.emitWarning(
@@ -64,14 +63,11 @@ module.exports = function (content) {
64
63
  )
65
64
  }
66
65
 
67
- let ctorType = 'app'
68
- if (pagesMap[resourcePath]) {
69
- // page
70
- ctorType = 'page'
71
- } else if (componentsMap[resourcePath]) {
72
- // component
73
- ctorType = 'component'
74
- }
66
+ let ctorType = pagesMap[resourcePath]
67
+ ? 'page'
68
+ : componentsMap[resourcePath]
69
+ ? 'component'
70
+ : 'app'
75
71
 
76
72
  // 支持资源query传入isPage或isComponent支持页面/组件单独编译
77
73
  if (ctorType === 'app' && (queryObj.isComponent || queryObj.isPage)) {
@@ -130,7 +126,7 @@ module.exports = function (content) {
130
126
  warn: emitWarning,
131
127
  error: emitError
132
128
  }
133
- if (!isApp) {
129
+ if (ctorType !== 'app') {
134
130
  rulesRunnerOptions.mainKey = pagesMap[resourcePath] ? 'page' : 'component'
135
131
  }
136
132
  const rulesRunner = getRulesRunner(rulesRunnerOptions)
@@ -278,17 +274,12 @@ module.exports = function (content) {
278
274
  }
279
275
 
280
276
  // 注入构造函数
281
- let ctor = 'App'
282
- if (ctorType === 'page') {
283
- // swan也默认使用Page构造器
284
- if (mpx.forceUsePageCtor || mode === 'ali') {
285
- ctor = 'Page'
286
- } else {
287
- ctor = 'Component'
288
- }
289
- } else if (ctorType === 'component') {
290
- ctor = 'Component'
291
- }
277
+ const ctor = ctorType === 'page'
278
+ ? (mpx.forceUsePageCtor || mode === 'ali') ? 'Page' : 'Component'
279
+ : ctorType === 'component'
280
+ ? 'Component'
281
+ : 'App'
282
+
292
283
  output += `global.currentCtor = ${ctor}\n`
293
284
  output += `global.currentCtorType = ${JSON.stringify(ctor.replace(/^./, (match) => {
294
285
  return match.toLowerCase()
@@ -6,8 +6,12 @@ const createHelpers = require('./helpers')
6
6
  const getJSONContent = require('./utils/get-json-content')
7
7
  const async = require('async')
8
8
  const { matchCondition } = require('./utils/match-condition')
9
- const { JSON_JS_EXT } = require('./utils/const')
9
+ const { JSON_JS_EXT, MPX_APP_MODULE_ID } = require('./utils/const')
10
10
  const getRulesRunner = require('./platform')
11
+ const getEntryName = require('./utils/get-entry-name')
12
+ const AppEntryDependency = require('./dependencies/AppEntryDependency')
13
+ const RecordResourceMapDependency = require('./dependencies/RecordResourceMapDependency')
14
+
11
15
  // todo native-loader考虑与mpx-loader或加强复用,原生组件约等于4个区块都为src的.mpx文件
12
16
  module.exports = function (content) {
13
17
  this.cacheable()
@@ -21,8 +25,8 @@ module.exports = function (content) {
21
25
  const loaderContext = this
22
26
  const isProduction = this.minimize || process.env.NODE_ENV === 'production'
23
27
  const filePath = this.resourcePath
24
- const moduleId = 'm' + mpx.pathHash(filePath)
25
28
  const { resourcePath, queryObj } = parseRequest(this.resource)
29
+ const packageRoot = queryObj.packageRoot || mpx.currentPackageRoot
26
30
  const mode = mpx.mode
27
31
  const globalSrcMode = mpx.srcMode
28
32
  const localSrcMode = queryObj.mode
@@ -31,7 +35,6 @@ module.exports = function (content) {
31
35
  const componentsMap = mpx.componentsMap[packageName]
32
36
  const parsed = path.parse(resourcePath)
33
37
  const resourceName = path.join(parsed.dir, parsed.name)
34
- const isApp = !(pagesMap[resourcePath] || componentsMap[resourcePath])
35
38
  const srcMode = localSrcMode || globalSrcMode
36
39
  const typeExtMap = config[srcMode].typeExtMap
37
40
  const typeResourceMap = {}
@@ -143,6 +146,7 @@ module.exports = function (content) {
143
146
  useJSONJS
144
147
  }, null, this, callback)
145
148
  }, (content, callback) => {
149
+ let componentPlaceholder = []
146
150
  let json
147
151
  try {
148
152
  json = JSON5.parse(content)
@@ -158,7 +162,35 @@ module.exports = function (content) {
158
162
  warn: emitWarning,
159
163
  error: emitError
160
164
  }
161
- if (!isApp) {
165
+
166
+ let ctorType = pagesMap[resourcePath]
167
+ ? 'page'
168
+ : componentsMap[resourcePath]
169
+ ? 'component'
170
+ : 'app'
171
+
172
+ // 支持资源query传入isPage或isComponent支持页面/组件单独编译
173
+ if (ctorType === 'app' && (queryObj.isComponent || queryObj.isPage)) {
174
+ const entryName = getEntryName(this) || mpx.getOutputPath(resourcePath, queryObj.isComponent ? 'component' : 'page')
175
+ ctorType = queryObj.isComponent ? 'component' : 'page'
176
+ this._module.addPresentationalDependency(new RecordResourceMapDependency(resourcePath, ctorType, entryName, packageRoot))
177
+ }
178
+
179
+ // 处理构造器类型
180
+ const ctor = ctorType === 'page'
181
+ ? (mpx.forceUsePageCtor || mode === 'ali') ? 'Page' : 'Component'
182
+ : ctorType === 'component'
183
+ ? 'Component'
184
+ : 'App'
185
+
186
+ if (ctorType === 'app') {
187
+ const appName = getEntryName(this)
188
+ if (appName) this._module.addPresentationalDependency(new AppEntryDependency(resourcePath, appName))
189
+ }
190
+
191
+ const moduleId = ctorType === 'app' ? MPX_APP_MODULE_ID : 'm' + mpx.pathHash(filePath)
192
+
193
+ if (ctorType !== 'app') {
162
194
  rulesRunnerOptions.mainKey = pagesMap[resourcePath] ? 'page' : 'component'
163
195
  }
164
196
  const rulesRunner = getRulesRunner(rulesRunnerOptions)
@@ -166,6 +198,9 @@ module.exports = function (content) {
166
198
  if (json.usingComponents) {
167
199
  usingComponents = usingComponents.concat(Object.keys(json.usingComponents))
168
200
  }
201
+ if (json.componentPlaceholder) {
202
+ componentPlaceholder = componentPlaceholder.concat(Object.values(json.componentPlaceholder))
203
+ }
169
204
  const {
170
205
  getRequire
171
206
  } = createHelpers(loaderContext)
@@ -180,14 +215,16 @@ module.exports = function (content) {
180
215
 
181
216
  switch (type) {
182
217
  case 'template':
183
- if (isApp) return ''
218
+ if (ctorType === 'app') return ''
184
219
  Object.assign(extraOptions, {
185
220
  hasScoped,
186
221
  hasComment,
187
222
  isNative,
188
223
  moduleId,
189
- usingComponents
224
+ usingComponents,
225
+ componentPlaceholder
190
226
  })
227
+ // if (template.src) extraOptions.resourcePath = resourcePath
191
228
  break
192
229
  case 'styles':
193
230
  if (cssLang) part.lang = cssLang
@@ -209,20 +246,6 @@ module.exports = function (content) {
209
246
  output += `global.currentResource = ${JSON.stringify(filePath)}\n`
210
247
  }
211
248
 
212
- // 注入构造函数
213
- let ctor = 'App'
214
- let ctorType = 'app'
215
- if (pagesMap[resourcePath]) {
216
- ctorType = 'page'
217
- if (mpx.forceUsePageCtor || mode === 'ali') {
218
- ctor = 'Page'
219
- } else {
220
- ctor = 'Component'
221
- }
222
- } else if (componentsMap[resourcePath]) {
223
- ctor = 'Component'
224
- ctorType = 'component'
225
- }
226
249
  output += `global.currentCtor = ${ctor}\n`
227
250
  output += `global.currentCtorType = ${JSON.stringify(ctor.replace(/^./, (match) => {
228
251
  return match.toLowerCase()
@@ -7,7 +7,7 @@ module.exports = function ({ print }) {
7
7
  return {
8
8
  test: TAG_NAME,
9
9
  web (tag, { el }) {
10
- if (el.hasEvent) {
10
+ if (el.hasModel) {
11
11
  el.isBuiltIn = true
12
12
  }
13
13
  if (el.isBuiltIn) {
@@ -10,9 +10,10 @@ module.exports = function ({ print }) {
10
10
  return {
11
11
  test: TAG_NAME,
12
12
  web (tag, { el }) {
13
- if (el.hasEvent) {
13
+ if (el.hasModel) {
14
14
  el.isBuiltIn = true
15
15
  }
16
+
16
17
  if (el.isBuiltIn) {
17
18
  return 'mpx-text'
18
19
  } else {
@@ -12,7 +12,7 @@ module.exports = function ({ print }) {
12
12
  // return 'a:view'
13
13
  // },
14
14
  web (tag, { el }) {
15
- if (el.hasEvent) {
15
+ if (el.hasModel) {
16
16
  el.isBuiltIn = true
17
17
  }
18
18
  if (el.isBuiltIn) {
@@ -124,7 +124,7 @@ module.exports = function getSpec ({ warn, error }) {
124
124
  {
125
125
  test: 'wx:model',
126
126
  web ({ value }, { el }) {
127
- el.hasEvent = true
127
+ el.hasModel = true
128
128
  const attrsMap = el.attrsMap
129
129
  const tagRE = /\{\{((?:.|\n|\r)+?)\}\}(?!})/
130
130
  const stringify = JSON.stringify
@@ -387,8 +387,6 @@ module.exports = function getSpec ({ warn, error }) {
387
387
  modifierStr
388
388
  }
389
389
  const isComponent = usingComponents.indexOf(el.tag) !== -1 || el.tag === 'component'
390
- // 记录event监听信息用于后续判断是否需要使用内置基础组件
391
- el.hasEvent = true
392
390
  const rPrefix = runRules(spec.event.prefix, prefix, { mode: 'web', meta })
393
391
  const rEventName = runRules(eventRules, eventName, { mode: 'web', data: { isComponent } })
394
392
  return {
@@ -0,0 +1,100 @@
1
+ import { extendEvent } from './getInnerListeners'
2
+ import { isBrowser } from '../../env'
3
+
4
+ function MpxEvent (layer) {
5
+ this.targetElement = null
6
+
7
+ this.touches = []
8
+
9
+ this.touchStartX = 0
10
+
11
+ this.touchStartY = 0
12
+
13
+ this.startTimer = null
14
+
15
+ this.needTap = true
16
+
17
+ this.isTouchDevice = document && ('ontouchstart' in document.documentElement)
18
+
19
+ this.onTouchStart = (event) => {
20
+ if (event.targetTouches?.length > 1) {
21
+ return true
22
+ }
23
+
24
+ this.touches = event.targetTouches
25
+ this.targetElement = event.target
26
+ this.needTap = true
27
+ this.startTimer = null
28
+ this.touchStartX = this.touches[0].pageX
29
+ this.touchStartY = this.touches[0].pageY
30
+ this.startTimer = setTimeout(() => {
31
+ this.needTap = false
32
+ this.sendEvent(this.targetElement, 'longpress', event)
33
+ this.sendEvent(this.targetElement, 'longtap', event)
34
+ }, 350)
35
+ }
36
+
37
+ this.onTouchMove = (event) => {
38
+ const touch = event.changedTouches[0]
39
+ if (Math.abs(touch.pageX - this.touchStartX) > 1 || Math.abs(touch.pageY - this.touchStartY) > 1) {
40
+ this.needTap = false
41
+ this.startTimer && clearTimeout(this.startTimer)
42
+ this.startTimer = null
43
+ }
44
+ }
45
+
46
+ this.onTouchEnd = (event) => {
47
+ if (event.targetTouches?.length > 1) {
48
+ return true
49
+ }
50
+ this.startTimer && clearTimeout(this.startTimer)
51
+ this.startTimer = null
52
+ if (this.needTap) {
53
+ this.sendEvent(this.targetElement, 'tap', event)
54
+ }
55
+ }
56
+
57
+ this.onClick = (event) => {
58
+ this.targetElement = event.target
59
+ this.sendEvent(this.targetElement, 'tap', event)
60
+ }
61
+ this.sendEvent = (targetElement, type, event) => {
62
+ // eslint-disable-next-line no-undef
63
+ const touchEvent = new TouchEvent(type, {
64
+ view: window,
65
+ bubbles: true,
66
+ cancelable: true
67
+ })
68
+ const changedTouches = event.changedTouches || []
69
+ extendEvent(touchEvent, {
70
+ timeStamp: event.timeStamp,
71
+ currentTarget: event.target,
72
+ changedTouches,
73
+ touches: changedTouches,
74
+ detail: {
75
+ // pc端点击事件可能没有changedTouches,所以直接从 event中取
76
+ x: changedTouches[0]?.pageX || event.pageX || 0,
77
+ y: changedTouches[0]?.pageY || event.pageY || 0
78
+ }
79
+ })
80
+ targetElement && targetElement.dispatchEvent(touchEvent)
81
+ }
82
+
83
+ this.addListener = () => {
84
+ if (this.isTouchDevice) {
85
+ layer.addEventListener('touchstart', this.onTouchStart, true)
86
+ layer.addEventListener('touchmove', this.onTouchMove, true)
87
+ layer.addEventListener('touchend', this.onTouchEnd, true)
88
+ } else {
89
+ layer.addEventListener('click', this.onClick, true)
90
+ }
91
+ }
92
+ this.addListener()
93
+ }
94
+
95
+ if (isBrowser) {
96
+ document.addEventListener('DOMContentLoaded', function () {
97
+ // eslint-disable-next-line no-new
98
+ new MpxEvent(document.body)
99
+ }, false)
100
+ }
@@ -1,6 +1,3 @@
1
- import { isEmptyObject } from '../../utils'
2
- import { isBrowser } from '../../env'
3
-
4
1
  function processModel (listeners, context) {
5
2
  // 该函数只有wx:model的情况下才调用,而且默认e.detail.value有值
6
3
  // 该函数必须在产生merge前执行
@@ -44,80 +41,6 @@ function mergeListeners (listeners, otherListeners, options = {}) {
44
41
  })
45
42
  }
46
43
 
47
- function processTap (listeners, context) {
48
- const listenerMap = {}
49
- const tapEvents = ['tap', 'longpress', 'longtap']
50
- const isTouchDevice = isBrowser ? document && ('ontouchstart' in document.documentElement) : true
51
- tapEvents.forEach((eventName) => {
52
- if (listeners[eventName]) {
53
- listenerMap[eventName] = true
54
- delete listeners[eventName]
55
- }
56
- })
57
- if (isEmptyObject(listenerMap)) return
58
- context.__mpxTapInfo = context.__mpxTapInfo || {}
59
- let events
60
- if (isTouchDevice) {
61
- events = {
62
- touchstart (e) {
63
- context.__mpxTapInfo.detail = {
64
- x: e.changedTouches[0].pageX,
65
- y: e.changedTouches[0].pageY
66
- }
67
- context.__mpxTapInfo.startTimer = null
68
- context.__mpxTapInfo.needTap = true
69
- if (listenerMap.longpress || listenerMap.longtap) {
70
- context.__mpxTapInfo.startTimer = setTimeout(() => {
71
- context.__mpxTapInfo.needTap = false
72
- if (listenerMap.longpress) {
73
- const re = inheritEvent('longpress', e, context.__mpxTapInfo.detail)
74
- context.$emit('longpress', re)
75
- }
76
- if (listenerMap.longtap) {
77
- const re = inheritEvent('longtap', e, context.__mpxTapInfo.detail)
78
- context.$emit('longtap', re)
79
- }
80
- }, 350)
81
- }
82
- },
83
- touchmove (e) {
84
- const tapDetailInfo = context.__mpxTapInfo.detail || {}
85
- const currentPageX = e.changedTouches[0].pageX
86
- const currentPageY = e.changedTouches[0].pageY
87
- if (Math.abs(currentPageX - tapDetailInfo.x) > 1 || Math.abs(currentPageY - tapDetailInfo.y) > 1) {
88
- context.__mpxTapInfo.needTap = false
89
- context.__mpxTapInfo.startTimer && clearTimeout(context.__mpxTapInfo.startTimer)
90
- context.__mpxTapInfo.startTimer = null
91
- }
92
- },
93
- touchend (e) {
94
- context.__mpxTapInfo.startTimer && clearTimeout(context.__mpxTapInfo.startTimer)
95
- if (listenerMap.tap && context.__mpxTapInfo.needTap) {
96
- const re = inheritEvent('tap', e, context.__mpxTapInfo.detail)
97
- context.$emit('tap', re)
98
- }
99
- }
100
- }
101
- } else {
102
- events = {
103
- click (e) {
104
- if (listenerMap.tap) {
105
- context.__mpxTapInfo.detail = {
106
- x: e.pageX,
107
- y: e.pageY
108
- }
109
- const re = inheritEvent('tap', e, context.__mpxTapInfo.detail)
110
- context.$emit('tap', re)
111
- }
112
- }
113
- }
114
- }
115
-
116
- mergeListeners(listeners, events, {
117
- force: true
118
- })
119
- }
120
-
121
44
  export function extendEvent (e, extendObj = {}) {
122
45
  Object.keys(extendObj).forEach((key) => {
123
46
  Object.defineProperty(e, key, {
@@ -181,7 +104,6 @@ export default function getInnerListeners (context, options = {}) {
181
104
  }
182
105
 
183
106
  processModel(listeners, context)
184
- processTap(listeners, context)
185
107
  mergeListeners(listeners, mergeBefore, mergeBeforeOptions)
186
108
  mergeListeners(listeners, mergeAfter, mergeAfterOptions)
187
109
  ignoredListeners.forEach((key) => {
@@ -95,7 +95,7 @@
95
95
  },
96
96
  mounted () {
97
97
  if (this.formType) {
98
- this.$on('tap', () => {
98
+ this.$el.addEventListener('tap', () => {
99
99
  if (this.form && this.form[this.formType]) {
100
100
  this.form[this.formType]()
101
101
  }
@@ -36,7 +36,7 @@
36
36
  }
37
37
  },
38
38
  mounted () {
39
- this.$on('tap', () => {
39
+ this.$el.addEventListener('tap', () => {
40
40
  const mpx = global.__mpx
41
41
  if (mpx) {
42
42
  switch (this.openType) {
@@ -3,12 +3,13 @@
3
3
  import { processSize } from '../../utils'
4
4
  import BScroll from '@better-scroll/core'
5
5
  import PullDown from '@better-scroll/pull-down'
6
- import ObserveDom from '@better-scroll/observe-dom'
7
6
  import throttle from 'lodash/throttle'
7
+ import debounce from 'lodash/debounce'
8
8
 
9
- BScroll.use(ObserveDom)
10
9
  BScroll.use(PullDown)
11
10
 
11
+ let mutationObserver = null
12
+
12
13
  export default {
13
14
  name: 'mpx-scroll-view',
14
15
  props: {
@@ -36,10 +37,6 @@
36
37
  return {}
37
38
  }
38
39
  },
39
- updateRefresh: {
40
- type: Boolean,
41
- default: true
42
- },
43
40
  scrollIntoView: String,
44
41
  scrollWithAnimation: Boolean,
45
42
  enableFlex: Boolean,
@@ -66,7 +63,11 @@
66
63
  currentX: 0,
67
64
  currentY: 0,
68
65
  lastX: 0,
69
- lastY: 0
66
+ lastY: 0,
67
+ lastContentWidth: 0,
68
+ lastContentHeight: 0,
69
+ lastWrapperWidth: 0,
70
+ lastWrapperHeight: 0
70
71
  }
71
72
  },
72
73
  computed: {
@@ -101,12 +102,28 @@
101
102
  }
102
103
  return className
103
104
  },
104
- scroll() {
105
+ scroll () {
105
106
  return this.scrollX || this.scrollY
106
107
  }
107
108
  },
108
109
  mounted () {
110
+ this.debounceRefresh = debounce(function () {
111
+ this.refresh()
112
+ }, 200, {
113
+ leading: false,
114
+ trailing: true
115
+ })
116
+ this.dispatchScrollTo = throttle(function (direction) {
117
+ let eventName = 'scrolltoupper'
118
+ if (direction === 'bottom' || direction === 'right') eventName = 'scrolltolower'
119
+ this.$emit(eventName, getCustomEvent(eventName, { direction }, this))
120
+ }, 200, {
121
+ leading: true,
122
+ trailing: false
123
+ })
109
124
  this.initBs()
125
+ this.observeAnimation('add')
126
+ this.handleMutationObserver()
110
127
  },
111
128
  activated () {
112
129
  if (!this.__mpx_deactivated) {
@@ -121,11 +138,11 @@
121
138
  deactivated () {
122
139
  this.__mpx_deactivated = true
123
140
  },
141
+
124
142
  beforeDestroy () {
125
143
  this.destroyBs()
126
- },
127
- updated () {
128
- if (this.updateRefresh) this.refresh()
144
+ this.observeAnimation('remove')
145
+ this.destroyMutationObserver()
129
146
  },
130
147
  watch: {
131
148
  scrollIntoView (val) {
@@ -153,7 +170,7 @@
153
170
  }
154
171
  },
155
172
  },
156
- scroll(val) {
173
+ scroll (val) {
157
174
  if (val) {
158
175
  this.initBs()
159
176
  } else {
@@ -162,12 +179,19 @@
162
179
  }
163
180
  },
164
181
  methods: {
182
+ observeAnimation (type) {
183
+ const eventNames = ['transitionend', 'animationend']
184
+ const behaviorType = type === 'add' ? 'addEventListener' : 'removeEventListener'
185
+ eventNames.forEach(eventName => {
186
+ this.$refs.scrollContent?.[behaviorType](eventName, this.handleObserveAnimation)
187
+ })
188
+ },
165
189
  destroyBs () {
166
190
  if (!this.bs) return
167
191
  this.bs.destroy()
168
192
  delete this.bs
169
193
  },
170
- disableBs() {
194
+ disableBs () {
171
195
  if (!this.bs) return
172
196
  this.bs.disable()
173
197
  this.currentX = -this.bs.x
@@ -176,6 +200,9 @@
176
200
  initBs () {
177
201
  this.destroyBs()
178
202
  this.initLayerComputed()
203
+ if (this.scrollOptions.observeDOM) {
204
+ console.warn('[Mpx runtime warn]The observeDOM attribute in scroll-view has been deprecated, please stop using it')
205
+ }
179
206
  const originBsOptions = {
180
207
  startX: -this.currentX,
181
208
  startY: -this.currentY,
@@ -194,11 +221,8 @@
194
221
  stop: 56
195
222
  }
196
223
  }
197
- const bsOptions = Object.assign({}, originBsOptions, this.scrollOptions)
224
+ const bsOptions = Object.assign({}, originBsOptions, this.scrollOptions, { observeDOM: false })
198
225
  this.bs = new BScroll(this.$refs.wrapper, bsOptions)
199
- this.bs.scroller.hooks.on('beforeRefresh', () => {
200
- this.initLayerComputed()
201
- })
202
226
  this.lastX = -this.currentX
203
227
  this.lastY = -this.currentY
204
228
  this.bs.on('scroll', throttle(({ x, y }) => {
@@ -283,6 +307,7 @@
283
307
  }
284
308
  }
285
309
  })
310
+
286
311
  this.bs.on('pullingDown', () => {
287
312
  this.$emit('refresherrefresh', getCustomEvent('refresherrefresh', {}, this))
288
313
  })
@@ -297,13 +322,17 @@
297
322
  },
298
323
  initLayerComputed () {
299
324
  const wrapper = this.$refs.wrapper
300
- const computedStyle = getComputedStyle(wrapper)
301
- // 考虑子元素样式可能会设置100%,如果直接继承 scrollContent 的样式可能会有问题
302
- // 所以使用 wrapper 作为 innerWrapper 的宽高参考依据
303
- this.$refs.innerWrapper.style.width = `${wrapper.clientWidth - parseInt(computedStyle.paddingLeft) - parseInt(computedStyle.paddingRight)}px`
304
- this.$refs.innerWrapper.style.height = `${wrapper.clientHeight - parseInt(computedStyle.paddingTop) - parseInt(computedStyle.paddingBottom)}px`
325
+ const scrollWrapperWidth = wrapper?.clientWidth || 0
326
+ const scrollWrapperHeight = wrapper?.clientHeight || 0
327
+ if (wrapper) {
328
+ const computedStyle = getComputedStyle(wrapper)
329
+ // 考虑子元素样式可能会设置100%,如果直接继承 scrollContent 的样式可能会有问题
330
+ // 所以使用 wrapper 作为 innerWrapper 的宽高参考依据
331
+ this.$refs.innerWrapper.style.width = `${scrollWrapperWidth - parseInt(computedStyle.paddingLeft) - parseInt(computedStyle.paddingRight)}px`
332
+ this.$refs.innerWrapper.style.height = `${scrollWrapperHeight - parseInt(computedStyle.paddingTop) - parseInt(computedStyle.paddingBottom)}px`
333
+ }
305
334
  const innerWrapper = this.$refs.innerWrapper
306
- const childrenArr = Array.from(innerWrapper.children)
335
+ const childrenArr = innerWrapper ? Array.from(innerWrapper.children) : []
307
336
 
308
337
  const getMinLength = (min, value) => {
309
338
  if (min === undefined) {
@@ -334,27 +363,72 @@
334
363
  maxRight = getMaxLength(maxRight, temp.right)
335
364
  maxBottom = getMaxLength(maxBottom, temp.bottom)
336
365
  })
337
-
338
- const width = maxRight - minLeft
339
- const height = maxBottom - minTop
340
- this.$refs.scrollContent.style.width = `${width}px`
341
- this.$refs.scrollContent.style.height = `${height}px`
366
+ const width = maxRight - minLeft || 0
367
+ const height = maxBottom - minTop || 0
368
+ if (this.$refs.scrollContent) {
369
+ this.$refs.scrollContent.style.width = `${width}px`
370
+ this.$refs.scrollContent.style.height = `${height}px`
371
+ }
372
+ return {
373
+ scrollContentWidth: width,
374
+ scrollContentHeight: height,
375
+ scrollWrapperWidth,
376
+ scrollWrapperHeight
377
+ }
342
378
  },
343
379
  refresh () {
344
380
  if (this.__mpx_deactivated) {
345
381
  this.__mpx_deactivated_refresh = true
346
382
  return
347
383
  }
348
- if (this.bs) this.bs.refresh()
384
+ const { scrollContentWidth, scrollContentHeight, scrollWrapperWidth, scrollWrapperHeight} = this.initLayerComputed()
385
+ if (!this.compare(scrollWrapperWidth, this.lastWrapperWidth) || !this.compare(scrollWrapperHeight, this.lastWrapperHeight) || !this.compare(scrollContentWidth, this.lastContentWidth) || !this.compare(scrollContentHeight, this.lastContentHeight)) {
386
+ this.lastContentWidth = scrollContentWidth
387
+ this.lastContentHeight = scrollContentHeight
388
+ this.lastWrapperWidth = scrollWrapperWidth
389
+ this.lastWrapperHeight = scrollWrapperHeight
390
+ if (this.bs) this.bs.refresh()
391
+ }
349
392
  },
350
- dispatchScrollTo: throttle(function (direction) {
351
- let eventName = 'scrolltoupper'
352
- if (direction === 'bottom' || direction === 'right') eventName = 'scrolltolower'
353
- this.$emit(eventName, getCustomEvent(eventName, { direction }, this))
354
- }, 200, {
355
- leading: true,
356
- trailing: false
357
- })
393
+ compare(num1, num2, scale = 1) {
394
+ return Math.abs(num1 - num2) < scale
395
+ },
396
+ handleMutationObserver () {
397
+ if (typeof MutationObserver !== 'undefined') {
398
+ mutationObserver = new MutationObserver((mutations) => this.mutationObserverHandler(mutations))
399
+ const config = { attributes: true, childList: true, subtree: true }
400
+ mutationObserver.observe(this.$refs.wrapper, config)
401
+ }
402
+ },
403
+ mutationObserverHandler (mutations) {
404
+ let needRefresh = false
405
+ for (let i = 0; i < mutations.length; i++) {
406
+ const mutation = mutations[i]
407
+ if (mutation.type !== 'attributes') {
408
+ needRefresh = true
409
+ break
410
+ } else {
411
+ if (mutation.target !== this.$refs.scrollContent && mutation.target !== this.$refs.innerWrapper) {
412
+ needRefresh = true
413
+ break
414
+ }
415
+ }
416
+ }
417
+ if (needRefresh) {
418
+ this.debounceRefresh()
419
+ }
420
+ },
421
+ handleObserveAnimation (e) {
422
+ if (e.target !== this.$refs.scrollContent) {
423
+ this.debounceRefresh()
424
+ }
425
+ },
426
+ destroyMutationObserver () {
427
+ if (mutationObserver) {
428
+ mutationObserver.disconnect()
429
+ mutationObserver = null
430
+ }
431
+ }
358
432
  },
359
433
  render (createElement) {
360
434
  const data = {
@@ -418,11 +492,13 @@
418
492
  bottom: 20px
419
493
  left: 50%
420
494
  transform: translateX(-50%)
495
+
421
496
  .mpx-pull-down-slot
422
497
  position: absolute
423
498
  width: 100%
424
499
  height: auto
425
500
  bottom: 0
501
+
426
502
  .mpx-pull-down-content-black
427
503
  .circle
428
504
  display: inline-block;
@@ -0,0 +1,16 @@
1
+ // 可选链wxs
2
+ module.exports.g = function (val, valKeyArr) {
3
+ var res = val
4
+ if (typeof val !== 'object') {
5
+ return undefined
6
+ }
7
+ var len = valKeyArr.length
8
+ var i = 0
9
+ while (i < len) {
10
+ if ((res = res[valKeyArr[i]]) === undefined) {
11
+ break
12
+ }
13
+ i++
14
+ }
15
+ return res
16
+ }
@@ -86,7 +86,7 @@ export function getComponent (component, extendOptions) {
86
86
  export function getWxsMixin (wxsModules) {
87
87
  if (!wxsModules || !Object.keys(wxsModules).length) return
88
88
  return {
89
- created () {
89
+ beforeCreate () {
90
90
  Object.keys(wxsModules).forEach((key) => {
91
91
  if (key in this) {
92
92
  console.error(`[Mpx runtime error]: The wxs module key [${key}] exist in the component/page instance already, please check and rename it!`)
@@ -104,6 +104,7 @@ let env
104
104
  let platformGetTagNamespace
105
105
  let filePath
106
106
  let refId
107
+ let haveOptionChain = false
107
108
 
108
109
  function updateForScopesMap () {
109
110
  forScopes.forEach((scope) => {
@@ -164,6 +165,8 @@ const i18nWxsRequest = '~' + i18nWxsLoaderPath + '!' + i18nWxsPath
164
165
  const i18nModuleName = '__i18n__'
165
166
  const stringifyWxsPath = '~' + normalize.lib('runtime/stringify.wxs')
166
167
  const stringifyModuleName = '__stringify__'
168
+ const optionsChainWxsPath = '~' + normalize.lib('runtime/oc.wxs')
169
+ const optionsChainWxsName = '__oc__'
167
170
 
168
171
  const tagRES = /(\{\{(?:.|\n|\r)+?\}\})(?!})/
169
172
  const tagRE = /\{\{((?:.|\n|\r)+?)\}\}(?!})/
@@ -637,6 +640,7 @@ function parse (template, options) {
637
640
  forScopes = []
638
641
  forScopesMap = {}
639
642
  hasI18n = false
643
+ haveOptionChain = false
640
644
 
641
645
  platformGetTagNamespace = options.getTagNamespace || no
642
646
 
@@ -761,6 +765,10 @@ function parse (template, options) {
761
765
  }
762
766
  }
763
767
 
768
+ if (haveOptionChain) {
769
+ injectWxs(meta, optionsChainWxsName, optionsChainWxsPath)
770
+ }
771
+
764
772
  injectNodes.forEach((node) => {
765
773
  addChild(root, node, true)
766
774
  })
@@ -1156,6 +1164,7 @@ function parseMustacheWithContext (raw = '') {
1156
1164
  })
1157
1165
  }
1158
1166
 
1167
+ exp = parseOptionChain(exp)
1159
1168
  if (i18n) {
1160
1169
  for (const i18nFuncName of i18nFuncNames) {
1161
1170
  const funcNameRE = new RegExp(`(?<![A-Za-z0-9_$.])${i18nFuncName}\\(`)
@@ -1860,7 +1869,6 @@ function getVirtualHostRoot (options, meta) {
1860
1869
  value: '$listeners'
1861
1870
  }
1862
1871
  ])
1863
- rootView.hasEvent = true
1864
1872
  processElement(rootView, rootView, options, meta)
1865
1873
  return rootView
1866
1874
  }
@@ -2393,6 +2401,169 @@ function genNode (node) {
2393
2401
  return exp
2394
2402
  }
2395
2403
 
2404
+ /**
2405
+ * 处理可选链用法
2406
+ * @param str
2407
+ * @returns
2408
+ */
2409
+ function parseOptionChain (str) {
2410
+ const wxsName = `${optionsChainWxsName}.g`
2411
+ let optionsRes
2412
+ while (optionsRes = /\?\./.exec(str)) {
2413
+ const strLength = str.length
2414
+ const grammarMap = {
2415
+ init () {
2416
+ const initKey = [
2417
+ {
2418
+ mapKey: '[]',
2419
+ mapValue: [
2420
+ {
2421
+ key: '[',
2422
+ value: 1
2423
+ },
2424
+ {
2425
+ key: ']',
2426
+ value: -1
2427
+ }
2428
+ ]
2429
+ },
2430
+ {
2431
+ mapKey: '()',
2432
+ mapValue: [
2433
+ {
2434
+ key: '(',
2435
+ value: 1
2436
+ },
2437
+ {
2438
+ key: ')',
2439
+ value: -1
2440
+ }
2441
+ ]
2442
+ }
2443
+ ]
2444
+ this.count = {}
2445
+ initKey.forEach(({ mapKey, mapValue }) => {
2446
+ mapValue.forEach(({ key, value }) => {
2447
+ this[key] = this.changeState(mapKey, value)
2448
+ })
2449
+ })
2450
+ },
2451
+ changeState (key, value) {
2452
+ if (!this.count[key]) {
2453
+ this.count[key] = 0
2454
+ }
2455
+ return () => {
2456
+ this.count[key] = this.count[key] + value
2457
+ return this.count[key]
2458
+ }
2459
+ },
2460
+ checkState () {
2461
+ return Object.values(this.count).find(i => i)
2462
+ }
2463
+ }
2464
+ let leftIndex = optionsRes.index
2465
+ let rightIndex = leftIndex + 2
2466
+ let haveNotGetValue = true
2467
+ let chainValue = ''
2468
+ let chainKey = ''
2469
+ let notCheck = false
2470
+ grammarMap.init()
2471
+ // 查 ?. 左边值
2472
+ while (haveNotGetValue && leftIndex > 0) {
2473
+ const left = str[leftIndex - 1]
2474
+ const grammar = grammarMap[left]
2475
+ if (notCheck) {
2476
+ // 处于表达式内
2477
+ chainValue = left + chainValue
2478
+ if (grammar) {
2479
+ grammar()
2480
+ if (!grammarMap.checkState()) {
2481
+ // 表达式结束
2482
+ notCheck = false
2483
+ }
2484
+ }
2485
+ } else if (~[']', ')'].indexOf(left)) {
2486
+ // 命中表达式,开始记录表达式
2487
+ chainValue = left + chainValue
2488
+ notCheck = true
2489
+ grammar()
2490
+ } else if (left !== ' ') {
2491
+ if (!/[A-Za-z0-9_$.]/.test(left)) {
2492
+ // 结束
2493
+ haveNotGetValue = false
2494
+ leftIndex++
2495
+ } else {
2496
+ // 正常语法
2497
+ chainValue = left + chainValue
2498
+ }
2499
+ }
2500
+ leftIndex--
2501
+ }
2502
+ if (grammarMap.checkState() && haveNotGetValue) {
2503
+ // 值查找结束但是语法未闭合或者处理到边界还未结束,抛异常
2504
+ throw new Error('[optionChain] option value illegal!!!')
2505
+ }
2506
+ haveNotGetValue = true
2507
+ let keyValue = ''
2508
+ // 查 ?. 右边key
2509
+ while (haveNotGetValue && rightIndex < strLength) {
2510
+ const right = str[rightIndex]
2511
+ const grammar = grammarMap[right]
2512
+ if (notCheck) {
2513
+ // 处于表达式内
2514
+ if (grammar) {
2515
+ grammar()
2516
+ if (grammarMap.checkState()) {
2517
+ keyValue += right
2518
+ } else {
2519
+ // 表达式结束
2520
+ notCheck = false
2521
+ chainKey += `,${keyValue}`
2522
+ keyValue = ''
2523
+ }
2524
+ } else {
2525
+ keyValue += right
2526
+ }
2527
+ } else if (~['[', '('].indexOf(right)) {
2528
+ // 命中表达式,开始记录表达式
2529
+ grammar()
2530
+ if (keyValue) {
2531
+ chainKey += `,'${keyValue}'`
2532
+ keyValue = ''
2533
+ }
2534
+ notCheck = true
2535
+ } else if (!/[A-Za-z0-9_$.?]/.test(right)) {
2536
+ // 结束
2537
+ haveNotGetValue = false
2538
+ rightIndex--
2539
+ } else if (right !== '?') {
2540
+ // 正常语法
2541
+ if (right === '.') {
2542
+ if (keyValue) {
2543
+ chainKey += `,'${keyValue}'`
2544
+ }
2545
+ keyValue = ''
2546
+ } else {
2547
+ keyValue += right
2548
+ }
2549
+ }
2550
+ rightIndex++
2551
+ }
2552
+ if (grammarMap.checkState() && haveNotGetValue) {
2553
+ // key值查找结束但是语法未闭合或者处理到边界还未结束,抛异常
2554
+ throw new Error('[optionChain] option key illegal!!!')
2555
+ }
2556
+ if (keyValue) {
2557
+ chainKey += `,'${keyValue}'`
2558
+ }
2559
+ str = str.slice(0, leftIndex) + `${wxsName}(${chainValue},[${chainKey.slice(1)}])` + str.slice(rightIndex)
2560
+ if (!haveOptionChain) {
2561
+ haveOptionChain = true
2562
+ }
2563
+ }
2564
+ return str
2565
+ }
2566
+
2396
2567
  module.exports = {
2397
2568
  parseComponent,
2398
2569
  parse,
@@ -1,7 +1,10 @@
1
+ const toPosix = require('./to-posix')
2
+
1
3
  module.exports = (loaders, loaderIndex) => {
2
4
  for (let i = loaderIndex; i >= 0; i--) {
3
5
  const currentLoader = loaders[i]
4
- if (currentLoader.path.endsWith('node_modules/ts-loader/dist/stringify-loader.js')) {
6
+ const currentLoaderPath = toPosix(currentLoader.path)
7
+ if (currentLoaderPath.endsWith('node_modules/ts-loader/dist/stringify-loader.js')) {
5
8
  return i
6
9
  }
7
10
  }
@@ -2,6 +2,7 @@
2
2
  const addQuery = require('../utils/add-query')
3
3
  const normalize = require('../utils/normalize')
4
4
  const optionProcessorPath = normalize.lib('runtime/optionProcessor')
5
+ const eventPath = normalize.lib('runtime/components/web/event')
5
6
  const {
6
7
  buildComponentsMap,
7
8
  buildPagesMap,
@@ -65,6 +66,8 @@ module.exports = function (script, {
65
66
  globalTabBar
66
67
  })
67
68
 
69
+ output += `\n require(${stringifyRequest(loaderContext, eventPath)})\n`
70
+
68
71
  output += `\n var App = require(${stringifyRequest(loaderContext, addQuery(resource, { isApp: true }))}).default\n`
69
72
 
70
73
  output += `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpxjs/webpack-plugin",
3
- "version": "2.9.17",
3
+ "version": "2.9.18",
4
4
  "description": "mpx compile core",
5
5
  "keywords": [
6
6
  "mpx"
@@ -83,5 +83,5 @@
83
83
  "engines": {
84
84
  "node": ">=14.14.0"
85
85
  },
86
- "gitHead": "80dd826352ab7178952f6ac1f2d7dbcf610fe6cf"
86
+ "gitHead": "ec6a5d714daf59eed0e5651aa4611a9f9383d233"
87
87
  }