node-自动化-gulp

379 阅读6分钟

使用

Node.js 的使用就是在 Node.js 环境下运行 JavaScript 代码。Node.js 下运行 JavaScript 有两种方式。

运行方式

  1. 脚本模式

    # 声明 app.js
    console.log("Hello Node.js")
    
    # 运行:node 代码路径/文件名.js
    node app.js # 返回 Hello Node.js
    
  2. 交互模式

    # 在命令行中,进入交互模式
    node # 回车,然后进入交互模式
    
    # 运行代码
    > console.log('Hello Node'); # 回车
    Hello Node
    
    # 退出交互模式
    两次 ctrl+c
    # 或
    输入 .exit
    

    交互模式适合调试代码,相当于浏览器上的控制台。

内置模块(也叫核心模块)是官方提供的,无需下载,可以直接使用的模块。

官网:nodejs.cn/api/

  1. console

    console 模块提供了一个简单的调试控制台,类似于 Web 浏览器提供的 JavaScript 控制台。

    // 不同类型的数据,输出后颜色不同
    console.log('1');
    console.log(1);
    
    var obj = { name: 'Tom', age: 19 }
    console.log(obj)
    console.table(obj) // 以表格方式展示数据
    
    console.time('for');//开始计时
    for (var i = 1; i <= 100000; i++) {
    }
    console.timeEnd('for');//结束计时
    
    console.time('while');
    var i = 1;
    while (i <= 100000) {
      i++;
    }
    console.timeEnd('while');
    
  2. process

    process 对象是一个全局变量,提供了有关当前 Node.js 进程的信息并对其进行控制。 作为全局变量,它始终可供 Node.js 应用程序使用,无需使用 require()。 它也可以使用 require() 显式地访问:

    // process 是全局变量,可以不写 require 引入
    const process = require('process');
    
    // 获取操作系统架构  x64
    console.log(process.arch)
    
    // 当前系统平台信息  win32
    console.log(process.platform)
    
    // 获取当前文件所在的目录 D:\cliu\Desktop\node
    console.log(process.cwd())
    
    // 环境变量
    console.log(process.env)
    // 自定义环境变量
    process.env.NODE_ENV = 'develop'
    console.log(process.env)
    
    // 获取进程的编号
    console.log(process.pid)
    
    // 杀死进程  process.kill(进程编号)
    
  3. path

    path 模块负责文件路径的

    • ./ 表示当前目录
    • ../ 表示上一级目录
    • __dirname 返回当前文件所在的目录
    • __filename 返回当前文件的完整路径(目录+文件)
    const path = require('path')
    
    // __dirname 获取当前文件所在的目录
    path.join(__dirname, 'dist')
    
    console.log('join用于拼接多个路径部分,并转化为正常格式');
    const temp = path.join(__dirname, '..', 'lyrics', './友谊之光.lrc');
    console.log(temp);
    
    console.log('获取路径中的文件名');
    console.log(path.basename(temp));
    
    console.log('获取一个路径中的目录部分');
    console.log(path.dirname(temp));
    
    console.log('获取一个路径中最后的扩展名');
    console.log(path.extname(temp));
    
  4. fs

    fs (file system)模块主要负责文件基本操作

    1. 文件操作

      // 使用 fs 之前先引入
      const fs = require('fs')
      
      // 写文件 (清空写入:写入之前会先将文件清空)
      # fs.writeFile('文件路径','写入内容',回调函数)
      fs.writeFile('./1.txt', '曾经有一首歌,她感动了我', (err) => {
          if (err) throw err
          console.log('写入成功')
      })
      
      // 读文件
      # fs.readFile('文件路径', 回调函数)
      fs.readFile('./1.txt', (err, data) => {
          if (err) throw err
          // data 是二进制数据,默认输出时,以十六进制的方式展示
          // 想要看到正常的效果,需要通过 toString() 转换
          console.log(data.toString())
      })
      
      // 删除文件
      # fs.unlink('文件路径', 回调函数)
      fs.unlink(__dirname+'/1.txt', (err) => {
          if (err) throw err
          console.log('删除成功')
      })
      
      // 追加写入(多次执行,文件中会有多条数据)
      # fs.appendFile('文件路径','写入内容',回调函数)
      fs.appendFile(__dirname+'/2.txt', '曾经有一首歌,她是这样唱的\n', (err) => {
          if (err) throw err
          console.log('追加写入成功') 
      })
      
    2. 目录操作

      // 创建目录
      fs.mkdir('./d1', (err) => {
        if (err) throw err
        console.log('创建成功')
      })
      
      // 删除目录
      fs.rmdir('./d1', (err) => {
        if (err) throw err
        console.log('删除成功')
      })
      
      // 重命名目录
      fs.rename(__dirname+'/d1', __dirname+'/d2', (err) => {
        if (err) throw err
        console.log('重命名成功')
      })
      
      // 读取目录
      fs.readdir(__dirname, (err, data) => {
        if (err) throw err
        // console.log(data) // data 是数组
        data.map((d) => {
          console.log(d)
        })
      })
      
      // 判断文件是否存在
      if (!fs.existsSync('./d2')) {
        fs.mkdirSync('./d2')
      }
      
    3. fs.stat 查看状态

      // 查看文件信息
      fs.stat(__dirname+"/a.txt", (err, stat) => {
        if (err) throw err
        if (stat.isDirectory()) {
          // 判断当前文件是否是目录
          console.log('目录:', d)
        } else if (stat.isFile()) {
          // 判断当前文件是否是普通文件
          console.log('文件:', d)
        }
      })
      
  5. http

    以前,我们使用 Apache 或 Nginx 来搭建服务器。Node.js 中,也有搭建服务器的模块。就是 http 模块。

    const http = require('http')
    
    // 1. 创建服务器
    /**
     * req = request  请求
     * res = response 响应
     */
    const server = http.createServer((req, res) => {
        res.statusCode = 200
        res.setHeader('Content-Type', 'text/plain; charset=utf-8')
        res.end('你好:Node.js')
    })
    
    // 2. 发布 web 服务
    const port = 3000
    const host = 'localhost'
    // 在浏览器中访问 http://localhost:3000 然后能看到效果
    server.listen(port, host, () => {
        console.log(`服务器运行在 http://${host}:${port}`)
    })
    

Yeoman 最佳实践

仅需要几条安装命令,我们就能获得一个项目的半成品。( 这里以 generator-webapp 这一款生成器为例 )

# 全局安装 yo
npm install --global yo

# 安装对应的生成器( generator )
npm install --global generator-webapp

# 创建项目
mkdir project-name
cd project-name

# 通过 yo 运行 generator ( 时间有点长 )
yo webapp

# 运行看效果
npm run start

npm scripts

  1. 什么是 npm scripts

    npm 允许在 package.json 文件里面,使用 scripts 字段定义脚本命令。

    {
      "scripts": {
        // 命令名称: 任务
        "foo": "node bar.js"
      }
    }
    

    scripts 字段是一个对象。它的每一个属性,对应一段脚本。比如,foo 命令对应的脚本是node bar.js

    # 命令行下使用 npm run <命令>,就可以执行这段脚本。
    $ npm run foo
    # 等同于执行
    $ node bar.js
    
  2. 通配符

    npm 脚本就是 Shell 脚本,因为可以使用 Shell 通配符

    "style": "lessc *.less"
    "style": "lessc **/*.less"
    

    * 表示任意文件名,** 表示任意一层子目录。

  3. 执行顺序 如果是并行执行(即同时的平行执行),可以使用 & 符号。

    {
      "scripts": {
        "parallel": "node task1.js & node task2.js & node task3.js"
      }
    }
    

    如果是串行执行(前一个任务成功后,才执行下一个任务),可以使用 && 符号。

    {
      "scripts": {
        "series": "node task1.js && node task2.js && node task3.js"
      }
    }
    

    **但是,& 符号在 Windows 操作系统下不起作用。**此时,我们可以借助插件,在 Windows 下实现并行操作:

    npm-run-all

先在项目中安装

npm i npm-run-all -D

并行执行:其中 p 是 parallel(并行)的意思

npm-run-all -p 脚本1 脚本2 脚本3

或简写为

run-p 脚本1 脚本2 脚本3

串行执行:其中 s 是 series(串行)的意思

npm-run-all -s 脚本1 脚本2 脚本3

或简写为

run-s 脚本1 脚本2 脚本3

基本用法

Gulp 使用的基本逻辑是:先声明任务,再从命令行中执行任务;具体步骤如下:

  1. 使用 Gulp 之前,先在全局安装 gulp-cli

    # 安装 gulp 命令行工具
    npm i -g gulp-cli 
    
    # 验证安装是否成功
    gulp -v
    
  2. 初始化项目

    # 创建项目目录
    mkdir project-name
    
    # 进入项目目录
    cd project-name
    
    # 初始化项目
    npm init --yes
    
  3. 安装 Gulp 包

    # 安装 gulp 包,作为开发时依赖项
    npm i gulp -D
    
  4. 创建 gulpfile 文件

    gulpfile 文件是项目根目录下的 gulpfile.js在运行 gulp 命令时会被自动加载。在这个文件中,你经常会看到类似 src()dest()series()parallel() 函数之类的 Gulp API,除此之外,纯 JavaScript 代码或 Node.js 模块也会被使用。任何导出( exports )的函数都将注册到 Gulp 的任务(task)系统中。

    # 创建任务,任务结束后,需要通过回调函数去标记
    exports.foo = () => {
      console.log('foo task is running')
    }
    

    报错:

    The following tasks did not complete: task Did you forget to signal async completion?

    解释:在最新的 Gulp 中,取消了同步代码模式。约定每个任务都必须是一个异步任务

    解决:再函数参数中,设定回调函数(回调函数是异步操作)

  5. 在 gulpfile.js 中注册 Gulp 任务

    # 创建任务,并导出任务
    exports.foo = cb => {
      console.log('foo task is running')
        
      cb()
    }
    
    # 旧版 Gulp 注册任务的语法(无需执行导出操作)
    gulp.task('foo', function(cb) {
      console.log('foo task is running')
        
      cb()
    });
    
  6. 运行 Gulp 任务

    # 运行 foo 任务
    # 如需运行多个任务(task),可以执行 gulp <task> <othertask>
    gulp foo
    
  7. 创建默认任务

    # 默认任务的名称是 default
    exports.default = cb => {
        console.log('default task is running')
        
        cb()
    }
    
    # 运行默认任务, gulp 后无需指定任务名称
    gulp # 效果等同于 gulp default
    

组合任务

Gulp 提供了两个强大的组合方法: series()parallel()

如果需要让任务(task)按顺序执行,请使用 series() 方法(相当于 npm scripts 中的 && )。

如果希望任务(tasks)并行执行,可以使用 parallel() 方法将它们组合起来(相当于 npm scripts 中的 & )。

const gulp = require('gulp')

const task1 = cb => {
  setTimeout(() => {
    console.log('Task 1 is running')
    cb()
  }, 1000)
}

const task2 = cb => {
  setTimeout(() => {
    console.log('Task 2 is running')
    cb()
  }, 1000)
}

const task3 = cb => {
  setTimeout(() => {
    console.log('Task 3 is running')
    cb()
  }, 1000)
}

# 串行方式执行任务,先执行task1, 然后执行task2, 然后执行task3
exports.foo = gulp.series(task1, task2, task3)

# 并行方式执行任务,同时执行task1,task2,task3
exports.bar = gulp.parallel(task1, task2, task3)

# 执行命令
gulp foo # 串行执行
gulp bar # 并行执行

series()parallel() 可以被嵌套到任意深度。通过这两个函数,构建任务可以被任意排列组合,从而满足各种复杂的构建需求。

文件操作

gulp 暴露了 src()dest() 方法用于处理计算机上存放的文件。在代码构建过程中,需要将源文件,写入到目标目录。

# 通过 解构 的方式引入 gulp 中的函数
const { src, dest } = require('gulp')

exports.default = () => {
  // 文件操作
  // 将 src/styles 目录下的 main.css 文件,复制到 dist/styles 目录下
  return src('src/styles/main.less', { base: 'src' }).pipe(dest('dist'))
}

# 执行命令
gulp default
# 或直接执行
gulp