parent
6222454bf6
commit
5ce9f4cc15
@ -0,0 +1,306 @@ |
||||
# dependencies |
||||
/node_modules |
||||
|
||||
# production |
||||
/dist |
||||
|
||||
# misc |
||||
.DS_Store |
||||
.vscode |
||||
.ideal |
||||
|
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
yarn.lock |
||||
package-lock.json |
||||
|
||||
|
||||
# General |
||||
.DS_Store |
||||
.AppleDouble |
||||
.LSOverride |
||||
|
||||
# Icon must end with two \r |
||||
Icon |
||||
|
||||
# Thumbnails |
||||
._* |
||||
|
||||
# Files that might appear in the root of a volume |
||||
.DocumentRevisions-V100 |
||||
.fseventsd |
||||
.Spotlight-V100 |
||||
.TemporaryItems |
||||
.Trashes |
||||
.VolumeIcon.icns |
||||
.com.apple.timemachine.donotpresent |
||||
|
||||
# Directories potentially created on remote AFP share |
||||
.AppleDB |
||||
.AppleDesktop |
||||
Network Trash Folder |
||||
Temporary Items |
||||
.apdisk |
||||
|
||||
|
||||
# Compiled class file |
||||
*.class |
||||
|
||||
# Log file |
||||
*.log |
||||
|
||||
# BlueJ files |
||||
*.ctxt |
||||
|
||||
# Mobile Tools for Java (J2ME) |
||||
.mtj.tmp/ |
||||
|
||||
# Package Files # |
||||
*.jar |
||||
*.war |
||||
*.nar |
||||
*.ear |
||||
*.zip |
||||
*.tar.gz |
||||
*.rar |
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml |
||||
hs_err_pid* |
||||
|
||||
|
||||
|
||||
# Built application files |
||||
*.apk |
||||
*.ap_ |
||||
*.aab |
||||
|
||||
# Files for the ART/Dalvik VM |
||||
*.dex |
||||
|
||||
# Java class files |
||||
*.class |
||||
|
||||
# Generated files |
||||
bin/ |
||||
gen/ |
||||
out/ |
||||
release/ |
||||
|
||||
# Gradle files |
||||
.gradle/ |
||||
build/ |
||||
|
||||
# Local configuration file (sdk path, etc) |
||||
local.properties |
||||
|
||||
# Proguard folder generated by Eclipse |
||||
proguard/ |
||||
|
||||
# Log Files |
||||
*.log |
||||
|
||||
# Android Studio Navigation editor temp files |
||||
.navigation/ |
||||
|
||||
# Android Studio captures folder |
||||
captures/ |
||||
|
||||
# IntelliJ |
||||
*.iml |
||||
.idea/workspace.xml |
||||
.idea/tasks.xml |
||||
.idea/gradle.xml |
||||
.idea/assetWizardSettings.xml |
||||
.idea/dictionaries |
||||
.idea/libraries |
||||
# Android Studio 3 in .gitignore file. |
||||
.idea/caches |
||||
.idea/modules.xml |
||||
# Comment next line if keeping position of elements in Navigation Editor is relevant for you |
||||
.idea/navEditor.xml |
||||
|
||||
# Keystore files |
||||
# Uncomment the following lines if you do not want to check your keystore files in. |
||||
#*.jks |
||||
#*.keystore |
||||
|
||||
# External native build folder generated in Android Studio 2.2 and later |
||||
.externalNativeBuild |
||||
|
||||
# Google Services (e.g. APIs or Firebase) |
||||
# google-services.json |
||||
|
||||
# Freeline |
||||
freeline.py |
||||
freeline/ |
||||
freeline_project_description.json |
||||
|
||||
# fastlane |
||||
fastlane/report.xml |
||||
fastlane/Preview.html |
||||
fastlane/screenshots |
||||
fastlane/test_output |
||||
fastlane/readme.md |
||||
|
||||
# Version control |
||||
vcs.xml |
||||
|
||||
# lint |
||||
lint/intermediates/ |
||||
lint/generated/ |
||||
lint/outputs/ |
||||
lint/tmp/ |
||||
# lint/reports/ |
||||
|
||||
|
||||
# Xcode |
||||
# |
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore |
||||
|
||||
## Build generated |
||||
build/ |
||||
DerivedData/ |
||||
|
||||
## Various settings |
||||
*.pbxuser |
||||
!default.pbxuser |
||||
*.mode1v3 |
||||
!default.mode1v3 |
||||
*.mode2v3 |
||||
!default.mode2v3 |
||||
*.perspectivev3 |
||||
!default.perspectivev3 |
||||
xcuserdata/ |
||||
|
||||
## Other |
||||
*.moved-aside |
||||
*.xccheckout |
||||
*.xcscmblueprint |
||||
|
||||
## Obj-C/Swift specific |
||||
*.hmap |
||||
*.ipa |
||||
*.dSYM.zip |
||||
*.dSYM |
||||
|
||||
# CocoaPods |
||||
# |
||||
# We recommend against adding the Pods directory to your .gitignore. However |
||||
# you should judge for yourself, the pros and cons are mentioned at: |
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control |
||||
# |
||||
# Pods/ |
||||
# |
||||
# Add this line if you want to avoid checking in source code from the Xcode workspace |
||||
# *.xcworkspace |
||||
|
||||
# Carthage |
||||
# |
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies. |
||||
# Carthage/Checkouts |
||||
|
||||
Carthage/Build |
||||
|
||||
# fastlane |
||||
# |
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the |
||||
# screenshots whenever they are needed. |
||||
# For more information about the recommended setup visit: |
||||
# https://docs.fastlane.tools/best-practices/source-control/#source-control |
||||
|
||||
fastlane/report.xml |
||||
fastlane/Preview.html |
||||
fastlane/screenshots/**/*.png |
||||
fastlane/test_output |
||||
|
||||
# Code Injection |
||||
# |
||||
# After new code Injection tools there's a generated folder /iOSInjectionProject |
||||
# https://github.com/johnno1962/injectionforxcode |
||||
|
||||
iOSInjectionProject/ |
||||
|
||||
|
||||
# Xcode |
||||
# |
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore |
||||
|
||||
## Build generated |
||||
build/ |
||||
DerivedData/ |
||||
|
||||
## Various settings |
||||
*.pbxuser |
||||
!default.pbxuser |
||||
*.mode1v3 |
||||
!default.mode1v3 |
||||
*.mode2v3 |
||||
!default.mode2v3 |
||||
*.perspectivev3 |
||||
!default.perspectivev3 |
||||
xcuserdata/ |
||||
|
||||
## Other |
||||
*.moved-aside |
||||
*.xccheckout |
||||
*.xcscmblueprint |
||||
|
||||
## Obj-C/Swift specific |
||||
*.hmap |
||||
*.ipa |
||||
*.dSYM.zip |
||||
*.dSYM |
||||
|
||||
## Playgrounds |
||||
timeline.xctimeline |
||||
playground.xcworkspace |
||||
|
||||
# Swift Package Manager |
||||
# |
||||
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. |
||||
# Packages/ |
||||
# Package.pins |
||||
# Package.resolved |
||||
.build/ |
||||
|
||||
# CocoaPods |
||||
# |
||||
# We recommend against adding the Pods directory to your .gitignore. However |
||||
# you should judge for yourself, the pros and cons are mentioned at: |
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control |
||||
# |
||||
# Pods/ |
||||
# |
||||
# Add this line if you want to avoid checking in source code from the Xcode workspace |
||||
# *.xcworkspace |
||||
|
||||
# Carthage |
||||
# |
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies. |
||||
# Carthage/Checkouts |
||||
|
||||
Carthage/Build |
||||
|
||||
# Accio dependency management |
||||
Dependencies/ |
||||
.accio/ |
||||
|
||||
# fastlane |
||||
# |
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the |
||||
# screenshots whenever they are needed. |
||||
# For more information about the recommended setup visit: |
||||
# https://docs.fastlane.tools/best-practices/source-control/#source-control |
||||
|
||||
fastlane/report.xml |
||||
fastlane/Preview.html |
||||
fastlane/screenshots/**/*.png |
||||
fastlane/test_output |
||||
|
||||
# Code Injection |
||||
# |
||||
# After new code Injection tools there's a generated folder /iOSInjectionProject |
||||
# https://github.com/johnno1962/injectionforxcode |
||||
|
||||
iOSInjectionProject/ |
@ -0,0 +1,16 @@ |
||||
# dependencies |
||||
/node_modules |
||||
|
||||
# production |
||||
/dist |
||||
|
||||
# misc |
||||
.DS_Store |
||||
.vscode |
||||
.ideal |
||||
|
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
yarn.lock |
||||
package-lock.json |
@ -0,0 +1,45 @@ |
||||
# JSBridgeH5 |
||||
`JSBridge` 实例中 `H5` 端代码实现。 |
||||
|
||||
此项目使用 react 进行编写,但是关于 react 的代码不多,其中关于 jsbridge 使用的代码在任意项目或者框架中均可使用。 |
||||
|
||||
代码中有详细注释。 |
||||
|
||||
|
||||
## 下载项目 |
||||
``` bash |
||||
git clone https://github.com/beichensky/jsbridge-example.git |
||||
``` |
||||
|
||||
## 安装插件 |
||||
``` bash |
||||
# 打开 JSBridgeH5 项目 |
||||
cd JSBridgeH5 |
||||
|
||||
# 安装依赖 |
||||
npm install |
||||
``` |
||||
|
||||
|
||||
## 运行项目 |
||||
``` bash |
||||
npm run start |
||||
``` |
||||
|
||||
打开浏览器,输入:`localhost:8000` 查看项目运行效果。 |
||||
|
||||
运行项目后,才能在原生应用中展示,否则移动端 Webview 加载不出来页面。请记得在将当前的 ip 地址和端口替换到移动端 url 上。 |
||||
|
||||
|
||||
|
||||
## 项目打包 |
||||
``` bash |
||||
npm run build |
||||
``` |
||||
|
||||
新增 `dist` 目录,里面是打包好的项目代码 |
||||
|
||||
|
||||
|
||||
## 更多 |
||||
关于项目的更多介绍请查看我的博客:[使用 JSBridge 与原生 IOS、Android 进行交互(含H5、Android、IOS端代码,附 Demo)]() |
@ -0,0 +1,69 @@ |
||||
module.exports = (api) => { |
||||
api.cache(true); |
||||
|
||||
return { |
||||
presets: [ |
||||
"@babel/preset-env", |
||||
"@babel/preset-react" |
||||
], |
||||
plugins: [ |
||||
[ |
||||
"@babel/plugin-proposal-decorators", |
||||
{ |
||||
"legacy": true |
||||
} |
||||
], |
||||
[ |
||||
"import", |
||||
{ |
||||
"libraryName": "antd", |
||||
"style": true |
||||
} |
||||
], |
||||
[ |
||||
"@babel/plugin-transform-runtime", |
||||
{ |
||||
"corejs": 2 |
||||
} |
||||
], |
||||
[ |
||||
"@babel/plugin-proposal-class-properties",
|
||||
{
|
||||
"loose": true |
||||
} |
||||
], |
||||
"@babel/plugin-syntax-dynamic-import", |
||||
// "@babel/plugin-syntax-import-meta",
|
||||
// 可以用 const ex = "before
|
||||
// after"; 这种方式编写字符串
|
||||
// "@babel/plugin-proposal-json-strings",
|
||||
// 可以使用 generate 语法
|
||||
// "@babel/plugin-proposal-function-sent",
|
||||
// 可以使用 export * 这种命名空间的方式导出模块
|
||||
"@babel/plugin-proposal-export-namespace-from", |
||||
// 可以使用数字分离器书写数字
|
||||
// "@babel/plugin-proposal-numeric-separator"
|
||||
// 可以使用异常抛出表达式,
|
||||
"@babel/plugin-proposal-throw-expressions", |
||||
// 默认导出
|
||||
"@babel/plugin-proposal-export-default-from", |
||||
// 可以使用逻辑赋值运算符
|
||||
"@babel/plugin-proposal-logical-assignment-operators", |
||||
// 可以使用可选链的方式访问深层嵌套的属性或者函数 ?.
|
||||
"@babel/plugin-proposal-optional-chaining", |
||||
// 可以使用管道运算符 |>
|
||||
[ |
||||
"@babel/plugin-proposal-pipeline-operator", |
||||
{ |
||||
"proposal": "minimal" |
||||
} |
||||
], |
||||
// 可以使用空值合并语法 ??
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator", |
||||
// 可以使用 do 表达式(可以认为是三元运算符的复杂版本)
|
||||
"@babel/plugin-proposal-do-expressions", |
||||
// 可以使用功能绑定语法 obj::func
|
||||
"@babel/plugin-proposal-function-bind" |
||||
] |
||||
} |
||||
} |
@ -0,0 +1,64 @@ |
||||
{ |
||||
"name": "jsbridge-h5", |
||||
"version": "1.0.0", |
||||
"description": "", |
||||
"private": true, |
||||
"scripts": { |
||||
"start": "webpack-dev-server --config webpack/webpack.config.dev.js", |
||||
"build": "webpack --config webpack/webpack.config.prod.js" |
||||
}, |
||||
"keywords": [], |
||||
"author": "", |
||||
"license": "ISC", |
||||
"devDependencies": { |
||||
"@babel/core": "^7.0.0", |
||||
"@babel/plugin-proposal-class-properties": "^7.0.0", |
||||
"@babel/plugin-proposal-decorators": "^7.0.0", |
||||
"@babel/plugin-proposal-do-expressions": "^7.0.0", |
||||
"@babel/plugin-proposal-export-default-from": "^7.0.0", |
||||
"@babel/plugin-proposal-export-namespace-from": "^7.0.0", |
||||
"@babel/plugin-proposal-function-bind": "^7.0.0", |
||||
"@babel/plugin-proposal-logical-assignment-operators": "^7.0.0", |
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0", |
||||
"@babel/plugin-proposal-optional-chaining": "^7.0.0", |
||||
"@babel/plugin-proposal-pipeline-operator": "^7.0.0", |
||||
"@babel/plugin-proposal-throw-expressions": "^7.0.0", |
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0", |
||||
"@babel/plugin-transform-runtime": "^7.0.0", |
||||
"@babel/preset-env": "^7.0.0", |
||||
"@babel/preset-react": "^7.0.0", |
||||
"@babel/runtime-corejs2": "^7.0.0", |
||||
"autoprefixer": "^9.5.1", |
||||
"babel-loader": "^8.0.0", |
||||
"babel-plugin-import": "^1.11.0", |
||||
"clean-webpack-plugin": "^2.0.1", |
||||
"css-loader": "^2.1.1", |
||||
"csv-loader": "^3.0.2", |
||||
"file-loader": "^3.0.1", |
||||
"friendly-errors-webpack-plugin": "^1.7.0", |
||||
"html-loader": "^0.5.5", |
||||
"html-webpack-plugin": "^3.2.0", |
||||
"less": "^3.9.0", |
||||
"less-loader": "^4.1.0", |
||||
"markdown-loader": "^5.0.0", |
||||
"mini-css-extract-plugin": "^0.5.0", |
||||
"optimize-css-assets-webpack-plugin": "^5.0.1", |
||||
"postcss-loader": "^3.0.0", |
||||
"style-loader": "^0.23.1", |
||||
"uglifyjs-webpack-plugin": "^2.1.2", |
||||
"webpack": "^4.29.6", |
||||
"webpack-cli": "^3.3.0", |
||||
"webpack-dev-server": "^3.1.4", |
||||
"webpack-merge": "^4.2.1", |
||||
"xml-loader": "^1.2.1" |
||||
}, |
||||
"dependencies": { |
||||
"antd": "^3.16.2", |
||||
"js-cookie": "^2.2.0", |
||||
"moment": "^2.24.0", |
||||
"prop-types": "^15.7.2", |
||||
"qs": "^6.7.0", |
||||
"react": "^16.8.6", |
||||
"react-dom": "^16.8.6" |
||||
} |
||||
} |
After Width: | Height: | Size: 3.8 KiB |
@ -0,0 +1,12 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge"> |
||||
<title>JSBridge Demo</title> |
||||
</head> |
||||
<body> |
||||
<div id="root"></div> |
||||
</body> |
||||
</html> |
@ -0,0 +1,79 @@ |
||||
import React, { Component, useState } from 'react'; |
||||
import { Button, Input, message } from 'antd'; |
||||
import Cookie from 'js-cookie'; |
||||
|
||||
// 使用 CSS Module 的方式引入 App.less
|
||||
import styles from './App.less'; |
||||
|
||||
|
||||
const isAndroid = navigator.userAgent.endsWith('android'); |
||||
|
||||
export default (props) => { |
||||
|
||||
const [user, setUser] = useState(); |
||||
const [name, setName] = useState(); |
||||
const [token, setToken] = useState(Cookie.get('token')); |
||||
|
||||
window.setupWebViewJavascriptBridge(bridge => { |
||||
bridge.registerHandler("changeName", (data, fn) => { |
||||
setName(data); |
||||
fn && fn(""); |
||||
}); |
||||
|
||||
bridge.registerHandler("syncCookie", (data, fn) => {
|
||||
setToken(Cookie.get('token')); |
||||
fn && fn(""); |
||||
}); |
||||
}); |
||||
|
||||
/** |
||||
* 调用原生的默认接受方法 |
||||
*/ |
||||
const handleInit = () => { |
||||
window.setupWebViewJavascriptBridge(bridge => { |
||||
bridge.send("JS 传递给原生的消息", (data) => { |
||||
message.success(data); |
||||
}) |
||||
}) |
||||
}; |
||||
|
||||
/** |
||||
* 调用原生方法刷新 H5 界面 |
||||
*/ |
||||
const handleReload = () => { |
||||
window.setupWebViewJavascriptBridge(bridge => { |
||||
bridge.callHandler('reloadUrl'); |
||||
}) |
||||
}; |
||||
|
||||
/** |
||||
* 修改原生界面的 User 名称 |
||||
*/ |
||||
const handleChangeUser = () => { |
||||
window.setupWebViewJavascriptBridge(bridge => { |
||||
bridge.callHandler('changeUser', user, () => { |
||||
message.success('user 修改成功!'); |
||||
setUser(''); |
||||
}); |
||||
}) |
||||
}; |
||||
|
||||
return ( |
||||
<div className={ styles.app }> |
||||
<h2>H5 界面:</h2> |
||||
{ isAndroid ? <Button style={{ margin: '10px 0 30px' }} type="primary" onClick={ handleInit }>调用原生默认接收方法</Button> : null} |
||||
<br /> |
||||
<Button style={{ marginBottom: 30 }} type="primary" onClick={ handleReload }>调用原生方法刷新 H5 界面</Button> |
||||
<br /> |
||||
<Button type="primary" onClick={ handleChangeUser }>修改原生界面的 user 值</Button> |
||||
<Input value={ user } onChange={ (e) => setUser(e.target.value) } style={{ marginLeft: 10, width: 160 }} placeholder="请输入新的 user 名称" /> |
||||
<div style={{ marginTop: 30, fontSize: 16 }}> |
||||
<label>Name 值:</label><span style={{ marginLeft: 40 }}>{ name }</span> |
||||
</div> |
||||
<div style={{ marginTop: 30, fontSize: 16 }}> |
||||
<label>同步原生设置的 Cookie 值:</label><span style={{ marginLeft: 20 }}>{ token }</span> |
||||
</div> |
||||
</div> |
||||
) |
||||
|
||||
} |
@ -0,0 +1,4 @@ |
||||
.app { |
||||
padding: 20px; |
||||
background: #f0f0f0; |
||||
} |
@ -0,0 +1,91 @@ |
||||
import React from 'react'; |
||||
import ReactDom from 'react-dom'; |
||||
import { LocaleProvider, message } from 'antd'; |
||||
import zh_CN from 'antd/lib/locale-provider/zh_CN'; |
||||
import 'moment/locale/zh-cn'; |
||||
|
||||
import App from './App'; |
||||
|
||||
/** |
||||
* 使用 JSBridge 总结: |
||||
* 1、跟 IOS 交互的时候,只需要且必须注册 iosFuntion 方法即可, |
||||
* 不能在 setupWebViewJavascriptBridge 中执行 bridge.init 方法,否则 IOS 无法调用到 H5 的注册函数; |
||||
* 2、与安卓进行交互的时候 |
||||
* ①、使用 iosFuntion,就可以实现 H5 调用 安卓的注册函数,但是安卓无法调用 H5 的注册函数, |
||||
* 并且 H5 调用安卓成功后的回调函数也无法执行 |
||||
* ②、使用 andoirFunction 并且要在 setupWebViewJavascriptBridge 中执行 bridge.init 方法, |
||||
* 安卓才可以正常调用 H5 的回调函数,并且 H5 调用安卓成功后的回调函数也可以正常执行了 |
||||
*/ |
||||
|
||||
const isAndroid = navigator.userAgent.endsWith('android'); |
||||
|
||||
/** |
||||
* Android 与安卓交互时: |
||||
* 1、不调用这个函数安卓无法调用 H5 注册的事件函数; |
||||
* 2、但是 H5 可以正常调用安卓注册的事件函数; |
||||
* 3、还必须在 setupWebViewJavascriptBridge 中执行 bridge.init 方法,否则: |
||||
* ①、安卓依然无法调用 H5 注册的事件函数 |
||||
* ①、H5 正常调用安卓事件函数后的回调函数无法正常执行 |
||||
*
|
||||
* @param {*} callback
|
||||
*/ |
||||
const andoirFunction = (callback) => { |
||||
if (window.WebViewJavascriptBridge) { |
||||
callback(window.WebViewJavascriptBridge); |
||||
} else { |
||||
document.addEventListener('WebViewJavascriptBridgeReady', function () { |
||||
callback(window.WebViewJavascriptBridge); |
||||
}, false) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* IOS 与 IOS 交互时,使用这个函数即可,别的操作都不需要执行 |
||||
* @param {*} callback
|
||||
*/ |
||||
const iosFuntion = (callback) => { |
||||
if (window.WebViewJavascriptBridge) { return callback(window.WebViewJavascriptBridge) } |
||||
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback) } |
||||
window.WVJBCallbacks = [callback]; |
||||
var WVJBIframe = document.createElement('iframe'); |
||||
WVJBIframe.style.display = 'none'; |
||||
WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'; |
||||
document.documentElement.appendChild(WVJBIframe); |
||||
setTimeout(function(){ |
||||
document.documentElement.removeChild(WVJBIframe); |
||||
}, 0); |
||||
} |
||||
|
||||
/** |
||||
* 注册 setupWebViewJavascriptBridge 方法 |
||||
* 之所以不将上面两个方法融合成一个方法,是因为放在一起,那么就只有 iosFuntion 中相关的方法体生效 |
||||
*/ |
||||
window.setupWebViewJavascriptBridge = isAndroid ? andoirFunction : iosFuntion; |
||||
|
||||
/** |
||||
* 这里如果不做判断是不是安卓,而是直接就执行下面的方法,就会导致
|
||||
* 1、IOS 无法调用 H5 这边注册的事件函数 |
||||
* 2、H5 可以正常调用 IOS 这边的事件函数,并且 H5 的回调函数可以正常执行 |
||||
*/ |
||||
if (isAndroid) { |
||||
/** |
||||
* 与安卓交互时,不调用这个函数会导致: |
||||
* 1、H5 可以正常调用 安卓这边的事件函数,但是无法再调用到 H5 的回调函数 |
||||
*
|
||||
* 前提 setupWebViewJavascriptBridge 这个函数使用的是 andoirFunction 这个,否则还是会导致上面 1 的现象出现 |
||||
*/ |
||||
window.setupWebViewJavascriptBridge(function (bridge) { |
||||
// 注册 H5 界面的默认接收函数(与安卓交互时,不注册这个事件无法接收回调函数)
|
||||
bridge.init(function (msg, responseCallback) { |
||||
message.success(msg); |
||||
responseCallback("JS 返回给原生的消息内容"); |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
ReactDom.render( |
||||
<LocaleProvider locale={zh_CN}> |
||||
<App /> |
||||
</LocaleProvider>, |
||||
document.querySelector('#root') |
||||
); |
@ -0,0 +1,146 @@ |
||||
const path = require('path'); |
||||
const HTMLWebpackPlugin = require('html-webpack-plugin'); |
||||
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin'); |
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); |
||||
const autoprefixer = require('autoprefixer'); |
||||
|
||||
const appSrc = path.resolve(__dirname, '../src'); |
||||
const appDist = path.resolve(__dirname, '../dist'); |
||||
const appPublic = path.resolve(__dirname, '../public'); |
||||
const appIndex = path.resolve(appSrc, 'index.js'); |
||||
const appHtml = path.resolve(appPublic, 'index.html'); |
||||
|
||||
module.exports = { |
||||
entry: { |
||||
main: [appIndex], |
||||
common: ['react', 'react-dom'] |
||||
}, |
||||
output: { |
||||
filename: 'public/js/[name].[hash:8].js', |
||||
path: appDist, |
||||
publicPath: '/' |
||||
}, |
||||
plugins: [ |
||||
// 自动在出口目录生成 html 并自动引入 js 文件
|
||||
new HTMLWebpackPlugin({ |
||||
template: appHtml, |
||||
filename: 'index.html' |
||||
}), |
||||
// 在命令行展示更清晰地提示信息
|
||||
new FriendlyErrorsWebpackPlugin() |
||||
], |
||||
module: { |
||||
rules: [ |
||||
// 解析 js
|
||||
{ |
||||
test: /\.(js|jsx)$/, |
||||
loader: 'babel-loader?cacheDirectory', |
||||
include: [ appSrc ], |
||||
exclude: /node_modules/ |
||||
}, |
||||
// 解析样式
|
||||
{ |
||||
test: /\.(css|less)$/, |
||||
exclude: /node_modules/, |
||||
use: [ |
||||
{ |
||||
// 使用 MiniCssExtractPlugin.loader 代替 style-loader
|
||||
loader: MiniCssExtractPlugin.loader |
||||
}, |
||||
{ |
||||
loader: 'css-loader', |
||||
options: { |
||||
sourceMap: true, |
||||
modules: true, |
||||
localIdentName: '[local].[hash:8]' |
||||
} |
||||
}, |
||||
{ |
||||
loader: 'postcss-loader', |
||||
options: { |
||||
plugins: () => [autoprefixer()] |
||||
} |
||||
}, |
||||
{ |
||||
loader: 'less-loader', |
||||
options: { |
||||
javascriptEnabled: true |
||||
} |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
test: /\.(css|less)$/, |
||||
include: /node_modules/, |
||||
use: [ |
||||
{ |
||||
loader: MiniCssExtractPlugin.loader |
||||
}, |
||||
{ |
||||
loader: 'css-loader', |
||||
options: {} |
||||
}, |
||||
{ |
||||
loader: 'postcss-loader', |
||||
options: { |
||||
plugins: () => [autoprefixer()] |
||||
} |
||||
}, |
||||
{ |
||||
loader: 'less-loader', |
||||
options: { |
||||
javascriptEnabled: true |
||||
} |
||||
} |
||||
] |
||||
}, |
||||
// 解析图片资源
|
||||
{ |
||||
test: /\.(png|svg|jpg|gif)$/, |
||||
use: [ |
||||
'file-loader' |
||||
] |
||||
}, |
||||
// 解析 字体
|
||||
{ |
||||
test: /\.(woff|woff2|eot|ttf|otf)$/, |
||||
use: [ |
||||
'file-loader' |
||||
] |
||||
}, |
||||
// 解析数据资源
|
||||
{ |
||||
test: /\.(csv|tsv)$/, |
||||
use: [ |
||||
'csv-loader' |
||||
] |
||||
}, |
||||
// 解析数据资源
|
||||
{ |
||||
test: /\.xml$/, |
||||
use: [ |
||||
'xml-loader' |
||||
] |
||||
}, |
||||
// 解析 MakeDown 文件
|
||||
{ |
||||
test: /\.md$/, |
||||
use: [ |
||||
'html-loader', |
||||
'markdown-loader' |
||||
] |
||||
} |
||||
] |
||||
}, |
||||
resolve: { |
||||
// 设置别名
|
||||
alias: { |
||||
src: appSrc, |
||||
utils: path.resolve(__dirname, '../src/utils'), |
||||
pages: path.resolve(__dirname, '../src/pages'), |
||||
components: path.resolve(__dirname, '../src/components') |
||||
}, |
||||
// 设置模块查找范围
|
||||
modules: [path.resolve(__dirname, '../node_modules')] |
||||
} |
||||
} |
@ -0,0 +1,49 @@ |
||||
const path = require('path'); |
||||
const webpack = require('webpack'); |
||||
const merge = require('webpack-merge'); |
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); |
||||
const common = require('./webpack.common'); |
||||
|
||||
const appPublic = path.resolve(__dirname, '../public'); |
||||
|
||||
const config = merge(common, { |
||||
mode: 'development', |
||||
devtool: 'inline-source-map', |
||||
devServer: { |
||||
// 作为服务器发布的目录
|
||||
contentBase: appPublic, |
||||
// 热加载
|
||||
hot: true, |
||||
// host 地址,设置成 0.0.0.0 才能在移动端使用 ip 地址访问到
|
||||
host: '0.0.0.0', |
||||
// 端口号
|
||||
port: 8000, |
||||
historyApiFallback: true, |
||||
// 是否在浏览器蒙层展示错误信息
|
||||
overlay: true, |
||||
inline: true, |
||||
// 展示的统计信息
|
||||
stats: 'errors-only', |
||||
// 配置代理
|
||||
proxy: { |
||||
'/api': { |
||||
changeOrigin: true, |
||||
target: 'https://easy-mock.com/mock/5c2dc9665cfaa5209116fa40/example', |
||||
pathRewrite: { |
||||
'^/api/': '/' |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
plugins: [ |
||||
// 热加载插件
|
||||
new webpack.HotModuleReplacementPlugin(), |
||||
// 提取 css 文件
|
||||
new MiniCssExtractPlugin({ |
||||
filename: 'public/styles/[name].css', |
||||
chunkFilename: 'public/styles/[name].chunk.css' |
||||
}) |
||||
] |
||||
}); |
||||
|
||||
module.exports = config; |
@ -0,0 +1,95 @@ |
||||
const webpack = require('webpack'); |
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); |
||||
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); |
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); |
||||
const CleanWebpackPlugin = require('clean-webpack-plugin'); |
||||
const merge = require('webpack-merge'); |
||||
const common = require('./webpack.common'); |
||||
|
||||
const config = merge(common, { |
||||
mode: 'production', |
||||
devtool: 'hidden-source-map', |
||||
plugins: [ |
||||
// 提取 css 文件
|
||||
new MiniCssExtractPlugin({ |
||||
filename: 'public/styles/[name].[contenthash:8].css', |
||||
chunkFilename: 'public/styles/[name].[contenthash:8].chunk.css' |
||||
}), |
||||
// 区分环境
|
||||
new webpack.DefinePlugin({ |
||||
// 定义 NODE_ENV 环境变量为 production
|
||||
'process.env': { |
||||
NODE_ENV: JSON.stringify('production') |
||||
} |
||||
}), |
||||
// 清理 dist 文件,2.0。0 版本之后不需要设置参数就可以自动清除打包生成的目录
|
||||
new CleanWebpackPlugin() |
||||
], |
||||
optimization: { |
||||
// 打包压缩js/css文件
|
||||
minimizer: [ |
||||
new UglifyJsPlugin({ |
||||
uglifyOptions: { |
||||
compress: { |
||||
// 在UglifyJs删除没有用到的代码时不输出警告
|
||||
warnings: false, |
||||
// 删除所有的 `console` 语句,可以兼容ie浏览器
|
||||
drop_console: true, |
||||
// 内嵌定义了但是只用到一次的变量
|
||||
collapse_vars: true, |
||||
// 提取出出现多次但是没有定义成变量去引用的静态值
|
||||
reduce_vars: true, |
||||
}, |
||||
output: { |
||||
// 最紧凑的输出
|
||||
beautify: false, |
||||
// 删除所有的注释
|
||||
comments: false, |
||||
} |
||||
} |
||||
}), |
||||
// 压缩 CSS 代码
|
||||
new OptimizeCSSAssetsPlugin({}) |
||||
], |
||||
// 拆分公共模块
|
||||
splitChunks: { |
||||
cacheGroups: { |
||||
styles: { |
||||
name: 'styles', |
||||
test: /\.(css|less)/, |
||||
chunks: 'all', |
||||
enforce: true, |
||||
// 表示是否使用已有的 chunk
|
||||
reuseExistingChunk: true
|
||||
}, |
||||
commons: { |
||||
name: 'commons', |
||||
chunks: 'initial', |
||||
minChunks: 2, |
||||
reuseExistingChunk: true |
||||
}, |
||||
vendors: { |
||||
name: 'vendors', |
||||
test: /[\\/]node_modules[\\/]/, |
||||
priority: -10, |
||||
reuseExistingChunk: true |
||||
} |
||||
} |
||||
}, |
||||
// 为每个仅含有 runtime 的入口起点添加一个额外 chunk
|
||||
runtimeChunk: true |
||||
}, |
||||
// 性能提醒
|
||||
performance: { |
||||
hints: false |
||||
}, |
||||
// 统计信息展示
|
||||
stats: { |
||||
modules: false, |
||||
children: false, |
||||
chunks: false, |
||||
chunkModules: false |
||||
} |
||||
}); |
||||
|
||||
module.exports = config; |
Loading…
Reference in new issue