Vue cli3 整合SuperMap巧遇js異步加載的坑


最近使用到superMap做三維地圖,而項目又分為可視化大屏與后台管理系統兩部分,所以項目配置了多入口,然引入cesium依賴就成了問題,在vue cli3 整合Cesium,處理build 時內存溢出問題雖然知道了整合原生的cesium的方法,

但是在實際開發中會發現superMap 官方擴展的接口是無法使用的,必須引入superMap官方重寫的cesium.js才可以, 然而superMap官方並提供模塊化的開發版本,所以只有使用原生script 引入了

一、使用script 標簽加載cesium

於是我分別在public目錄下創建了兩個html文件
目錄結構

分別是admin 和screen,然后在頁面中使用<script src="libs/Cesium/Cesium.js"></script>進行js引入,然而測試發現一個奇怪的問題是cli3的多入口。在初始化時會使用默認的index.html頁面作為默認入口頁面

image.png
image.png

發現我自己增加的script 都沒有了。。。這個怎么辦?於是想到了document.createElement("script");在vue 初始化的時候去動態加載js
於是引入了如下專門用於異步加載的函數


var Head = document.getElementsByTagName('head')[0], style = document.createElement('style');

//異步加載css,js文件
function linkScript(parm, fn) {
    var linkScript;
    if (/\.css[^\.]*$/.test(parm)) {
        linkScript = document.createElement("link");
        linkScript.type = "text/" + ("css");
        linkScript.rel = "stylesheet";
        linkScript.href = parm;
    } else {
        // debugger
        linkScript = document.createElement("script");
        linkScript.type = "text/" + ("javascript");
        linkScript.src = parm;
    }
    Head.insertBefore(linkScript, Head.lastChild)
    linkScript.onload = linkScript.onerror = function (res) {
        if (fn) fn(res)
    }
}

linkScript("libs/Cesium/Cesium.js");

admin.js中import '../../components/common/js/asyncLoadExtraAssest'引入

這樣是可以將cesium加載進來

二、刷新cesium 實例丟失

然而在點擊瀏覽器左上角刷新按鈕強制刷新頁面時發現cesium在window中的實例丟失了引起了undefined異常
刷新前

刷新后

三、第一種處理方案(原型緩存)

開始我想到了是否是因為刷新后頁面間window 上的實例都清除了,於是想到使用Vue的實例原型來緩存第一次加載進來的cesium實例(使用到了函數柯里化來進行cesium緩存)最后發現沒有任何作用
函數柯里化緩存cesium

四、第二種處理方案(更改 script 為同步)

再后來打斷點一跟發現,
首先引入了cesium js

然后執行到了初始化地圖的函數,發現這個時候cesium為空
最后才是cesium.js加載完成

不對其實,window中的cesium實例是存在的只是較晚於頁面中加載地圖的組件,於是想到了是script 中異步加載解析cesium.js的鍋,於是想將script加載解析更改為同步執行;

但是這個想法好像行不通,document加載的script 默認就是異步,就算為其增加 async:false也沒用(瀏覽器會自動屏棄掉)

五、第三種處理方案(使用HtmlWebpackPlugin插件)

於是有想到了使用 HtmlWebpackPlugin為其設置頁面配置
修改index.html
增加一個page.json

增加一個mutilPageConfig.js來配置每個頁面的屬性

const fs = require("fs");
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 通過頁面配置文件過去頁面json
function generateByConfig() {
    return JSON.parse(fs.readFileSync("./src/page.json"));
}

// 生成extraEntry
const extraEntry = generateByConfig();

let newExtraEntry = {};

// 生成HtmlWebpackPlugin
let extraHtmlWebpackPlugins = [];

for (let i in extraEntry) {
    let chunk = i;
    newExtraEntry[chunk] = extraEntry[i].path;
    extraHtmlWebpackPlugins.push(
        new HtmlWebpackPlugin({
            filename: chunk + ".html",
            template: "public/index.html",
            chunks: [chunk],
            title: extraEntry[i].title,
            favicon:extraEntry[i].favicon,
            iconfont:' <link rel="stylesheet" href="//at.alicdn.com/t/font_1668594_l9pybqe35u.css">',
            widgets:' <link rel="stylesheet" href="libs//Cesium/Widgets/widgets.css">',
            cesium:' <script src="libs/Cesium/Cesium.js"></script>',
        })
    );
};

// 復制靜態資源
// extraHtmlWebpackPlugins.push(
// new CopyWebpackPlugin([
// {
// from: './src/assets/libs',
// to: 'libs',
// ignore: ['.*']
// }
// ])
// )

exports.extraEntry = newExtraEntry;
exports.extraHtmlWebpackPlugins = extraHtmlWebpackPlugins;

vue.config.js引入

const multiBuilder = require("./multiPageConfig");
const { extraEntry, extraHtmlWebpackPlugins } = multiBuilder;

module.exports = {
    configureWebpack: {
        entry: {
            ...extraEntry
        },
        plugins: extraHtmlWebpackPlugins,
     }
}

再刪除掉pages相關配置,運行發現提示

 ERROR  Failed to compile with 2 errors                                                                         11:17:52

This relative module was not found:

* ./src/main.js in multi (webpack)-dev-server/client?http://192.168.0.101:8081/sockjs-node (webpack)/hot/dev-server.js ./src/main.js, multi (webpack)/hot/dev-server.js (webpack)-dev-server/client?http://192.168.0.101:8081/sockjs-node ./src/main.js

轉念一想,糊塗這是cli2的加載方式,cli3使用的是pages進行配置的,於是我將pages恢復並修改為

修改pages配置

發現頁面title也icon可以加載正確

雖然頁面一片空白,但是頁面title也icon可以加載正確!

六、最終處理方案(使用pages屬性配置外部資源)

有了上面的初步測試發現多個頁面可以使用一個index.html,

於是再到vue官網去看看
官網對pages描述
這個比較坑的是平時喜歡使用中文版的

中文翻譯是否漏了一段

看中文翻譯是否漏了一段 哎,看來英語很重要呀

有了上訴經歷,現在基本明白怎么進行接下來的操作了


修改page中的配置來更改頁面加載的外部資源,而上一步配置的mutilPageConfig可改下,進行配置整合,再運行發現頁面OK了,刷新cesium也不會再丟失了

效果圖


最后在完整配置如下


1、page.json

{
  "index": {
    "entry": "./src/views/admin/admin.js",
    "template": "public/index.html",
    "filename": "index.html",
    "favicon": "public/favicon_management.ico",
    "title": "后台管理系統",
    "iconfont":" <link rel='stylesheet' href='//at.alicdn.com/t/font_1668594_l9pybqe35u.css'>",
    "widgets":" <link rel='stylesheet'' href='libs//Cesium/Widgets/widgets.css'>",
    "cesium":" <script type='text/javascript'' src='libs/Cesium/Cesium.js'></script>",
    "chunks": ["chunk-vendors", "chunk-common", "index"]
  },
  "view": {
    "entry": "./src/views/bigScreen/screen.js",
    "emplate": "public/index.html",
    "filename": "view.html",
    "title": "可視化平台",
    "favicon": "public/favicon_view.ico",
    "iconfont":" <link rel='stylesheet' href='//at.alicdn.com/t/font_1668594_l9pybqe35u.css'>",
    "widgets":" <link rel='stylesheet'' href='libs//Cesium/Widgets/widgets.css'>",
    "cesium":" <script type='text/javascript'' src='libs/Cesium/Cesium.js'></script>",
    "chunks": ["chunk-vendors", "chunk-common", "view"]
  }
}

2、multiPageConfig.js

const fs = require("fs");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;

const isProduction = process.env.NODE_ENV === 'production';

// 生成HtmlWebpackPlugin
let extraHtmlWebpackPlugins = [];
// 復制靜態資源
extraHtmlWebpackPlugins.push(
    new CopyWebpackPlugin([
        {
            from: './src/assets/libs',
            to: 'libs',
            ignore: ['.*']
        }
    ])
)
if(isProduction){
    extraHtmlWebpackPlugins.push(
        new UglifyJsPlugin({
            uglifyOptions: {
                compress: {
                    drop_console: true,
                    drop_debugger: false,
                    pure_funcs: ['console.log']//移除console
                }
            },
            sourceMap: false,
            parallel: true
        }),
        new CompressionWebpackPlugin({
            filename: '[path].gz[query]',
            algorithm: 'gzip',
            test: productionGzipExtensions,
            threshold: 10240,
            minRatio: 0.8
        })
    )
}

// 通過頁面配置文件過去頁面json
function generateByConfig() {
    return JSON.parse(fs.readFileSync("./src/page.json"));
}

// 生成extraEntry
const entryPages = generateByConfig();


exports.extraEntry = entryPages;

exports.extraHtmlWebpackPlugins = extraHtmlWebpackPlugins

3、index.js

const path = require("path");
const multiBuilder = require("./multiPageConfig");
const { extraEntry, extraHtmlWebpackPlugins } = multiBuilder;

const isProduction = process.env.NODE_ENV === 'production';
function assetsPath (_path) {
    const assetsSubDirectory = isProduction ? './': '/';
    return path.posix.join(assetsSubDirectory, _path)
}

module.exports = {
    pages:extraEntry,
    configureWebpack: {
        output: {
            sourcePrefix: ' '
        },
        amd: {
            toUrlUndefined: true
        },
        resolve: {
            // alias: {
            // 'cesium': path.resolve(__dirname, '../',cesiumSource)
            // }
        },
        plugins: extraHtmlWebpackPlugins,
        module: {
            rules: [
                {
                    test: /\.(cur)(\?.*)?$/,
                    loader: 'url-loader',
                    options: {
                        limit: 10000,
                        name: assetsPath('cur/[name].[hash:7].[ext]')
                    }
                },
            ],
            unknownContextCritical: /^.\/.*$/,
            unknownContextCritical: false

        }
    },
    css: {
        loaderOptions: {
            postcss: {
                plugins: [
                    require("postcss-px-to-viewport")({
                        unitToConvert: "px",
                        viewportWidth: 1920,
                        viewportHeight: 960,
                        unitPrecision: 3,
                        propList: ["*"],
                        viewportUnit: "vw",
                        selectorBlackList: ['.ignore', 'calc'],
                        minPixelValue: 1,
                        mediaQuery: false,
                        exclude: /((\/|\\)(node_modules|src)(\/|\\))/,
                    })
                ]
            }
        }
    }
}

4、vue.config.js


const extraConfig = require('./configHelper/index.js')
module.exports = {
    publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
    devServer: {
        port: 8081,
        disableHostCheck: true,
    },
    pages: extraConfig.pages,
    productionSourceMap: false,
    configureWebpack: extraConfig.configureWebpack,
    css: extraConfig.css
}

七、配置SuperMap全局構造

上面說到的將SuperMap重新過的Cesium.js整合到了項目中,也解決了頁面刷新異步加載外部js 的問題,但是在全局使用SuperMap構造函數時還是會提示undefined,因為這里還需要將從SuperMap官網下載的下載后的SuperMap包

中的SuperMap擴展文件夾

這些擴展文件夾加入到vue的項目中去

1、首先將其拷貝到libs 文件夾下拷貝到libs中
2、在page.json中新增"superMap":"<script type='text/javascript' src='libs/SuperMap.Include.js'></script>",

如:

新增supermap script

3、在index.html中新增<%= htmlWebpackPlugin.options.superMap %>

如:

index.html修改

4、現在就可以在項目中使用了

組件中使用



OK 到此問題總算解決了,也對js 異步加載和vue多入口配置有了進一步了解,特此記錄希望對小伙伴有幫助,感謝你的閱讀,如覺得ok請點贊或分享,贈人玫瑰手有余香,你會快樂開心每一天的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM