NodeJS學習筆記

CommonJS

學習資料:CommonJS規範 javascript

概述

Node應用由模塊組成,採用的就是CommonJS規範css

規範指明,每一個文件就是一個模塊,有本身的做用域。在一個文件裏面,類、函數、方法都是私有的,其餘文件不可見html

// example.js
var x = 5;
var addX = function (value) {
  return value + x;
};

上面代碼中x變量和addx方法,就是當前文件私有,其餘不可見。前端

CommonJS規定,每一個模塊內部,module 變量表明當前模塊。這個變量是一個對象,他的 exports 屬性是對外的接口。java

加載某個模塊,實際上是加載該模塊的 module.exports 屬性。node

var x = 5;
var addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;

經過module.exports輸出變量x和函數addx。webpack

require 方法用於加載模塊git

var example = require('./example.js');
console.log(example.x); // 5
console.log(example.addX(1)); // 6

CommonJS模塊的特色以下:github

  • 全部代碼都運行在模塊做用域,不會污染全局
  • 模塊能夠屢次加載,但只會在第一次運行,將結果緩存。其餘時候讀的緩存。若是想再次運行,就須要手動清除緩存。
  • 模塊的加載順序,按照其在代碼中的順序執行

Module對象

Node內部提供一個Module構造函數,全部模塊都是Module的實例。web

每一個內部都有一個 module 對象,表明當前模塊,幷包含如下屬性

  • module.id 模塊的識別符,一般是帶有絕對路徑的模塊文件名。
  • module.filename 模塊的文件名,帶有絕對路徑。
  • module.loaded 返回一個布爾值,表示模塊是否已經完成加載。
  • module.parent 返回一個對象,表示調用該模塊的模塊。
  • module.children 返回一個數組,表示該模塊要用到的其餘模塊。
  • module.exports 表示模塊對外輸出的值。

console.log(module) 會輸出當前module對象的全部信息。使用module.parent,能夠判斷是不是入口文件。

Module.exports

module.exports屬性表示當前模塊對外輸出的接口,其餘文件加載該模塊,實際上讀取module.exports變量。

exports變量

爲了方便,NodeJS爲每一個模塊提供一個exports變量,指向module.exports。這等同在每一個模塊頭部,有這段命令

var exports = module.exports;

能夠直接給這個變量添加對象。但不能將這個變量直接賦值。

若是一個模塊的對外接口,就是一個單一的值,則不能使用exports變量。須要module.exports = function(){}

AMD規範和CommonJS規範

CommonJS規範是同步進行的,也就是說只有加載完成纔會執行後面的操做。

AMD規範是非同步加載模塊,容許指定回掉函數。。

NodeJS主要用於服務端編程,模塊文件通常都已經存在於本地硬盤,因此加載速度會比較快。

若是是瀏覽器環境,要從服務器加載模塊,就必須採用非同步模式,所以通常瀏覽器使用AMD模式。

require命令

CommonJS模塊規範,內置的require命令用於加載模塊

require命令會讀取並執行一個Javascript文件,而後返回該文件模塊的exports對象。若是沒有會報錯

加載規則

require命令用於加載文件,後綴名默認爲*.js

var foo = require('foo');
//  等同於
var foo = require('foo.js');

根據參數不一樣,require命令會去不一樣路徑查找文件

  1. 若是以 / 開頭,查找絕對路徑
  2. 若是以 ./ 開頭,查找相對路徑
  3. 若是以 文件開頭,查找位於NodeJS安裝目錄,和各層級node_module目錄已安裝模塊
  4. 若是指定模塊文件沒有發現,Node會嘗試添加各類後綴名。.js .json .node,再去搜索。
  5. js文件會執行腳本解析\json文件會以JSON格式解析\node會以二進制解析
  6. 若是想獲得require命令加載的確切文件名,使用 require.resolve() 方法

目錄加載規則

一般咱們會把相關的文件放在一個目錄裏面,便於組織。

此時最好爲該目錄設置一個入口文件,讓require方法能夠經過這個入口文件,加載整個目錄

目錄中防止要給 package.json,可使用 npm init 直接建立一份package.json

{
  "name": "Cxy",
  "version": "1.0.0",
  "description": "描述",
  "main": "index.js", //入口文件
  "scripts": {
    "test": "null"
  },
  "repository": {
    "type": "git",
    "url": "https://git.coding.net/chenxygx/CodeSave.git"
  },
  "author": "",
  "license": "MIT"
}

require發現參數字符串指向一個目錄後,就會自動查找該目錄的 package.json,而後加載main的入口文件

若是沒有,則默認加載index.js 或 index.node

模塊緩存

第一次加載模塊,Node會緩存該模塊。之後再加載該模塊,就直接從緩存中取出該模塊的module.exports屬性

全部模塊都緩存在require.cache之中。若是想刪除模塊的緩存,能夠以下寫法

// 刪除指定模塊的緩存
delete require.cache[moduleName];
// 刪除全部模塊的緩存
Object.keys(require.cache).forEach(function(key) {
  delete require.cache[key];
})

環境變量NODE_PATH

Node執行一個腳本會先查看環境變量Node_path。他是一組以冒號分隔的絕對路徑,在其餘位置找不到指定模塊時,Node會去此路徑查找

有兩種方式能夠解決

1.將文件添加到node_modules目錄

2.修改NODE_PATH環境變量,package.json採用以下寫法

{
  "name": "node_path",
  "main": "index.js",
  "scripts": {
    "start": "NODE_PATH=lib node index.js"
  }
}

NODE_PATH是歷史遺留的解決方案,儘可能不要採用此方法,採用node_modules會更好

require.main

require方法有一個main屬性,能夠用來判斷模塊是直接執行仍是被調用執行

直接執行的時候(node module.js) require.main屬性指向自己

require.main === module // true

模塊加載機制

CommonJS模塊的加載機制是,輸入的是被輸出的值的拷貝,也就是一旦輸出一個值,內部變化就不會影響

require命令是CommonJS規範中,用來加載其餘模塊的命令。他指向當前模塊的module.require命令,而後調用Node命令Module._load

Module._load 會執行下面操做

Module._load = function(request, parent, isMain) {
  // 1. 檢查 Module._cache,是否緩存之中有指定模塊
  // 2. 若是緩存之中沒有,就建立一個新的Module實例
  // 3. 將它保存到緩存
  // 4. 使用 module.load() 加載指定的模塊文件,
  //    讀取文件內容以後,使用 module.compile() 執行文件代碼
  // 5. 若是加載/解析過程報錯,就從緩存刪除該模塊
  // 6. 返回該模塊的 module.exports
};

//第四步module.compile
Module.prototype._compile = function(content, filename) {
  // 1. 生成一個require函數,指向module.require
  // 2. 加載其餘輔助方法到require
  // 3. 將文件內容放到一個函數之中,該函數可調用 require
  // 4. 執行該函數
};

主要使用到的函數和輔助方法以下:

  • require(): 加載外部模塊
  • require.resolve():將模塊名解析到一個絕對路徑
  • require.main:指向主模塊
  • require.cache:指向全部緩存的模塊
  • require.extensions:根據文件的後綴名,調用不一樣的執行函數

一旦requrire函數準備完畢,整個所要加載的腳本內容,就會被放到一個新的函數之中,就能夠避免全局污染

新的函數包含,require\module\exports,

Module._compile是同步的,Module._load要等它執行完成,纔會向用戶返回module.exports的值

Webpack

學習資料:

webpack2官網 CoffeeScript  ES2015  ES2015核心內容 

傻瓜教程  傻瓜教程二  項目Demo  

概念

Webpack是一個現代的Javascript應用程序的模塊打包器 (module bundler)。有很是高的可配置性。

Webpack是一個前端模塊管理器,會把一堆文件中的每一個做爲一個模塊,找出他們之間的依賴關係

並將它們捆綁到準備部署的靜態文件上。

舉一個簡單的例子,加入咱們有一堆CommonJS模塊,他們不能直接在瀏覽器中運行。

咱們須要將他們綁定到一個<script>標籤包含的文件裏。webpack能夠跟隨require() 調用的依賴關係,爲咱們作這些事

Webpack能作更多的事情,經過 "loaders" 咱們能讓Webpack按照咱們想要的方式打包輸出。例如:

  • 編譯ES201五、CoffeeScript、TypeScript模塊成ES5 CommonJS的模塊
  • 編譯以前,能夠經過 Linter 校驗源代碼
  • 編譯Jade模板成HTML並內聯JavaScript字符串
  • 編譯SASS文件成CSS,而後把生成的CSS插入到<style>標籤內,在轉成JavaScript代碼段
  • 處理在HTML或CSS文件中引用的圖片文件,根據配置路徑把它們移動到任意位置,根據MD5 hash命名
  • Webpack很是強大,能夠改善你的前端開發效率,可是配置麻煩

四個核心概念

入口(Entry)

webpack將建立全部應用程序的依賴關係圖表,圖表的起點稱之爲入口起點。入口起點告訴webpack從哪裏開始

並遵循着依賴關係表知道要打包什麼。能夠理解爲入口就是根上下文或者app第一個執行的文件。

在webpack中,咱們使用 entry 屬性來定義入口

module.exports = {
  entry: './path/to/my/entry/file.js'
};

出口(Output)

將全部的資源歸攏在一塊兒之後,還需告訴webpack打包在哪裏。webpack的output屬性描述瞭如何處理歸攏在一塊兒的代碼(bundled code)

上面例子中,咱們經過output.filename 和 output.path 屬性,來告訴webpack bundled的名稱,以及咱們想要生成的路徑

__dirname變量指向爲根目錄

const path = require('path');
module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};

加載器(Loaders)

webpack把每一個文件(css,html,scss,jpg,etc)都做爲模塊處理。

然而webpack只理解javascript。會把這些文件轉換爲模塊,而轉換後的文件會被添加到依賴圖表中

webpack的配置有兩個目標

1. 識別出identify 應該被對應的loader 進行轉換(transform)的那些文件

2. 因爲進行過文件轉換,因此可以將被轉換的文件添加到依賴圖表中

const path = require('path');

const config = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      {test: /\.(js|jsx)$/, use: 'babel-loader'}
    ]
  }
};

module.exports = config;

以上配置中,對一個單獨的module 對象定義了 rules 屬性,裏面包含兩個必須屬性:test 和 use

當requrie() 或 import 語句中被解析爲 .js 或 .jsx的路徑時,在你把他們添加並打包以前,要先使用 babel-loader 去轉換

插件(Plugins)

因爲loader 僅在每一個文件的基礎上執行轉換,而插件 最經常使用於在打包模塊的 compilation 和 chunk生命週期執行操做和自定義功能

webpack 的插件系統極其強大,而且可定製化強。

想要使用一個插件,只須要require(),而後放到plugins數組中。多數插件能夠經過選項option自定義。

也能夠在一個配置中,因不一樣的目的屢次使用同一個插件。只須要new一下便可

const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins
const path = require('path');

const config = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      {test: /\.(js|jsx)$/, use: 'babel-loader'}
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

module.exports = config;

安裝

首先須要安裝,最新版NodeJS。

而後 

$ npm install webpack -g

最後啓動打包

$ webpack // 最基本的啓動webpack方法
$ webpack -w // 提供watch方法,實時進行打包更新
$ webpack -p // 對打包後的文件進行壓縮,提供production
$ webpack -d // 提供source map,方便調試。

NodeJS

學習資料:中文官網  

Package.json

每一個項目的根目錄下,通常都有一個package.json文件,定義了這個項目所須要的各類模塊,以及項目的配置信息(名稱、版本、許可等)

npm install 命令會根據這個配置文件,自動下載所需的模塊,也就是配置項目所需的運行和開發環境。

npm install -g cnpm --registry=https://registry.npm.taobao.org

使用cnpm能夠設置爲淘寶鏡像的npm,而後下載使用cnpm下載便可

package.json文件就是一個json對象,對象的每個成員就是當前項目的一項設置。能夠經過npm init來生成package.json

看一個完整的package.json,而後挨個解答一下對應的意思。

{
  "name": "Hello World",
  "version": "0.0.1",
  "author": "張三",
  "contributors": "ai wo bie zou",
  "description": "第一個node.js程序",
  "Homepage": "www.baidu.com",
  "main": "index.js",
  "Files":"",
  "keywords": [
    "node.js",
    "javascript"
  ],
  "repository": {
    "type": "git",
    "url": "https://path/to/url"
  },
  "license": "MIT",
  "engines": {
    "node": "0.10.x"
  },
  "bugs": {
    "url": "http://path/to/bug",
    "email": "bug@example.com"
  },
  "contributors": [
    {
      "name": "李四",
      "email": "lisi@example.com"
    }
  ],
  "bin": {
    "someTool": "./bin/someTool.js"
  },
  "scripts": {
    "start": "node index.js && someTool build",
    "test": "tap test/*.js"
  },
  "dependencies": {
    "express": "latest",
    "mongoose": "~3.8.3",
    "handlebars-runtime": "~1.0.12",
    "express3-handlebars": "~0.5.0",
    "MD5": "~1.2.0"
  },
  "devDependencies": {
    "bower": "~1.2.8",
    "grunt": "~0.4.1",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-jshint": "~0.7.2",
    "grunt-contrib-uglify": "~0.2.7",
    "grunt-contrib-clean": "~0.5.0",
    "browserify": "2.36.1",
    "grunt-browserify": "~1.3.0"
  },
  "peerDependencies": {
    "chai": "1.x"
  }
}

name:項目名稱,不能包含js、node字樣,不能以點或下劃線開頭

version:項目版本

author:做者

contributors:做者團隊

description:描述

keywords:關鍵字,會在搜索的時候使用

repository:指示代碼存放位置。

license:版權

Homepage:主頁,不用填寫協議

Files:項目包含的一組文件

engines:指定node版本

bugs:問題追蹤系統的URL或郵箱地址

scripts

指定運行腳本命令的npm命令和縮寫,好比start指定了運行npm run start時,所要執行的命令

上文中,指定了npm run start和npm run test,所要執行的命令。

dependencies

指定了項目運行時所依賴的模塊,該對象的各個成員,分別由模塊名和對應版本要求組成。

使用npm install能夠安裝全部模塊。

npm install --production只安裝dependencies

npm install express --save 將模塊寫入dependencies屬性

對應的版本限定,主要有如下幾種

  • 指定版本:好比1.2.2,遵循「大版本.次要版本.小版本」的格式規定,安裝時只安裝指定版本。
  • 波浪號(tilde)+指定版本:好比~1.2.2,表示安裝1.2.x的最新版本(不低於1.2.2),可是不安裝1.3.x,也就是說安裝時不改變大版本號和次要版本號。
  • 插入號(caret)+指定版本:好比ˆ1.2.2,表示安裝1.x.x的最新版本(不低於1.2.2),可是不安裝2.x.x,也就是說安裝時不改變大版本號。須要注意的是,若是大版本號爲0,則插入號的行爲與波浪號相同,這是由於此時處於開發階段,即便是次要版本號變更,也可能帶來程序的不兼容。
  • latest:安裝最新版本。

devDependencies

指定了項目開發時所須要的模塊

npm install express --dev 將模塊寫入devDependencies屬性。

npm install express --save-dev 兩個都寫入

peerDependencies

供插件指定其所須要的主工具的版本。

上面代碼指,安裝Hello World須要主程序chai一塊兒安裝。並且版本必須是1.x。若是依賴是2.0就會報錯。

bin

用來指定各個內部命令對應的可執行文件的位置。

someTool命令對應的可執行文件爲bin子目錄下的someTool.js。

npm會尋找這個文件,在node_modules/.bin/目錄下創建符號連接。上面例子中

someTool.js會創建符號連接npm_module/.bin/someTool。

因爲node_modules/.bin/目錄會在運行時加入系統path變量,所以在運行Npm時,就能夠不帶路徑。

全部node_module/.bin/目錄下的命令,均可以用npm run [命令]的格式運行

main

指定加載的入口文件,require('moduleName')會加載這個文件。默認值是根目錄下的index.js

$

相關文章
相關標籤/搜索