Day35 | WebView元件开发 - Webpack打包工具整合地雷陷阱排除

大家好,今天继续来开发元件,并动手解决实务上我们遇到的设定配置的问题。在昨天的练习里,我们可以使用babal-plugin-import按需载入使用到的第三方UI元件的css,之後使用yarn start启动react的web,并在浏览器显示正确的结果,但其实在Webview里,这样设定仍有些地方需要调整。

Webpack Splited Chunk档案处理


在之前的Webview单元我们提到,VSCode的WebView里要访问script的资源跟css时有限制,要将script跟css边谦里的url专换为webview专用的Uri才能顺利生效。

在没有使用rewired多import其他ui的library前,使用Create React App生成的的asset-manifest.json是这个样子,如下所示,js档案分为三支档案。css有一支,对应到files属性下的main.css路径。

{
  "files": {
    "main.css": "./static/css/main.5f361e03.chunk.css",
    ...
  },
  "entrypoints": [
    "static/js/runtime-main.e139f7bb.js",
    "static/js/2.390c405e.chunk.js",
    "static/css/main.5f361e03.chunk.css",
    "static/js/main.2ba94f4f.chunk.js"
  ]
}

在webview专案底下使用rewired改写过後的yarn build後,让我们到out/build/asset-manifest.json底下查看,可以发现在多打包一个UI Library的css後,entrypoints下面的css档案变为两支,且有一支我们无法直接在files下面透过随机产生的static/css的key值获取正确路径的css分块档案。

{
   "files": {
    "main.css": "./static/css/main.dc44bd41.chunk.css",
    ...
    "static/css/2.438752f9.chunk.css": "./static/css/2.438752f9.chunk.css",
    ...
   },
   "entrypoints": [
    "static/js/runtime-main.2206c2b6.js",
    "static/css/2.438752f9.chunk.css",
    "static/js/2.f197f274.chunk.js",
    "static/css/main.dc44bd41.chunk.css",
    "static/js/main.b3ed2647.chunk.js"
  ]
}

因此,当我们在WebviewPanel里使用前面的loadWebViewContent方法时,我们读取css档案的底下这段

<link rel="stylesheet" type="text/css" href="${this.webviewUri(mainfest.files['main.css'])}">

路径会失效。

这边笔者处理被webpack分块後的css档案做法和前面处理分块後的js档案做法一样。在我们的loadWebcontent方法里,我们直接透过字串filter出我们的所有分块过的css档案。

const entrypointsCss = mainfest['entrypoints'].filter((p: string) => p.includes('static/css'));

之後,在样板字串里我们直接透过阵列index取得所有css的档案路径,并使用先前我们准备好的webviewUri转换方法转换css路径,即可让WebView正确的读取所有我们所需的css档案。

<link rel="stylesheet" type="text/css" href="${this.webviewUri(entrypointsCss[0])}">
<link rel="stylesheet" type="text/css" href="${this.webviewUri(entrypointsCss[1])}">

loadWebviewContent完整的精简版方法如下所示:

class WebviewPanel {
 ...
 public loadWebviewContent() {
    const mainfest = readJSON(path.join(this.context.extensionPath, 'out/build/asset-manifest.json'));
  const entrypointsJs = mainfest['entrypoints'].filter((p: string) => p.includes('static/js'));
  const entrypointsCss = mainfest['entrypoints'].filter((p: string) => p.includes('static/css'));
  return `<!DOCTYPE html>
   <html lang="en">
   <head>
        <link rel="icon" href="${this.webviewUri('./favicon.ico')}"/>
         <meta name="viewport" content="width=device-width,initial-scale=1"/>
         <meta name="theme-color" content="#000000"/>
         <meta name="description" content="Web site created using create-react-app"/>
         <title>React App</title>
         <base href="${this.webviewUri('/')}">
         <link rel="stylesheet" type="text/css" href="${this.webviewUri(mainfest.files['main.css'])}">
         <link rel="stylesheet" type="text/css" href="${this.webviewUri(entrypointsCss[0])}">
         <link rel="stylesheet" type="text/css" href="${this.webviewUri(entrypointsCss[1])}">
         <link rel="manifest" href="${this.webviewUri('./manifest.json')}"/>
   </head>
   <body
      <div id="root"></div>
      <script src="${this.webviewUri(entrypointsJs[0])}"></script>
      <script src="${this.webviewUri(entrypointsJs[1])}"></script>
      <script src="${this.webviewUri(entrypointsJs[2])}"></script>
   </body>
   </html>`;
 }
}

现在我们调整完了loadWebviewContent方法,再启动extension,即会正确的在VSCode里面显示react使用第三方元件後的css的样式。

停用Create React App的Webpack档案分块设定


在笔者上面的做法里,需在build完後的asset-manifest.json里检查有几只切分出去的档案,并手动套用到loadWebviewContent的html里。但其实我们也可以使用另一种方式解决问题,在Open Source的世界里,有一个vscode-webview-react的react vscode webview template专案,里面使用了直接禁用webpack的split chunk设定的方式来解决问题。

在vscode-webview-react专案里,有一支build-non-split.js档,引入了rewired这个套件停用webpack的相关split chunk属性。

#!/usr/bin/env node

// Disables code splitting into chunks
// See https://github.com/facebook/create-react-app/issues/5306#issuecomment-433425838

const rewire = require("rewire");
const defaults = rewire("react-scripts/scripts/build.js");
let config = defaults.__get__("config");

config.optimization.splitChunks = {
  cacheGroups: {
    default: false
  }
};

config.optimization.runtimeChunk = false;

这里我们可以参考相关的Wepack设定做配置。

在我们的专案里,我们会在config-overrides.js下面使用不同套件做配置。

这里笔者参考套件写法的风格撰写一个客制化的buildNonSplit方法进行webpack的设定,全部配置如下。

const {
 override,
 fixBabelImports
} = require('customize-cra');

const buildNonSplit = () => config => {
 config.optimization.splitChunks = {
   cacheGroups: {
     default: false
   }
 };
 config.optimization.runtimeChunk = false;
 return config;
};

module.exports = override(
 fixBabelImports('import', {
  libraryName: 'antd-mobile',
  style: 'true',
 }),
 buildNonSplit()
);

2020/11/03更正: 阅读套件程序後了解,customize-cra套件实际上有针对同一个issue提供禁用code split的工具方法,详见Github连结,我们可以直接使用内建的disableChunk方法。

const {
  override,
  disableChunk,
  fixBabelImports
} = require('customize-cra');

module.exports = override(
 fixBabelImports('import', {
  libraryName: 'antd-mobile',
  style: 'true',
 }),
 disableChunk()
);

好的,现在我们使用yarn build,再到out/build/asset-manifest.json档案下面查看结果,可以确认entrypoints下面仅有main.js与main.css两只档案

```json=
{
  "files": {
    "main.css": "./static/css/main.e7c5d175.css",
    "main.js": "./static/js/main.1d4ca48f.js",
    "main.js.map": "./static/js/main.1d4ca48f.js.map",
    "index.html": "./index.html",
    "static/css/main.e7c5d175.css.map": "./static/css/main.e7c5d175.css.map",
    "static/js/main.1d4ca48f.js.LICENSE.txt": "./static/js/main.1d4ca48f.js.LICENSE.txt"
  },
  "entrypoints": [
    "static/css/main.e7c5d175.css",
    "static/js/main.1d4ca48f.js"
  ]
}

现在在我们的loadWebviewContent方法里,我们仅需简单的引用files属性下的main.js与main.css路径即可让Webview使用。

// 引入main.css
<link rel="stylesheet" type="text/css" href="${this.webviewUri(mainfest.files['main.css'])}">
...
// 引入main.js
<script src="${this.webviewUri(mainfest.files['main.js'])}"></script>

结语


好的,今天我们花了些时间处理跟Create React App里的Webpack设定,并将其套用到webview上。会让人感到有些繁琐,但这是实务开发上必然会遇到的过程。

在今天笔者也示范了两种解决问题的方式,选用哪种方式处理引入的css或js档案路径,视乎专案是否需要Webpack的分块与相关优化设定。Open Source世界里的专案解法是我们很好的参考资源,但未必就会是当下最适合我们情境的解法。在我们理解背後相关原理後,我们可以为专案找到符合各自情境的解决方法,视情况采取不同方式。

参考资源



<<:  第 57 天 - 才知道 && 用法

>>:  [鼠年全马] W35 - Vue出一个旅馆预约平台(9)

[Day 26] 从 AsyncPipe 出发,微探讨 Angular 处理 pipe 的流程

昨天介绍了 AsyncPipe 的用法以及它可以带来的便利,今天要来看一下在这方便的背後是由那些东西...

前端工程师也能开发全端网页:挑战 30 天用 React 加上 Firebase 打造社群网站|Day17 文章留言功能

连续 30 天不中断每天上传一支教学影片,教你如何用 React 加上 Firebase 打造社群...

Day 29 AWS云端服务启用一条龙抓起来-CloudFormation

想要更便捷的来开启云端服务吗?CloudFormation帮我们搞定! 1. CloudFormat...

[Day5] 策略买卖讯号回测

延续前一天的程序码,首先先把图片里的程序码搬到箭头的地方,固定前面放函数後面放程序,这样看起来比较清...

AWSome day Taipei 2020 为什麽去AWS呀?

2020/10/29 在几年前我就一直实践Mobile first, Cloud first这两大准...