在NPM发布自己造的轮子的方法步骤

1、前言

自从Node.js出现,它的好基友npm(node package manager)也是我们日常开发中必不可少的东西。npm让js实现了模块化,使得复用其他人写好的模块(搬砖)变得更加方便,也让我们可以分享一些自己的作品给大家使用(造轮子),今天这里我就给大家分享一个用命令行压缩图片的工具,它的用法大致是这样的:

// 全局安装后,在图片目录下,运行这行
$ tinyhere

这样就把文件夹内的图片进行压缩。这里压缩采用的是 tinypng提供的接口,压缩率大致上是50%,基本可以压一半的大小。以前在写项目的时候,测试验收完成后总是要自己手动去压一次图片,后来想把这个枯燥重复的事自动化去完成(懒),但是公司脚手架又没有集成这个东西,就想自己写一个轮子做出来用用就好了。它的名字叫做tinyhere,大家可以去安装使用试一下

$ npm i tinyhere -g

2、npm简介

如果要写一个模块发布到npm,那么首先要了解一下npm的用法。

给这个模块建一个文件夹,然后在目录内运行npm init来初始化它的package.json,就是这个包的描述

// 个人比较喜欢后面带--yes,它会生成一个带默认参数的package.json
$ npm init (--yes)

package.json详情:

{
 "name": "pkgname", // 包名,默认文件夹的名字
 "version": "1.0.0",
 "description": "my package",
 "main": "index.js", // 如果只是用来全局安装的话,可以不写
 "bin": "cli", // 如果是命令行使用的话,必须要这个,名字就是命令名
 "scripts": {
  "test": "echo \"Error: no test specified\" && exit 1" // npm run test对应的test
 },
 "keywords": ['cli', 'images', 'compress'],
 "author": "croc-wend",
 "license": "MIT",
 ...
}

更多配置信息可以参考一下vue的package.json的https://github.com/vuejs/vue/blob/dev/package.json

初始化完成之后,你就可以着手写这个包了,当你觉得你写好了之后,就可以发布到npm上面

npm login
npm publish
+ pkgname@1.0.0 // 成功

这时,你在npm上面搜你的包名,你写在package.json 的信息都会被解析,然后你的包的页面介绍内容就是你的README.md

3、写这个包

包初始化好了之后,我们就可以开始写这个包了

对于这个压缩工具来说,要用到的素材只有两个,tinypng接口要用到的 api-key,需要压缩的图片,所以我对这两个素材需要用到的一些操作进行了以下分析:

我的初衷是想把这个命令写的尽量简单,让我可以联想到压缩图片=简单,所以我待定了整个包只有一个单词就能跑,是这样:

$ tinyhere

其他的操作都放在子命令和可选项上。

然后开始划分项目结构

大致上是这样,把全局命令执行的 tinyhere 放在bin目录下,然后subCommand负责提供操作函数,然后把可复用的函数(比如读写操作)抽离出来放在util上,比较复杂的功能单独抽离成一个文件,比如compress,然后导出一个函数给subCommand。至于存放用户的api-key,就存放在data下面的key里。

tinyhere的执行文件就负责解析用户的输入,然后执行subCommand给出的对应函数。

4、过程解析

压缩图片的这个包的过程是这样的:

1、解析当前目录内的所有图片文件,这里应该根据二进制流及文件头获取文件类型mime-type,然后读取文件二进制的头信息,获取其真实的文件类型,来判断它是否真的是图片文件,而不是那些仅仅是后缀名改成.png的假货

2、 如果用户有要求把压缩的图片存放到指定目录,那就需要生成一个文件夹来存放它们。那么,首先要判断这个路径是否合法,然后再去生成这个目录

3、判断用户的api-key的剩余次数是否足够这次的图片压缩,如果这个key不够,就换到下一个key,知道遍历文件内所有的key找到有可用的key为止。

4、图片和key都有了,这时可以进行压缩了。用一个数组把压缩失败的存起来,然后每次压缩完成都输出提示,在所有图片都处理完成后,如果存在压缩失败的,就询问是否把压缩失败的图继续压缩

5、这样,一次压缩就处理完成了。压缩过的图片会覆盖原有的图片,或者是存放到指定的路径里

ps:$ tinyhere deep >>> 把目录内的所有图片都进行压缩(含子目录)。这个命令和上述的主命令的流程有点不同,目前有点头绪,还没有开发完成,考虑到文件系统是树形结构,我目前的想法是通过深度遍历,把存在图片的文件夹当作一个单位,然后递归执行压缩。

其他:

这里吐槽一下tinypng 的接口写的真的烂。。在查询key的合法性的 validate 函数只接受报错的回调,但是成功却没有任何动作。我真是服了,之前是做延时来判断用户的key的合法性,最后实在是受不了这个bug一样的写法了,决定用Object.defineProperty来监听它的使用次数的变化。如果它的setter被调用则说明它是一个合法的key了

5、小结

在这里,我想跟大家说,如果你做了一个你觉得很酷的东西,也想给更多的人去使用,来让它变得更好,选择发布在NPM上面就是一个非常好的途径,看了上面的内容你会发现分享其实真的不难,你也有机会让世界看到属于你的风采!

如果大家觉得我有哪里写错了,写得不好,有其它什么建议(夸奖),非常欢迎大家补充。希望能让大家交流意见,相互学习,一起进步! 我是一名 19 的应届新人,以上就是今天的分享,新手上路中,后续不定期周更(或者是月更哈哈),我会努力让自己变得更优秀、写出更好的文章,文章中有不对之处,烦请各位大神斧正。如果你觉得这篇文章对你有所帮助,请记得点赞或者品论留言哦~。

6、写在最后

欢迎大家提issue或者建议!地址在这:

https://github.com/Croc-ye/tinyhere

https://www.npmjs.com/package/tinyhere

最后贴上部分代码,内容过长,可以跳过哦

bin/tinyhere

#!/usr/bin/env node

const commander = require('commander');
const {init, addKey, deleteKey, emptyKey, list, compress} = require('../libs/subCommand.js');
const {getKeys} = require('../libs/util.js');

// 主命令
commander
.version(require('../package').version, '-v, --version')
.usage('[options]')
.option('-p, --path <newPath>', '压缩后的图片存放到指定路径(使用相对路径)')
.option('-a, --add <key>', '添加api-key')
.option('--delete <key>', '删除指定api-key')
.option('-l, --list', '显示已储存的api-key')
.option('--empty', '清空已储存的api-key')

// 子命令
commander
.command('deep')
.description('把该目录内的所有图片(含子目录)的图片都进行压缩')
.action(()=> {
  // deepCompress();
  console.log('尚未完成,敬请期待');
})

commander.parse(process.argv);

// 选择入口
if (commander.path) {
  // 把图片存放到其他路径
  compress(commander.path);
} else if (commander.add) {
  // 添加api-key
  addKey(commander.add);
} else if (commander.delete) {
  // 删除api-key
  deleteKey(commander.delete);
} else if (commander.list) {
  // 显示api-key
  list();
} else if (commander.empty) {
  // 清空api-key
  emptyKey();
} else {
  // 主命令
  if (typeof commander.args[0] === 'object') {
    // 子命令
    return;
  }
  if (commander.args.length !== 0) {
    console.log('未知命令');
    return;
  }
  if (getKeys().length === 0) {
    console.log('请初始化你的api-key')
    init();
  } else {
    compress();
  }
};

libs/compress.js

const tinify = require('tinify');
const fs = require("fs");
const path = require('path');
const imageinfo = require('imageinfo');
const inquirer = require('inquirer');
const {checkApiKey, getKeys} = require('./util');

// 对当前目录内的图片进行压缩
const compress = (newPath = '')=> {
  const imageList = readDir();
  if (imageList.length === 0) {
    console.log('当前目录内无可用于压缩的图片');
    return;
  }
  newPath = path.join(process.cwd(), newPath);
  mkDir(newPath);

  findValidateKey(imageList.length);
  console.log('===========开始压缩=========');
  if (newPath !== process.cwd()) {
    console.log('压缩到: ' + newPath.replace(/\./g, ''));
  }
  compressArray(imageList, newPath);
};

// 生成目录路径
const mkDir = (filePath)=> {
  if (filePath && dirExists(filePath) === false) {
    fs.mkdirSync(filePath);
  }
}

// 判断目录是否存在
const dirExists = (filePath)=> {
  let res = false;
  try {
    res = fs.existsSync(filePath);
  } catch (error) {
    console.log('非法路径');
    process.exit();
  }
  return res;
};

/**
 * 检查api-key剩余次数是否大于500
 * @param {*} count 本次需要压缩的图片数目
 */
const checkCompressionCount = (count = 0)=> {
  return (500 - tinify.compressionCount - count) >> 0;
}

/**
 * 找到可用的api-key
 * @param {*} imageLength 本次需要压缩的图片数目
 */
const findValidateKey = async imageLength=> { // bug高发处
  const keys = getKeys();
  for (let i = 0; i < keys.length; i++) {
    await checkApiKey(keys[i]);
    res = checkCompressionCount(imageLength);
    if (res) return;
  }
  console.log('已存储的所有api-key都超出了本月500张限制,如果要继续使用请添加新的api-key');
  process.exit();
}

// 获取当前目录的所有png/jpg文件
const readDir = ()=> {
  const filePath = process.cwd()
  const arr = fs.readdirSync(filePath).filter(item=> {
    // 这里应该根据二进制流及文件头获取文件类型mime-type,然后读取文件二进制的头信息,获取其真实的文件类型,对与通过后缀名获得的文件类型进行比较。
    if (/(\.png|\.jpg|\.jpeg)$/.test(item)) { // 求不要出现奇奇怪怪的文件名。。
      const fileInfo = fs.readFileSync(item);
      const info = imageinfo(fileInfo);
      return /png|jpg|jpeg/.test(info.mimeType);
    }
    return false;
  });
  return arr;
};

/**
 * 对数组内的图片名进行压缩
 * @param {*} imageList 存放图片名的数组
 * @param {*} newPath 压缩后的图片的存放地址
 */
const compressArray = (imageList, newPath)=> {
  const failList = [];
  imageList.forEach(item=> {
    compressImg(item, imageList.length, failList, newPath);
  });
}

/**
 * 压缩给定名称的图片
 * @param {*} name 文件名
 * @param {*} fullLen 全部文件数量
 * @param {*} failsList 压缩失败的数组
 * @param {*} filePath 用来存放的新地址
 */
const compressImg = (name, fullLen, failsList, filePath)=> {
  fs.readFile(name, function(err, sourceData) {
    if (err) throw err;
    tinify.fromBuffer(sourceData).toBuffer(function(err, resultData) {
     if (err) throw err;
     filePath = path.join(filePath, name);
     const writerStream = fs.createWriteStream(filePath);
     // 标记文件末尾
     writerStream.write(resultData,'binary');
     writerStream.end();

     // 处理流事件 --> data, end, and error
     writerStream.on('finish', function() {
      failsList.push(null);
      record(name, true, failsList.length, fullLen);
      if (failsList.length === fullLen) {
        finishcb(failsList, filePath);
      }
     });

     writerStream.on('error', function(err){
      failsList.push(name);
      record(name, false, failsList.length, fullLen);
      if (failsList.length === fullLen) {
        finishcb(failsList, filePath);
      }
     });
    });
  });
}

// 生成日志
const record = (name, success = true, currNum, fullLen)=> {
  const status = success ? '完成' : '失败';
  console.log(`${name} 压缩${status}。 ${currNum}/${fullLen}`);
}

/**
 * 完成调用的回调
 * @param {*} failList 存储压缩失败图片名的数组
 * @param {*} filePath 用来存放的新地址
 */
const finishcb = (failList, filePath)=> {
  const rest = 500 - tinify.compressionCount;
  console.log('本月剩余次数:' + rest);
  const fails = failList.filter(item=> item !== null);
  if (fails.length > 0) {
    // 存在压缩失败的项目(展示失败的项目名),询问是否把压缩失败的继续压缩 y/n
    // 选择否之后,询问是否生成错误日志
    inquirer.prompt({
      type: 'confirm',
      name: 'compressAgain',
      message: '存在压缩失败的图片,是否将失败的图片继续压缩?',
      default: true
    }).then(res=> {
      if (res) {
        compressArray(failList, filePath);
      } else {
        // 询问是否生成错误日志
      }
    })
  } else {
    // 压缩完成
    console.log('======图片已全部压缩完成======');
  }
}

module.exports = {
  compress
}

libs/subCommand.js

const inquirer = require('inquirer');
const {compress} = require('./compress.js');
const {checkApiKey, getKeys, addKeyToFile, list} = require('./util.js');

module.exports.compress = compress;
module.exports.init = ()=> {
  inquirer.prompt({
    type: 'input',
    name: 'apiKey',
    message: '请输入api-key:',
    validate: (apiKey)=> {
      // console.log('\n正在检测,请稍候...');
      process.stdout.write('\n正在检测,请稍候...');
      return new Promise(async (resolve)=> {
        const res = await checkApiKey(apiKey);
        resolve(res);
      });
    }
  }).then(async res=> {
    await addKeyToFile(res.apiKey);
    console.log('apikey 已完成初始化,压缩工具可以使用了');
  })
}

module.exports.addKey = async key=> {
  await checkApiKey(key);
  const keys = await getKeys();
  if (keys.includes(key)) {
    console.log('该api-key已存在文件内');
    return;
  }
  const content = keys.length === 0 ? '' : keys.join(' ') + ' ';
  await addKeyToFile(key, content);
  list();
}

module.exports.deleteKey = async key=> {
  const keys = await getKeys();
  const index = keys.indexOf(key);
  if (index < 0) {
    console.log('该api-key不存在');
    return;
  }
  keys.splice(index, 1);
  console.log(keys);
  const content = keys.length === 0 ? '' : keys.join(' ');
  await addKeyToFile('', content);
  list();
}

module.exports.emptyKey = async key=> {
  inquirer.prompt({
    type: 'confirm',
    name: 'emptyConfirm',
    message: '确认清空所有已存储的api-key?',
    default: true
  }).then(res=> {
    if (res.emptyConfirm) {
      addKeyToFile('');
    } else {
      console.log('已取消');
    }
  })
}

module.exports.list = list;

libs/util.js

const fs = require('fs');
const path = require('path');
const tinify = require('tinify');
const KEY_FILE_PATH = path.join(__dirname, './data/key');

// 睡眠
const sleep = (ms)=> {
  return new Promise(function(resolve) {
    setTimeout(()=> {
      resolve(true);
    }, ms);
  });
}
// 判定apikey是否有效
const checkApiKey = async apiKey=> {
  return new Promise(async resolve=> {
    let res = true;
    res = /^\w{32}$/.test(apiKey);
    if (res === false) {
      console.log('api-key格式不对');
      resolve(res);
      return;
    }
    res = await checkKeyValidate(apiKey);
    resolve(res);
  })
}
// 检查api-key是否存在
const checkKeyValidate = apiKey=> {
  return new Promise(async (resolve)=> {
    tinify.key = apiKey;
    tinify.validate(function(err) {
      if (err) {
        console.log('该api-key不是有效值');
        resolve(false);
      }
    });
    let count = 500;
    Object.defineProperty(tinify, 'compressionCount', {
      get: ()=> {
        return count;
      },
      set: newValue => {
        count = newValue;
        resolve(true);
      },
      enumerable : true,
      configurable : true
    });
  });
};

// 获取文件内的key,以数组的形式返回
const getKeys = ()=> {
  const keys = fs.readFileSync(KEY_FILE_PATH, 'utf-8').split(' ');
  return keys[0] === '' ? [] : keys;
}

// 把api-key写入到文件里
const addKeyToFile = (apiKey, content = '')=> {
  return new Promise(async resolve=> {
    const writerStream = fs.createWriteStream(KEY_FILE_PATH);
    // 使用 utf8 编码写入数据
    writerStream.write(content + apiKey,'UTF8');

    // 标记文件末尾
    writerStream.end();

    // 处理流事件 --> data, end, and error
    writerStream.on('finish', function() {
      console.log('=====已更新=====');
      resolve(true);
    });

    writerStream.on('error', function(err){
      console.log(err.stack);
      console.log('写入失败。');
      resolve(false);
    });
  })
}

// 显示文件内的api-key
const list = ()=> {
  const keys = getKeys();
  if (keys.length === 0) {
    console.log('没有存储api-key');
  } else {
    keys.forEach((key)=> {
      console.log(key);
    });
  }
};
module.exports = {
  sleep,
  checkApiKey,
  getKeys,
  addKeyToFile,
  list
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 发布一款npm包帮助理解npm的使用

    npm 在前端工程化中扮演着不可或缺的角色,前端工程师每天通过npm安装项目依赖,通过npm发布自己的包,更新包,通过npm 脚本运行开发环境,打包等. 平时专注于实现业务,一直没有去了解一款包的发布流程,忙里偷闲,今天就体验下编写并发布一个package的过程,记录下来分享给大家. 一个包诞生的过程无非是:编写-测试-发布-迭代-测试-发布..., 初始化项目 mkdir math-tool_demo 新建文件夹 npm init初始化package.json文件,根据提示输入项目信息: pa

  • 详解如何制作并发布一个vue的组件的npm包

    前提:1.会vue基础,以及vue的组件(官网:https://cn.vuejs.org/v2/guide/components.html)相关的基础. 因为本文主要是讲如何把一个vue组件做成npm包并发布. 分为2大步骤: 一.按照相应格式写我们的vue代码(就如同写一个jquery插件时.有其固定的格式一样). 二.发布到npm上的流程 一.书写一个vue组件 不用脚手架,我们自己从头开始做起,因为脚手架会附带很多没用的东西. 就做一个最简单的vue组件:就是传入用户名字,页面打印出'he

  • Vue 组件封装 并使用 NPM 发布的教程

    正文开始 Vue 开发插件 我们可以先查看Vue的插件的开发规范 我们开发的之后期望的结果是支持 import.require 或者直接使用 script 标签的形式引入,就像这样: ps: 这里注意一下包的名字前缀是 unisoft ,组件的名字前缀是 uni import UniSoftUI from 'unisoft-ui'; // 或者 const CustomUI = require('unisoft-ui'); // 或者 <script src="...">&

  • 使用npm发布Node.JS程序包教程

    npm是Node.JS的程序包管理器.进行Node.JS开发时,经常使用它安装/卸载程序包.实际上,发布程序包的工作也是由它来完成的. 配置package.json 要打包程序,首先要配好各项设置,这些设置都由程序包根目录下的package.json指定.package.json的内容必须是严格的JSON格式,也就是说: 1.字符串要用双引号括起来,而不能用单引号: 2.属性名一定要加双引号: 3.最后一个属性后千万不要多加一个逗号. 配置对象的属性很多,具体可以参阅这里,这里列一下常用的项目:

  • Vue组件库发布到npm详解

    制作了一套自己的组件库,并发布到npm上,项目代码见https://github.com/hamger/hg-vcomponents 前期准备 有一个npm账号 安装了vue-cli 搭建项目 vue init webpack hg-vcomponents cd hg-vcomponents cnpm install 目录结构 - vue-flag-list + build + dist // 存放发布到npm的代码 - src - components // 存放组件源代码 和 README.

  • 自定义vue组件发布到npm的方法

    本文介绍了自定义vue组件发布到npm的方法,分享给大家,具体如下: 为什么会有这个想法呢,主要是vue项目中自定义的组件在多个项目中使用.导致修改bug的时候,总是要在项目分支中修改,然后同步到主线上,这样容易导致分支修改后没有同步到主线,慢慢的就会导致组件版本不统一,而导致升级组件很繁琐,最后可能都要去维护多个组件的不同版本,这样不是我们想要的. 所以就打算将组件打包发布到npm上,每个项目中只需要在package.json中修改组件版本即可同步最新版本. 组件发布历程 1.开始对打包不是很

  • 从零开始在NPM上发布一个Vue组件的方法步骤

    TL;DR 本文细致讲解了在NPM上发布一个Vue组件的全过程,包括创建项目.编写组件.打包和发布四个环节. 创建项目 这里我们直接利用@vue/cli来生成项目.如果没有安装@vue/cli的话,可以使用以下方法进行安装: # 如果喜欢npm npm i -g @vue/cli # 如果喜欢yarn yarn global add @vue/cli 此外,如果安装了npx(高版本的nodejs发行版会自带这一工具)的话,还可以很方便地通过npx vue这一方式实现免安装使用. 接下来就可以创建

  • 在NPM发布自己造的轮子的方法步骤

    1.前言 自从Node.js出现,它的好基友npm(node package manager)也是我们日常开发中必不可少的东西.npm让js实现了模块化,使得复用其他人写好的模块(搬砖)变得更加方便,也让我们可以分享一些自己的作品给大家使用(造轮子),今天这里我就给大家分享一个用命令行压缩图片的工具,它的用法大致是这样的: // 全局安装后,在图片目录下,运行这行 $ tinyhere 这样就把文件夹内的图片进行压缩.这里压缩采用的是 tinypng提供的接口,压缩率大致上是50%,基本可以压一

  • 用vue封装插件并发布到npm的方法步骤

    一.基于vue的国家区号列表 vue-flag-list包含了大部分国家的区号,点击右边的三角形展开列表可以选择国家区号,若列表中没有区号,也可以自己输入区号. 全球区号列表 1.1 初始化组件 用的是vue-cli来初始化组件,虽然有很多东西不需要,因为对这个比较熟悉,所以还是按照这个步骤来. vue init webpack vue-flag-list cd vue-flag-list cnpm install npm run dev 1.2 根据自己的需求,实现具体功能,我的主要功能写在v

  • vue2.0+ 从插件开发到npm发布的示例代码

    vue: V2.5.11 此篇尽量详细,清楚的讲解vue插件的开发到npm的发布,想想将你自己做的东西展示给广大"网民",心里还是有点小激动的...-^_^ 先上一下插件效果图------github传送门 下面我们就来说说详细做法: 1. 初始化项目 vue init webpack-simple vue-pay-keyboard 使用vue创建一个简单的项目,删除src中除了main.js和app.vue外的文件,清空app.vue中无用内容 整理完后项目目录 2.编写插件 vue

  • 利用原生JavaScript实现造日历轮子实例代码

    前言 在日常开发中,大多数都是在和框架打交道,久而久之便遗忘了原生JS的感觉,个人感觉中原生JS基础还是很重要的,所以最近就利用了空余时间造一个轮子出来,虽然以我的水平造出来的轮子质量还是不太可靠的,但是我觉得用来练练手还是不错的,哈哈!! So, Let's begin! github:github.com/Zero-jian/p- 以下是日历的样子,是有点难看,讲究讲究,重点在于JS部分,嘻嘻!!! 关于日历组件的实现思路 设置默认参数 检查节点参数是否传入,否则抛出错误 动态创建显示本日星

  • 使用命令行工具npm新创建一个vue项目的方法

    Vue.js 提供一个官方命令行工具,可用于快速搭建大型单页应用.该工具提供开箱即用的构建工具配置,带来现代化的前端开发流程. 只需几分钟即可创建并启动一个带热重载.保存时静态检查以及可用于生产环境的构建配置的项目: # 全局安装 vue-cli $ npm install --global vue-cli # 创建一个基于 webpack 模板的新项目 $ vue init webpack my-project $ vue init webpack test //输入命令 ? Project

  • vue中Npm run build 根据环境传递参数方法来打包不同域名

    项目开发中,前端在配置后端api域名时很困扰,常常出现: 本地开发环境: api-dev.demo.com 测试环境: api-test.demo.com 线上生产环境: api.demo.com, 这次是在Vue.js项目中打包,教大家个方法: 使用 npm run build -- xxx   ,根据传递参数xxx来判定不同的环境,给出不同的域名配置. 1.项目中/config/dev.env.js修改: 新增:HOST: '"dev"' 'use strict' const me

  • jenkins自动构建发布vue项目的方法步骤

    简介 Jenkins是一个开源的.提供友好操作界面的持续集成(CI)工具,起源于Hudson(Hudson是商用的),主要用于持续.自动的构建/测试软件项目.监控外部任务的运行(这个比较抽象,暂且写上,不做解释).Jenkins用Java语言编写,可在Tomcat等流行的servlet容器中运行,也可独立运行.通常与版本管理工具(SCM).构建工具结合使用.常用的版本控制工具有SVN.GIT,构建工具有Maven.Ant.Gradle. jenkins安装 1.安装JDK yum install

  • vue项目打包发布上线的方法步骤

    目录 一.开发环境到生产环境的转变 二.设置统一的请求路径 三.运行打包命令 vue项目开发完成后,我们需要将项目打包上线,同时我们希望可以在本地预览生产环境项目 (以vue-cli脚手架生成的项目为例) 一.开发环境到生产环境的转变 项目开发结束之后,首先我们需要通知后端,获取一个线上的路径,之后将之前的开发路径切换为线上路径. 打开项目中config文件夹里面的 dev.env.js 文件,将后端给的线上路径填入. 'use strict' module.exports = { NODE_E

  • Node.js npm命令运行node.js脚本的方法

    //通过npm运行node脚本 (控制台应用程序) cmd---cd package.json所在的目录---npm start (package.json中的scripts属性中设置start命令) cmd---npm (通过该命令可以查看npm后面能够设置的具体命令) package.json: { "scripts":{ "start": "node demo.js", // cmd环境中,npm start命令 就相当于 node dem

随机推荐