vOn.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.wrapperVOn = exports.transformOn = void 0;
  4. const uni_cli_shared_1 = require("@dcloudio/uni-cli-shared");
  5. const compiler_core_1 = require("@vue/compiler-core");
  6. const shared_1 = require("@vue/shared");
  7. const __1 = require("..");
  8. const runtimeHelpers_1 = require("../runtimeHelpers");
  9. const transformExpression_1 = require("./transformExpression");
  10. const vFor_1 = require("./vFor");
  11. const fnExpRE = /^\s*([\w$_]+|(async\s*)?\([^)]*?\))\s*=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/;
  12. const transformOn = (dir, node, _context, augmentor) => {
  13. const context = _context;
  14. const { loc, modifiers, arg } = dir;
  15. if (!dir.exp && !modifiers.length) {
  16. context.onError((0, compiler_core_1.createCompilerError)(35 /* ErrorCodes.X_V_ON_NO_EXPRESSION */, loc));
  17. }
  18. let eventName;
  19. if (arg.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */) {
  20. if (arg.isStatic) {
  21. const rawName = arg.content;
  22. // for all event listeners, auto convert it to camelCase. See issue #2249
  23. eventName = (0, compiler_core_1.createSimpleExpression)((0, shared_1.toHandlerKey)((0, shared_1.camelize)(rawName)), true, arg.loc);
  24. }
  25. else {
  26. // #2388
  27. eventName = (0, compiler_core_1.createCompoundExpression)([
  28. // `${context.helperString(TO_HANDLER_KEY)}(`,
  29. arg,
  30. // `)`,
  31. ]);
  32. }
  33. }
  34. else {
  35. // already a compound expression.
  36. eventName = arg;
  37. eventName.children.unshift(`${context.helperString(compiler_core_1.TO_HANDLER_KEY)}(`);
  38. eventName.children.push(`)`);
  39. }
  40. // handler processing
  41. let exp = dir.exp;
  42. if (exp && !exp.content.trim()) {
  43. exp = undefined;
  44. }
  45. let shouldCache = context.cacheHandlers && !exp && !context.inVOnce;
  46. if (exp) {
  47. const isMemberExp = (0, compiler_core_1.isMemberExpression)(exp.content, context);
  48. const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content));
  49. const hasMultipleStatements = exp.content.includes(`;`);
  50. // process the expression since it's been skipped
  51. if (context.prefixIdentifiers) {
  52. isInlineStatement && context.addIdentifiers(`$event`);
  53. exp = dir.exp = (0, transformExpression_1.processExpression)(exp, context, false, hasMultipleStatements);
  54. isInlineStatement && context.removeIdentifiers(`$event`);
  55. // with scope analysis, the function is hoistable if it has no reference
  56. // to scope variables.
  57. shouldCache =
  58. context.cacheHandlers &&
  59. // unnecessary to cache inside v-once
  60. !context.inVOnce &&
  61. // runtime constants don't need to be cached
  62. // (this is analyzed by compileScript in SFC <script setup>)
  63. !(exp.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ && exp.constType > 0) &&
  64. // #1541 bail if this is a member exp handler passed to a component -
  65. // we need to use the original function to preserve arity,
  66. // e.g. <transition> relies on checking cb.length to determine
  67. // transition end handling. Inline function is ok since its arity
  68. // is preserved even when cached.
  69. !(isMemberExp && node.tagType === 1 /* ElementTypes.COMPONENT */) &&
  70. // bail if the function references closure variables (v-for, v-slot)
  71. // it must be passed fresh to avoid stale values.
  72. !(0, compiler_core_1.hasScopeRef)(exp, context.identifiers) &&
  73. // wxs event
  74. !isFilterExpr(exp, context);
  75. // If the expression is optimizable and is a member expression pointing
  76. // to a function, turn it into invocation (and wrap in an arrow function
  77. // below) so that it always accesses the latest value when called - thus
  78. // avoiding the need to be patched.
  79. if (shouldCache && isMemberExp) {
  80. if (exp.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */) {
  81. exp.content = `${exp.content} && ${exp.content}(...args)`;
  82. }
  83. else {
  84. exp.children = [...exp.children, ` && `, ...exp.children, `(...args)`];
  85. }
  86. }
  87. }
  88. if (isInlineStatement || (shouldCache && isMemberExp)) {
  89. // wrap inline statement in a function expression
  90. exp = (0, compiler_core_1.createCompoundExpression)([
  91. `${isInlineStatement
  92. ? context.isTS
  93. ? `($event: any)`
  94. : `$event`
  95. : `${context.isTS ? `\n//@ts-ignore\n` : ``}(...args)`} => ${hasMultipleStatements ? `{` : `(`}`,
  96. exp,
  97. hasMultipleStatements ? `}` : `)`,
  98. ]);
  99. }
  100. }
  101. let ret = {
  102. props: [
  103. (0, compiler_core_1.createObjectProperty)(eventName, exp || (0, compiler_core_1.createSimpleExpression)(`() => {}`, false, loc)),
  104. ],
  105. };
  106. // apply extended compiler augmentor
  107. if (augmentor) {
  108. ret = augmentor(ret);
  109. }
  110. // TODO
  111. if (shouldCache) {
  112. // cache handlers so that it's always the same handler being passed down.
  113. // this avoids unnecessary re-renders when users use inline handlers on
  114. // components.
  115. // ret.props[0].value = wrapper(
  116. // context.cache(ret.props[0].value) as ExpressionNode,
  117. // context
  118. // )
  119. ret.props[0].value = wrapperVOn(ret.props[0].value, node, context);
  120. }
  121. else {
  122. ret.props[0].value = wrapperVOn(ret.props[0].value, node, context);
  123. }
  124. // mark the key as handler for props normalization check
  125. ret.props.forEach((p) => (p.key.isHandlerKey = true));
  126. return ret;
  127. };
  128. exports.transformOn = transformOn;
  129. function isFilterExpr(value, context) {
  130. if (context.filters.length && value.type === 8 /* NodeTypes.COMPOUND_EXPRESSION */) {
  131. const firstChild = value.children[0];
  132. if (firstChild.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ &&
  133. context.filters.includes(firstChild.content)) {
  134. return true;
  135. }
  136. }
  137. return false;
  138. }
  139. function wrapperVOn(value, node, context) {
  140. if ((0, transformExpression_1.isBuiltInIdentifier)(value)) {
  141. return value;
  142. }
  143. // wxs event
  144. if (isFilterExpr(value, context)) {
  145. return value;
  146. }
  147. const keys = [];
  148. if (context.miniProgram.event?.key && context.inVFor) {
  149. let keyProp = (0, compiler_core_1.findProp)(node, 'key');
  150. if (!keyProp) {
  151. const vForScope = (0, vFor_1.parseVForScope)(context.currentScope);
  152. if (vForScope) {
  153. keyProp = (0, compiler_core_1.findProp)(vForScope.node, 'key');
  154. }
  155. }
  156. // 对 for 中的所有事件增加 key 标记,避免微信小程序不更新事件对象
  157. if (keyProp && (0, uni_cli_shared_1.isDirectiveNode)(keyProp) && keyProp.exp) {
  158. const keyCode = (0, __1.genExpr)(keyProp.exp);
  159. if (keyCode) {
  160. keys.push(',');
  161. keys.push((0, __1.genExpr)(keyProp.exp));
  162. }
  163. }
  164. }
  165. return (0, compiler_core_1.createCompoundExpression)([
  166. `${context.helperString(runtimeHelpers_1.V_ON)}(`,
  167. value,
  168. ...keys,
  169. `)`,
  170. ]);
  171. }
  172. exports.wrapperVOn = wrapperVOn;