基於 Lerna 管理 packages 的 Monorepo 項目最佳實踐

来源:https://www.cnblogs.com/vivotech/archive/2019/08/12/11316961.html
-Advertisement-
Play Games

對於維護過多個package的同學來說,都會遇到一個選擇題,這些package是放在一個倉庫里維護還是放在多個倉庫里單獨維護,本文通過一個示例講述瞭如何基於Lerna管理多個package,並和其它工具整合,打造高效、完美的工作流,最終形成一個最佳實踐 ...


本文首發於 vivo互聯網技術 微信公眾號 https://mp.weixin.qq.com/s/NlOn7er0ixY1HO40dq5Gag
作者:孔垂亮

目錄

一、背景
二、Monorepo vs Multirepo
三、Lerna
1、Lerna 是什麼
2、開始使用
(1)安裝
(2)項目構建
四、Lerna的最佳實踐
1、優雅的提交
2、自動生成日誌
3、編譯、壓縮、調試
五、結語
六、參考文獻

 

對於維護過多個package的同學來說,都會遇到一個選擇題,這些package是放在一個倉庫里維護還是放在多個倉庫里單獨維護,本文通過一個示例講述瞭如何基於Lerna管理多個package,並和其它工具整合,打造高效、完美的工作流,最終形成一個最佳實踐

背景

最近在工作中接觸到一個項目,這個項目是維護一套 CLI,發到 npm 上供開發者使用。先看一張圖:

項目倉庫中的根目錄上就三個子模塊的文件夾,分別對應三個 package,在熟悉了構建和發佈流程後,有點傻了。工作流程如圖中所示:

  1. 使用webpack、babel和uglifyjs把 pkg-a 的 src 編譯到 dist

  2. 使用webpack、babel和uglifyjs把 pkg-b 的 src 編譯到 dist

  3. 使用webpack、babel和uglifyjs把 pkg-main 的 src 編譯到 dist

  4. 最後使用拷貝文件的方式,把pkg-main、pkg-a、pkg-b中編譯後的文件組裝到 pkg-npm 中,最終用於發佈到 npm 上去。

痛點

  1. **不好調試。**因為最終的包是通過文件拷貝的方式組裝到一起的,並且都是壓縮過的,無法組建一個自上到下的調試流程(實際工作中只能加log,然後重新把包編譯組裝一遍看效果)

  2. **包的依賴關係不清晰。**pkg-a、pkg-b索性沒有版本管理,更像是源碼級別的,但邏輯又比較獨立。pkg-main中的package.json最終會拷貝到 pkg-npm 中,但又依賴pkg-a、pkg-b中的某些包,所以要把pkg-a、pkg-b中的依賴合併到pkg-main中。pkg-main和pkg-npm的package.json耦合在一起,導致一些本來是工程的開發依賴也會發佈到 npm 上去,變成pkg-npm 的依賴包。

  3. **依賴的包冗餘。**可以看到,pkg-a、pkg-b、pkg-main要分別編譯,都依賴了babel、webpack等,要分別 cd 到各個目錄安裝依賴。

  4. 發佈需要手動修改版本號。 因為最終只發佈了一個包,但實際邏輯要求這個包即要全局安裝又要本地安裝,業務沒有拆開,導致要安裝兩遍。耦合一起,即便使用 npm link 也會導致調試困難,

  5. 發版沒有 CHANGELOG.md 因為pkg-a、pkg-b都沒有真正管理版本,所以也沒有完善的CHANGELOG來記錄自上個版本發佈已來的變動。

整個項目像是一個沒有被管理起來的 Monorepo。那什麼又是 Monorepo 呢?

Monorepo vs Multirepo

Monorepo 的全稱是 monolithic repository,即單體式倉庫,與之對應的是 Multirepo(multiple repository),這裡的“單”和“多”是指每個倉庫中所管理的模塊數量。

Multirepo 是比較傳統的做法,即每一個 package 都單獨用一個倉庫來進行管理。例如:Rollup, ...

Monorep 是把所有相關的 package 都放在一個倉庫里進行管理,每個 package 獨立發佈。例如:React, Angular, Babel, Jest, Umijs, Vue ...

一圖勝千言:

當然到底哪一種管理方式更好,仁者見仁,智者見智。前者允許多元化發展(各項目可以有自己的構建工具、依賴管理策略、單元測試方法),後者希望集中管理,減少項目間的差異帶來的溝通成本。

雖然拆分子倉庫、拆分子 npm 包是進行項目隔離的天然方案,但當倉庫內容出現關聯時,沒有任何一種調試方式比源碼放在一起更高效。

結合我們項目的實際場景和業務需要,天然的 MonoRepo ! 因為工程化的最終目的是讓業務開發可以 100% 聚焦在業務邏輯上,那麼這不僅僅是腳手架、框架需要從自動化、設計上解決的問題,這涉及到倉庫管理的設計。

一個理想的開發環境可以抽象成這樣:

“只關心業務代碼,可以直接跨業務復用而不關心復用方式,調試時所有代碼都在源碼中。”

在前端開發環境中,多 Git Repo,多 npm 則是這個理想的阻力,它們導致復用要關心版本號,調試需要 npm link。而這些是 MonoRepo 最大的優勢。

上圖中提到的利用相關工具就是今天的主角 Lerna ! Lerna是業界知名度最高的 Monorepo 管理工具,功能完整。

Lerna

一、Lerna 是什麼

A tool for managing JavaScript projects with multiple packages.

Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm.

Lerna 是一個管理多個 npm 模塊的工具,是 Babel 自己用來維護自己的 Monorepo 並開源出的一個項目。優化維護多包的工作流,解決多個包互相依賴,且發佈需要手動維護多個包的問題。

Lerna 現在已經被很多著名的項目組織使用,如:Babel, React, Vue, Angular, Ember, Meteor, Jest 。

一個基本的 Lerna 管理的倉庫結構如下:

安裝

推薦全局安裝,因為會經常用到 lerna 命令

npm i -g lerna

項目構建

1.初始化

lerna init

init 命令詳情 請參考 lerna init

其中 package.json & lerna.json 如下:

// package.json
{
  "name": "root",
  "private": true, // 私有的,不會被髮布,是管理整個項目,與要發佈到npm的解耦
  "devDependencies": {
    "lerna": "^3.15.0"
  }
}
 
// lerna.json
{
  "packages": [
    "packages/*"
  ],
  "version": "0.0.0"
}

2.增加兩個 packages

lerna create @mo-demo/cli
lerna create @mo-demo/cli-shared-utils

create 命令詳情 請參考 lerna create

3.分別給相應的 package 增加依賴模塊

lerna add chalk                                           // 為所有 package 增加 chalk 模塊
lerna add semver --scope @mo-demo/cli-shared-utils        // 為 @mo-demo/cli-shared-utils 增加 semver 模塊
lerna add @mo-demo/cli-shared-utils --scope @mo-demo/cli  // 增加內部模塊之間的依賴

add 命令詳情 請參考 lerna add

4.發佈

lerna publish

publish 命令詳情 請參考 lerna publish

如下是發佈的情況,lerna會讓你選擇要發佈的版本號,我發了@0.0.1-alpha.0 的版本。

發佈 npm 包需要登陸 npm 賬號

5.安裝依賴包 & 清理依賴包

上述1-4步已經包含了 Lerna 整個生命周期的過程了,但當我們維護這個項目時,新拉下來倉庫的代碼後,需要為各個 package 安裝依賴包。

我們在第4步 lerna add 時也發現了,為某個 package 安裝的包被放到了這個 package 目錄下的 node_modules 目錄下。這樣對於多個 package 都依賴的包,會被多個 package 安裝多次,並且每個 package 下都維護 node_modules ,也不清爽。於是我們使用 --hoist 來把每個 package 下的依賴包都提升到工程根目錄,來降低安裝以及管理的成本

lerna bootstrap --hoist

bootstrap 命令詳情 請參考 lerna bootstrap

為了省去每次都輸入 --hoist 參數的麻煩,可以在 lerna.json 配置:

{
  "packages": [
    "packages/*"
  ],
  "command": {
    "bootstrap": {
      "hoist": true
    }
  },
  "version": "0.0.1-alpha.0"
}

配置好後,對於之前依賴包已經被安裝到各個 package 下的情況,我們只需要清理一下安裝的依賴即可:

lerna clean

然後執行 lerna bootstrap 即可看到 package 的依賴都被安裝到根目錄下的 node_modules 中了。

Lerna的最佳實踐

lerna不負責構建,測試等任務,它提出了一種集中管理package的目錄模式,提供了一套自動化管理程式,讓開發者不必再深耕到具體的組件里維護內容,在項目根目錄就可以全局掌控,基於 npm scripts,使用者可以很好地完成組件構建,代碼格式化等操作。接下來我們就來看看,如果基於 Lerna,並結合其它工具來搭建 Monorepo 項目的最佳實踐。

一、優雅的提交

1.commitizen && cz-lerna-changelog

commitizen 是用來格式化 git commit message 的工具,它提供了一種問詢式的方式去獲取所需的提交信息。

cz-lerna-changelog 是專門為 Lerna 項目量身定製的提交規範,在問詢的過程,會有類似影響哪些 package 的選擇。如下:

我們使用 commitizen 和 cz-lerna-changelog 來規範提交,為後面自動生成日誌作好準備。

因為這是整個工程的開發依賴,所以在根目錄安裝:

npm i -D commitizen
npm i -D cz-lerna-changelog

安裝完成後,在 package.json 中增加 config 欄位,把 cz-lerna-changelog 配置給 commitizen。同時因為commitizen不是全局安全的,所以需要添加 scripts 腳本來執行 git-cz

{
  "name": "root",
  "private": true,
  "scripts": {
    "c": "git-cz"
  },
  "config": {
    "commitizen": {
      "path": "./node_modules/cz-lerna-changelog"
    }
  },
  "devDependencies": {
    "commitizen": "^3.1.1",
    "cz-lerna-changelog": "^2.0.2",
    "lerna": "^3.15.0"
  }
}

之後在常規的開發中就可以使用 npm run c 來根據提示一步一步輸入,來完成代碼的提交。

2.commitlint && husky

上面我們使用了 commitizen 來規範提交,但這個要靠開發自覺使用 npm run c 。萬一忘記了,或者直接使用 git commit 提交怎麼辦?答案就是在提交時對提交信息進行校驗,如果不符合要求就不讓提交,並提示。校驗的工作由 commitlint 來完成,校驗的時機則由 husky 來指定。husky 繼承了 Git 下所有的鉤子,在觸發鉤子的時候,husky 可以阻止不合法的 commit,push 等等。

// 安裝 commitlint 以及要遵守的規範
npm i -D @commitlint/cli @commitlint/config-conventional
// 在工程根目錄為 commitlint 增加配置文件 commitlint.config.js 為commitlint 指定相應的規範
module.exports = { extends: ['@commitlint/config-conventional'] }
// 安裝 husky
npm i -D husky
// 在 package.json 中增加如下配置
"husky": {
  "hooks": {
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
  }
}

"commit-msg"是git提交時校驗提交信息的鉤子,當觸發時便會使用 commitlit 來校驗。安裝配置完成後,想通過 git commit 或者其它第三方工具提交時,只要提交信息不符合規範就無法提交。從而約束開發者使用 npm run c 來提交。

3.standardjs && lint-staged

除了規範提交信息,代碼本身肯定也少了靠規範來統一風格。

standardjs就是完整的一套 JavaScript 代碼規範,自帶 linter & 代碼自動修正。它無需配置,自動格式化代碼並修正,提前發現風格以及程式問題。

lint-staged staged 是 Git 里的概念,表示暫存區,lint-staged 表示只檢查並矯正暫存區中的文件。一來提高校驗效率,二來可以為老的項目帶去巨大的方便。

// 安裝
npm i -D standard lint-staged
// package.json
{
  "name": "root",
  "private": true,
  "scripts": {
    "c": "git-cz"
  },
  "config": {
    "commitizen": {
      "path": "./node_modules/cz-lerna-changelog"
    }
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },
  "lint-staged": {
    "*.js": [
      "standard --fix",
      "git add"
    ]
  },
  "devDependencies": {
    "@commitlint/cli": "^8.1.0",
    "@commitlint/config-conventional": "^8.1.0",
    "commitizen": "^3.1.1",
    "cz-lerna-changelog": "^2.0.2",
    "husky": "^3.0.0",
    "lerna": "^3.15.0",
    "lint-staged": "^9.2.0",
    "standard": "^13.0.2"
  }
}

安裝完成後,在 package.json 增加 lint-staged 配置,如上所示表示對暫存區中的 js 文件執行 standard --fix 校驗並自動修複。那什麼時候去校驗呢,就又用到了上面安裝的 husky ,husky的配置中增加'pre-commit'的鉤子用來執行 lint-staged 的校驗操作,如上所示。

此時提交 js 文件時,便會自動修正並校驗錯誤。即保證了代碼風格統一,又能提高代碼質量。

二、自動生成日誌

有了之前的規範提交,自動生成日誌便水到渠成了。再詳細看下 lerna publish 時做了哪些事情:

1.調用 lerna version

  • 找出從上一個版本發佈以來有過變更的 package

  • 提示開發者確定要發佈的版本號

  • 將所有更新過的的 package 中的package.json的version欄位更新

  • 將依賴更新過的 package 的 包中的依賴版本號更新

  • 更新 lerna.json 中的 version 欄位

  • 提交上述修改,並打一個 tag

  • 推送到 git 倉庫

2.使用 npm publish 將新版本推送到 npm

CHANGELOG 很明顯是和 version 一一對應的,所以需要在 lerna version 中想辦法,查看 lerna version 命令的詳細說明後,會看到一個配置參數 --conventional-commits。沒錯,只要我們按規範提交後,在 lerna version 的過程中會便會自動生成當前這個版本的 CHANGELOG。為了方便,不用每次輸入參數,可以配置在 lerna.json中,如下:

{
  "packages": [
    "packages/*"
  ],
  "command": {
    "bootstrap": {
      "hoist": true
    },
    "version": {
      "conventionalCommits": true
    }
  },
  "ignoreChanges": [
    "**/*.md"
  ],
  "version": "0.0.1-alpha.1"
}

lerna version 會檢測從上一個版本發佈以來的變動,但有一些文件的提交,我們不希望觸發版本的變動,譬如 .md 文件的修改,並沒有實際引起 package 邏輯的變化,不應該觸發版本的變更。可以通過 ignoreChanges 配置排除。如上。

實際 lerna version 很少直接使用,因為它包含在 lerna publish 中了,直接使用 lerna publish就好了。

Lerna 在管理 package 的版本號上,提供了兩種模式供選擇 Fixed or Independent。預設是 Fixed,更多細節,以及 Lerna 的更多玩法,請參考官網文檔:

https://github.com/lerna/lerna/blob/master/README.md

三、編譯、壓縮、調試

採用 Monorepo 結構的項目,各個 package 的結構最好保持統一。

根據目前的項目狀況,設計如下:

  1. 各 package 入口統一為 index.js

  2. 各 package 源碼入口統一為 src/index.js

  3. 各 package 編譯入口統一為 dist/index.js

  4. 各 package 統一使用 ES6 語法、使用 Babel 編譯、壓縮並輸出到 dist

  5. 各 package 發佈時只發佈 dist 目錄,不發佈 src 目錄

  6. 各 package 註入 LOCAL_DEBUG 環境變數, 在index.js 中區分是調試還是發佈環境,調試環境 ruquire(./src/index.js) 保證所有源碼可調試。發佈環境 ruquire(./dist/index.js) 保證所有源碼不被髮布。

因為 dist 是 Babel 編譯後的目錄,我們在搜索時不希望搜索它的內容,所以在工程的設置中把 dist 目錄排除在搜索的範圍之外。

接下來,我們按上面的規範,搭建 package 的結構。

首先安裝依賴

npm i -D @babel/cli @babel/core @babel/preset-env  // 使用 Babel 必備 詳見官網用法
npm i -D @babel/node                               // 用於調試 因為用了 import&export 等 ES6 的語法
npm i -D babel-preset-minify                       // 用於壓縮代碼

由於各 package 的結構統一,所以類似 Babel 這樣的工具,只在根目錄安裝就好了,不需要在各 package 中安裝,簡直是清爽的要死了。

增加 Babel 配置

// 根目錄新建 babel.config.js
module.exports = function (api) {
  api.cache(true)
 
  const presets = [
    [
      '@babel/env',
      {
        targets: {
          node: '8.9'
        }
      }
    ]
  ]
 
  // 非本地調試模式才壓縮代碼,不然調試看不到實際變數名
  if (!process.env['LOCAL_DEBUG']) {
    presets.push([
      'minify'
    ])
  }
 
  const plugins = []
 
  return {
    presets,
    plugins,
    ignore: ['node_modules']
  }
}

修改各 package 的代碼

// @mo-demo/cli/index.js
if (process.env.LOCAL_DEBUG) {
  require('./src/index')                        // 如果是調試模式,載入src中的源碼
} else {
  require('./dist/index')                       // dist會發到npm
}
 
// @mo-demo/cli/src/index.js
import { log } from '@mo-demo/cli-shared-utils'  // 從 utils 模塊引入依賴並使用 log 函數
log('cli/index.js as cli entry exec!')
 
// @mo-demo/cli/package.json
{
  "main": "index.js",
  "files": [
    "dist"                                       // 發佈 dist
  ]
}
 
 
// @mo-demo/cli-shared-utils/index.js
if (process.env.LOCAL_DEBUG) {
  module.exports = require('./src/index')        // 如果是調試模式,載入src中的源碼
} else {
  module.exports = require('./dist/index')       // dist會發到npm
}
 
// @mo-demo/cli-shared-utils/src/index.js
const log = function (str) {
  console.log(str)
}
export {                                         //導出 log 介面
  log
}
 
// @mo-demo/cli-shared-utils/package.json
{
  "main": "index.js",
  "files": [
    "dist"
  ]
}

修改發佈的腳本

npm run b 用來對各 pacakge 執行 babel 的編譯,從 src 目錄輸出出 dist 目錄,使用根目錄的配置文件 babel.config.js。

npm run p 用來取代 lerna publish,在 publish 前先執行 npm run b來編譯。

其它常用的 lerna 命令也添加到 scripts 中來,方便使用。

// 工程根目錄 package.json
 "scripts": {
   "c": "git-cz",
   "i": "lerna bootstrap",
   "u": "lerna clean",
   "p": "npm run b && lerna publish",
   "b": "lerna exec -- babel src -d dist --config-file ../../babel.config.js"
 }

調試

我們使用vscode自帶的調試功能調試,也可以使用 Node + Chrome 調試,看開發者習慣。

我們就 vscode 為例,請參考 https://code.visualstudio.com/docs/editor/debugging

增加如下調試配置文件:

// .vscode/launch.json
{
    // 使用 IntelliSense 瞭解相關屬性。
    // 懸停以查看現有屬性的描述。
    // 欲瞭解更多信息,請訪問: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "debug cli",
            "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/babel-node",
            "runtimeArgs": [
                "${workspaceRoot}/packages/cli/src/index.js"
            ],
            "env": {
                "LOCAL_DEBUG": "true"
            },
            "console": "integratedTerminal"
        }
    ]
}

因為 src 的代碼是 ES6 的,所以要使用 babel-node去跑調試,@babel/node 已經在前面安裝過了。

**最棒的是,可以直接使用單步調試,調到依賴的模塊中去,**如上圖,我們要執行 @mo-demo/cli-shared-utils 模塊中的 log 方法,單步進入,會直接跳到 @mo-demo/cli-shared-utils src 源碼中去執行。如下圖

結語

到這裡,基本上已經構建了基於 Lerna 管理 packages 的 Monorepo 項目的最佳實踐了,該有的功能都有:

  • 完善的工作流

  • 流暢的調試體驗

  • 風格統一的編碼

  • 一鍵式的發佈機制

  • 完美的更新日誌

  • ……

當然,Lerna 還有更多的功能等待著你去發掘,還有很多可以結合 Lerna 一起使用的工具。構建一套完善的倉庫管理機制,可能它的收益不是一些量化的指標可以衡量出來的,也沒有直接的價值輸出,但它能在日常的工作中極大的提高工作效率,解放生產力,節省大量的人力成本。

——— 參考文獻  ———

  1. 手摸手教你玩轉 Lerna http://www.uedlinker.com/2018/08/17/lerna-trainning/

  2. 精讀《Monorepo 的優勢》https://mp.weixin.qq.com/s/f2ehHTNK9rx8jNBUyhSwAA

  3. 使用lerna優雅地管理多個package  https://zhuanlan.zhihu.com/p/35237759

  4. 用 husky 和 lint-staged 構建超溜的代碼檢查工作流  https://segmentfault.com/a/1190000009546913

更多內容敬請關註 vivo 互聯網技術 微信公眾號

註:轉載文章請先與微信號:labs2020 聯繫。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 08.12自我總結 樣式模板快捷鍵設置 一.Bootstrap 樣式載入 目前3+版本比較穩定,4+有些樣式可能沒法顏色 點擊 ,選擇樣式 複製粘貼即可 註意 :可選的 Bootstrap 主題文件一般不導入 導入後更具提示選擇對應的class改變他的類,如果他每提示比如圖標你可以F12裡面看圖標的 ...
  • 一、JS變數的聲明、數據類型和變數的轉換 1.js變數聲明關鍵字:var 註意:a:js變數區分大小寫; b:js中字元串可使用雙引號,也可使用單引號; c:js中可聲明同名變數,控制台不會報錯,但後面變數會覆蓋前面的。 2.數據類型分類 a:number(數字類型) b:string(字元串類型) ...
  • vue中的事件修飾符(.stop、.prevent、.self、.capture、.once) (1)實例代碼 (2)摘要 使用.stop阻止事件的冒泡行為。 使用.prevent阻止事件的預設行為。 使用.self實現只有點擊當前元素才會觸發事件處理函數。 使用.capture實現捕獲觸發事件的機 ...
  • JavaScript輪播圖的實現 HTML部分: CSS部分 JavaScript部分 ...
  • var arr = ["北京","上海","天津","重慶","河北","河南","安徽","湖北","湖南"]; $.each(arr, function(i, val) { console.log(i+"-- --"+val); }); var obj= {name:"小明",male:"男"} ...
  • 一. javascript三種引入方式 head 頭部引入<script type="text/javascript"></script> 行內引入<p ....="javascript"></p> 例如 <a href="javascript:confim('。。。。。。。');"></a> <p ...
  • 寫在前面:時間是物理學七大常量之一。生活中記錄時間有兩種方式(或者說有兩種計時系統):GMT(格林尼治時間)和UTC(協調世界時間)。 一 創建Date對象 JS中的Date對象只能通過new關鍵字創建。 var now = new Date(); 需要註意的是:若將Date()作為常規函數調用(即 ...
  • HTML是超文本標記語言的縮寫,不同於C或JAVA等編程語言,HTML由標簽組成。通過標簽可以在網頁中插入文字、圖片、鏈接、音頻、視頻等元素,進而描述網頁。和Windows一樣,隨著技術的發展,HTML經歷了多次版本更新。 HTML1.0:1993年6月,HTML作為互聯網工程工作小組(IETF)工 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...