uni.mp.esm.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014
  1. import { SLOT_DEFAULT_NAME, EventChannel, invokeArrayFns, MINI_PROGRAM_PAGE_RUNTIME_HOOKS, ON_LOAD, ON_SHOW, ON_HIDE, ON_UNLOAD, ON_RESIZE, ON_TAB_ITEM_TAP, ON_REACH_BOTTOM, ON_PULL_DOWN_REFRESH, ON_ADD_TO_FAVORITES, isUniLifecycleHook, ON_READY, once, ON_LAUNCH, ON_ERROR, ON_THEME_CHANGE, ON_PAGE_NOT_FOUND, ON_UNHANDLE_REJECTION, addLeadingSlash, stringifyQuery, customizeEvent } from '@dcloudio/uni-shared';
  2. import { isArray, isFunction, hasOwn, extend, hyphenate, isPlainObject, isObject } from '@vue/shared';
  3. import { ref, nextTick, findComponentPropsData, toRaw, updateProps, hasQueueJob, invalidateJob, devtoolsComponentAdded, getExposeProxy, pruneComponentPropsCache } from 'vue';
  4. import { normalizeLocale, LOCALE_EN } from '@dcloudio/uni-i18n';
  5. const MP_METHODS = [
  6. 'createSelectorQuery',
  7. 'createIntersectionObserver',
  8. 'selectAllComponents',
  9. 'selectComponent',
  10. ];
  11. function createEmitFn(oldEmit, ctx) {
  12. return function emit(event, ...args) {
  13. const scope = ctx.$scope;
  14. if (scope && event) {
  15. const detail = { __args__: args };
  16. // 百度小程序,快手小程序,自定义组件不能绑定动态事件
  17. {
  18. detail.__ins__ = scope;
  19. }
  20. {
  21. scope.triggerEvent(event, detail);
  22. }
  23. }
  24. return oldEmit.apply(this, [event, ...args]);
  25. };
  26. }
  27. function initBaseInstance(instance, options) {
  28. const ctx = instance.ctx;
  29. // mp
  30. ctx.mpType = options.mpType; // @deprecated
  31. ctx.$mpType = options.mpType;
  32. ctx.$mpPlatform = "mp-kuaishou";
  33. ctx.$scope = options.mpInstance;
  34. // TODO @deprecated
  35. ctx.$mp = {};
  36. if (__VUE_OPTIONS_API__) {
  37. ctx._self = {};
  38. }
  39. // slots
  40. instance.slots = {};
  41. if (isArray(options.slots) && options.slots.length) {
  42. options.slots.forEach((name) => {
  43. instance.slots[name] = true;
  44. });
  45. if (instance.slots[SLOT_DEFAULT_NAME]) {
  46. instance.slots.default = true;
  47. }
  48. }
  49. ctx.getOpenerEventChannel = function () {
  50. if (!this.__eventChannel__) {
  51. this.__eventChannel__ = new EventChannel();
  52. }
  53. return this.__eventChannel__;
  54. };
  55. ctx.$hasHook = hasHook;
  56. ctx.$callHook = callHook;
  57. // $emit
  58. instance.emit = createEmitFn(instance.emit, ctx);
  59. }
  60. function initComponentInstance(instance, options) {
  61. initBaseInstance(instance, options);
  62. const ctx = instance.ctx;
  63. MP_METHODS.forEach((method) => {
  64. ctx[method] = function (...args) {
  65. const mpInstance = ctx.$scope;
  66. if (mpInstance && mpInstance[method]) {
  67. return mpInstance[method].apply(mpInstance, args);
  68. }
  69. };
  70. });
  71. }
  72. function initMocks(instance, mpInstance, mocks) {
  73. const ctx = instance.ctx;
  74. mocks.forEach((mock) => {
  75. if (hasOwn(mpInstance, mock)) {
  76. instance[mock] = ctx[mock] = mpInstance[mock];
  77. }
  78. });
  79. }
  80. function hasHook(name) {
  81. const hooks = this.$[name];
  82. if (hooks && hooks.length) {
  83. return true;
  84. }
  85. return false;
  86. }
  87. function callHook(name, args) {
  88. if (name === 'mounted') {
  89. callHook.call(this, 'bm'); // beforeMount
  90. this.$.isMounted = true;
  91. name = 'm';
  92. }
  93. {
  94. if (name === 'onLoad' &&
  95. args &&
  96. args.__id__ &&
  97. isFunction(ks.getEventChannel)) {
  98. this.__eventChannel__ = ks.getEventChannel(args.__id__);
  99. delete args.__id__;
  100. }
  101. }
  102. const hooks = this.$[name];
  103. return hooks && invokeArrayFns(hooks, args);
  104. }
  105. const PAGE_INIT_HOOKS = [
  106. ON_LOAD,
  107. ON_SHOW,
  108. ON_HIDE,
  109. ON_UNLOAD,
  110. ON_RESIZE,
  111. ON_TAB_ITEM_TAP,
  112. ON_REACH_BOTTOM,
  113. ON_PULL_DOWN_REFRESH,
  114. ON_ADD_TO_FAVORITES,
  115. // 'onReady', // lifetimes.ready
  116. // 'onPageScroll', // 影响性能,开发者手动注册
  117. // 'onShareTimeline', // 右上角菜单,开发者手动注册
  118. // 'onShareAppMessage' // 右上角菜单,开发者手动注册
  119. ];
  120. function findHooks(vueOptions, hooks = new Set()) {
  121. if (vueOptions) {
  122. Object.keys(vueOptions).forEach((name) => {
  123. if (isUniLifecycleHook(name, vueOptions[name])) {
  124. hooks.add(name);
  125. }
  126. });
  127. if (__VUE_OPTIONS_API__) {
  128. const { extends: extendsOptions, mixins } = vueOptions;
  129. if (mixins) {
  130. mixins.forEach((mixin) => findHooks(mixin, hooks));
  131. }
  132. if (extendsOptions) {
  133. findHooks(extendsOptions, hooks);
  134. }
  135. }
  136. }
  137. return hooks;
  138. }
  139. function initHook(mpOptions, hook, excludes) {
  140. if (excludes.indexOf(hook) === -1 && !hasOwn(mpOptions, hook)) {
  141. mpOptions[hook] = function (args) {
  142. return this.$vm && this.$vm.$callHook(hook, args);
  143. };
  144. }
  145. }
  146. const EXCLUDE_HOOKS = [ON_READY];
  147. function initHooks(mpOptions, hooks, excludes = EXCLUDE_HOOKS) {
  148. hooks.forEach((hook) => initHook(mpOptions, hook, excludes));
  149. }
  150. function initUnknownHooks(mpOptions, vueOptions, excludes = EXCLUDE_HOOKS) {
  151. findHooks(vueOptions).forEach((hook) => initHook(mpOptions, hook, excludes));
  152. }
  153. function initRuntimeHooks(mpOptions, runtimeHooks) {
  154. if (!runtimeHooks) {
  155. return;
  156. }
  157. const hooks = Object.keys(MINI_PROGRAM_PAGE_RUNTIME_HOOKS);
  158. hooks.forEach((hook) => {
  159. if (runtimeHooks & MINI_PROGRAM_PAGE_RUNTIME_HOOKS[hook]) {
  160. initHook(mpOptions, hook, []);
  161. }
  162. });
  163. }
  164. const findMixinRuntimeHooks = /*#__PURE__*/ once(() => {
  165. const runtimeHooks = [];
  166. const app = isFunction(getApp) && getApp({ allowDefault: true });
  167. if (app && app.$vm && app.$vm.$) {
  168. const mixins = app.$vm.$.appContext.mixins;
  169. if (isArray(mixins)) {
  170. const hooks = Object.keys(MINI_PROGRAM_PAGE_RUNTIME_HOOKS);
  171. mixins.forEach((mixin) => {
  172. hooks.forEach((hook) => {
  173. if (hasOwn(mixin, hook) && !runtimeHooks.includes(hook)) {
  174. runtimeHooks.push(hook);
  175. }
  176. });
  177. });
  178. }
  179. }
  180. return runtimeHooks;
  181. });
  182. function initMixinRuntimeHooks(mpOptions) {
  183. initHooks(mpOptions, findMixinRuntimeHooks());
  184. }
  185. const HOOKS = [
  186. ON_SHOW,
  187. ON_HIDE,
  188. ON_ERROR,
  189. ON_THEME_CHANGE,
  190. ON_PAGE_NOT_FOUND,
  191. ON_UNHANDLE_REJECTION,
  192. ];
  193. function parseApp(instance, parseAppOptions) {
  194. const internalInstance = instance.$;
  195. if (__VUE_PROD_DEVTOOLS__) {
  196. // 定制 App 的 $children
  197. Object.defineProperty(internalInstance.ctx, '$children', {
  198. get() {
  199. return getCurrentPages().map((page) => page.$vm);
  200. },
  201. });
  202. }
  203. const appOptions = {
  204. globalData: (instance.$options && instance.$options.globalData) || {},
  205. $vm: instance,
  206. onLaunch(options) {
  207. this.$vm = instance; // 飞书小程序可能会把 AppOptions 序列化,导致 $vm 对象部分属性丢失
  208. const ctx = internalInstance.ctx;
  209. if (this.$vm && ctx.$scope) {
  210. // 已经初始化过了,主要是为了百度,百度 onShow 在 onLaunch 之前
  211. return;
  212. }
  213. initBaseInstance(internalInstance, {
  214. mpType: 'app',
  215. mpInstance: this,
  216. slots: [],
  217. });
  218. ctx.globalData = this.globalData;
  219. instance.$callHook(ON_LAUNCH, options);
  220. },
  221. };
  222. const { onError } = internalInstance;
  223. if (onError) {
  224. internalInstance.appContext.config.errorHandler = (err) => {
  225. instance.$callHook(ON_ERROR, err);
  226. };
  227. }
  228. initLocale(instance);
  229. const vueOptions = instance.$.type;
  230. initHooks(appOptions, HOOKS);
  231. initUnknownHooks(appOptions, vueOptions);
  232. if (__VUE_OPTIONS_API__) {
  233. const methods = vueOptions.methods;
  234. methods && extend(appOptions, methods);
  235. }
  236. if (parseAppOptions) {
  237. parseAppOptions.parse(appOptions);
  238. }
  239. return appOptions;
  240. }
  241. function initCreateApp(parseAppOptions) {
  242. return function createApp(vm) {
  243. return App(parseApp(vm, parseAppOptions));
  244. };
  245. }
  246. function initCreateSubpackageApp(parseAppOptions) {
  247. return function createApp(vm) {
  248. const appOptions = parseApp(vm, parseAppOptions);
  249. const app = isFunction(getApp) &&
  250. getApp({
  251. allowDefault: true,
  252. });
  253. if (!app)
  254. return;
  255. vm.$.ctx.$scope = app;
  256. const globalData = app.globalData;
  257. if (globalData) {
  258. Object.keys(appOptions.globalData).forEach((name) => {
  259. if (!hasOwn(globalData, name)) {
  260. globalData[name] = appOptions.globalData[name];
  261. }
  262. });
  263. }
  264. Object.keys(appOptions).forEach((name) => {
  265. if (!hasOwn(app, name)) {
  266. app[name] = appOptions[name];
  267. }
  268. });
  269. initAppLifecycle(appOptions, vm);
  270. if (process.env.UNI_SUBPACKAGE) {
  271. (ks.$subpackages || (ks.$subpackages = {}))[process.env.UNI_SUBPACKAGE] = {
  272. $vm: vm,
  273. };
  274. }
  275. };
  276. }
  277. function initAppLifecycle(appOptions, vm) {
  278. if (isFunction(appOptions.onLaunch)) {
  279. const args = ks.getLaunchOptionsSync && ks.getLaunchOptionsSync();
  280. appOptions.onLaunch(args);
  281. }
  282. if (isFunction(appOptions.onShow) && ks.onAppShow) {
  283. ks.onAppShow((args) => {
  284. vm.$callHook('onShow', args);
  285. });
  286. }
  287. if (isFunction(appOptions.onHide) && ks.onAppHide) {
  288. ks.onAppHide((args) => {
  289. vm.$callHook('onHide', args);
  290. });
  291. }
  292. }
  293. function initLocale(appVm) {
  294. const locale = ref(normalizeLocale(ks.getSystemInfoSync().language) || LOCALE_EN);
  295. Object.defineProperty(appVm, '$locale', {
  296. get() {
  297. return locale.value;
  298. },
  299. set(v) {
  300. locale.value = v;
  301. },
  302. });
  303. }
  304. function initVueIds(vueIds, mpInstance) {
  305. if (!vueIds) {
  306. return;
  307. }
  308. const ids = vueIds.split(',');
  309. const len = ids.length;
  310. if (len === 1) {
  311. mpInstance._$vueId = ids[0];
  312. }
  313. else if (len === 2) {
  314. mpInstance._$vueId = ids[0];
  315. mpInstance._$vuePid = ids[1];
  316. }
  317. }
  318. const EXTRAS = ['externalClasses'];
  319. function initExtraOptions(miniProgramComponentOptions, vueOptions) {
  320. EXTRAS.forEach((name) => {
  321. if (hasOwn(vueOptions, name)) {
  322. miniProgramComponentOptions[name] = vueOptions[name];
  323. }
  324. });
  325. }
  326. function initWxsCallMethods(methods, wxsCallMethods) {
  327. if (!isArray(wxsCallMethods)) {
  328. return;
  329. }
  330. wxsCallMethods.forEach((callMethod) => {
  331. methods[callMethod] = function (args) {
  332. return this.$vm[callMethod](args);
  333. };
  334. });
  335. }
  336. function selectAllComponents(mpInstance, selector, $refs) {
  337. const components = mpInstance.selectAllComponents(selector);
  338. components.forEach((component) => {
  339. const ref = component.properties.uR;
  340. $refs[ref] = component.$vm || component;
  341. });
  342. }
  343. function initRefs(instance, mpInstance) {
  344. Object.defineProperty(instance, 'refs', {
  345. get() {
  346. const $refs = {};
  347. selectAllComponents(mpInstance, '.r', $refs);
  348. const forComponents = mpInstance.selectAllComponents('.r-i-f');
  349. forComponents.forEach((component) => {
  350. const ref = component.properties.uR;
  351. if (!ref) {
  352. return;
  353. }
  354. if (!$refs[ref]) {
  355. $refs[ref] = [];
  356. }
  357. $refs[ref].push(component.$vm || component);
  358. });
  359. return $refs;
  360. },
  361. });
  362. }
  363. function findVmByVueId(instance, vuePid) {
  364. // 标准 vue3 中 没有 $children,定制了内核
  365. const $children = instance.$children;
  366. // 优先查找直属(反向查找:https://github.com/dcloudio/uni-app/issues/1200)
  367. for (let i = $children.length - 1; i >= 0; i--) {
  368. const childVm = $children[i];
  369. if (childVm.$scope._$vueId === vuePid) {
  370. return childVm;
  371. }
  372. }
  373. // 反向递归查找
  374. let parentVm;
  375. for (let i = $children.length - 1; i >= 0; i--) {
  376. parentVm = findVmByVueId($children[i], vuePid);
  377. if (parentVm) {
  378. return parentVm;
  379. }
  380. }
  381. }
  382. const EVENT_OPTS = 'eO';
  383. /**
  384. * 需要搭配:
  385. * ./componentInstance/index.ts:24 triggerEvent 时传递 __ins__
  386. * ./componentProps.ts:49 增加 properties eO
  387. * @param this
  388. * @param event
  389. * @returns
  390. */
  391. function handleEvent(event) {
  392. const { type, currentTarget: { dataset }, detail: { __ins__ }, } = event;
  393. let methodName = type;
  394. // 快手小程序的 __l 方法也会走此处逻辑,但没有 __ins__
  395. if (__ins__) {
  396. // 自定义事件,通过 triggerEvent 传递 __ins__
  397. let eventObj = {};
  398. try {
  399. // https://github.com/dcloudio/uni-app/issues/3647
  400. // 通过字符串序列化解决百度小程序修改对象不触发组件properties变化的Bug
  401. eventObj = JSON.parse(__ins__.properties[EVENT_OPTS]);
  402. }
  403. catch (e) { }
  404. methodName = resolveMethodName(type, eventObj);
  405. }
  406. else if (dataset && dataset[EVENT_OPTS]) {
  407. // 快手小程序 input 等内置组件的 input 事件也会走此逻辑,所以从 dataset 中读取
  408. methodName = resolveMethodName(type, dataset[EVENT_OPTS]);
  409. }
  410. if (!this[methodName]) {
  411. return console.warn(type + ' not found');
  412. }
  413. this[methodName](event);
  414. }
  415. function resolveMethodName(name, obj) {
  416. return obj[name] || obj[hyphenate(name)];
  417. }
  418. function nextSetDataTick(mpInstance, fn) {
  419. // 随便设置一个字段来触发回调(部分平台必须有字段才可以,比如头条)
  420. mpInstance.setData({ r1: 1 }, () => fn());
  421. }
  422. function initSetRef(mpInstance) {
  423. if (!mpInstance._$setRef) {
  424. mpInstance._$setRef = (fn) => {
  425. nextTick(() => nextSetDataTick(mpInstance, fn));
  426. };
  427. }
  428. }
  429. const builtInProps = [
  430. // 百度小程序,快手小程序自定义组件不支持绑定动态事件,动态dataset,故通过props传递事件信息
  431. // event-opts
  432. 'eO',
  433. // 组件 ref
  434. 'uR',
  435. // 组件 ref-in-for
  436. 'uRIF',
  437. // 组件 id
  438. 'uI',
  439. // 组件类型 m: 小程序组件
  440. 'uT',
  441. // 组件 props
  442. 'uP',
  443. // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots
  444. 'uS',
  445. ];
  446. function initDefaultProps(options, isBehavior = false) {
  447. const properties = {};
  448. if (!isBehavior) {
  449. // 均不指定类型,避免微信小程序 property received type-uncompatible value 警告
  450. builtInProps.forEach((name) => {
  451. properties[name] = {
  452. type: null,
  453. value: '',
  454. };
  455. });
  456. // 小程序不能直接定义 $slots 的 props,所以通过 vueSlots 转换到 $slots
  457. properties.uS = {
  458. type: null,
  459. value: [],
  460. observer: function (newVal) {
  461. const $slots = Object.create(null);
  462. newVal &&
  463. newVal.forEach((slotName) => {
  464. $slots[slotName] = true;
  465. });
  466. this.setData({
  467. $slots,
  468. });
  469. },
  470. };
  471. }
  472. if (options.behaviors) {
  473. // wx://form-field
  474. if (options.behaviors.includes('ks://form-field')) {
  475. if (!options.properties || !options.properties.name) {
  476. properties.name = {
  477. type: null,
  478. value: '',
  479. };
  480. }
  481. if (!options.properties || !options.properties.value) {
  482. properties.value = {
  483. type: null,
  484. value: '',
  485. };
  486. }
  487. }
  488. }
  489. return properties;
  490. }
  491. function initVirtualHostProps(options) {
  492. const properties = {};
  493. return properties;
  494. }
  495. /**
  496. *
  497. * @param mpComponentOptions
  498. * @param isBehavior
  499. */
  500. function initProps(mpComponentOptions) {
  501. if (!mpComponentOptions.properties) {
  502. mpComponentOptions.properties = {};
  503. }
  504. extend(mpComponentOptions.properties, initDefaultProps(mpComponentOptions), initVirtualHostProps(mpComponentOptions.options));
  505. }
  506. const PROP_TYPES = [String, Number, Boolean, Object, Array, null];
  507. function parsePropType(type, defaultValue) {
  508. // [String]=>String
  509. if (isArray(type) && type.length === 1) {
  510. return type[0];
  511. }
  512. return type;
  513. }
  514. function normalizePropType(type, defaultValue) {
  515. const res = parsePropType(type);
  516. return PROP_TYPES.indexOf(res) !== -1 ? res : null;
  517. }
  518. /**
  519. * 初始化页面 props,方便接收页面参数,类型均为String,默认值均为''
  520. * @param param
  521. * @param rawProps
  522. */
  523. function initPageProps({ properties }, rawProps) {
  524. if (isArray(rawProps)) {
  525. rawProps.forEach((key) => {
  526. properties[key] = {
  527. type: String,
  528. value: '',
  529. };
  530. });
  531. }
  532. else if (isPlainObject(rawProps)) {
  533. Object.keys(rawProps).forEach((key) => {
  534. const opts = rawProps[key];
  535. if (isPlainObject(opts)) {
  536. // title:{type:String,default:''}
  537. let value = opts.default;
  538. if (isFunction(value)) {
  539. value = value();
  540. }
  541. const type = opts.type;
  542. opts.type = normalizePropType(type);
  543. properties[key] = {
  544. type: opts.type,
  545. value,
  546. };
  547. }
  548. else {
  549. // content:String
  550. properties[key] = {
  551. type: normalizePropType(opts),
  552. };
  553. }
  554. });
  555. }
  556. }
  557. function findPropsData(properties, isPage) {
  558. return ((isPage
  559. ? findPagePropsData(properties)
  560. : findComponentPropsData(properties.uP)) || {});
  561. }
  562. function findPagePropsData(properties) {
  563. const propsData = {};
  564. if (isPlainObject(properties)) {
  565. Object.keys(properties).forEach((name) => {
  566. if (builtInProps.indexOf(name) === -1) {
  567. propsData[name] = properties[name];
  568. }
  569. });
  570. }
  571. return propsData;
  572. }
  573. function initFormField(vm) {
  574. // 同步 form-field 的 name,value 值
  575. const vueOptions = vm.$options;
  576. if (isArray(vueOptions.behaviors) &&
  577. vueOptions.behaviors.includes('uni://form-field')) {
  578. vm.$watch('modelValue', () => {
  579. vm.$scope &&
  580. vm.$scope.setData({
  581. name: vm.name,
  582. value: vm.modelValue,
  583. });
  584. }, {
  585. immediate: true,
  586. });
  587. }
  588. }
  589. function initData(_) {
  590. return {};
  591. }
  592. function initPropsObserver(componentOptions) {
  593. const observe = function observe() {
  594. const up = this.properties.uP;
  595. if (!up) {
  596. return;
  597. }
  598. if (this.$vm) {
  599. updateComponentProps(up, this.$vm.$);
  600. }
  601. else if (this.properties.uT === 'm') {
  602. // 小程序组件
  603. updateMiniProgramComponentProperties(up, this);
  604. }
  605. };
  606. {
  607. componentOptions.properties.uP.observer = observe;
  608. }
  609. }
  610. function updateMiniProgramComponentProperties(up, mpInstance) {
  611. const prevProps = mpInstance.properties;
  612. const nextProps = findComponentPropsData(up) || {};
  613. if (hasPropsChanged(prevProps, nextProps, false)) {
  614. mpInstance.setData(nextProps);
  615. }
  616. }
  617. function updateComponentProps(up, instance) {
  618. const prevProps = toRaw(instance.props);
  619. const nextProps = findComponentPropsData(up) || {};
  620. if (hasPropsChanged(prevProps, nextProps)) {
  621. updateProps(instance, nextProps, prevProps, false);
  622. if (hasQueueJob(instance.update)) {
  623. invalidateJob(instance.update);
  624. }
  625. {
  626. instance.update();
  627. }
  628. }
  629. }
  630. function hasPropsChanged(prevProps, nextProps, checkLen = true) {
  631. const nextKeys = Object.keys(nextProps);
  632. if (checkLen && nextKeys.length !== Object.keys(prevProps).length) {
  633. return true;
  634. }
  635. for (let i = 0; i < nextKeys.length; i++) {
  636. const key = nextKeys[i];
  637. if (nextProps[key] !== prevProps[key]) {
  638. return true;
  639. }
  640. }
  641. return false;
  642. }
  643. function initBehaviors(vueOptions) {
  644. const vueBehaviors = vueOptions.behaviors;
  645. let vueProps = vueOptions.props;
  646. if (!vueProps) {
  647. vueOptions.props = vueProps = [];
  648. }
  649. const behaviors = [];
  650. if (isArray(vueBehaviors)) {
  651. vueBehaviors.forEach((behavior) => {
  652. behaviors.push(behavior.replace('uni://', 'ks://'));
  653. if (behavior === 'uni://form-field') {
  654. if (isArray(vueProps)) {
  655. vueProps.push('name');
  656. vueProps.push('modelValue');
  657. }
  658. else {
  659. vueProps.name = {
  660. type: String,
  661. default: '',
  662. };
  663. vueProps.modelValue = {
  664. type: [String, Number, Boolean, Array, Object, Date],
  665. default: '',
  666. };
  667. }
  668. }
  669. });
  670. }
  671. return behaviors;
  672. }
  673. function applyOptions(componentOptions, vueOptions) {
  674. componentOptions.data = initData();
  675. componentOptions.behaviors = initBehaviors(vueOptions);
  676. }
  677. function parseComponent(vueOptions, { parse, mocks, isPage, initRelation, handleLink, initLifetimes, }) {
  678. vueOptions = vueOptions.default || vueOptions;
  679. const options = {
  680. multipleSlots: true,
  681. // styleIsolation: 'apply-shared',
  682. addGlobalClass: true,
  683. pureDataPattern: /^uP$/,
  684. };
  685. if (isArray(vueOptions.mixins)) {
  686. vueOptions.mixins.forEach((item) => {
  687. if (isObject(item.options)) {
  688. extend(options, item.options);
  689. }
  690. });
  691. }
  692. if (vueOptions.options) {
  693. extend(options, vueOptions.options);
  694. }
  695. const mpComponentOptions = {
  696. options,
  697. lifetimes: initLifetimes({ mocks, isPage, initRelation, vueOptions }),
  698. pageLifetimes: {
  699. show() {
  700. if (__VUE_PROD_DEVTOOLS__) {
  701. devtoolsComponentAdded(this.$vm.$);
  702. }
  703. this.$vm && this.$vm.$callHook('onPageShow');
  704. },
  705. hide() {
  706. this.$vm && this.$vm.$callHook('onPageHide');
  707. },
  708. resize(size) {
  709. this.$vm && this.$vm.$callHook('onPageResize', size);
  710. },
  711. },
  712. methods: {
  713. __l: handleLink,
  714. },
  715. };
  716. if (__VUE_OPTIONS_API__) {
  717. applyOptions(mpComponentOptions, vueOptions);
  718. }
  719. initProps(mpComponentOptions);
  720. initPropsObserver(mpComponentOptions);
  721. initExtraOptions(mpComponentOptions, vueOptions);
  722. initWxsCallMethods(mpComponentOptions.methods, vueOptions.wxsCallMethods);
  723. if (parse) {
  724. parse(mpComponentOptions, { handleLink });
  725. }
  726. return mpComponentOptions;
  727. }
  728. function initCreateComponent(parseOptions) {
  729. return function createComponent(vueComponentOptions) {
  730. return Component(parseComponent(vueComponentOptions, parseOptions));
  731. };
  732. }
  733. let $createComponentFn;
  734. let $destroyComponentFn;
  735. function getAppVm() {
  736. if (process.env.UNI_MP_PLUGIN) {
  737. return ks.$vm;
  738. }
  739. if (process.env.UNI_SUBPACKAGE) {
  740. return ks.$subpackages[process.env.UNI_SUBPACKAGE].$vm;
  741. }
  742. return getApp().$vm;
  743. }
  744. function $createComponent(initialVNode, options) {
  745. if (!$createComponentFn) {
  746. $createComponentFn = getAppVm().$createComponent;
  747. }
  748. const proxy = $createComponentFn(initialVNode, options);
  749. return getExposeProxy(proxy.$) || proxy;
  750. }
  751. function $destroyComponent(instance) {
  752. if (!$destroyComponentFn) {
  753. $destroyComponentFn = getAppVm().$destroyComponent;
  754. }
  755. return $destroyComponentFn(instance);
  756. }
  757. function parsePage(vueOptions, parseOptions) {
  758. const { parse, mocks, isPage, initRelation, handleLink, initLifetimes } = parseOptions;
  759. const miniProgramPageOptions = parseComponent(vueOptions, {
  760. mocks,
  761. isPage,
  762. initRelation,
  763. handleLink,
  764. initLifetimes,
  765. });
  766. initPageProps(miniProgramPageOptions, (vueOptions.default || vueOptions).props);
  767. const methods = miniProgramPageOptions.methods;
  768. methods.onLoad = function (query) {
  769. this.options = query;
  770. this.$page = {
  771. fullPath: addLeadingSlash(this.route + stringifyQuery(query)),
  772. };
  773. return this.$vm && this.$vm.$callHook(ON_LOAD, query);
  774. };
  775. initHooks(methods, PAGE_INIT_HOOKS);
  776. {
  777. initUnknownHooks(methods, vueOptions);
  778. }
  779. initRuntimeHooks(methods, vueOptions.__runtimeHooks);
  780. initMixinRuntimeHooks(methods);
  781. parse && parse(miniProgramPageOptions, { handleLink });
  782. return miniProgramPageOptions;
  783. }
  784. function initCreatePage(parseOptions) {
  785. return function createPage(vuePageOptions) {
  786. return Component(parsePage(vuePageOptions, parseOptions));
  787. };
  788. }
  789. /**
  790. * 用于延迟调用 setData
  791. * 在 setData 真实调用的时机需执行 fixSetDataEnd
  792. * @param {*} mpInstance
  793. */
  794. function fixSetDataStart(mpInstance) {
  795. const setData = mpInstance.setData;
  796. const setDataArgs = [];
  797. mpInstance.setData = function () {
  798. setDataArgs.push(arguments);
  799. };
  800. mpInstance.__fixInitData = function () {
  801. this.setData = setData;
  802. const fn = () => {
  803. setDataArgs.forEach((args) => {
  804. setData.apply(this, args);
  805. });
  806. };
  807. if (setDataArgs.length) {
  808. if (this.groupSetData) {
  809. this.groupSetData(fn);
  810. }
  811. else {
  812. fn();
  813. }
  814. }
  815. };
  816. }
  817. /**
  818. * 恢复真实的 setData 方法
  819. * @param {*} mpInstance
  820. */
  821. function fixSetDataEnd(mpInstance) {
  822. if (mpInstance.__fixInitData) {
  823. mpInstance.__fixInitData();
  824. delete mpInstance.__fixInitData;
  825. }
  826. }
  827. const MPPage = Page;
  828. const MPComponent = Component;
  829. function initTriggerEvent(mpInstance) {
  830. const oldTriggerEvent = mpInstance.triggerEvent;
  831. const newTriggerEvent = function (event, ...args) {
  832. return oldTriggerEvent.apply(mpInstance, [customizeEvent(event), ...args]);
  833. };
  834. // 京东小程序triggerEvent为只读属性
  835. try {
  836. mpInstance.triggerEvent = newTriggerEvent;
  837. }
  838. catch (error) {
  839. mpInstance._triggerEvent = newTriggerEvent;
  840. }
  841. }
  842. function initMiniProgramHook(name, options, isComponent) {
  843. const oldHook = options[name];
  844. if (!oldHook) {
  845. options[name] = function () {
  846. initTriggerEvent(this);
  847. };
  848. }
  849. else {
  850. options[name] = function (...args) {
  851. initTriggerEvent(this);
  852. return oldHook.apply(this, args);
  853. };
  854. }
  855. }
  856. Page = function (options) {
  857. initMiniProgramHook(ON_LOAD, options);
  858. return MPPage(options);
  859. };
  860. Component = function (options) {
  861. initMiniProgramHook('created', options);
  862. // 小程序组件
  863. const isVueComponent = options.properties && options.properties.uP;
  864. if (!isVueComponent) {
  865. initProps(options);
  866. initPropsObserver(options);
  867. }
  868. return MPComponent(options);
  869. };
  870. // @ts-ignore
  871. function initLifetimes({ mocks, isPage, initRelation, vueOptions, }) {
  872. return {
  873. attached() {
  874. // 微信 和 QQ 不需要延迟 setRef
  875. {
  876. initSetRef(this);
  877. }
  878. let properties = this.properties;
  879. initVueIds(properties.uI, this);
  880. const relationOptions = {
  881. vuePid: this._$vuePid,
  882. };
  883. // 处理父子关系
  884. initRelation(this, relationOptions);
  885. // 初始化 vue 实例
  886. const mpInstance = this;
  887. const isMiniProgramPage = isPage(mpInstance);
  888. let propsData = properties;
  889. if (isMiniProgramPage) {
  890. {
  891. propsData = this.options;
  892. }
  893. }
  894. this.$vm = $createComponent({
  895. type: vueOptions,
  896. props: findPropsData(propsData, isMiniProgramPage),
  897. }, {
  898. mpType: isMiniProgramPage ? 'page' : 'component',
  899. mpInstance,
  900. slots: properties.uS || {},
  901. parentComponent: relationOptions.parent && relationOptions.parent.$,
  902. onBeforeSetup(instance, options) {
  903. initRefs(instance, mpInstance);
  904. initMocks(instance, mpInstance, mocks);
  905. initComponentInstance(instance, options);
  906. },
  907. });
  908. if (!isMiniProgramPage) {
  909. initFormField(this.$vm);
  910. }
  911. },
  912. ready() {
  913. // 当组件 props 默认值为 true,初始化时传入 false 会导致 created,ready 触发, 但 attached 不触发
  914. // https://developers.weixin.qq.com/community/develop/doc/00066ae2844cc0f8eb883e2a557800
  915. if (this.$vm) {
  916. {
  917. nextSetDataTick(this, () => {
  918. this.$vm.$callHook('mounted');
  919. this.$vm.$callHook(ON_READY);
  920. });
  921. }
  922. }
  923. },
  924. detached() {
  925. if (this.$vm) {
  926. pruneComponentPropsCache(this.$vm.$.uid);
  927. $destroyComponent(this.$vm);
  928. }
  929. },
  930. };
  931. }
  932. const mocks = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__'];
  933. function isPage(mpInstance) {
  934. return !!mpInstance.route;
  935. }
  936. function initRelation(mpInstance, detail) {
  937. mpInstance.triggerEvent('__l', detail);
  938. }
  939. function handleLink(event) {
  940. // detail 是微信,value 是百度(dipatch)
  941. const detail = (event.detail ||
  942. event.value);
  943. const vuePid = detail.vuePid;
  944. let parentVm;
  945. if (vuePid) {
  946. parentVm = findVmByVueId(this.$vm, vuePid);
  947. }
  948. if (!parentVm) {
  949. parentVm = this.$vm;
  950. }
  951. detail.parent = parentVm;
  952. }
  953. var baseParseOptions = /*#__PURE__*/Object.freeze({
  954. __proto__: null,
  955. handleLink: handleLink,
  956. initLifetimes: initLifetimes,
  957. initRelation: initRelation,
  958. isPage: isPage,
  959. mocks: mocks
  960. });
  961. function parse$1(pageOptions) {
  962. // 快手小程序自定义组件,不支持绑定动态事件,故由 __e 分发
  963. pageOptions.methods.__e = handleEvent;
  964. }
  965. var parsePageOptions = extend({}, baseParseOptions, {
  966. parse: parse$1,
  967. });
  968. function parse(componentOptions) {
  969. const oldAttached = componentOptions.lifetimes.attached;
  970. componentOptions.lifetimes.attached = function attached() {
  971. // 暂不区分版本
  972. if (isPage(this)) {
  973. // 解决快手小程序页面 attached 生命周期 setData 导致数据同步异常的问题
  974. fixSetDataStart(this);
  975. setTimeout(() => {
  976. fixSetDataEnd(this);
  977. }, 0);
  978. }
  979. oldAttached.call(this);
  980. };
  981. // 快手小程序自定义组件,不支持绑定动态事件,故由 __e 分发
  982. componentOptions.methods.__e = handleEvent;
  983. }
  984. var parseComponentOptions = extend({}, baseParseOptions, {
  985. parse,
  986. });
  987. const createApp = initCreateApp();
  988. const createPage = initCreatePage(parsePageOptions);
  989. const createComponent = initCreateComponent(parseComponentOptions);
  990. const createSubpackageApp = initCreateSubpackageApp();
  991. ks.EventChannel = EventChannel;
  992. ks.createApp = global.createApp = createApp;
  993. ks.createPage = createPage;
  994. ks.createComponent = createComponent;
  995. ks.createSubpackageApp = global.createSubpackageApp =
  996. createSubpackageApp;
  997. export { createApp, createComponent, createPage, createSubpackageApp };