likes
comments
collection
share

arcgis js + webpack 完全本地化部署流程

作者站长头像
站长
· 阅读数 23

这里先解释下什么叫完全本地化部署,现在的 arcgis js 已经可以通过 npm 安装到本地项目里进行开发和使用(名字叫做 @arcgis/core),虽然 npm 包已经下到本地了,但是代码在实际运行中依旧会去访问在线的 arcgis 服务获取资源,例如样式、assets 资源、字体等。

如果你的项目可以公网访问的话,这些都不是问题。但是有一些项目是内网部署的,也就是说上生产没法访问这些外网资源,就导致地图功能无法使用。所以说需要把这些在线资源迁移到本地部署,让 arcgis 在运行时从本地加载依赖。

本文的内容就是介绍如何在现代前端工程里把所有的 arcgis js 依赖迁移至本地,同时也会讲一些中间可能遇到的问题和细节。

要迁移的东西

先介绍一下都要本地部署哪些东西,大致分为三方面:

  • assets 资源文件:arcgis 在运行时会去加载一些 js、wasm、json 配置等文件,默认情况下这些文件都是从线上加载的。

arcgis js + webpack 完全本地化部署流程

  • theme css 文件:这个文件是在开发者自己配的,例如官方示例 demo 就会在项目的入口 css 里配置从线上加载样式:
@import 'https://js.arcgis.com/4.25/@arcgis/core/assets/esri/themes/light/main.css'
  • arcgis 字体文件:这个非常常见但是很多人都会忽略。arcgis 里在线字体文件的引用有两种途径,如果你引入了一个在线的图层,例如 new TileLayer({ url }),这个图层里使用的字体就会去对应的 Rest MapServer 里找,比如下面这种:

arcgis js + webpack 完全本地化部署流程

这个不用担心,毕竟这个字体和地图图层放在一起的,地图图层都访问不到那问题可严重多了。

另一种方式是自己实例化的对象里配置了字体,比如说下面这种:

import TextSymbol from "@arcgis/core/symbols/TextSymbol";
const symbol = new TextSymbol({ text: '123' });

这种因为是前端自己创建的,所以字体文件就会去 arcgis 自己的在线服务里找:

arcgis js + webpack 完全本地化部署流程

总共就可以分为这三部分,本文的目标就是把这些内容包含进前端项目的最终打包结果里,并修改 arcgis 的配置让线上部署后的地图可以访问本地的这些资源。

下面的内容会以 creat-react-app 创建的 react + webpack 工程为例进行介绍。不过不用担心,涉及到 react 的内容几乎没有,使用 vue 的同学也可以放心观看。

本地化 assets 资源文件

咱们一个一个来说,首先是 assets 里的资源文件,其实 @arcgis/core 这个包里已经有完整的 assets 资源了,只是默认没用到。你可以在 node_modules\@arcgis\core\assets 里找到这些文件。

这时候有人就会想了,那我直接把这个文件夹复制到项目的 public 目录里不就行了。当然可以,首先修改 arcgis 的配置:

在项目里引用 esriConfig,然后配置 assetsPath/arcgis/assets

import esriConfig from '@arcgis/core/config';
esriConfig.assetsPath = '/arcgis/assets';

然后把 node_modules\@arcgis\core\assets 复制到项目根目录下的 public\arcgis\assets 就行了。

事实上官方就是这么教的,文档在这里 Build with ES modules | Overview | ArcGIS Maps SDK for JavaScript 4.25 | ArcGIS Developers

但是这种做法并不算太好,一方面文件数量很多,总共六千多个,并且都是静态资源,放项目里就会有被人改动的风险。并且上面的官方文档里也提到了,使用这种直接复制的方式要注意本地的资源版本和 @arcgis/core 的版本是否一致,所以这种方式会带来一定的麻烦。如果你也觉得这种不太好的话,这里介绍另一种方案:构建时自动复制资源文件。

你可以用 CopyWebpackPlugin 插件来复制资源,文档在 这里。或者如果你的项目也是用 create-react-app 创建的,那你可以直接 eject config 之后在 scripts/build.js 里搜 copyPublicFolder 方法,应该长下面这样:

function copyPublicFolder() {
  fs.copySync(paths.appPublic, paths.appBuild, {
    dereference: true,
    filter: file => file !== paths.appHtml,
  });
}

这个就是 react 用来把 public 文件夹复制到 dist 目录下的函数(paths 变量是一个常用路径的集合)。

所以说,完全可以照葫芦画瓢,也用这种方法把 arcgis 的 assets 资源复制过去:

function copyArcgisAssetsFolder() {
  fs.copySync(
    path.resolve(paths.appNodeModules, '@arcgis/core/assets'),
    // 下面这个 arcgis/assets 就是要复制到的路径,根据你自己的情况进行修改
    path.resolve(paths.appBuild, 'arcgis/assets'), 
    {
      dereference: true,
      filter: file => file !== paths.appHtml,
    }
  );
}

写好了记得调用,写 copyPublicFolder 调用后面就行。这样,在执行 build 命令的时候,就会自动的把 node_modules 里的 arcgis 资源复制到 dist 打包目录下。

写好之后刚才的 esriConfig 也要改成下面这样:

esriConfig.fontsUrl = getFullFrontendUrl('/arcgis/fonts');
if (process.env.NODE_ENV === 'production') {
  esriConfig.assetsPath = '/arcgis/assets';
}

很简单,如果当前是开发环境的话就还是用在线资源。因为在本地开发时,webpack-dev-server 托管的静态资源目录是 /public 文件夹,而自动复制 assets 的话,public 文件夹里是没有这些东西的,所以开发时访问不到这些本地资源。

有些同学可能会担心本地开发和部署之后用的依赖不一样的话会导致线上效果和开发时有区别。其实问题不大,因为 @arcgis/core 会根据自己的版本自动选择对应的线上资源,所以本地 node_modules 里的 assets 和线上 arcgis 自己托管的 assets 实际上是一模一样的,毕竟都是静态资源。

如果你实在担心的话,也可以修改 webpack 配置里的 devServer.static,这个字段支持数组形式添加多个静态资源目录,把 node_modules\@arcgis\core\assets 也托管上就行了。配置文档见 DevServer | webpack

本地化 theme css 文件

第二个要改的东西是项目引入的主题文件。这个改起来很简单,直接把在线的 css 连接换成本地的就行了:

// import 'https://js.arcgis.com/4.25/@arcgis/core/assets/esri/themes/light/main.css';
import '@arcgis/core/assets/esri/themes/light/main.css';

这个也没必要放在项目的入口 css 里调用,直接放在 js 里就行,webpack 会通过 style-loader 自己处理依赖。这样配合路由懒加载,在没有访问地图页面时还可以提高加载速度。

一般情况下直接这样改就行了,但是呢,如果你是使用 create-react-app 创建的 react 项目,这么改的话就会报错:

Module build failed (from ./node_modules/less-loader/dist/cjs.js):

# 一堆输出

Unrecognised input. Possibly missing opening '{'
      in D:\project\node_modules\@arcgis\core\assets\esri\themes\light\main.css (line 1, column 56)

原因在于 create-react-app 对 webpack 的 css-loader 做了封装,位置在 config/webpack.config.jsgetStyleLoaders 方法:

// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor,  preProcessorOptions = {
  sourceMap: true,
}) => {
  const loaders = [
    isEnvDevelopment && require.resolve('style-loader'),
    isEnvProduction && {
      loader: MiniCssExtractPlugin.loader,
      // ...
    },
    {
      loader: require.resolve('css-loader'),
      options: cssOptions,
    },
    {
      loader: require.resolve('less-loader'),
      options: cssOptions,
    },
    {
      // Options for PostCSS as we reference these options twice
      // Adds vendor prefixing based on your specified browser support in
      // package.json
      loader: require.resolve('postcss-loader'),
      options: {
        postcssOptions: {
          // ...
        },
        sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
      },
    },
  ].filter(Boolean);
  // ...
  return loaders;
};

可以看到这个方法封装了 less、postcss 等常见 css 工具的 loader。但是问题在于,单纯的 css 文件匹配上也用了 getStyleLoaders 方法:

arcgis js + webpack 完全本地化部署流程

也就是说,普通的 css 文件也是按照 postcss-loader > less-loader > css-loader > style-loader 的顺序加载的,然后就导致了上面这个问题。

所以,我们把这段封装修改一下,让普通的 css 只走 css-loader > style-loader 的解析就好了:

const getBaseStyleLoaders = (cssOptions) => {
    const loaders = [
      isEnvDevelopment && require.resolve('style-loader'),
      isEnvProduction && {
        loader: MiniCssExtractPlugin.loader,
        // css is located in `static/css`, use '../../' to locate index.html folder
        // in production `paths.publicUrlOrPath` can be a relative path
        options: paths.getPublicUrlOrPathWithEnv().startsWith('.')
          ? { publicPath: '../../' }
          : {},
      },
      {
        loader: require.resolve('css-loader'),
        options: cssOptions,
      },
    ].filter(Boolean);

    return loaders;
  }

其实就是把基础的 style-loader 和 css-loader 独立出来,然后在 getStyleLoaders 里引入这个方法。

arcgis js + webpack 完全本地化部署流程

然后把两个 css 的解析换成 getBaseStyleLoaders 就可以了,参数保持不变:

arcgis js + webpack 完全本地化部署流程

本地化字体文件

最后一个就是把 arcgis 的在线字体挪到本地了,这个麻烦的点在于 arcgis 进行了字体分割,所以每个字体都会拆成数百个 pbf 文件,手工下就很慢了。所以我写了一个命令行小工具,传入一个字体名就可以直接把 arcgis 的在线字体下载到本地,已经发到 npm 了。用法也很简单,比如这个字体:

arcgis js + webpack 完全本地化部署流程

通过链接找到它的名字,然后执行下面两个命令就可以了,更详细的用法可以看 npm 介绍:get-arcgis-font

# npm 安装工具
npm install -g get-arcgis-font
# 下载字体
get-arcgis-font arial-unicode-ms-regular

下载完成后会把字体文件放在命令执行目录下的 result 文件夹里,复制到自己项目的 public 文件夹下,比如 public\arcgis\fonts,然后修改 esriConfig 即可:

esriConfig.fontsUrl = '/arcgis/fonts';

至此,所有需要的内容都已经转移到本地了。

转载自:https://juejin.cn/post/7187700867582132285
评论
请登录