自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何在 Npm 上發(fā)布二進(jìn)制文件?

開發(fā) 前端
作為一個(gè)cli工具,我們的f_cli需要發(fā)配給團(tuán)隊(duì)伙伴使用。此時(shí)就會(huì)出現(xiàn)一個(gè)問題,團(tuán)隊(duì)伙伴的開發(fā)環(huán)境(處理器架構(gòu)/操作系統(tǒng))可能和我們本機(jī)不一樣,所以我們需要將Rust編譯成適配不同的處理器架構(gòu)和操作系統(tǒng)。

??????號(hào)外,號(hào)外。我們的f_cli現(xiàn)在有了npm版本了。有兩種主流的方式來(lái)訪問。

  1. 全局安裝

npm i -g f_cli_f

f_cli_f create 你的項(xiàng)目名稱

  1. npx 操作
  2. npx f_cli_f create 你的項(xiàng)目名稱

隨意選中任意一個(gè)方式,不出意外的話,就在指定的文件路徑下,生成了一個(gè)功能完備的前端項(xiàng)目。

前言

我們主要的精力放在如何配置一個(gè)「功能全備」的前端項(xiàng)目。

然后,有些同學(xué)說(shuō),既然cli都有了,但是下載二進(jìn)制文件很麻煩。最好是將f_cli發(fā)布到npm上。畢竟,在前端開發(fā)中,npm大家都熟悉。

所以,今天我們就來(lái)講講「如何將二進(jìn)制文件發(fā)布到npm」。

好了,天不早了,干點(diǎn)正事哇。

我們能所學(xué)到的知識(shí)點(diǎn)

  1. Rust項(xiàng)目交叉編譯
  2. 構(gòu)建&發(fā)布目標(biāo)npm項(xiàng)目
  3. 構(gòu)建&發(fā)布主包
  4. 本地應(yīng)用

1. Rust項(xiàng)目交叉編譯

要將源代碼編譯到與本地平臺(tái)不同的平臺(tái)上,需要指定一個(gè)目標(biāo)(target)。這將告訴編譯器應(yīng)該為哪個(gè)平臺(tái)編譯代碼。

確定target

作為一個(gè)cli工具,我們的f_cli需要發(fā)配給團(tuán)隊(duì)伙伴使用。此時(shí)就會(huì)出現(xiàn)一個(gè)問題,團(tuán)隊(duì)伙伴的開發(fā)環(huán)境(處理器架構(gòu)/操作系統(tǒng))可能和我們本機(jī)不一樣,所以我們需要將Rust編譯成適配不同的處理器架構(gòu)和操作系統(tǒng)。

以下是我們工作中比較常見的開發(fā)環(huán)境。

  • Darwin(arm)
  • Darwin(arm64)
  • Darwin(x64)
  • Linux (arm)
  • Windows (i686)
  • Windows (x64)

針對(duì)f_cli我們只兼容比較場(chǎng)景的開發(fā)環(huán)境。(后期有需要會(huì)兼容更多版本)

  • Darwin(arm64) - MacOS的M1版本
  • Darwin(x64) - MacOS的Intel版本
  • Windows (x64) - Windows

安裝指定target

我們要想將Rust項(xiàng)目編譯成指定的目標(biāo)二進(jìn)制,我們可以在cargo build時(shí),使用--target xxx參數(shù)來(lái)指定目標(biāo)環(huán)境。

還記得rustup嗎?我們?cè)赗ust環(huán)境配置和入門指南中有過(guò)介紹。

rustup的命令行工具來(lái)完成Rust的下載和安裝,這個(gè)工具被用來(lái)管理不同的Rust發(fā)行版本及其附帶工具鏈。

其實(shí)rustup除了安裝和更新Rust,它還可以查看rust在交叉編譯[1]時(shí),能夠轉(zhuǎn)換的目標(biāo)環(huán)境。

我們可以通過(guò)rustup target list來(lái)查看這些信息。

圖片圖片

上圖是我本機(jī)已經(jīng)安裝的target。(我多加了一個(gè)參數(shù)--installed)

  • aarch64-apple-darwin -支持Mac Arm
  • x86_64-apple-darwin - 支持Mac Intel(也是我本機(jī)環(huán)境)
  • x86_64-pc-windows-gnu - 支持Windows環(huán)境

其中wasm32-unknown-unknown是我們處理Rust轉(zhuǎn)WebAssembly時(shí),才用到。關(guān)于這點(diǎn),可以參考我們之前的文章Rust 編譯為WebAssembly 在前端項(xiàng)目中使用。

既然,目標(biāo)環(huán)境已經(jīng)確定,那我們就需要將目標(biāo)環(huán)境加入到Rust環(huán)境中。

rustup target add xxxx

通過(guò)上述命令,我們就將xxxx的環(huán)境加入到Rust中。除了像上面使用rustup target list --installed來(lái)查看已經(jīng)安裝的目標(biāo)環(huán)境。

我們也可以使用rustup show來(lái)查看本機(jī)的工具環(huán)境。

圖片圖片

執(zhí)行編譯

其實(shí)這步也沒啥可說(shuō)的。要想Rust編譯成目標(biāo)環(huán)境我們僅需在cargo build時(shí),新增target參數(shù)即可。

cargo build --release ----target = xxxx

在執(zhí)行完build后,會(huì)在Rust項(xiàng)目中target目錄下生成對(duì)應(yīng)的編譯結(jié)果。

圖片圖片

由于我本機(jī)屬于x86_64-apple-darwin,所以在build時(shí)可以不加target參數(shù)。

然后我們可以在目標(biāo)目錄中的release中找到f_cli二進(jìn)制文件。

圖片圖片

針對(duì)Windows環(huán)境的特殊處理

在MacOS中將Rust編譯為可以在Windows環(huán)境下執(zhí)行的二進(jìn)制時(shí),需要做額外的處理。

圖片圖片

更多詳情可以參考如何在 Mac 上為 Windows 編譯 Rust 程序[2]

2. 構(gòu)建&發(fā)布目標(biāo)npm項(xiàng)目

我們的目標(biāo)是- 將build后的二進(jìn)制文件放置到npm包中,然后通過(guò)node進(jìn)行下載安裝。

如果將所有平臺(tái)的二進(jìn)制放到一個(gè)npm是極其耗費(fèi)流量的。所以,我們采用的是「按需下載」的方式。

所以,我們就把上一節(jié)中交叉編譯的三個(gè)二進(jìn)制文件「分別發(fā)布」成一個(gè)npm包。

  • f_cli_darwin_arm64
  • f_cli_darwin_x64
  • f_cli_windows_x64

對(duì)于快速構(gòu)建一個(gè)npm目錄我們可以使用npm init然后一路回車。但是,我們不這樣做,我們這里采用手動(dòng)構(gòu)建package.json。然后配置一些參數(shù)即可。關(guān)于package.json中各個(gè)字段的含義,可以參考package.json的字段信息[3]

子包的目錄結(jié)構(gòu)

由于我們子包的作用就是存儲(chǔ)二進(jìn)制文件,所以我們采用最簡(jiǎn)單的目錄結(jié)構(gòu)

由于子包的處理邏輯很類似,我們下文中除了要特殊說(shuō)明,都是按照一個(gè)子包的處理方式來(lái)講解

"f_cli_darwin_arm64"/"f_cli_darwin_x64"
 ├── package.json
 └── bin/
     └── f_cli

"f_cli_windows_x64"
 ├── package.json
 └── bin/
     └── f_cli.exe

bin文件夾中就是存放我們二進(jìn)制源文件的,這里沒啥可說(shuō)的。我們來(lái)簡(jiǎn)單聊聊package.json

package.json

下面的package.json的內(nèi)容是f_cli_darwin_arm64的。其他兩個(gè)子包的信息也是大差不差的。

{
  "name": "f_cli_darwin_arm64",
  "version": "1.0.0",
  "description": "f_cli適配MACOS_ARM64架構(gòu)",
  "keywords": [
    "f_cli",
    "MACOS_ARM64"
  ],
  "author": "",
  "license": "ISC",
  "os": ["darwin"],
  "cpu": ["arm64"]
}

其中有幾個(gè)屬性我們需要額外說(shuō)明一下:

  1. name該字段是我們發(fā)布npm包時(shí),最主要的字段,你可以將起認(rèn)為是數(shù)據(jù)庫(kù)中的主鍵,我們平時(shí)通過(guò)npm install xxx安裝包時(shí),xxx就是此處的name的值

在發(fā)布包之前,我們可以為其指定具有特殊含義的名稱,同時(shí)該名稱需要在npm倉(cāng)庫(kù)中唯一,不然在npm publish時(shí)就會(huì)發(fā)生錯(cuò)誤

同時(shí)該名稱的格式也有要求,它需要符合^(?:(?:@(?:[a-z0-9-*~][a-z0-9-*._~]*)?/[a-z0-9-._~])|[a-z0-9-~])[a-z0-9-._~]*$正則規(guī)則

  1. os:指定模塊將在哪些操作系統(tǒng)上運(yùn)行
  • 該值由node中的process.platform[4]決定,用于獲取操作系統(tǒng)平臺(tái)信息。
  • 值為aix, android, darwin, freebsd, linux, openbsd, sunprocess, win32
  1. cpu:指定代碼只能在某些 CPU 架構(gòu)上運(yùn)行
  • 該值由node中的process.arch[5]決定,用于獲取操作系統(tǒng)平臺(tái)信息。
  • 值為x32, x64, arm, arm64, s390, s390x, mipsel, ia32, mips, ppc, ppc64.

我們后期會(huì)有關(guān)于package.json各個(gè)字段的介紹文章

發(fā)布子包到npm

其實(shí)這步特別簡(jiǎn)單就是兩個(gè)命令

  • npm login
  • npm publish

對(duì)于如何發(fā)布一個(gè)npm包,這里我們就不再贅述。后期如果有需求可以單寫一篇。

通過(guò)上述的操作,我們就把三個(gè)二進(jìn)制文件發(fā)布到npm上了。

圖片圖片

上面還有一個(gè)f_cli_f,別著急,我們馬上會(huì)講到。

3. 構(gòu)建&發(fā)布主包

上面我們通過(guò)各自上傳子包到npm,實(shí)現(xiàn)了資源的分離處理。下面我們就需要通過(guò)一些方式讓主包在被安裝時(shí),能夠自動(dòng)識(shí)別出工作平臺(tái)所需要目標(biāo)并且執(zhí)行對(duì)應(yīng)的下載和安裝任務(wù)。

簡(jiǎn)而言之,我們需要在主包被安裝時(shí),實(shí)現(xiàn)按需下載

npm 按需下載原理

在package.json中有兩種方式可以下載特定于平臺(tái)的二進(jìn)制文件,而無(wú)需下載所有二進(jìn)制文件。

optionalDependencies

所有常用的 JavaScript 包管理器都支持 package.json 中的 optionalDependencies[6] 字段。包管理器通常會(huì)安裝 optionalDependencies 中列出的所有軟件包,但他們可能會(huì)根據(jù)某些條件選擇不安裝。

其中一個(gè)標(biāo)準(zhǔn)就是依賴項(xiàng) package.json 文件中的 os 和 cpu 字段。(我們?cè)谔幚碜影鼤r(shí)就已經(jīng)把這些值賦值了)

「只有當(dāng)這些字段的值與當(dāng)前系統(tǒng)的操作系統(tǒng)和架構(gòu)相匹配時(shí),才會(huì)安裝依賴包」。這意味著我們可以發(fā)布單獨(dú)的軟件包,每個(gè)軟件包只包含一個(gè)特定于平臺(tái)的二進(jìn)制文件,但其中的os和cpu字段指明了這些軟件包適用的體系結(jié)構(gòu),軟件包管理器將自動(dòng)安裝正確的軟件包。

postinstall 腳本

如果在 package.json 中包含一個(gè)名為 postinstall 的腳本,則該腳本將在包安裝后「立即執(zhí)行」,即使它是作為安裝包安裝的一種依賴。(在前端項(xiàng)目里都有啥?,我們講過(guò)prepare,其實(shí)他們的作用是類似的)

我們可以使用 postinstall 腳本下載當(dāng)前平臺(tái)的二進(jìn)制文件并將其存儲(chǔ)在系統(tǒng)上的某個(gè)位置。其實(shí)我們可以把這個(gè)包的位置存放到任何你信得過(guò)的地方,此處我們?yōu)榱朔奖銓⒍M(jìn)制文件都放置到了npm倉(cāng)庫(kù)了。

最優(yōu)解

這兩種方法都有缺點(diǎn),可能不適用于所有設(shè)置。

  • 如果禁用optionalDependencies可能會(huì)遇到問題(例如,通過(guò)yarn的--ignore-optional標(biāo)志)。
  • postinstall 腳本也可以被禁用,并且可能會(huì)出現(xiàn)更多問題,因?yàn)橥ǔ=ㄗh禁用它們,因?yàn)樗鼈內(nèi)菀资艿焦簟?/li>

為了最大限度地提高成功的可能性,我們將兩種方式都融合進(jìn)主包中。

目錄結(jié)構(gòu)

其實(shí)主包的目錄結(jié)構(gòu)也很簡(jiǎn)單。和子包類似,有package.json/bin/二進(jìn)制源文件

f_cli
 ├── install.js
 ├── package.json
 └── bin/
     └── f_cli

那么下面我們就依次解釋上面文件的含義。

package.json

{
  "name": "f_cli_f",
  "version": "1.0.3",
  "description": "針對(duì)f_cli的npm 包",
  "scripts": {
    "postinstall": "node ./install.js"
  },
  "bin": {
    "f_cli_f": "bin/cli"
  },
  "optionalDependencies": {
    "f_cli_darwin": "1.0.0",
    "f_cli_linux": "1.0.0",
    "f_cli_win32": "1.0.0"
  }
}

上面出現(xiàn)的scripts.postinstall和optionalDependencies我們?cè)诒竟?jié)剛開始就解釋了。這里就不再啰嗦。

在這里我們來(lái)講講bin字段。

bin

bin 字段允許將包中的特定文件鏈接到全局的可執(zhí)行路徑,使其成為全局命令,方便用戶在命令行中直接調(diào)用。

bin 是 package.json 文件中的一個(gè)字段,用于定義「將包安裝為全局命令時(shí)的可執(zhí)行文件」。

bin 字段是一個(gè)對(duì)象,其中鍵是要?jiǎng)?chuàng)建的全局命令的名稱,值是要執(zhí)行的本地文件的路徑。

當(dāng)用戶全局安裝該包時(shí),bin 字段允許將指定的本地文件鏈接到全局的可執(zhí)行路徑,使用戶可以在命令行中直接運(yùn)行該文件。

像上文中bin 字段為 { "f_cli_f": "bin/cli" },那么在全局安裝該包后,用戶可以直接在命令行中運(yùn)行 f_cli_f,實(shí)際上會(huì)執(zhí)行 bin/cli 文件。

# 方式1: 全局按照
$ npm i -g f_cli_f
$ f_cli_f create xxx

# 方式2:包管理器
$ npx f_cli_f

install.js

// 引入必要的Node.js模塊
const fs = require('fs'); // 文件系統(tǒng)模塊
const path = require('path'); // 路徑模塊
const zlib = require('zlib'); // 壓縮模塊
const https = require('https'); // HTTPS模塊


// 所有平臺(tái)和二進(jìn)制分發(fā)包的查找表
const BINARY_DISTRIBUTION_PACKAGES = {
  'darwin-x64': 'f_cli_darwin_x64',
  'darwin-arm64': 'f_cli_darwin_arm64',
  'win32-x64': 'f_cli_windows_x64',
}

// 調(diào)整你想要安裝的版本。也可以將其設(shè)置為動(dòng)態(tài)的。
const BINARY_DISTRIBUTION_VERSION = '1.0.0';

// Windows平臺(tái)的二進(jìn)制文件以.exe結(jié)尾,因此需要特殊處理。
const binaryName = process.platform === 'win32' ? 'f_cli.exe' : 'f_cli';

// 確定當(dāng)前平臺(tái)的包名
const platformSpecificPackageName =
  BINARY_DISTRIBUTION_PACKAGES[`${process.platform}-${process.arch}`];

// 計(jì)算我們要生成的備用二進(jìn)制文件的路徑
const fallbackBinaryPath = path.join(__dirname, binaryName);

// 創(chuàng)建HTTP請(qǐng)求的Promise函數(shù)
function makeRequest(url) {
  return new Promise((resolve, reject) => {
    https
      .get(url, (response) => {
        if (response.statusCode >= 200 && response.statusCode < 300) {
          const chunks = [];
          response.on('data', (chunk) => chunks.push(chunk));
          response.on('end', () => {
            resolve(Buffer.concat(chunks));
          });
        } else if (
          response.statusCode >= 300 &&
          response.statusCode < 400 &&
          response.headers.location
        ) {
          // 跟隨重定向
          makeRequest(response.headers.location).then(resolve, reject);
        } else {
          reject(
            new Error(
              `npm在下載包時(shí)返回狀態(tài)碼 ${response.statusCode}!`
            )
          );
        }
      })
      .on('error', (error) => {
        reject(error);
      });
  });
}

// 從tarball中提取文件的函數(shù)
function extractFileFromTarball(tarballBuffer, filepath) {
  let offset = 0
  while (offset < tarballBuffer.length) {
    const header = tarballBuffer.subarray(offset, offset + 512)
    offset += 512

    const fileName = header.toString('utf-8', 0, 100).replace(/\0.*/g, '')
    const fileSize = parseInt(header.toString('utf-8', 124, 136).replace(/\0.*/g, ''), 8)

    if (fileName === filepath) {
      return tarballBuffer.subarray(offset, offset + fileSize)
    }

    // 將offset固定到512的上限倍數(shù)
    offset = (offset + fileSize + 511) & ~511
  }
}

// 從Npm下載二進(jìn)制文件的異步函數(shù)
async function downloadBinaryFromNpm() {
  // 下載正確二進(jìn)制分發(fā)包的tarball
  const tarballDownloadBuffer = await makeRequest(
    `https://registry.npmjs.org/${platformSpecificPackageName}/-/${platformSpecificPackageName}-${BINARY_DISTRIBUTION_VERSION}.tgz`
  )

  const tarballBuffer = zlib.unzipSync(tarballDownloadBuffer)

  // 從軟件包中提取二進(jìn)制文件并寫入磁盤
  fs.writeFileSync(
    fallbackBinaryPath,
    extractFileFromTarball(tarballBuffer, `package/bin/${binaryName}`),
    { mode: 0o755 } // 使二進(jìn)制文件可執(zhí)行
  )
}

// 檢查是否已安裝平臺(tái)特定的軟件包
function isPlatformSpecificPackageInstalled() {
  try {
    // 如果optionalDependency未安裝,解析將失敗
    require.resolve(`${platformSpecificPackageName}/bin/${binaryName}`)
    return true
  } catch (e) {
    return false
  }
}

// 如果不支持當(dāng)前平臺(tái),拋出錯(cuò)誤
if (!platformSpecificPackageName) {
  throw new Error('不支持的平臺(tái)!')
}

// 如果通過(guò)optionalDependencies已安裝二進(jìn)制文件,則跳過(guò)下載
if (!isPlatformSpecificPackageInstalled()) {
  console.log('未找到平臺(tái)特定的軟件包。將手動(dòng)下載二進(jìn)制文件。')
  downloadBinaryFromNpm()
} else {
  console.log(
    '平臺(tái)特定的軟件包已安裝。將回退到手動(dòng)下載二進(jìn)制文件。'
  )
}

?

這段代碼的作用是根據(jù)當(dāng)前的操作系統(tǒng)和架構(gòu),從 Npm 下載特定平臺(tái)的二進(jìn)制文件,并將其寫入磁盤。

?

大部分的代碼都有注釋,具體的功能也一目了然,這里就不再過(guò)多解釋。我們挑幾個(gè)比較重要的點(diǎn)來(lái)說(shuō)明一下。

  • BINARY_DISTRIBUTION_PACKAGES: 用于存儲(chǔ)所有平臺(tái)和二進(jìn)制包的信息
  • 使用process.platform和process.arch用于確定符合當(dāng)前工作環(huán)境的二進(jìn)制包名稱
  • isPlatformSpecificPackageInstalled方法用于判斷是否根據(jù)optionalDependency安裝了指定的包,如果因?yàn)樘厥庠驔]安裝成功,我們就需要執(zhí)行手動(dòng)下載操作(downloadBinaryFromNpm)

如果上述操作一切順利的話,我們就會(huì)在主包的根目錄下,按照了我們的二進(jìn)制文件。

bin/cli

#!/usr/bin/env node

const path = require("path");
const childProcess = require("child_process");

// 存儲(chǔ)所有平臺(tái)和二進(jìn)制分發(fā)包的查找表
const BINARY_DISTRIBUTION_PACKAGES = {
  'darwin-x64': 'f_cli_darwin_x64',
  'darwin-arm64': 'f_cli_darwin_arm64',
  'win32-x64': 'f_cli_windows_x64',
};

// Windows平臺(tái)的二進(jìn)制文件以.exe結(jié)尾,因此需要特殊處理
const binaryName = process.platform === "win32" ? "f_cli.exe" : "f_cli";

// 確定此平臺(tái)的軟件包名稱
const platformSpecificPackageName =
  BINARY_DISTRIBUTION_PACKAGES[`${process.platform}-${process.arch}`]

function getBinaryPath() {
  try {
    // 如果optionalDependency未安裝,解析將失敗
    return require.resolve(`${platformSpecificPackageName}/bin/${binaryName}`);
  } catch (e) {
    // 如果未安裝,返回二進(jìn)制文件的路徑
    return path.join(__dirname, "..", binaryName);
  }
}

// 使用child_process模塊執(zhí)行二進(jìn)制文件并傳遞命令行參數(shù)
childProcess.execFileSync(getBinaryPath(), process.argv.slice(2), {
  stdio: "inherit",
});

上面的具體邏輯和我們install.js是類似的,都是基于process.platform和process.arch確定當(dāng)前工作環(huán)境匹配的二進(jìn)制源文件,并且執(zhí)行下載操作。

就像上面說(shuō)的一樣,bin/cli這個(gè)方式是可以在命令行直接執(zhí)行的。npx f_cli_f create xxx。

有一個(gè)點(diǎn)還是忍不住的想介紹一下

  1. #!/usr/bin/env node 是一個(gè)稱為"shebang"的特殊注釋,通常出現(xiàn)在Unix或類Unix系統(tǒng)中的腳本文件的開頭。

這行代碼告訴操作系統(tǒng)使用/usr/bin/env來(lái)查找node命令,并使用它來(lái)解釋和執(zhí)行該腳本文件。這樣做的好處是,它允許腳本在不同的系統(tǒng)上找到正確的node解釋器,而不需要硬編碼node的路徑。

注意點(diǎn)

像使用bin/cli這種方式在命令行執(zhí)行命令時(shí),有一點(diǎn)需要額外的注意。如果你當(dāng)前工作環(huán)境中只有一個(gè)Node環(huán)境,因?yàn)槲覀僣li中存在文件的寫入操作,此時(shí)在執(zhí)行命令時(shí),會(huì)有一個(gè)寫入操作權(quán)限的錯(cuò)誤警告。

其實(shí)這是一類錯(cuò)誤,也就是npm在執(zhí)行時(shí)候需要sudo的操作權(quán)限。

圖片圖片

在stackoverflow中有很多關(guān)于npmthrowing error without sudo的解決方案[7]

其中一個(gè)高贊回答就是讓我們使用nvm等node版本管理工具。在之前我們寫過(guò)文章如何更優(yōu)雅的使用node版本管理工具 - fnm 高階版的nvm。

發(fā)布主包到npm

其實(shí)這步特別簡(jiǎn)單就是兩個(gè)命令

  • npm login
  • npm publish

這樣我們所有的資源都上傳到npm了。然后,我們就可以通過(guò)我們熟悉的包管理器yarn/npm來(lái)安裝了。

額外說(shuō)明

在上面的處理邏輯中我們只依據(jù)process.platfrom和process.arch做了最簡(jiǎn)單的環(huán)境適配,其實(shí)這里還可以有很多的分支處理。

如果大家看過(guò)oxlint-npm的源碼[8]的話,它就對(duì)環(huán)境有很多的處理。

4. 本地應(yīng)用

在npm中我們已經(jīng)看到我們的cli已經(jīng)上傳成功了。

接下來(lái),我們就可以利用yarn/npm等執(zhí)行下載操作了。

全局安裝

npm i -g f_cli_f

在控制臺(tái)中執(zhí)行上述操作,然后我們就將f_cli_f安裝到npm全局環(huán)境了。

我們可以通過(guò)npm list -g來(lái)查看是否在全局按照成功。

然后我們就可以下面的命令在本地使用我們的cli創(chuàng)建項(xiàng)目了。

f_cli_f create project

npx

除了全局安裝,我們也可以使用npx f_cli_f create project進(jìn)行項(xiàng)目的初始化。

責(zé)任編輯:武曉燕 來(lái)源: 前端柒八九
相關(guān)推薦

2022-10-31 08:02:42

二進(jìn)制計(jì)算乘法

2009-12-16 10:49:42

Ruby操作二進(jìn)制文件

2020-05-06 09:51:37

二進(jìn)制Linux命令行工具

2009-08-12 18:06:53

C#讀取二進(jìn)制文件

2022-11-18 10:17:01

2013-04-28 15:37:35

JBoss

2009-12-10 09:24:50

PHP函數(shù)fwrite

2023-09-18 23:50:25

二進(jìn)制文件裁剪Layout

2021-11-10 09:15:00

CPU01 二進(jìn)制Linux

2022-08-14 08:29:21

npmNode

2020-05-22 18:00:26

Go二進(jìn)制文件編程語(yǔ)言

2023-12-26 15:10:00

處理二進(jìn)制文件

2009-11-02 11:27:42

VB.NET二進(jìn)制文件

2018-10-22 14:37:16

二進(jìn)制數(shù)據(jù)存儲(chǔ)

2009-02-27 09:37:33

Google二進(jìn)制代碼

2017-04-11 10:48:53

JS二進(jìn)制

2022-07-26 13:00:01

安全符號(hào)源代碼

2023-03-20 08:24:31

工具GoReleaser

2010-10-13 15:45:23

MySQL二進(jìn)制日志

2010-06-09 13:02:29

MySQL啟用二進(jìn)制日
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)