zoukankan      html  css  js  c++  java
  • 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请点赞或分享,赠人玫瑰手有余香,你会快乐开心每一天的。

  • 相关阅读:
    阿里架构师用3点讲透数据中台,这些都是你没看过的
    深度学习入门笔记(八):深层网络的原理
    创建一个最基本的SpringMvc项目
    区块链隐私资源
    weblogic wlst 例子
    如何找靠谱的2020网赚兼职?可信度高的网赚兼职推荐
    thinkphp链接多个数据库时怎么调用M方法?
    thinkphp链接多个数据库时怎么调用M方法?
    thinkphp链接多个数据库时怎么调用M方法?
    thinkphp链接多个数据库时怎么调用M方法?
  • 原文地址:https://www.cnblogs.com/dengxiaoning/p/12871804.html
Copyright © 2011-2022 走看看