Node的模板系统

06.模块系统

在node中JavaScript还有一个很重要的概念:模块系统

  • 模块作用域
  • 使用require方法用来加载模块
  • 使用exports 接口对象用来到处模块中的成员

    1.加载require

    语法:
    1
    const 自定义变量名称 = require('模块')
    俩个作用:
  • 执行被加载模块中的代码
  • 得到被加载模块中的 exports导出接口对象

    2.导出 exports

  • node中的模块作用域,默认文件中所有成员只在当前文件模块有效
  • 对于希望可以被其他模块访问的信息,我们就需要把这些公共的成员都挂载到 exports 接口对象中就可以导出多个成员(必须在对象中)

导出多个成员(必须在对象中:此时exports是个对象):

1
2
3
4
5
exports.a=123
exports.b='hello'
exports.c=function(){
console.log('ccc')
}

导出单个成员(就是拿到:函数,字符串等)

1
module.exports = 'hello'

以下情况会覆盖:最下边会覆盖上边的

1
2
3
4
module.exports = 'hello'
module.exports = function(){
console.log('ccc')
}

也可以导出多个成员:就是把它变为对象了

1
2
3
4
5
6
module.exports = {
add:function(){

}
str : 'heelo'
}

3.导出 module.exports 原理解析

exportsmodule.exports 的一个引用
内部隐藏代码实际是这样子的:

1
2
3
4
5
6
7
8
9
10
let module = {
exports = {

}
}
let exports = module.exports

// console.log(exports === module.exports) // true

return module.exports

4.exports 和 module.exports 的 区别

  • 每个模块中都有一个module对象

  • module对象中都有一个exports对象

  • 我们可以把需要导出的成员都挂载到module.exports接口对象中

  • 也就是, module.exports.xxx= xxx 的方式

  • 但是每次都 module.exports.xxx = xxx 很麻烦

  • 所以 node 为了使用方便, 同时在每一个模块中都提供了一个成员叫 exports

  • exports = module.exports 结果为 true 就证明了此点

  • 所以对于: module.exports.xxx= xxx 的方式完全可以 exports.xxx= xxx

  • 当每一个模块需要导出单个成员的时候,这个时候必须使用: module.exports= xxx 的方式,

  • 不要使用 exports = xxx 不管用

  • 因为每个模块最终向外 return 的是 module.exports

  • exports 只是 module.exports 的一个引用

  • 所以即便你为 exports =xxx 重新赋值,也不会影响到module.exports

  • 但是有一种赋值比较特殊 : exports = module.exports这个可以重新建立引用关系

  • 5.模块查找机制

  • 自己的定义的模块 (路径形式的模块)

    • 自定义模块形式的书写:
    • ./ 代表 当前目录,不可省略
    • ../ 代表 上一级目录, 不可省略
    • /xxx 和 d:/a/foo.js 这种形式的 不用
    • 首位的 / 在这里表示的是当前文件模块所属磁盘根路径
    • .js 后缀名可以省略
    • require(‘./foo.js’)
  • 优先从缓存加载

  • 核心模块

    • 核心模块已经被编译到了二进制二文件中,我们只需要按照名字来加载模块就可以了
    • require(‘fs’)
    • require(‘http’)
  • 第三方模块

    • 查找步骤:以为 第三方 express 模块为例
    • node_modules/express/
    • node_modules/express/package.json
    • node_modules/express/package.json/文件中找main属性
    • 如果找不到main属性 就 默认 index.js 为备选项
    • 以上找不到 就 进入上一级目录 node_modules 再次查找
    • 按照这个规则依次往上找,类似于 js中的原型链, 直到磁盘根目录还找不到,就会报错 :cannot find module xxxx

6.npm

  • node package manager 意思就是 node 包管理者,帮助我们去安装一些第三方包的

    6.1npm 网站

  • [npmjs.com] 用来搜索第三方包, 所有的第三方包都在这里

    6.2 npm命令行工具

  • npm 的第二次含义就是一个命令行工具,只要你安装了 node 就已经安装了npm
  • npm也有版本这个概念 可以通过在命令行中输入 查看版本号
    1
    2
    npm --version   //查看版本号
    npm install --global npm // 升级npm

    6.3解决npm被墙的问题

    npm存储包文件的服务器在国外,有时候会被强,速度会很慢。
  • [http://npm.taobao.org/] 淘宝的开发团队把npm在国内做了一个备份
  • 安装淘宝的npm
    1
    2
    3
    # 在任意目录执行都可以
    # -- global 表示安装到全局, 而非当前目录 注意不能省略 --global
    npm install --global cnpm
    接下来你安装包的时候把之前的npm替换成cnpm就行了
    1
    2
    3
    4
    # 这里 还是走国外的服务器,速度慢
    npm install jquert
    # 使用cnpm 就会通过淘宝的服务器来下载jquery
    cnpm install jquert
    如果不想安装 cnpm 又想使用淘宝的服务器下载:
    1
    npm install jquery --registry=https://registry.npm.taobao.org
    但是每一次手都这样会很麻烦,所有可以把这个选项加入配置文件中
    1
    2
    3
    npm config set registry https://registry.npm.taobao.org
    # 查看npm配置信息
    npm config list
    只要经过上面的命令配置,则以后你所有的 npm install 都会默认通过淘宝的服务器来下载

7.package.json

  • 我们建议每一个项目都需要有一个 package.json文件(包描述文件,就像产品的说明书一样)
  • 这个文件可以通过npm init 的方式来自动化出来, 但现在的版本都可以自动生成了。
  • 对于我们新手来说,最有用的是 dependencies 选项,可以用来帮我们保存第三方包的依赖信息
  • 如果你的 node_module 删除了也不用担心,我们只需要: npm install 就会自动把 package.json 中的 dependencies中所有依赖项都会下载回来
  • 要求每个项目的根目录下都有一个 package,json文件
  • 建议执行 npm install 包名 时候 都加上--save(新版本也就不用啦) 来保存依赖项信息

7.1 package-lock.json 和 package.json

npm 5 版本 以前是不会有 package-lock.json 这个文件

npm 5 版本之后才加入这个文件
当你安装包的时候,npm都会生成或者更新 package-lock.json 这个文件

  • npm 5版本以后的 安装包不需要加 --save参数就会自动保存依赖信息
  • 当你安装包的时候,会自动创建或者是更新 package-lock.json 这个文件
  • package-lock.json 这个文件会保存 node_modules中所有包的信息(版本,下载地址等)
    • 这样的话,重新 npm install 的时候 速度会提升
  • 从文件名来看 lock 称 为 锁
    • 这个 lock用来锁定版本的
    • 如果项目 依赖这个包 1.1.1这个版本, 重新 npm install时候就会下载最新版本 ,而不是 1.1.1 版本
    • 所以我们希望 锁 住 这个 版本
    • 所以package-lock.json 这个文件的其中作用就是 锁定版本号,防止自动升级版本

      8.文件操作路径和模块路径

  • 文件操作路径
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 在文件操作的相对路径中
    // ./data/a.txt 相当于当前目录
    // data/a.txt 相当于当前目录

    // 以下俩种几乎不用 (不要用)
    // /data/a.txt 绝对路径,当前文件模块所处磁盘根目录
    // c;/xx/xx 绝对路径

    fs.readFile('./data/a.txt',(err)=>{data}){

    }
  • 模块操作路径
    1
    2
    3
    4
    5
    // 如果这里忽略了 .   则也是磁盘根目录
    require('/data/foo.js')

    // 相当路径 在模块路径中 一定不能省略 ./
    require('./data/foo.js')

    9.nodemon 修改代码自动重启服务器

  • 我们这里可以使用一个nodemon 第三方命令行工具 来解决我们频繁修改代码重启服务器的问题
  • nodemon 是一个基于Node.js开发的一个第三方命令行工具,需要独立安装
    1
    2
    #在任意目录中执行该命令都行
    npm install --global nodemon
  • 安装完毕后,使用:
    1
    2
     # 使用nodemon
    nodemon app.js

    10.path 路径操作模块

  • path.basename
    • 获取一个路径的文件名 (默认包含扩展名)
      1
      2
      3
      4
      5
      > path.basename('c:/a/b/c/index.js')
      'index.js'

      > path.basename('c:/a/b/c/index.js','.js')
      'index'
  • path.dirname
    • 获取一个路径中的目录部分
      1
      2
       > path.dirname('c:/a/b/c/index.js')
      'c:/a/b/c'
  • path.extname
    • 获取一个路径中的扩展名部分
      1
      2
      > path.extname('c:/a/b/c/index.js')
      '.js'
  • path.pars
    • 把一个路径转为对象
      • root 根路径
      • dir 目录
      • base 包含后缀名的文件名
      • ext 后缀名
      • name 不包含后缀名的文件名
        1
        2
        3
        4
        5
        6
        7
        8
        > path.parse('c:/a/b/c/index.js')
        {
        root: 'c:/',
        dir: 'c:/a/b/c',
        base: 'index.js',
        ext: '.js',
        name: 'index'
        }
  • path.join
    • 当你需要进行路径拼接的时候, 推荐使用
      1
      2
      > path.join('c:/a','b')
      'c:\\a\\b'
  • path.isAbsolute
    • 判断一个路径是否为绝对路径
      1
      2
      3
      4
      5
      6
      > path.isAbsolute('c:/a/b/c/index.js')
      true
      > path.isAbsolute('/a/b/c/index.js')
      true
      > path.isAbsolute('./a/b/c/index.js')
      false

      11.Node中的其他成员 (__dirname 和 __filename)

      在每个模块中, 除了 requireexports 等模块相关AIP之外,还有俩个特殊成员:
  • __dirname 动态获取 可以用来获取当前文件的所属目录的绝对路径 (这个没有加文件 只是文件的绝对目录)
  • __filename 动态获取 可以用来获取当前文件的绝对路径 (这个加上了文件)
  • __dirname__filename是不受执行node命令所属路径影响的

在文件操作中,使用相对路径是不可靠的,因为在Node 中 文件操作 的路径被设计为相对于执行node命令所处的路径(不是bug,人家这样设计是有使用场景)
所以为了解决这个问题,只需要把 相对路径变为绝对路径 就可以了
所以我们可以使用__dirname__filename来帮我们解决这个问题

在拼接路径过程中,为了 避免手动凭借带来的一些低级错误, 我们推荐使用: path.join() 来辅助拼接。

所以为了尽量避免刚才所描述的问题,以后必须在文件操作中使用相对路径都统一转换为 动态的绝对路径

注意: 模块中的路径标识 和 文件操作 中的 相对路径标识 不一致

模块中的路径表示就是相当于当前文件模块,不受执行node 命令所处路径影响