uni.mp.esm.js 35 KB

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