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

$

相关文章
相关标签/搜索