iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

0
Software Development

自己用的工具自己做! 30天玩轉VS Code Extension之旅系列 第 35

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世界裡的專案解法是我們很好的參考資源,但未必就會是當下最適合我們情境的解法。在我們理解背後相關原理後,我們可以為專案找到符合各自情境的解決方法,視情況採取不同方式。

參考資源



上一篇
Day34 | WebView元件開發 - 使用Ant Design Mobile UI Library
下一篇
Day36 | WebView Snippets管理頁面設計與開發
系列文
自己用的工具自己做! 30天玩轉VS Code Extension之旅36

尚未有邦友留言

立即登入留言