Thousands and thousands of the front wheels, but still some bottlenecks, the company needs to call its own tcp agreement in the front end, the agreement only c ++ version of the package. Leaders hope can be directly transferred to the module, do not repeat create the wheel.
Honestly I'm still a little impression of C, C Language, after all, is also a certified person .. but it was useless for a long time, watching the C language type definitions lot, let me use this perennial implicit type of jser feel then. this is the most difficult to achieve since I practitioners hello world
project.
Overall description
Native Addon
A Native Addon is a binary file, which is a low-level language such as C or C ++ implementation in Nodejs environment, we can call like any other module require () import Native Addon
Native Addon .js other end of the same, will expose module.exports
or exports
objects, which are encapsulated into a file node module also referred to as Native Module (primary module) .
So how Native Addon can load and run applications in js? Js allow Native Addon compatible with the environment and exposed API can be the same as a normal node module to use it?
Here have to say DLL ( Dynamic Linked Library ) DLL, he was used by the standard C or C ++ compiler is made in linux or macOS also referred to as Shared Library . A DLL that can be run in a dynamic program loaded, DLL containing C or C ++ API source code, and can communicate. there are also static and dynamic whether it? - can really have a reference here to see this difference between the two, it simply static faster than dynamic, not static because You need to go look for a dependency file and load, but smaller particle size can be dynamically modified packed files.
In Nodejs, when compiling a DLL time, it will be exported to .node suffix file. You can then require the file js file like the same, but some of the code hints impossible.
How Native Addon is work?
Nodejs is actually a collection of many open-source libraries, you can look at his warehouse , looking deps in package.json in use is the Google open source V8 engine to execute js code, V8 happens to be written in C ++, do not believe you look v8 the warehouse . for as asynchronous IO, cycling events and other low-level features are dependent on Libuv library.
When the installation is finished nodejs, actually it contains a compiled version of the entire Nodejs and its dependent source code installed, so you do not have a dependence and a manually install these, but Nodejs be compiled from the source code for these libraries. so what to do with Native Addon? because Nodejs is a low-level C ++ and C compilers made, so itself has the ability to C and C ++ call each other .
Nodejs can be dynamically loaded C and C ++ DLL files, and use its API js operate the program. These are the basic Native Addon works in Nodejs in.
ABI Application Binary Interface Application Binary Interface
ABI is specific to the application to access compiled | compiled program, with the API (Application Programming Interface) is very similar, just interact with binary files, but also to access a memory address to find Symbols, such as numbers, objects, classes and functions
Then the ABI with Native Addon what does it matter? He is the bridge Native Addon and Nodejs communicate. DDL file is actually to register or access to the value provided by ABI Nodejs, and to execute commands by Nodejs exposed API and libraries .
For example, there is a Native Addon want to add a sayHello
method to exports
the subject, he can create a new thread, asynchronous tasks via API access Libuv, and then call the callback function after finished. ABI such Nodejs provided work is complete.
Generally speaking, will be compiled as C or C ++ DLL, some will use is called header header file metadata. Are at .h
the end. Of course, these header files, can be exposed Nodejs and node library lets go Native Addon information referenced header file can reference
A typical use is cited #include
for example #inlude<v8.h>
, and then use the statement to write Nodejs executable code. There are four ways to use the header file.
1. Using the core implementation
For example v8.h
-> v8 engine, uv.h
-..> Libuv library these two files in the installation directory node in but the problem is the dependence between Native Addon and Nodejs too high because these libraries are likely to Nodejs With Node version of the update and change, then every time whether it should go to adaptation to change after change Native Addon? such high maintenance costs. you can look at the node official document for a description of this method, there is a better way below
2. Using Native Abstractions for Node (NAN)
NAN project initially is for internal abstract nodejs and v8 engine implementation. The basic concept is to provide the installation package is a npm, you can through the front of the package management tool yarn
or npm
installation, he includes the nan.h
header file, which for nodejs module and v8 . abstracts but NAN has the following disadvantages:
- Incomplete abstract of the V8 api
- Nodejs does not provide all the support libraries
- Nodejs not maintain official library.
So also recommend two ways
3. N-API
N-API similar NAN project, but is maintained by the official nodejs, since there is no need to install external dependencies to import header file. Reliable and provides an abstraction layer
was exposing node_api.h
header, the internal implementation nodejs abstract and packages, every Nodejs update, N-API will be simultaneously optimized to ensure the reliability of ABI
here are all N-API interface documentation, and here is the official description of the ABI stability of the N-API
N-API suitable for both C and C ++, C ++ but more simple to use the API, then, node-addon-api is made.
4. Module node-addon-api
With these two, he has his own header file napi.h
that contains all of the C ++ package of N-API, and the like N-API is maintained by the official, click here to see the warehouse because he uses in comparison to other more simple, so during the C ++ API package when the preferred method.
I began to realize Hello World
Preparing the Environment
Global need to install yarn global add node-gyp
, because also on Python, (GYP stands for Generate Your Project, is written in Python using a tool ). Specific python path to develop environmental and reference documentation .
After the installation is completed with a C or C ++ compiler to generate the template code Native Addon DLL or the CLI, the operation after the meal fierce as a tiger, will generate a .node
file, but this template is how to generate it? This is the following binding.gyp
file
binding.gyp
binding.gyp
Contains the name of the module, which files should be compiled and so on. Template contains the necessary build instructions file based on a different platform or architecture (32 or 64), but also it provides the necessary header or source file to compile C or C ++, similar to JSON format details can click to see .
Setting project
After installation dependent, the real beginning of our hello world project, the overall structure of the project file:
├── binding.gyp
├── index.js
├── package.json
├── src
│ ├── greeting.cpp
│ ├── greeting.h
│ └── index.cpp
└── yarn.lock
Installation depends
Native Module with normal node modules or other packages as NPM. First yarn init -y
initialize the project, and then install Addon-API-node yarn add node-addon-api
.
Create a C ++ example
Create a file greeting.h
#include <string>
std::string helloUser(std::string name);
Create a file greeting.cpp
#include <iostream>
#include <string>
#include "greeting.h"
std::string helloUser(std::string name) {
return "Hello " + name + "!";
}
Creating index.cpp file, which contains napi.h
#include <napi.h>
#include <string>
#include "greeting.h"
// 定义一个返回类型为 Napi String 的 greetHello 函数, 注意此处的 info
Napi::String greetHello(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
std::string result = helloUser('Lorry');
return Napi::String::New(env, result);
}
// 设置类似于 exports = {key:value}的模块导出
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(
Napi::String::New(env, "greetHello"), // key
Napi::Function::New(env, greetHello) // value
);
return exports;
}
NODE_API_MODULE(greet, Init)
Note that you see a lot of Napi :: this writing, in fact, this is between js and C ++ data format bridge, both sides see the definition of data types to understand.
Here experienced the following process:
- Import
napi.h
header file, he would say the following to resolve the specified path binding.gyp - Import string standard headers and
greeting.h
custom headers. Note the use of "" and the difference between <>, and "" will find the current path, please see - :: Use Napi are beginning to use the
node-addon-api
header files. Napi is a namespace. Because macro does not support namespaces, soNODE_API_MODULE
the absence NODE_API_MODULE
Is anode-api
(N-API) encapsulatedNAPI_MODULE
macro function provided ( macro ) which will be used in jsrequire
invoked when introduced into a Native Addon.- The first parameter to a unique value used to register into the node in the export representing the module name. The best and
binding.gyp
be consistent in target_name, but here is the use of a tag label format rather than a string of - The second parameter is a function of C ++, he will be called when the Nodejs begin registering this method. Respectively will pass
env
andexports
parameters env
Value is aNapi::env
type, comprising the environment (Environment) at the registration module, this is used when the N-API operationNapi::String::New
represents the creation of a newNapi::String
type of value. Such a helloUser willstd:string
convertedNapi::String
exports
Is amodule.exports
low-level API, he was theNapi::Object
type, can be usedSet
to add attributes method, reference documents , the function must return aexports
Create a binding.gyp
file
{
"targets": [
{
"target_name": "greet", // 定义文件名
"cflags!": [ "-fno-exceptions" ], // 不要报错
"cflags_cc!": [ "-fno-exceptions" ],
"sources": [ // 包含的待编译为 DLL 的文件们
"./src/greeting.cpp",
"./src/index.cpp"
],
"include_dirs": [ // 包含的头文件路径, 让 sources 中的文件可以找到头文件
"<!@(node -p \"require('node-addon-api').include\")"
],
'defines': [
'NAPI_DISABLE_CPP_EXCEPTIONS' // 去掉所有报错
],
}
]
}
Generate a template file
In binding.gyp
use at the same level directory
node-gyp configure
It will generate a build folder contains the following files:
./build
├── Makefile // 包含如何构建 native 源代码到 DLL 的指令, 并且兼容 Nodejs 的运行时
├── binding.Makefile // 生成文件的配置
├── config.gypi // 包含编译时的配置列表
├── greet.target.mk // 这个 greet 就是之前配置的 target_name 和 NODE_API_MODULE 的第一个参数
└── gyp-mac-tool // mac 下打包的python 工具
Build and compile
node-gyp build
We will build a .node
file
./build
├── Makefile
├── Release
│ ├── greet.node // 这个就是编译出来的node文件, 可直接被 js require 引用
│ └── obj.target
│ └── greet
│ └── src
│ ├── greeting.o
│ └── index.o
├── binding.Makefile
├── config.gypi
├── greet.target.mk
└── gyp-mac-tool
Come this far you will find the .node
file can not be opened, because he would not give the reading, is a binary file. This time you can try to wave
// index.js
const addon = require('./build/Release/greet.node')
console.log(addon.greetHello())
Directly node index.js
run the code you will find print out Hello Lorry !
, it is the content helloUser inside. Really not easy.
Only this it? Not enough
Parameter passing
The code is written dead Lorry, if I were Mike, Jane, John Doe king five it? And can not function parameter passing is not a good function
Thus said before the info on the function, details, refer to as the info [] operator overloading , can enable access to the class C ++ The following is an array. index.cpp
File greetHello
modification function of:
Napi::String greetHello(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
std::string user = (std::string) info[0].ToString();
std::string result = helloUser(user);
return Napi::String::New(env, result);
}
Then use
node-gyp rebuild
Referenced in the file modification index.js
const addon = require('./build/Release/greet.node')
console.log(addon.greetHello('张三')) // Hello 张三!
At this point, finally achieved a relatively complete our hello world. Do not worry, there are goods
If you want to be like other packages can still publish it, just like the normal operating procedures of npm package almost in. package.json
Specified in the main field index.js
, and then modify the index.js
content as follows:
const addon = require('./build/Release/greet.node')
module.exports = addon.greetHello
Re-use yarn pack
can be a packaged .tgz
, can be introduced in other projects. Are there any? A little bit
About bale of cross-platform
Usually when the release of the module, not the build
folders into account, but the .node
file is on the inside. And .node
to say before the file, depending on the system architecture and, if you are using macOS packaged .node
certainly not be used on the windows so how to achieve compatibility it? Yes, every time reconfiguring build it again when the user in accordance with the corresponding hardware installation, which is to use node-gyp rebuild
, npm or yarn found in the installation-dependent process binding.gyp
, then automatically installed locally node-gyp
, so rebuild
to success.
However, remember? Processes other than node-gyp there are other preconditions, which is why often being given node-gyp will appear when you install some libraries, such as python version? Node version? May lead to users install the module crazy so there is a way: package a .node file for each platform architecture , which can be achieved by installing distinguished install script pacakge.json, there is a third-party packages node-pre-gyp
can be automated.
If you do not want to use node-pre-gyp in less complex configuration, you can also try prebuild-install
the wheel
But there is a problem, how do we achieve a package files on different platforms and architectures? Should I buy a variety of hardware to package? Unrealistic. All right, there are the wheels prebuild
, you can set different platforms, architecture and even node versions can be specified.
PS: There is also a vscode pit, using the C ++ extension time code hints always remind me #include <napi.h>
find the file, but the packaging is no problem, speculation is not capable of recognizing binding.gyp editor in head file search path to find a lot of places do not have the solution. Finally, turn the document found this plug-in can be configured clang.cxxflags
, and thus I try to add a header file to specify the path of -I${workspaceRoot}/node_modules/node-addon-api
no problem, you can enjoy the code hints, or really very easy to write good too !!