需求
小項(xiàng)目。純HTML+JS+CSS已經(jīng)部署上線,但是沒有做混淆加密,需要進(jìn)行混淆加密
分析
目前代碼里面需要混淆加密的有main.js,其他的不用混淆加密。所以只需要對(duì)main.js進(jìn)行混淆加密就可,但是要保證混淆加密之后能夠訪問方法。由于目前在index.html的script使用import導(dǎo)入main.js里面的方法,需要有名字,但是打包之后一般會(huì)報(bào)錯(cuò)找不到這個(gè)名字的模塊,因?yàn)椴皇菑膆tml里面的script進(jìn)行打包的,所以在script里面引入打包后的main.js(bundle.js)是不會(huì)引入成功的因?yàn)槟K方法變了。因?yàn)樽隽俗兞棵煜?/strong>。所以把html script方法放到main.js里面。
實(shí)踐
入口文件在libs/mian.js 最終webpack打包代碼,其中有兩個(gè)關(guān)鍵點(diǎn):
1. path: path.resolve(__dirname, ‘a(chǎn)-dist’),// 自定義明明,因?yàn)橐频椒?wù)器,所以不使用dist命名;
2. TerserPlugin中的混淆取消,只用去除console和代碼注釋,不能將混淆打開,否則會(huì)報(bào)錯(cuò),和另一個(gè)混淆工具沖突;
// webpack.config.js
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
// 加密
const WebpackObfuscator = require('webpack-obfuscator');
module.exports = {
entry: './libs/main.js',
mode: 'production',
output: {
filename: 'bundle.js',
// libraryTarget: 'amd', // 不用這樣設(shè)置。因?yàn)楦牧艘雃sri js api模塊的方式,不用AMD方式,改用esri-loader的自定義loadModules。原理是動(dòng)態(tài)加載,避開由于使用require找不到對(duì)應(yīng)模塊的問題
path: path.resolve(__dirname, 'a-dist'),// 自定義明明,因?yàn)橐频椒?wù)器,所以不使用dist
},
devtool: 'source-map',
devServer: {
// contentBase: path.join(__dirname, ''),
compress: true,
port: 8080,
open: true, historyApiFallback: {
index: 'index.html',
},
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
// mangle: true, // 是否混淆變量名,默認(rèn)為 true。因?yàn)橐肓薟ebpackObfuscator,先注釋
compress: {
// 壓縮選項(xiàng)
drop_console: true, // 是否去除控制臺(tái)輸出,默認(rèn)為 false
drop_debugger: true, // 是否去除調(diào)試語句,默認(rèn)為 false
},
output: {
beautify: false, // 是否美化輸出,默認(rèn)為 false
comments: false, // 是否保留注釋,默認(rèn)為 true
},
// 更多選項(xiàng)請(qǐng)參考 Terser 文檔
},
}),
],
},
plugins: [
new ProgressBarPlugin(),
new HtmlWebpackPlugin({
template: 'index.html', // 指定HTML模板文件的路徑
filename: 'index.html' // 生成的HTML文件名,默認(rèn)為index.html
// 還可以添加其他配置選項(xiàng),如title、favicon等
}),
new CopyWebpackPlugin({
patterns: [
{ from: 'img', to: 'img' },
{ from: 'static', to: 'static' },
{
from: 'libs', to: 'libs', filter: (resourcePath) => {
// 在這里添加你想要忽略的文件或目錄的邏輯判斷。因?yàn)槲业捻?xiàng)目結(jié)構(gòu)原因,打包時(shí)候不能把main.js也復(fù)制過去,排除掉。
return !resourcePath.endsWith('main.js') && !resourcePath.endsWith('map-action.js');
}/* globOptions: {
ignore: ['libs/main.js', 'libs/map-action.js'] // 設(shè)置要忽略的文件或目錄的匹配模式
} */
}, // 指定要拷貝的文件或目錄,從來源路徑到目標(biāo)路徑
// 可以繼續(xù)添加其他的規(guī)則
],
}),
// 目前沒有和TerserPlugin方法混淆,因?yàn)閠erserOptions中的mangle沒有設(shè)置以為true
new WebpackObfuscator({
rotateUnicodeArray: true,// 啟用 Unicode 數(shù)組的字符旋轉(zhuǎn)。默認(rèn)值為 true
compact: true,// 合并和混淆輸出代碼。默認(rèn)值為 true。
selfDefending: true,// 生成自保護(hù)的混淆代碼。默認(rèn)值為 true。
stringArray: true,// 啟用字符串?dāng)?shù)組混淆。默認(rèn)值為 true。
stringArrayEncoding: ['base64', 'rc4'],// 指定字符串?dāng)?shù)組的編碼方式??蛇x值為 'base64'、'rc4' 和 'none'。默認(rèn)值為 'base64'。
}),
],
// externals: {
// esri: 'esri', // 注意不用在這里在引入一次
// },
module: {
rules: [
// JS 文件的加載器
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader',
},
// CSS 文件的加載器
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
};
打包結(jié)束
難點(diǎn)
Webpack打包原理中會(huì)解析模塊引入語句,如import、require找到對(duì)應(yīng)模塊,但是esri在html+JS+CSS獲取是通過require獲取動(dòng)態(tài)鏈接。問題來了,那為什么一開始arcgis js用require來請(qǐng)求?為什么不用import?但是一般情況下引入require是沒有這個(gè)方法的,那require從哪里來的?參考:ArcGIS api for js中的require()究竟是什么。得出require()方法從init.js里面來。
為了配合webpack打包,不能使用require()進(jìn)行加載esri模塊,換種方式請(qǐng)求!查了一下集成和請(qǐng)求模塊方式,發(fā)現(xiàn)有esri-loader,其中有個(gè)方法loadModules。如下代碼
esri-loader原理
esri-loader具體原理如下(ChatGPT 3.5的回答):
動(dòng)態(tài)加載:esri-loader 使用動(dòng)態(tài)腳本加載技術(shù)來加載 ArcGIS API for
JavaScript。它會(huì)根據(jù)用戶的需求,在運(yùn)行時(shí)動(dòng)態(tài)地向 HTML 頁面中插入<script>標(biāo)簽,從指定的 CDN或本地目錄加載相關(guān)的 JavaScript 文件。異步加載:ArcGIS API for JavaScript 包含多個(gè) JavaScript文件,有些文件可能比較大。為了提高頁面加載速度,esri-loader 使用異步加載方式,按需加載所需的文件。這樣可以避免一次性加載整個(gè) API,而只加載需要的部分。
回調(diào)處理:當(dāng)相關(guān) JavaScript 文件加載完成后,esri-loader 會(huì)觸發(fā)用戶指定的回調(diào)函數(shù)。在回調(diào)函數(shù)中,您可以安全地使用ArcGIS API for JavaScript 的模塊和類。
模塊導(dǎo)入:esri-loader 通過自定義的 loadModules方法,簡(jiǎn)化了模塊的導(dǎo)入過程。它允許您以數(shù)組形式傳入需要導(dǎo)入的模塊名,并返回 Promise 對(duì)象。當(dāng)這些模塊加載完成后,Promise才會(huì)被解析,您就可以在回調(diào)函數(shù)中使用這些模塊
>./libs/main.js esri 地圖初始化
import * as esriLoader from './esri-loader/esm/esri-loader.js';
export async function initMap(data) {
// require和function中的參數(shù)必須對(duì)應(yīng)
console.log("?? ~ file: main.js:696 ~ initMap ~ window.$esriLocal:", window.$esriLocal)
// if (!esriLoader.isLoaded()) {
// esriLoader.loadScript({ url: "https://js.arcgis.com/3.44/" })
// esriLoader.loadCss("https://js.arcgis.com/3.44/esri/css/esri.css")
// 原來是require,改為loadModules,這樣webpack打包時(shí)候就不會(huì)解析require,也不用在webpack.config.js external里面配置了。參考官方:
esriLoader.loadModules([
"esri/map",
"esri/Color",
"esri/graphic",
"esri/geometry/Extent",
"esri/geometry/Point",
"esri/geometry/Polygon",
"esri/geometry/Polyline",
"esri/SpatialReference",
"esri/layers/KMLLayer",
"esri/layers/GraphicsLayer",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/FillSymbol",
"esri/symbols/LineSymbol",
"esri/symbols/SimpleFillSymbol",
"esri/symbols/SimpleLineSymbol",
"esri/symbols/TextSymbol",
"dojo/parser",
"esri/layers/TileInfo",
"esri/layers/WebTiledLayer",
"dojo/dom-style",
"esri/config"]).then(function ([
Map,
Color,
Graphic,
Extent,
Point,
Polygon,
Polyline,
SpatialReference,
KMLLayer,
GraphicsLayer,
SimpleMarkerSymbol,
FillSymbol,
LineSymbol,
SimpleFillSymbol,
SimpleLineSymbol,
TextSymbol,
parser, TileInfo, WebTiledLayer, domStyle,
esriConfig]) {
window.$esriLocal = {
Map: Map,
Color: Color,
SpatialReference: SpatialReference,
Extent: Extent,
WebTiledLayer: WebTiledLayer,
TileInfo: TileInfo,
Graphic: Graphic,
Point: Point,
Polyline: Polyline,
GraphicsLayer: GraphicsLayer,
SimpleMarkerSymbol: SimpleMarkerSymbol,
FillSymbol: FillSymbol,
LineSymbol: LineSymbol,
SimpleFillSymbol: SimpleFillSymbol,
SimpleLineSymbol: SimpleLineSymbol,
TextSymbol: TextSymbol,
}
// else {
console.log("?? ~ file: main.js:342 ~ initMap ~ window.$esriLocal:", window.$esriLocal)
/** 當(dāng)前地圖視角的顯示矩形范圍*/
let extentp = { west: 125.9738504337227, south: 61.624205573999646, east: 116.55005559052246, north: 38.67104274606849 };
let extent = new window.$esriLocal.Extent({
xmax: extentp.east,
xmin: extentp.west,
ymax: extentp.north,
ymin: extentp.south,
spatialReference: { wkid: 4490 },
});
map = new window.$esriLocal.Map("map", {
center: [],
zoom: 1,
fadeOnZoom: true,
fitExtent: true,
sliderPosition: "bottom-right",
logo: false,// 去除官方logo
// slider:false,
});
// 定位到范圍
map.setExtent(extent);
// 在這里添加自定義底圖
});
//}
// 老方法,但是這樣在webpack中會(huì)影響打包,因?yàn)閣ebpack默認(rèn)使用commonjs模塊系統(tǒng)進(jìn)行打包,找不到對(duì)應(yīng)模塊和包就會(huì)報(bào)錯(cuò)。
/* await require([
"esri/map",
"esri/Color",
"esri/graphic",
"esri/geometry/Extent",
"esri/geometry/Point",
"esri/geometry/Polygon",
"esri/geometry/Polyline",
"esri/SpatialReference",
"esri/layers/KMLLayer",
"esri/layers/GraphicsLayer",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/FillSymbol",
"esri/symbols/LineSymbol",
"esri/symbols/SimpleFillSymbol",
"esri/symbols/SimpleLineSymbol",
"esri/symbols/TextSymbol",
"dojo/parser",
"esri/layers/TileInfo",
"esri/layers/WebTiledLayer",
"dojo/dom-style",
"esri/config",
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
], function (
Map, Color, Graphic, Extent, Point, Polygon, Polyline,
SpatialReference, KMLLayer, GraphicsLayer, SimpleMarkerSymbol,
FillSymbol,
LineSymbol,
SimpleFillSymbol,
SimpleLineSymbol,
TextSymbol,
parser, TileInfo, WebTiledLayer, domStyle,
esriConfig
) {
window.$esriLocal = {
Map: Map,
Color: Color,
SpatialReference: SpatialReference,
Extent: Extent,
WebTiledLayer: WebTiledLayer,
TileInfo: TileInfo,
Graphic: Graphic,
Point: Point,
Polyline: Polyline,
GraphicsLayer: GraphicsLayer,
SimpleMarkerSymbol: SimpleMarkerSymbol,
FillSymbol: FillSymbol,
LineSymbol: LineSymbol,
SimpleFillSymbol: SimpleFillSymbol,
SimpleLineSymbol: SimpleLineSymbol,
TextSymbol: TextSymbol,
}
// else {
console.log("?? ~ file: main.js:342 ~ initMap ~ window.$esriLocal:", window.$esriLocal)
/** 當(dāng)前地圖視角的顯示矩形范圍*
let extentp = { west: 125.9738504337227, south: 61.624205573999646, east: 116.55005559052246, north: 38.67104274606849 };
let extent = new window.$esriLocal.Extent({
xmax: extentp.east,
xmin: extentp.west,
ymax: extentp.north,
ymin: extentp.south,
spatialReference: { wkid: 4490 },
});
map = new window.$esriLocal.Map("map", {
center: [],
zoom: 1,
fadeOnZoom: true,
fitExtent: true,
sliderPosition: "bottom-right",
logo: false,// 去除官方logo
// slider:false,
});
// 定位到范圍
map.setExtent(extent);
// }
}) */
}
缺點(diǎn)
也算優(yōu)點(diǎn)吧,就是每次修改代碼生效就需要用webpack的devServer進(jìn)行了,不能用直接訪問文件的形式。
注意
可能會(huì)出現(xiàn)multipleDefine的錯(cuò)誤。就是引入init.js多次,引入一次就夠了,還有網(wǎng)上說是jquery,但是我代碼里面沒用用jQuery,之前也出現(xiàn)了multipleDefine,但是設(shè)置這個(gè)之后就沒有出現(xiàn)了,后面我又用html引入init.js,發(fā)現(xiàn)又沒有報(bào)錯(cuò),下次報(bào)錯(cuò)了再補(bǔ)充(主要沒有發(fā)現(xiàn)哪里導(dǎo)致重復(fù)多次定義,init.js也就引用一次。
另一個(gè)猜想,是因?yàn)閣ebpac之前有一篇文章說打包成amd,就是這個(gè)參數(shù)libraryTarget,然后在手機(jī)上出現(xiàn)multipleDefine,去除了重新打包就沒有報(bào)錯(cuò),這一快比較模糊。)
知識(shí)儲(chǔ)備
解決這個(gè)問題需要的知識(shí)儲(chǔ)備為
- JavaScript模塊系統(tǒng);
- webpack原理;
- webpack、arcgis js api使用經(jīng)驗(yàn);
總結(jié)
從原則、框架邏輯開始,也就是各家官方網(wǎng)站庫(kù)使用教程開始,一般都有前言,包括背景介紹、庫(kù)開發(fā)注意事項(xiàng)、如何使用(繼承方式)
ArcGIS Maps SDK for JavaScript
webpack-getting-started
體系化知識(shí)的必要性。碎片化學(xué)習(xí)只在體系化學(xué)習(xí)之后。
你要學(xué)一個(gè)新東西,就要問這個(gè)東西是什么?歷史是什么?怎么用?目前能用在哪里?基礎(chǔ)內(nèi)容都有什么?
沒有具體老師的時(shí)候,官方就是最好的老師。
后記
webpack可以修改模塊系統(tǒng)標(biāo)識(shí),所以有人一開始就使用AMD模塊開發(fā),修改難度比較難的話就用
libraryTarget: ‘a(chǎn)md’文章來源:http://www.zghlxwxcb.cn/news/detail-514818.html
但是我沒時(shí)間測(cè)試,所以有人可以可以丟鏈接給我。文章來源地址http://www.zghlxwxcb.cn/news/detail-514818.html
到了這里,關(guān)于Webpack打包arcgis js api 3.x純html+JS+CSS項(xiàng)目的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!