Node.js 基础

134 阅读5分钟

nodejs 主要表现是 命令式编程

node 是什么?

Node.js 是⼀个 JS 的服务端运行环境,基于 V8,是在 JS 语言规范的基础上,封装了一些服务端的 runtime,让我们能够简单实现非常多的业务功能。

Node.js2009 年(第一版 npm 被创建)诞生之初是为了实现高性能的 web 服务器,再后来 Node.js 慢慢演化为了一门服务器“语言”。

  • commonJs 是一个 规范nodejscommonJs实现

  • LAMP - Linux + Apache + MySQL + php; (thinkPhP, CI)

  • MEAN - mongoDB + express + angular + node.js 2014 年

node 能做哪些事情?

npm run start 运行了 node

  • 跨平台开发:PC、web、H5、RN、Weex
  • 后端开发:API、RPC
  • 前端开发:前端工具链;
  • 工具开发:脚本、脚手架、命令行;
{
  "name": "node_basic",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "build": "rollup -c -w"
  },
  "devDependencies": {
    "rollup": "^2.70.2"
  }
}

如上 json 配置,如果 rollup 是本地安装,不是全局安装,直接在控制台执行 npm run build 可以执行,如果执行 rollup -c -w 不可以,需要执行 ./node_modules/.bin/rollup -c -w

分类举例

  • 压缩:UglifyJS、JSMin
  • 管理:npm、yarn、bower
  • 模块系统:Commonjs、ESM
  • 模块构建:Babel、Browserify、Webpack、Gulp、Grunt

问题

  • 单线程很脆弱,但是可以通过 cluster/pm2 多核并发实现负载均衡;
  • nodeMongoDB、Mysql、redis 支持比较好;对 neo4j、tigerGraph 支持不太好;
  • 安全问题;

和浏览器的区别

  • Node 环境中是没有 DOM、BOM,同样的,浏览器中也没有 fs、path 这些模块;
    • why? 如果浏览器支持 fs 会怎么样?—— 存在安全问题;
  • cjsesm
    • esm 值引用、cjs 值拷贝;
    • Node.js 使用 CommonJS 模块系统,而在浏览器中我们开始看到正在实施的 ESM 标准;

具体的内核

Node 具体的内核.png

node 安装

  • nvm:是一个 node 版本管理工具;
  • nrm:用于对 node 镜像源进行设置;

npm 相关

npm 的目标?

给你和你的团队、你的公司带来最好的开源库和依赖。

npm install 工作原理

npm install 过程.png

npm 缓存

npm config get cache
# 或者
yarn cache dir

image-20220725012545789.png

npm CI

npm install 不同的是:

  • 必须要有 package-lock.json 文件,且下载完全依赖该文件;
  • 会删除 node_modules
  • 如果和 package.json 冲突,则直接报错;
  • 只能一次性安装;
  • 永不改写 package.jsonpackage-lock.json 文件;

npm 的包依赖关系

xxxDependencies

比如,我们要构建一个 公共组件库 luyi-ui

dependencies 项目依赖(常用)

  • lodash (debounce、deepMerge)

devDependencies 开发依赖(常用)

  • webpack、rollup、jest

peerDependencies 同版本依赖(常用)

  • vue

  • 例如 vue 组件库;如果说你连 vue 都没有,那这个项目对你来说没有意义

bundledDependencies 捆绑依赖

{
  bundle1, bundle2
}

optionalDependencies 可选依赖

  • 可选依赖

element-ui 的 package.json

{
  "name": "element-ui",
  "version": "2.15.10",
  "description": "A Component Library for Vue.js.",
  "main": "lib/element-ui.common.js",
  "files": ["lib", "src", "packages", "types"],
  "typings": "types/index.d.ts",
  "scripts": {
    "bootstrap": "yarn || npm i",
    "build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js",
    "build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
    "build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",
    "build:umd": "node build/bin/build-locale.js",
    "clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage",
    "deploy:build": "npm run build:file && cross-env NODE_ENV=production webpack --config build/webpack.demo.js && echo element.eleme.io>>examples/element-ui/CNAME",
    "deploy:extension": "cross-env NODE_ENV=production webpack --config build/webpack.extension.js",
    "dev:extension": "rimraf examples/extension/dist && cross-env NODE_ENV=development webpack --watch --config build/webpack.extension.js",
    "dev": "npm run bootstrap && npm run build:file && cross-env NODE_ENV=development webpack-dev-server --config build/webpack.demo.js & node build/bin/template.js",
    "dev:play": "npm run build:file && cross-env NODE_ENV=development PLAY_ENV=true webpack-dev-server --config build/webpack.demo.js",
    "dist": "npm run clean && npm run build:file && npm run lint && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run build:utils && npm run build:umd && npm run build:theme",
    "i18n": "node build/bin/i18n.js",
    "lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet",
    "pub": "npm run bootstrap && sh build/git-release.sh && sh build/release.sh && node build/bin/gen-indices.js",
    "test": "npm run lint && npm run build:theme && cross-env CI_ENV=/dev/ BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
    "test:watch": "npm run build:theme && cross-env BABEL_ENV=test karma start test/unit/karma.conf.js"
  },
  "faas": [
    {
      "domain": "element",
      "public": "temp_web/element"
    },
    {
      "domain": "element-theme",
      "public": "examples/element-ui",
      "build": ["yarn", "npm run deploy:build"]
    }
  ],
  "repository": {
    "type": "git",
    "url": "git@github.com:ElemeFE/element.git"
  },
  "homepage": "http://element.eleme.io",
  "keywords": ["eleme", "vue", "components"],
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/ElemeFE/element/issues"
  },
  "unpkg": "lib/index.js",
  "style": "lib/theme-chalk/index.css",
  "dependencies": {
    "async-validator": "~1.8.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.0",
    "deepmerge": "^1.2.0",
    "normalize-wheel": "^1.0.1",
    "resize-observer-polyfill": "^1.5.0",
    "throttle-debounce": "^1.0.1"
  },
  "peerDependencies": {
    "vue": "^2.5.17"
  },
  "devDependencies": {
    "@vue/component-compiler-utils": "^2.6.0",
    "algoliasearch": "^3.24.5",
    "babel-cli": "^6.26.0",
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-plugin-add-module-exports": "^0.2.1",
    "babel-plugin-istanbul": "^4.1.1",
    "babel-plugin-module-resolver": "^2.2.0",
    "babel-plugin-syntax-jsx": "^6.18.0",
    "babel-plugin-transform-vue-jsx": "^3.7.0",
    "babel-preset-env": "^1.7.0",
    "babel-preset-stage-2": "^6.24.1",
    "babel-regenerator-runtime": "^6.5.0",
    "chai": "^4.2.0",
    "chokidar": "^1.7.0",
    "copy-webpack-plugin": "^5.0.0",
    "coveralls": "^3.0.3",
    "cp-cli": "^1.0.2",
    "cross-env": "^3.1.3",
    "css-loader": "^2.1.0",
    "es6-promise": "^4.0.5",
    "eslint": "4.18.2",
    "eslint-config-elemefe": "0.1.1",
    "eslint-loader": "^2.0.0",
    "eslint-plugin-html": "^4.0.1",
    "eslint-plugin-json": "^1.2.0",
    "file-loader": "^1.1.11",
    "file-save": "^0.2.0",
    "gulp": "^4.0.0",
    "gulp-autoprefixer": "^6.0.0",
    "gulp-cssmin": "^0.2.0",
    "gulp-sass": "^4.0.2",
    "highlight.js": "^9.3.0",
    "html-webpack-plugin": "^3.2.0",
    "json-loader": "^0.5.7",
    "json-templater": "^1.0.4",
    "karma": "^4.0.1",
    "karma-chrome-launcher": "^2.2.0",
    "karma-coverage": "^1.1.2",
    "karma-mocha": "^1.3.0",
    "karma-sinon-chai": "^2.0.2",
    "karma-sourcemap-loader": "^0.3.7",
    "karma-spec-reporter": "^0.0.32",
    "karma-webpack": "^3.0.5",
    "launch-editor-middleware": "^2.3.0",
    "markdown-it": "^8.4.1",
    "markdown-it-anchor": "^5.0.2",
    "markdown-it-chain": "^1.3.0",
    "markdown-it-container": "^2.0.0",
    "mini-css-extract-plugin": "^0.4.1",
    "mocha": "^6.0.2",
    "node-sass": "^4.11.0",
    "optimize-css-assets-webpack-plugin": "^5.0.1",
    "postcss": "^7.0.14",
    "progress-bar-webpack-plugin": "^1.11.0",
    "rimraf": "^2.5.4",
    "sass-loader": "^7.1.0",
    "select-version-cli": "^0.0.2",
    "sinon": "^7.2.7",
    "sinon-chai": "^3.3.0",
    "style-loader": "^0.23.1",
    "transliteration": "^1.1.11",
    "uglifyjs-webpack-plugin": "^2.1.1",
    "uppercamelcase": "^1.1.0",
    "url-loader": "^1.0.1",
    "vue": "2.5.21",
    "vue-loader": "^15.7.0",
    "vue-router": "^3.0.1",
    "vue-template-compiler": "2.5.21",
    "vue-template-es2015-compiler": "^1.6.0",
    "webpack": "^4.14.0",
    "webpack-cli": "^3.0.8",
    "webpack-dev-server": "^3.1.11",
    "webpack-node-externals": "^1.7.2"
  }
}

commonJS 模块化

手写一个 cjs 规范 require

步骤 1

  • ./module.js
module.exports = function () {
  return 'hello world';
};
  • ./index.js
// 执行 require 会报错,ReferenceError: require is not defined
// const hello = require('./module.js');
// console.log(`hello luyi, ${hello()}`);

// 可以执行,输出 hello test
console.log('hello test');
  • ./require.js
const { readFileSync } = require('fs');
const { Script } = require('vm');

const path = require('path');

const file = path.resolve(__dirname, './index.js');
const fileContent = readFileSync(file, 'utf-8');

// Script 是编译 js 代码的字符串
const scripts = new Script(fileContent);

// 在当前 global 对象的上下文中运行 vm.Script 包含的编译代码
const res = scripts.runInThisContext();

步骤 2

  • ./index.js
const hello = require('./module.js');

console.log(`hello luyi, ${hello()}`);
  • ./require.js
const { readFileSync } = require('fs');
const { Script } = require('vm');

const path = require('path');

const file = path.resolve(__dirname, './index.js');
const fileContent = readFileSync(file, 'utf-8');

const wrappedContent = `(function(require, module, exports) {
    ${fileContent}
  })`;

// console.log('wrappedContent ===== ', wrappedContent);
/* 
  输出的是函数体的字符串

  (function (require, module, exports) {
    const hello = require('./module.js');
    console.log(`hello luyi, ${hello()}`);
  });
*/

// Script 是编译 js 代码的字符串
const scripts = new Script(wrappedContent);

// 在当前 global 对象的上下文中运行 vm.Script 包含的编译代码
const res = scripts.runInThisContext();
// console.log(res); // 输出的是匿名函数 [Function (anonymous)]
// 下一步要执行这个匿名函数 res()

步骤 3

  • ./require.js
const { readFileSync } = require('fs');
const { Script } = require('vm');

const path = require('path');

function my_require(filename) {
  const file = path.resolve(__dirname, filename);
  const fileContent = readFileSync(file, 'utf-8');

  const wrappedContent = `(function(require, module, exports) {
    ${fileContent}
  })`;

  // console.log('wrappedContent ===== ', wrappedContent);
  /* 
  输出的是函数体的字符串

  (function (require, module, exports) {
    const hello = require('./module.js');
    console.log(`hello luyi, ${hello()}`);
  });
*/

  // Script 是编译 js 代码的字符串
  const scripts = new Script(wrappedContent);

  // 在当前 global 对象的上下文中运行 vm.Script 包含的编译代码
  const res = scripts.runInThisContext();
  // console.log(res); // 输出的是匿名函数 [Function (anonymous)]
  // 下一步要执行这个匿名函数 res()

  const module = {
    exports: {},
  };
  // res 是上面的那个包裹着文件的得到的函数
  res(my_require, module, module.exports);

  // 外部 require 的结果,就是内部 module.exports 的东西,module.exports 是个对象引用
  return module.exports;
}

步骤 4

  • ./index.js
const hello = my_require('./module.js');

console.log(`hello luyi, ${hello()}`);
  • ./require.js
const { readFileSync } = require('fs');
const { Script } = require('vm');

const path = require('path');

function my_require(filename) {
  const file = path.resolve(__dirname, filename);
  const fileContent = readFileSync(file, 'utf-8');

  const wrappedContent = `(function(require, module, exports) {
    ${fileContent}
  })`;

  // console.log('wrappedContent ===== ', wrappedContent);
  /* 
  输出的是函数体的字符串

  (function (require, module, exports) {
    const hello = require('./module.js');
    console.log(`hello luyi, ${hello()}`);
  });
*/

  // Script 是编译 js 代码的字符串
  const scripts = new Script(wrappedContent);

  // 在当前 global 对象的上下文中运行 vm.Script 包含的编译代码
  const res = scripts.runInThisContext();
  // console.log(res); // 输出的是匿名函数 [Function (anonymous)]
  // 下一步要执行这个匿名函数 res()

  const module = {
    exports: {},
  };
  // res 是上面的那个包裹着文件的得到的函数
  res(my_require, module, module.exports);

  // 外部 require 的结果,就是内部 module.exports 的东西,module.exports 是个对象引用
  return module.exports;
}

global.my_require = my_require;

my_require('./index.js');