1、注:此教程默認(rèn)你具備一定的 C/C++ 基礎(chǔ)語法知識
2、如:指針、結(jié)構(gòu)體等
3、本教程以 windows 操作系統(tǒng)為例,假設(shè)你會簡單的使用 Visual Studio
系列文章
- C/C++ Addons 入門 Hello world!
- C/C++ Addons 對象參數(shù)及回調(diào)函數(shù)
- C/C++ Addons 非阻塞多線程回調(diào)
- C/C++ Addons windows 下 .dll 動態(tài)鏈接庫 實用篇
前置準(zhǔn)備
工具
-
node-gyp- Google 出品的跨平臺構(gòu)建工具,初衷是用來打包 chromium 的
-
gyp即generate your package,將你的C/++代碼編譯成node.js可識別的文件 - 類似
webpack將vue、jsx等方言編譯成為瀏覽器可識別文件 - 也可以用 cMake.js 做同樣的事情
-
.node文件在 windows 平臺下既.dll在 *nix 平臺下.so文件,.node的尾綴只是看起來自然些(20.0404 - 深入淺出node.js)
-
python- 因為
node-gyp是用python寫的 - 不止一個博客中說只能用
python2.7- 騙人的?? - 官方的說明 Python v2.7, v3.5, v3.6, v3.7, or v3.8
- 因為
-
Visual Studio2017- C/C++ 在
windows下依賴 VS - 個人推薦 - 官方給出了木有 VS 的方案,windows-build-tools 但這沒法用最重要語法提示、報錯功能 - 不推薦
- C/C++ 在
姿勢
目前一共有三種方式可以編寫 node.js 擴(kuò)展,本文以官方推薦的
N-API為例
-
N-API
- node.js 由官方維護(hù)的
node.js擴(kuò)展 api - 純 C 語法不依賴
node.js版本,node.js更新后基于N-API寫的插件照樣用,官方的解釋是底層調(diào)用的node.js穩(wěn)定版的二進(jìn)制接口
- node.js 由官方維護(hù)的
-
node-addon-api
-
N-API的 C++ 包裝版本(有對象,更美好??),目前 (Release 2.0.0) 并未完全的包裝N-API的所有 api
-
-
nan
-
N-API沒出來之前主要的插件開發(fā)方式 - “雖然”依賴
node.js版本,但是維護(hù)團(tuán)隊很賣力,幫忙做好了每個版本的編譯所以就 不依賴node.js版本了 ??
-
-
原生 C/C++
- 極度復(fù)雜,需要用一些
v8api、源碼 - 依賴
node.js版本,所以很難用 ??
- 極度復(fù)雜,需要用一些
起步
- 安裝依賴
$ yarn add -D node-gyp # 就這一個依賴就夠了
- 個人很喜歡安裝到項目里面,而不是
yarn add -g node-gyp - package.js 配置
scripts
{
"scripts": {
"configure": "node-gyp configure",
"build": "node-gyp build",
"clean": "node-gyp clean",
"rebuild": "node-gyp rebuild"
}
}
-
configure會根據(jù)binding.gyp在build文件夾下生成當(dāng)前平臺的 C/C++ 工程 - 第一步執(zhí)行這個
image
ps: 下面的命令干的活都交給 VS 咯,不要去折騰命令咯(除非你沒有VS)
-
build(可選) 如果你不想用 VS,只是編譯已有的 C/C++ 程序,那么這條命令可以代替 VS 幫你構(gòu)建 - 需要 windows-build-tools -
clean(可選) 把build目錄清除 -
rebuild(可選) 依次執(zhí)行clean、configure、build三條命令
- 新建 binding.gyp
{
"targets": [
{
"target_name": "hello",
"sources": [ "src/hello.c" ],
}
]
}
- 構(gòu)建配置文件,語法同 js 版本的 json。等價于 webpack.config.js
-
targets下面的每一項都可以理解為一個node插件,等價于webpack打包bundle.js -
target_name即require([target_name]) -
sourcesC/C++ 源碼目錄 - 更多配置參考
- 生成目標(biāo)平臺項目
$ yarn configure
- 啟動
Visual Studio
image
image
編寫擴(kuò)展
- 一些 API 說明
napi_status 枚舉
· 調(diào)用任意 N-API 后返回值類型
napi_extended_error_info 結(jié)構(gòu)體
· 表示調(diào)用 N-API 后發(fā)生的錯誤對象
napi_env 結(jié)構(gòu)體
· 告訴 N-API 當(dāng)前執(zhí)行上下文,在調(diào)用 Addons 時自動(Init)傳入
· 調(diào)用任意多個、或嵌套 N-API 時候需要一直傳遞下去,不允許重用
napi_callback_info
· 用于 Addons 獲取 js 調(diào)用時候傳入的上下文信息,如參數(shù)
napi_value 不透明指針
· N-API 提供的在 C、js 中間的一種數(shù)據(jù)類型
· 任意的 js 數(shù)據(jù)類型都可以賦值給 napi_value,然后通過 N-API 提供的方法再把 napi_value 轉(zhuǎn)成 C 語言的類型,反之亦然
napi_threadsafe_function 不透明指針
· 代表一個 js 的 function,在多線程模式下通過 napi_call_threadsafe_function 調(diào)用實現(xiàn)異步 ??
# 函數(shù)
napi_create_string_utf8
· 創(chuàng)建 napi 類型的 string
· 相當(dāng)于 const str = 'Hello world!'
napi_get_property
· 從 napi 類型的對象中取值
· 相當(dāng)于對 json = { name: 'anan', age: 29 } 取值: console.log(json.name, json.age)
napi_get_cb_info
· **** 這個可以說是最重要的 API 了,再怎么強(qiáng)調(diào)也不為過 ****
· 用于獲取 js 的入?yún)?;如原始值類型、對象類型,甚至拿?function 類型的指針地址
· 可以說是 js 和 N-NAP 之前的橋梁
napi_call_function
· Addons 調(diào)用 js 回調(diào)
napi_create_function
· 創(chuàng)建 js 函數(shù)
napi_get_global
· 在 Addons 中獲取 js 的 global 對象
- C/C++
src/hello.c
#include <stdio.h>
#include <node_api.h>
#include <string.h>
napi_value Hello(napi_env env, napi_callback_info info) {
size_t argc = 1; // 只接受一個參數(shù)
napi_value argv; // 接收到的參數(shù)
char n[40];
char hello[90] = "Hello ";
napi_value result;
napi_get_cb_info(env, info, &argc, &argv, NULL, NULL); // 獲取接收參數(shù)
napi_get_value_string_utf8(env, argv, n, sizeof(n), NULL); // 將接收到的參數(shù)轉(zhuǎn)換為 C 語言類型
napi_create_string_utf8(env, strcat(hello, n), NAPI_AUTO_LENGTH, &result); // 拼接字符串
return result;
}
napi_value Init(napi_env env, napi_value exports) {
// 描述 hello 屬性
napi_property_descriptor desc = {
"hello",
NULL,
Hello,
NULL,
NULL,
NULL,
napi_default,
NULL };
// 將 hello 掛載到 exports 上面 === require('hello.node').hello;
napi_define_properties(env, exports, 1, &desc);
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
ps: 編寫好C代碼后,Ctrl+Shift+b (VS編譯快捷鍵)
- javascript
test/hello.js
// const addon = require('./build/Debug/hello.node'); // 如果 VS 編譯模式是 Debug
const addon = require('./build/Release/hello.node'); // 如果 VS 編譯模式是 Release
console.log(addon.hello('world!'));
- 運行
$ node test/hello.js
Hello world!
Boom Shakalaka


