feat(project):添加 H5 端项目 JSBridgeH5

master
beichen 5 years ago
parent 6222454bf6
commit 5ce9f4cc15
  1. 306
      .gitignore
  2. 16
      JSBridgeH5/.gitignore
  3. 45
      JSBridgeH5/README.md
  4. 69
      JSBridgeH5/babel.config.js
  5. 64
      JSBridgeH5/package.json
  6. BIN
      JSBridgeH5/public/favicon.ico
  7. 12
      JSBridgeH5/public/index.html
  8. 79
      JSBridgeH5/src/App.js
  9. 4
      JSBridgeH5/src/App.less
  10. 91
      JSBridgeH5/src/index.js
  11. 146
      JSBridgeH5/webpack/webpack.common.js
  12. 49
      JSBridgeH5/webpack/webpack.config.dev.js
  13. 95
      JSBridgeH5/webpack/webpack.config.prod.js

306
.gitignore vendored

@ -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"
}
}

Binary file not shown.

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;
/**
* 这里如果不做判断是不是安卓而是直接就执行下面的方法就会导致
* 1IOS 无法调用 H5 这边注册的事件函数
* 2H5 可以正常调用 IOS 这边的事件函数并且 H5 的回调函数可以正常执行
*/
if (isAndroid) {
/**
* 与安卓交互时不调用这个函数会导致
* 1H5 可以正常调用 安卓这边的事件函数但是无法再调用到 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…
Cancel
Save