Electron+Mobx+React開發(fā)記錄(二)

Hello World

> Contents

  1. 前言
  2. webpack4圖片打包的問題
  3. webpack4樣式表打包分離
  4. 應(yīng)用構(gòu)建工具electron-builder配置
  5. 應(yīng)用構(gòu)建工具electron-builder的問題

前言


前一篇文章主要記錄了開發(fā)環(huán)境的搭建和一些開發(fā)時(shí)遇到的問題,這篇文章主要說說自己在coding work之后進(jìn)行應(yīng)用打包時(shí)遇到的問題(webpack打包和electron打包),項(xiàng)目地址

webpack4圖片打包的問題


1. jsx中聲明的img:src不能被webpack識別和打包

在jsx中使用圖片時(shí),如下:

<div className="install-item-image" onClick={() => {showTerminalInfo(item.label)}}>
  <Dimmer active={loading} inverted>
    <Loader size="tiny">{ loadingLable }</Loader>
  </Dimmer>
  <img alt="error" src={item.url} />
</div>

這里img:src使用了一個(gè)變量,但無論是變量還是字符串,webpack在打包的時(shí)候都不能根據(jù)我們引用的資源路徑在dist目錄下生成正確的資源引用路徑結(jié)構(gòu),所以只能在我們需要引用圖片的地方,手動(dòng)require引入,如下:

...
const imgSrc = require('path/to/img');
...
<img src={imgSrc}>

那如果img:src真的是變量而且需要一次引入多個(gè)那怎么辦,如果你說想用for循環(huán)引入可以不,這其實(shí)是不行的,因?yàn)閣ebpack打包的時(shí)候是識別不了你for循環(huán)內(nèi)定義的變量的。引入辦法如下,可以用正則表達(dá)式對一個(gè)文件夾內(nèi)的所有文件進(jìn)行匹配引入,并且可以在項(xiàng)目任意位置引入:

// 匹配1:只匹配圖片
const requireContext = require.context('resources/install', true, /^\.\/.*\.(jpg|png)$/);
// 匹配2:匹配所有文件
const requireContext = require.context('resources/install', true, /.*/);
requireContext.keys().map(requireContext);
2. 生產(chǎn)環(huán)境和開發(fā)環(huán)境的publicPath配置

關(guān)于publicPath這里有一篇說得比較清楚的文章

output.path : 硬盤上的路徑,也就是你打算把文件打包到你的哪個(gè)目錄,與發(fā)布時(shí)的路徑完全無關(guān)。

output.publicPath: 主要用來轉(zhuǎn)換url中的相對路徑的。如果你引用到包含url的資源,一定要配置output.publicPath,配置了此項(xiàng),webpack在打包時(shí)才能根據(jù)配置動(dòng)態(tài)修改uri中的相對值。比如果你將所有打包生成好的文件托管在服務(wù)器上,訪問格式是www.yourhost.com/dist/index.html的話,那publicPath就需要指定為/dist/。

  • webpack-dev-server的publicPath默認(rèn)是/,也就是在開發(fā)環(huán)境下webpack-dev-server在內(nèi)存中生成的bundle.js文件路徑是/,我們在瀏覽器中訪問localhost:3000/bundle.js就能看見了,如果你在生產(chǎn)環(huán)境下的訪問路徑是localhost:3000/dist/bundle.js,就需要指定webpack-dev-server的publicPath為/dist/,這只是一個(gè)內(nèi)存中虛擬的路徑映射,目的是為了統(tǒng)一開發(fā)環(huán)境和生產(chǎn)環(huán)境的路徑問題。

  • 開發(fā)環(huán)境下:

webpack.config.js
...
devtool: 'source-map',
  entry: [
    'react-hot-loader/patch',
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
    './app/index',
  ],
  mode: 'development',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',
  },
...
  • 生產(chǎn)環(huán)境下:
webpack.prod.config.js
...
entry: [
    './app/index',
  ],
  mode: 'production',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',
  },
...
3. 統(tǒng)一生產(chǎn)環(huán)境和開發(fā)環(huán)境的資源引用路徑

可以在webpack.config文件中指定resolve.alias來將一個(gè)絕對路徑重命名,然后在項(xiàng)目任意位置直接使用重命名路徑就行了,不用在import的時(shí)候搞很多相對路徑聲明../../../,如下:

  • 聲明:
webpack.config.js
...
resolve: {
    alias: {
      resources: path.resolve(__dirname, 'resources'),
      app: path.resolve(__dirname, 'app'),
    },
  },
...
  • 使用:
<img src="resources/install/albert.png"}>

webpack4樣式表打包分離


1. css屬性backgroup-image: url(...)的路徑統(tǒng)一

我們在webpack.config中指定resolve.alias之后,如果你要在css屬性中引用那個(gè)絕對路徑的別名的話,需要在img:url字符前多加一個(gè)~路徑轉(zhuǎn)換符號,來讓webpack為你自動(dòng)替換路徑,如下:

.router-left-background {
  background-image: url(~resources/public/gohome.jpg); /* The image used */
  background-color: #f6f6f6; /* Used if the image is unavailable */
  background-position: center; /* Center the image */
  background-repeat: no-repeat; /* Do not repeat the image */
  background-size: cover; /* cover size */
}
2. 將樣式表從bundle.js文件中分離

如果項(xiàng)目比較大的話,直接將樣式表壓縮進(jìn)bundle.js文件中會導(dǎo)致頁面首頁加載時(shí)間比較長,這里我們使用extract-text-webpack-pluginwebpack插件分離樣式表,然后在index.html引入樣式表,這樣頁面加載的時(shí)候?yàn)g覽器就會發(fā)送異步請求來同時(shí)加載bundle.js文件和css文件,極大地提高加載速度。

  • index.html
<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>electronux</title>
  <link rel="stylesheet" href="style.scss.css">
  <link rel="stylesheet" href="style.css">
  <base href="./">
</head>
<body>
  <div id="root"></div>
  <script src="bundle.js"></script>
</body>
</html>
  • 開發(fā)環(huán)境下webpack插件extract-text-webpack-plugin配置
    這里的插件publicPath需要根據(jù)webpack-dev-server的publicPath配置(默認(rèn)是/),如果我們的樣式表會加載外部文件(例如圖片和字體文件)的話,那個(gè)實(shí)際資源請求路徑就會根據(jù)這里的publicPath來計(jì)算得出。
webpack.config.js
...
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

// 拆分樣式文件
const extractSass = new ExtractTextPlugin({
  filename: 'style.scss.css',
});
const extractCss = new ExtractTextPlugin({
  filename: 'style.css',
});
...
module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['babel-loader'],
      },
      {
        test: /\.css$/,
        use: extractCss.extract({
          fallback: 'style-loader',
          use: 'css-loader',
          publicPath: '/',
        }),
      },
      {
        test: /\.scss$/,
        use: extractSass.extract({
          use: [{
            loader: 'css-loader',
          }, {
            loader: 'sass-loader',
          }],
          fallback: 'style-loader', // 在開發(fā)環(huán)境使用 style-loader
          publicPath: '/',
        }),
      },
      ...
    ],
    ...
  },

  plugins: [
    extractSass,
    extractCss,
    ...
  ],
...
  • 生產(chǎn)環(huán)境下webpack插件extract-text-webpack-plugin配置
    生產(chǎn)環(huán)境下需要將publicPath設(shè)置為我們打包后生成的dist目錄,不然css中引用的外部資源如圖片等是不能生成到dist目錄中的。
webpack.prod.config.js
...
module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.css$/,
        use: extractCss.extract({
          fallback: 'style-loader',
          use: 'css-loader',
          publicPath: path.join(__dirname, 'dist/'),
        }),
      },
      {
        test: /\.scss$/,
        use: extractSass.extract({
          use: [{
            loader: 'css-loader',
          }, {
            loader: 'sass-loader',
          }],
          fallback: 'style-loader', // 在開發(fā)環(huán)境使用 style-loader
          publicPath: path.join(__dirname, 'dist/'),
        }),
      },
    ...

應(yīng)用構(gòu)建工具electron-builder配置


{
  "appId": "com.nojsja.electronux",
  "copyright": "nojsja",
  "productName": "electronux",
  "asar": false,
  "directories": {
    "buildResources": "build-assets/",
    "output": "build/"
  },
  "files": ["package.json", "index.js", "dist/", "app/", "node_modules/"],
  "linux": {
    "icon": "resources",
    "category": "System",
    "description": "A System Management Tool Build For Manjaro Linux 17",
    "synopsis": "electronux",
    "target": ["zip"]
  }
}

electron-builder打包主要解決兩個(gè)問題,一是怎么打包前端界面代碼目錄dist下的資源(渲染進(jìn)程代碼),二是怎么打包由根目錄下的index.js文件引入的資源(主進(jìn)程代碼)。配置文件中files參數(shù)項(xiàng)配置的就是所有需要最終打包進(jìn)我們應(yīng)用的所有文件了。

  • package.json -- 整個(gè)應(yīng)用程序的依賴配置文件
  • index.js -- 主進(jìn)程入口文件
  • dist -- 渲染進(jìn)程資源文件
  • app -- 運(yùn)行時(shí)引用的源代碼和資源目錄
  • node_modules -- 運(yùn)行時(shí)引用的第三方模塊和資源目錄

配置說明詳細(xì)見官方文檔

應(yīng)用構(gòu)建工具electron-builder的問題


1. 國內(nèi)墻導(dǎo)致打包工具依賴下載失敗

運(yùn)行electron-builder的時(shí)候會首先下載各個(gè)打包依賴,但是如果直接下載是會失敗的(下載源文件存在github)。但我這邊終端是用polipo配置了http-proxy的,下載的時(shí)候還是很慢,最后仍會導(dǎo)致下載失敗,這個(gè)真的比較頭痛,我索性將git倉庫clone到自己搭建的vps虛擬機(jī)上(日本節(jié)點(diǎn)),然后在服務(wù)器上運(yùn)行一次打包命令,再把~/.cache/electron-builder~/.cache/electron這兩個(gè)打包工具生成的目錄直接下載到本地對應(yīng)的目錄下,最后在本地運(yùn)行打包命令的時(shí)候就不會再去下載依賴了。

2. 打包成AppImage后在運(yùn)行時(shí)不能使用chmod更改文件權(quán)限的問題

先來看一段Linux上常見的AppImage打包應(yīng)用的定義:

AppImage不把Linux應(yīng)用程序安裝在文件系統(tǒng)相應(yīng)的目錄中,相反,它沒有進(jìn)行實(shí)際的安裝,AppImage文件只是個(gè)壓縮文件,在它運(yùn)行時(shí)候掛載,用AppImage打包的程序,一個(gè)程序就是一個(gè)文件。

在我的應(yīng)用中需要執(zhí)行一些shell腳本獲取系統(tǒng)信息,但是這些腳本在第一次運(yùn)行的時(shí)候是需要使用node.js中fs模塊的fs.chmod方法對shell腳本進(jìn)行賦予可執(zhí)行權(quán)限的(chmod 755),但是AppImage運(yùn)行時(shí)是不允許動(dòng)態(tài)更改文件屬性的,所有掛載的Applmage文件都是只讀的,無奈,我放棄了將應(yīng)用打包成AppImage這種格式。
為了便于測試可以直接打包成zip文件,解壓后就能運(yùn)行,如果要安裝到不同的發(fā)行版的話還能打包成pacmandeb、rpm、tar.gz等文件。

3. arar加密打包時(shí)造成絕對路徑查找失敗

electron-builder的打包參數(shù)中有一個(gè)參數(shù)是asar: true/false,如果指定了為true的話打包后的壓縮包內(nèi)的源代碼是會被arar加密的,這個(gè)對一些不開源的代碼來說還是很有必要,但在我這個(gè)應(yīng)用中應(yīng)用在運(yùn)行的時(shí)候會動(dòng)態(tài)加載一些自定義的模塊文件,如果你加載的路徑用的是絕對路徑的話,這個(gè)加載過程就會失敗,因?yàn)槿绻麊⒂昧薬rar的話,我們資源目錄下所有的源代碼都只是一個(gè)加密的壓縮包,此時(shí)你是不能通過系統(tǒng)的絕對路徑來找到我們要引入的那個(gè)模塊代碼路徑的,當(dāng)然如果你手動(dòng)解壓arar壓縮包的話就能看到所有源代碼的目錄結(jié)構(gòu)了。

4. 外部引用資源(img:src / css:url)的相對路徑和絕對路徑

html demo:

<div className="install-item-image" onClick={() => {showTerminalInfo(item.label)}}>
  <Dimmer active={loading} inverted>
    <Loader size="tiny">{ loadingLable }</Loader>
  </Dimmer>
  <img alt="error" src={item.url} />
</div>

css demo:

.router-left-background {
  background-image: url(~resources/public/gohome.jpg); /* The image used */
  background-color: #f6f6f6; /* Used if the image is unavailable */
  background-position: center; /* Center the image */
  background-repeat: no-repeat; /* Do not repeat the image */
  background-size: cover; /* cover size */
}

如果我們正確地通過webpack打包了前端界面的代碼,在dist目錄下生成了正確的資源目錄結(jié)構(gòu),然后嘗試使用electron index.js命令來模擬生產(chǎn)環(huán)境下應(yīng)用的運(yùn)行(使用file協(xié)議加載dist目錄下的資源),發(fā)現(xiàn)代碼中所有的引用資源請求都會失敗。這是因?yàn)閑lectron在生產(chǎn)環(huán)境下是使用file協(xié)議來加載文件的,首先我們在在執(zhí)行了electron index.js命令后,electron窗口會按照我們定義的路徑結(jié)構(gòu)去查找index.html文件,然后加載主窗口,這個(gè)過程沒有問題,如下:

window.loadURL(url.format({
  pathname: path.resolve(__dirname, 'dist', 'index.html'),
  protocol: 'file:',
  slashes: true,
}));

然后在這個(gè)index.html中的css圖片請求和react組件的圖片請求就是問題之處了,因?yàn)槲覀儧]有指定當(dāng)前工作目錄,file協(xié)議加載文件時(shí)就會直接從系統(tǒng)根目錄開始根據(jù)資源目錄結(jié)構(gòu)查找了,實(shí)際上,我們的所有資源都是在dist文件夾下的,而不是系統(tǒng)根目錄/,解決辦法是在我們的index.html文件里面指定一個(gè)base標(biāo)簽,指明當(dāng)前工作目錄就行了,如下:

<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>electronux</title>
  <link rel="stylesheet" href="style.scss.css">
  <link rel="stylesheet" href="style.css">
  <base href="./">
</head>
<body>
  <div id="root"></div>
  <script src="bundle.js"></script>
</body>
</html>
5. 使用node.js對shell腳本賦予可執(zhí)行權(quán)限

node.js的fs模塊可以為文件賦予可執(zhí)行權(quán)限,并且fs.chmod命令不用額外申請權(quán)限,估計(jì)是如果當(dāng)前用戶可以以root權(quán)限運(yùn)行文件的話,node會自動(dòng)為你獲取權(quán)限。

  • 定義fsChmod模塊遞歸為一個(gè)目錄內(nèi)的所有文件授予權(quán)限:
const fs = require('fs');
const path = require('path');

function chmod(target, opstr) {
  if (fs.statSync(target).isDirectory()) {
    const files = fs.readdirSync(target);
    if (files.length) {
      files.forEach((file) => {
        chmod(path.join(target, file), opstr);
      });
    }
  } else {
    fs.chmodSync(target, opstr);
  }
}
function fsChmod(dir, opstr) {
  chmod(dir, opstr);
}

module.exports = fsChmod;

  • 在fsChmod同級目錄下定義shell授權(quán)模塊
    這樣子的話會避開絕對路徑查找的問題
const path = require('path');
const fsChmod = require('./fs-chmod');

function fsChmodShell() {
  fsChmod(path.join(__dirname, '../shell'), 0o711);
}

module.exports = fsChmodShell;

  • 項(xiàng)目index.js中引入執(zhí)行
    注意:請盡量不要在項(xiàng)目的index.js文件中進(jìn)行運(yùn)行時(shí)絕對路徑查詢(使用path.resolve),因?yàn)樵陂_發(fā)環(huán)境下我們的代碼目錄結(jié)構(gòu)和electron-builder打包后生產(chǎn)環(huán)境下的的應(yīng)用代碼結(jié)構(gòu)是不一樣的,比如開發(fā)環(huán)境下,index.js文件位于項(xiàng)目根目錄/下,shell文件夾(存放shell scripts)的路徑是/app/service/shell,經(jīng)過electron-builder打包后,index.js(實(shí)際上被編譯成了一個(gè)可執(zhí)行文件 )仍然位于根目錄/下,但是shell文件夾位置卻變成了/resources/app/app/shell,這樣子如果在index.js文件中對shell文件夾進(jìn)行絕對路徑查詢的話就會發(fā)生嚴(yán)重錯(cuò)誤。electron-builder打包后的源代碼會被放到資源目錄/resources/app下,位于資源目錄下的代碼是可以進(jìn)行運(yùn)行時(shí)絕對路徑查詢(前提是沒有開啟arar源代碼加密)和相對路徑查詢的。
const fsChmodShell = require('./app/services/middleware/fs-chmod-shell.js');
fsChmodShell();
感謝閱讀,如有錯(cuò)誤,還請指正:- )
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容