diff --git a/examples/README.md b/examples/README.md
index e710cbdb6e..38a79edf2c 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -24,3 +24,5 @@ npm run build
* use-rax-compiled-component-in-runtime-miniapp: Rax 小程序运行时项目使用 Rax 编译时组件
* app-lifecycle: 使用 app 生命周期
* page-lifecycle-with-router: 使用 page 生命周期 + 路由跳转
+* event-handler: 事件绑定(编译时小程序)
+
diff --git a/examples/event-handler/.eslintrc.js b/examples/event-handler/.eslintrc.js
new file mode 100644
index 0000000000..04f3c852d6
--- /dev/null
+++ b/examples/event-handler/.eslintrc.js
@@ -0,0 +1,3 @@
+module.exports = {
+ extends: ['rax']
+};
\ No newline at end of file
diff --git a/examples/event-handler/.gitignore b/examples/event-handler/.gitignore
new file mode 100644
index 0000000000..50a53daceb
--- /dev/null
+++ b/examples/event-handler/.gitignore
@@ -0,0 +1,17 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+*~
+*.swp
+*.log
+
+.DS_Store
+.idea/
+.temp/
+
+build/
+dist/
+lib/
+coverage/
+node_modules/
+
+template.yml
diff --git a/examples/event-handler/README.md b/examples/event-handler/README.md
new file mode 100644
index 0000000000..94c45d1ddc
--- /dev/null
+++ b/examples/event-handler/README.md
@@ -0,0 +1,15 @@
+# rax-materials-basic-app
+
+## Getting Started
+
+### `npm run start`
+
+Runs the app in development mode.
+
+Open [http://localhost:9999](http://localhost:9999) to view it in the browser.
+
+The page will reload if you make edits.
+
+### `npm run build`
+
+Builds the app for production to the `build` folder.
diff --git a/examples/event-handler/build.json b/examples/event-handler/build.json
new file mode 100644
index 0000000000..1c2cbb9f30
--- /dev/null
+++ b/examples/event-handler/build.json
@@ -0,0 +1,20 @@
+{
+ "inlineStyle": false,
+ "plugins": [
+ [
+ "build-plugin-rax-app",
+ {
+ "targets": [
+ "miniapp",
+ "wechat-miniprogram"
+ ],
+ "miniapp": {
+ "buildType": "compile"
+ },
+ "wechat-miniprogram": {
+ "buildType": "compile"
+ }
+ }
+ ]
+ ]
+}
diff --git a/examples/event-handler/package.json b/examples/event-handler/package.json
new file mode 100644
index 0000000000..7d0a89bc64
--- /dev/null
+++ b/examples/event-handler/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@rax-materials/scaffolds-app-js",
+ "author": "rax",
+ "description": "Rax 无线跨端应用工程,使用 JavaScript。",
+ "version": "0.1.0",
+ "scripts": {
+ "build": "build-scripts build",
+ "start": "build-scripts start",
+ "lint": "eslint --ext .js --ext .jsx ./"
+ },
+ "dependencies": {
+ "rax": "^1.1.0",
+ "rax-app": "^2.0.0",
+ "driver-universal": "^3.0.0",
+ "rax-image": "^2.0.0",
+ "rax-link": "^1.0.1",
+ "rax-text": "^1.0.0",
+ "rax-view": "^1.0.0",
+ "rax-document": "^0.1.0"
+ },
+ "devDependencies": {
+ "@alib/build-scripts": "^0.1.0",
+ "babel-eslint": "^10.0.3",
+ "build-plugin-rax-app": "^5.0.0",
+ "eslint": "^6.8.0",
+ "eslint-config-rax": "^0.1.0",
+ "eslint-plugin-import": "^2.20.0",
+ "eslint-plugin-module": "^0.1.0",
+ "eslint-plugin-react": "^7.18.0"
+ },
+ "private": true,
+ "originTemplate": "@rax-materials/scaffolds-app-js"
+}
diff --git a/examples/event-handler/src/app.js b/examples/event-handler/src/app.js
new file mode 100644
index 0000000000..f04a4233b3
--- /dev/null
+++ b/examples/event-handler/src/app.js
@@ -0,0 +1,4 @@
+import { runApp } from 'rax-app';
+import appConfig from './app.json';
+
+runApp(appConfig);
diff --git a/examples/event-handler/src/app.json b/examples/event-handler/src/app.json
new file mode 100644
index 0000000000..ffbc0add84
--- /dev/null
+++ b/examples/event-handler/src/app.json
@@ -0,0 +1,11 @@
+{
+ "routes": [
+ {
+ "path": "/",
+ "source": "pages/Home/index"
+ }
+ ],
+ "window": {
+ "title": "Rax App"
+ }
+}
diff --git a/examples/event-handler/src/components/CustomComp/index.css b/examples/event-handler/src/components/CustomComp/index.css
new file mode 100644
index 0000000000..2f8d8d1144
--- /dev/null
+++ b/examples/event-handler/src/components/CustomComp/index.css
@@ -0,0 +1,5 @@
+.logo {
+ width: 200rpx;
+ height: 180rpx;
+ margin-bottom: 20rpx;
+}
\ No newline at end of file
diff --git a/examples/event-handler/src/components/CustomComp/index.jsx b/examples/event-handler/src/components/CustomComp/index.jsx
new file mode 100644
index 0000000000..ef191bc442
--- /dev/null
+++ b/examples/event-handler/src/components/CustomComp/index.jsx
@@ -0,0 +1,15 @@
+import { createElement } from 'rax';
+import Image from 'rax-image';
+
+import './index.css';
+
+export default (props) => {
+ const { uri } = props;
+ const source = { uri };
+ return (
+
+ );
+};
diff --git a/examples/event-handler/src/document/index.jsx b/examples/event-handler/src/document/index.jsx
new file mode 100644
index 0000000000..ae0b8d0cdb
--- /dev/null
+++ b/examples/event-handler/src/document/index.jsx
@@ -0,0 +1,21 @@
+import { createElement } from 'rax';
+import { Root, Style, Script} from 'rax-document';
+
+function Document() {
+ return (
+
+
+
+
+ rax-materials-basic-app
+
+
+
+ {/* root container */}
+
+
+
+
+ );
+}
+export default Document;
diff --git a/examples/event-handler/src/pages/Home/index.css b/examples/event-handler/src/pages/Home/index.css
new file mode 100644
index 0000000000..cd3b101696
--- /dev/null
+++ b/examples/event-handler/src/pages/Home/index.css
@@ -0,0 +1,16 @@
+.home {
+ align-items: center;
+ margin-top: 200rpx;
+}
+
+.title {
+ font-size: 45rpx;
+ font-weight: bold;
+ margin: 20rpx 0;
+}
+
+.info {
+ font-size: 36rpx;
+ margin: 8rpx 0;
+ color: #555;
+}
diff --git a/examples/event-handler/src/pages/Home/index.jsx b/examples/event-handler/src/pages/Home/index.jsx
new file mode 100644
index 0000000000..a2fc37a752
--- /dev/null
+++ b/examples/event-handler/src/pages/Home/index.jsx
@@ -0,0 +1,21 @@
+import { createElement } from 'rax';
+import View from 'rax-view';
+import Text from 'rax-text';
+import Image from 'rax-image';
+
+import './index.css';
+
+import CustomComp from '../../components/CustomComp';
+
+export default function Home() {
+ function noop() {}
+ return (
+
+ onClick -> onTap (alibaba miniapp) && bindtap (wechat miniprogram) in rax-view
+ onClick -> bindtap (wechat miniprogram) in rax-text
+ onClick/onChange are not transformed (alibaba miniapp) && onClick -> bindonClick/onChange -> bindonChange (wechat miniprogram)
+
+ not transformed
+
+ );
+}
diff --git a/examples/with-miniapp-plugin-component/package.json b/examples/with-miniapp-plugin-component/package.json
index 7d0a89bc64..a45e30da7b 100644
--- a/examples/with-miniapp-plugin-component/package.json
+++ b/examples/with-miniapp-plugin-component/package.json
@@ -16,7 +16,8 @@
"rax-link": "^1.0.1",
"rax-text": "^1.0.0",
"rax-view": "^1.0.0",
- "rax-document": "^0.1.0"
+ "rax-document": "^0.1.0",
+ "universal-env": "^3.0.0"
},
"devDependencies": {
"@alib/build-scripts": "^0.1.0",
diff --git a/examples/with-miniapp-plugin-component/src/components/Test/index.ali.jsx b/examples/with-miniapp-plugin-component/src/components/Test/index.ali.jsx
index 570d1f317f..9d1acbee77 100644
--- a/examples/with-miniapp-plugin-component/src/components/Test/index.ali.jsx
+++ b/examples/with-miniapp-plugin-component/src/components/Test/index.ali.jsx
@@ -1,5 +1,6 @@
import { createElement, useState } from 'rax';
import View from 'rax-view';
+// eslint-disable-next-line
import Test from 'plugin://myPlugin/test';
export default function Home() {
diff --git a/examples/with-miniapp-plugin-component/src/components/Test/index.wechat.jsx b/examples/with-miniapp-plugin-component/src/components/Test/index.wechat.jsx
index e8d9ff8cb7..178fa8dafc 100644
--- a/examples/with-miniapp-plugin-component/src/components/Test/index.wechat.jsx
+++ b/examples/with-miniapp-plugin-component/src/components/Test/index.wechat.jsx
@@ -1,6 +1,7 @@
import { createElement, useState } from 'rax';
import View from 'rax-view';
import Text from 'rax-text';
+// eslint-disable-next-line
import Plugin from 'plugin://myPlugin/hello-component';
export default function Home() {
diff --git a/packages/jsx-compiler/package.json b/packages/jsx-compiler/package.json
index d7060657f8..929ba826e2 100644
--- a/packages/jsx-compiler/package.json
+++ b/packages/jsx-compiler/package.json
@@ -1,6 +1,6 @@
{
"name": "jsx-compiler",
- "version": "0.4.17",
+ "version": "0.4.18",
"license": "BSD-3-Clause",
"description": "Parser for Rax JSX Statements.",
"files": [
diff --git a/packages/jsx-compiler/src/adapter.js b/packages/jsx-compiler/src/adapter.js
index 1a2a80ed4d..d03d7d8cfd 100644
--- a/packages/jsx-compiler/src/adapter.js
+++ b/packages/jsx-compiler/src/adapter.js
@@ -42,6 +42,8 @@ const parserAdapters = {
compatibleText: false,
// Need transform style & class keyword
styleKeyword: false,
+ // No need to transform onClick -> bindonclick
+ needTransformEvent: false,
slotScope: true,
// Need transform key
needTransformKey: false,
@@ -71,6 +73,8 @@ const parserAdapters = {
},
styleKeyword: true,
slotScope: false,
+ // Need transform onClick -> bindonclick
+ needTransformEvent: true,
needTransformKey: true,
triggerRef: true,
processSlider: true,
@@ -97,10 +101,13 @@ const parserAdapters = {
},
styleKeyword: true,
slotScope: false,
+ // Need transform onClick -> bindonclick
+ needTransformEvent: true,
needTransformKey: true,
triggerRef: true,
needRegisterProps: true,
},
+ componentCommonProps
};
module.exports = parserAdapters;
diff --git a/packages/jsx-compiler/src/baseComponents.js b/packages/jsx-compiler/src/baseComponents.js
index b8ed0d8492..ca6085a934 100644
--- a/packages/jsx-compiler/src/baseComponents.js
+++ b/packages/jsx-compiler/src/baseComponents.js
@@ -1,5 +1,4 @@
module.exports = [
- 'rax-text',
'rax-scrollview',
'rax-recyclerview',
'rax-textinput',
diff --git a/packages/jsx-compiler/src/modules/__tests__/attribute.js b/packages/jsx-compiler/src/modules/__tests__/attribute.js
index b207c5db0a..f43ff64e3a 100644
--- a/packages/jsx-compiler/src/modules/__tests__/attribute.js
+++ b/packages/jsx-compiler/src/modules/__tests__/attribute.js
@@ -35,6 +35,7 @@ describe('Transform JSX Attribute', () => {
it('should not transform wechat custom component className', () => {
const code = 'test';
const ast = parseExpression(code);
+ ast.openingElement.name.isCustom = true;
_transformAttribute(ast, code, wxAdapter);
expect(genCode(ast).code).toEqual('test');
});
@@ -49,6 +50,7 @@ describe('Transform JSX Attribute', () => {
it('should transform wechat custom component style into styleSheet', () => {
const code = "test";
const ast = parseExpression(code);
+ ast.openingElement.name.isCustom = true;
_transformAttribute(ast, code, wxAdapter);
expect(genCode(ast).code).toEqual(` {
it('should transform wechat custom component id', () => {
const code = 'test';
const ast = parseExpression(code);
+ ast.openingElement.name.isCustom = true;
_transformAttribute(ast, code, wxAdapter);
expect(genCode(ast).code).toEqual('test');
});
diff --git a/packages/jsx-compiler/src/modules/__tests__/element.js b/packages/jsx-compiler/src/modules/__tests__/element.js
index 02b267152f..2ce883bc19 100644
--- a/packages/jsx-compiler/src/modules/__tests__/element.js
+++ b/packages/jsx-compiler/src/modules/__tests__/element.js
@@ -156,6 +156,8 @@ describe('Transform JSXElement', () => {
/**
* { _e0: this.handleClick }
*/
+ ast.openingElement.name.isCustom = true;
+
const { dynamicEvents } = _transform({
templateAST: ast,
}, adapter);
@@ -169,6 +171,8 @@ describe('Transform JSXElement', () => {
onClick={props.onClick}
/>
`);
+ ast.openingElement.name.isCustom = true;
+
const { dynamicEvents } = _transform({
templateAST: ast
}, adapter);
@@ -184,6 +188,8 @@ describe('Transform JSXElement', () => {
onKeyPress={this.handleClick.bind(this, 'hello')}
/>
`);
+ ast.openingElement.name.isCustom = true;
+
const { dynamicEvents } = _transform({
templateAST: ast
}, adapter);
@@ -248,6 +254,88 @@ describe('Transform JSXElement', () => {
expect(genInlineCode(ast).code).toEqual('');
});
+
+ it('should not transform events in rax base components (except rax-view) in alibaba miniapp', () => {
+ const ast = parseExpression(`
+
+ `);
+ ast.openingElement.name.isCustom = true;
+ _transform({
+ templateAST: ast
+ }, adapter);
+ expect(genInlineCode(ast).code).toEqual('');
+ });
+
+ it('should transform events in rax base components (except rax-view/rax-text) in wechat miniprogram', () => {
+ const ast = parseExpression(`
+
+ `);
+ ast.openingElement.name.isCustom = true;
+ _transform({
+ templateAST: ast
+ }, wxAdapter);
+ expect(genInlineCode(ast).code).toEqual('');
+ });
+
+ it('should transform click event and not transform other normal event in native components in alibaba miniapp', () => {
+ const ast = parseExpression(`
+
+ `);
+ _transform({
+ templateAST: ast
+ }, adapter);
+ expect(genInlineCode(ast).code).toEqual('');
+ });
+
+ it('should transform events in native components in wechat miniprogram', () => {
+ const ast = parseExpression(`
+
+ `);
+ _transform({
+ templateAST: ast
+ }, wxAdapter);
+ expect(genInlineCode(ast).code).toEqual('');
+ });
+
+ it('should not transform events in rax components in alibaba miniapp', () => {
+ const ast = parseExpression(`
+
+ `);
+ ast.openingElement.name.isCustom = true;
+ _transform({
+ templateAST: ast
+ }, adapter);
+ expect(genInlineCode(ast).code).toEqual('');
+ });
+
+ it('should not transform normal events in rax components in wechat miniprogram', () => {
+ const ast = parseExpression(`
+
+ `);
+ ast.openingElement.name.isCustom = true;
+ _transform({
+ templateAST: ast
+ }, wxAdapter);
+ expect(genInlineCode(ast).code).toEqual('');
+ });
});
describe('element', () => {
diff --git a/packages/jsx-compiler/src/modules/attribute.js b/packages/jsx-compiler/src/modules/attribute.js
index f2c38b779b..5319188f45 100644
--- a/packages/jsx-compiler/src/modules/attribute.js
+++ b/packages/jsx-compiler/src/modules/attribute.js
@@ -12,6 +12,7 @@ function transformAttribute(ast, code, adapter) {
traverse(ast, {
JSXAttribute(path) {
const { node } = path;
+ const { node: componentTagNode } = path.parentPath.get('name');
const attrName = node.name.name;
switch (attrName) {
case 'key':
@@ -29,20 +30,20 @@ function transformAttribute(ast, code, adapter) {
break;
case 'className':
if (!adapter.styleKeyword) {
- if (isNativeComponent(path, adapter.platform)) {
+ if (isNativeComponent(componentTagNode, adapter.platform)) {
node.name.name = 'class';
} else {
// Object.assign for shallow copy, avoid self tag is same reference
path.parentPath.node.attributes.push(t.jsxAttribute(t.jsxIdentifier('class'),
Object.assign({}, node.value)));
}
- } else if (isNativeComponent(path, adapter.platform)) {
+ } else if (isNativeComponent(componentTagNode, adapter.platform)) {
node.name.name = 'class';
}
break;
case 'id':
if (adapter.styleKeyword) {
- if (!isNativeComponent(path, adapter.platform)) {
+ if (!isNativeComponent(componentTagNode, adapter.platform)) {
// Object.assign for shallow copy, avoid self tag is same reference
path.parentPath.node.attributes.push(t.jsxAttribute(t.jsxIdentifier('componentId'),
Object.assign({}, node.value)));
@@ -50,7 +51,7 @@ function transformAttribute(ast, code, adapter) {
}
break;
case 'style':
- if (adapter.styleKeyword && !isNativeComponent(path, adapter.platform)) {
+ if (adapter.styleKeyword && !isNativeComponent(componentTagNode, adapter.platform)) {
node.name.name = 'styleSheet';
}
break;
diff --git a/packages/jsx-compiler/src/modules/element.js b/packages/jsx-compiler/src/modules/element.js
index aef0f61f86..7a4b79f9f3 100644
--- a/packages/jsx-compiler/src/modules/element.js
+++ b/packages/jsx-compiler/src/modules/element.js
@@ -12,6 +12,8 @@ const { parseExpression } = require('../parser/index');
const isSlotScopeNode = require('../utils/isSlotScopeNode');
const { isDirectiveAttr, isEventHandlerAttr, isRenderPropsAttr, BINDING_REG } = require('../utils/checkAttr');
const handleValidIdentifier = require('../utils/handleValidIdentifier');
+const isNativeComponent = require('../utils/isNativeComponent');
+const { componentCommonProps } = require('../adapter');
const ATTR = Symbol('attribute');
const ELE = Symbol('element');
@@ -32,7 +34,7 @@ function transformTemplate(
dynamicValue
},
adapter,
- sourceCode,
+ sourceCode
) {
const dynamicEvents = new DynamicBinding('_e');
function handleJSXExpressionContainer(path) {
@@ -514,6 +516,28 @@ function transformTemplate(
);
}
}
+
+ // Handle native components
+ // In native components, events in componentCommonProps should be transformed
+ // Other Events should be changed if needTransformEvent
+ if (isNativeComponent(componentTagNode, adapter.platform)) {
+ node.attributes.forEach(attr => {
+ const attrName = attr.name.name;
+ if (componentCommonProps[adapter.platform][attrName]) {
+ attr.name.name = componentCommonProps[adapter.platform][attrName];
+ } else if (attr.value && attr.value.value && attr.value.value.indexOf('_e') > -1 && adapter.needTransformEvent) {
+ attr.name.name = attr.name.name.replace('on', 'bind').toLowerCase();
+ }
+ });
+ } else if (adapter.needTransformEvent && baseComponents.indexOf(name) > -1) {
+ // Rax base component should add bind before onXXX
+ // While events in custom component should not be changed
+ node.attributes.forEach(attr => {
+ if (attr.value && attr.value.value && attr.value.value.indexOf('_e') > -1) {
+ attr.name.name = `bind${attr.name.name}`;
+ }
+ });
+ }
}
},
},
diff --git a/packages/jsx-compiler/src/utils/checkAttr.js b/packages/jsx-compiler/src/utils/checkAttr.js
index c3884e07cb..925242c3e3 100644
--- a/packages/jsx-compiler/src/utils/checkAttr.js
+++ b/packages/jsx-compiler/src/utils/checkAttr.js
@@ -1,5 +1,5 @@
const isDirectiveAttr = attr => /^(a:|wx:|x-)/.test(attr);
-const isEventHandlerAttr = propKey => /^on[A-Z]/.test(propKey);
+const isEventHandlerAttr = propKey => /^on[A-Z]/.test(propKey) || /^bind[a-z:]/.test(propKey) || /^catch[a-z:]/.test(propKey);
const isRenderPropsAttr = propKey => /^render[A-Z]/.test(propKey);
const BINDING_REG = /{{|}}/g;
diff --git a/packages/jsx-compiler/src/utils/handleRefAttr.js b/packages/jsx-compiler/src/utils/handleRefAttr.js
index bde3d1d0f6..d08db20269 100644
--- a/packages/jsx-compiler/src/utils/handleRefAttr.js
+++ b/packages/jsx-compiler/src/utils/handleRefAttr.js
@@ -19,9 +19,9 @@ module.exports = function(attrPath, childExpression, refName, adapter, loopIndex
};
const attributes = attrPath.parent.attributes;
const componentNameNode = attrPath.parent.name;
- const isNative = isNativeComponent(attrPath, adapter.platform);
+ const isNative = isNativeComponent(componentNameNode, adapter.platform);
if (t.isJSXIdentifier(componentNameNode)) {
- if (componentNameNode.isCustom && !isNative) {
+ if (!isNative) {
refInfo.type = t.stringLiteral('component');
insertBindComRef(
attributes,
diff --git a/packages/jsx-compiler/src/utils/isNativeComponent.js b/packages/jsx-compiler/src/utils/isNativeComponent.js
index 46a3604e57..ca6f8c76ae 100644
--- a/packages/jsx-compiler/src/utils/isNativeComponent.js
+++ b/packages/jsx-compiler/src/utils/isNativeComponent.js
@@ -1,8 +1,12 @@
const getCompiledComponents = require('../getCompiledComponents');
-module.exports = function isNativeComponent(path, platform) {
- const {
- node: { name: tagName }
- } = path.parentPath.get('name');
- return !!getCompiledComponents(platform)[tagName];
+/**
+ * Judge whether node is native component
+ * @param {Object} node
+ * @param {string} platform
+ */
+module.exports = function isNativeComponent(node, platform) {
+ // Compiled rax components and native base components are recognized as native components
+ // If a component is not imported, then it is recognized as native base components (according to the tag isCustom in JSXOpeningElement node.name)
+ return !!getCompiledComponents(platform)[node.name] || node && !node.isCustom;
};