vue-cli学习

Posted on By yuanfang

package.json字段

官网英文 https://docs.npmjs.com/files/package.json
中文 https://blog.csdn.net/u013909970/article/details/80496704
学习参考文档 http://javascript.ruanyifeng.com/nodejs/packagejson.html
学习参考文档 – package.json 非官方字段集合

1)使用 npm init 即可在当前目录创建一个 package.json 文件
2)scripts 指定了运行脚本命令的npm命令行缩写,比如start指定了运行npm run start 时,所要执行的命令。
3)dependencies 字段指定了项目运行所依赖的模块,devDependencies 指定项目开发所需要的模块;–-save:模块名将被添加到 dependencies,可以简化为参数 -S 。-–save-dev:模块名将被添加到 devDependencies,可以简化为参数 -D
指定版本:比如”classnames”: “2.2.5”,表示安装2.2.5的版本
波浪号~+指定版本:比如 ”babel-plugin-import”: “~1.1.0”,表示安装1.1.x的最新版本(不低于1.1.0),但是不安装1.2.x,也就是说安装时不改变大版本号和次要版本号
^+指定版本:比如 ”antd”: “^3.1.4”,,表示安装3.1.4及以上的版本,但是不安装4.0.0,也就是说安装时不改变大版本号。
4)bin 项用来指定各个内部命令对应的可执行文件的位置。 "bin": { "someTool": "./bin/someTool.js"} 上面代码指定,someTool 命令对应的可执行文件为 bin 子目录下的 someTool.js。Npm会寻找这个文件,在node_modules/.bin/目录下建立符号链接。在上面的例子中,someTool.js会建立符号链接npm_modules/.bin/someTool。由于node_modules/.bin/目录会在运行时加入系统的PATH变量,因此在运行npm时(也就是执行npm run someTool),就可以不带路径,直接通过命令来调用这些脚本
5)main 字段指定了加载的入口文件,require(‘moduleName’)就会加载这个文件。这个字段的默认值是模块根目录下面的index.js
6)files 字段是一个被项目包含的文件名数组,如果你在里面放一个文件夹名,那么这个文件夹中的所有文件都会被包含进项目中(除非是那些在其他规则中被忽略的文件)
—-以下是非官方字段 7) unpkg: 让 npm 上所有的文件都开启 cdn 服务; 例如正常情况下,访问 jquery 的发布文件通过 https://unpkg.com/jquery@3.3.1/dist/jquery.js,当你使用省略的 url https://unpkg.com/jquery 时,便会按照如下的方式获取文件:# 定义了 unpkg 属性时 https://unpkg.com/jquery@[latestVersion]/[pkg.unpkg] # 未定义 unpkg 属性时,将回退到 main 属性 https://unpkg.com/jquery@[latestVersion]/[pkg.main]
8)types, typings 像 main 字段一样,定义一个针对 TypeScript 的入口文件
9)browserslist: 设置项目的浏览器兼容情况

npm 介绍

注意attention: 转载文章-来源 NPM 学习笔记整理https://segmentfault.com/p/1210000009653830/read
1)npm init 用来初始化生成一个新的 package.json 文件。它会向用户提问一系列问题,如果你觉得不用修改默认配置,一路回车就可以了。
如果使用了 -f(代表force)、-y(代表yes),则跳过提问阶段,直接生成一个新的 package.json 文件。
2)npm info 命令可以查看每个模块的具体信息。比如,查看 underscore 模块的信息 npm info underscore
3)npm list 命令以树形结构列出当前项目安装的所有模块,以及它们依赖的模块。 4)包的发布:在发布前,我们还需要获得一个账号用于今后维护自己的包,使用 npm adduser 根据提示完成账号的创建,完成后可以使用 npm whoami 检测是否已经取得了账号。接下来,在 package.json 所在目录下运行 npm publish,稍等片刻就可以完成发布了。
如果你的包将来有更新,只需要在 package.json 文件中修改 version 字段,然后重新使用 npm publish命令就行了。 如果你对已发布的包不满意,可以使用 npm unpublish 命令来取消发布。
5)npm link 创建全局链接,在本地包和全局包之间创建符号链接,适合用于包的开发与调试;学习参考-npm link的使用
6)npm 全局安装,npm install 添加参数 –unsafe-perm
7)npm之version和tag: npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git] –更改 package.json 中version,保存修改并生产一个新的commit
npm publish --tag betanpm install somepkg@beta –使用标签发布和安装 npm dist-tag ls –查看当前的tag和对应的version
npm dist-tag add <pkg>@<version> [<tag>] –标记添加到包的特定版本,比如更新tag到latest,npm dist-tag add <pkg>@<version> latest

npm中一般情况下,我们可以不指定tag,这时默认就会用latest这个tag,所有发布或者安装都是最新的正式版。而指定tag之后,我们可以在这个tag上发布一个更新的版本,用户安装的时候如果也指定这个tag,则会安装这个tag下的最新版。因此,npm中的tag类似于git中的branch。

commander.js

github来源 https://github.com/tj/commander.js

需要注意 When .command() is invoked with a description argument, no .action(callback) should be called to handle sub-commands, otherwise there will be an error. This tells commander that you’re going to use separate executables for sub-commands, much like git(1) and other popular tools.
The commander will try to search the executables in the directory of the entry script (like ./examples/pm) with the name program-command, like pm-install, pm-search.

#!/usr/bin/env node

var program = require('commander');

function range(val) {
    return val.split('..').map(Number);
}

function list(val) {
    return val.split(',');
}

function collect(val, memo) {
    memo.push(val);
    return memo;
}

function increaseVerbosity(v, total) {
    return total + 1;
}

program
    .version('0.0.1')
    .usage('test')
    .option('-i, --integer <n>', 'An integer argument', parseInt)
    .option('-f, --float <n>', 'A float argument', parseFloat)
    .option('-r, --range <a>..<b>', 'A range', range)
    .option('-l, --list <items>', 'A list', list)
    .option('-o, --optional [value]', 'An optional value')
    .option('-c, --collect [value]', 'A repeatable value', collect, [])
    .option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0);

program
    .command('exec <cmd>')
    .alias('ex')
    .description('execute the given remote cmd')
    .option("-e, --exec_mode <mode>", "Which exec mode to use")
    .action(function (cmd, options) {
        console.log('exec "%s" using %s mode', cmd, options.exec_mode);
    }).on('--help', function () {
        console.log('  Examples:');
        console.log();
        console.log('    $ deploy exec sequential');
        console.log('    $ deploy exec async');
        console.log();
    });

program.parse(process.argv);

console.log(' int: %j', program.integer);
console.log(' float: %j', program.float);
console.log(' optional: %j', program.optional);
program.range = program.range || [];
console.log(' range: %j..%j', program.range[0], program.range[1]);
console.log(' list: %j', program.list);
console.log(' collect: %j', program.collect);
console.log(' verbosity: %j', program.verbose);
console.log(' args: %j', program.args);

node ./test-commander.js exec async -e llmode  -i 5 -f 2.99 -r 8..9 -l 7,9,89 -o rr -v 99
exec "async" using llmode mode
 int: 5
 float: 2.99
 optional: "rr"
 range: 8..9
 list: ["7","9","89"]
 collect: []
 verbosity: 1
 args: [Circular]

vue-cli源码

注意attention: 文章转载来源 https://segmentfault.com/a/1190000013975247

1.node中文网

node中文网http://nodejs.cn/api/

const name = inPlace ? path.relative('../', process.cwd()) : rawName  //如果在当前目录下构建项目,当前目录名为项目构建目录名,否则是当前目录下的子目录【rawName】为项目构建目录名
//process cwd() 方法返回 Node.js 进程当前工作的目录
//path.relative() 方法返回从 from 到 to的相对路径(基于当前工作目录)。
const to = path.resolve(rawName || '.') //项目构建目录的绝对路径

2.handlebars

学习文档 英文http://handlebarsjs.com/block_helpers.html
中文http://keenwon.com/992.html

Handlebars.registerHelper('if', function(conditional, options) {
  if(conditional) {
    return options.fn(this);
  } else {
    return options.inverse(this);
  }
});

3.metalsmith静态网页生成器

学习文档 https://github.com/segmentio/metalsmith
学习参考文档 http://www.metalsmith.io/

Metalsmith(__dirname)    // instantiate Metalsmith in the cwd
  .source('sourcepath')        // specify source directory
  .destination('destpath')     // specify destination directory
  .use(markdown())             // transpile markdown into html
  .use(layouts())              // wrap a nunjucks layout around the html
  .build(function(err) {       // this is the actual build process
    if (err) throw err;    // throwing errors is required
  });
// 插件编写

/**
 * Expose `plugin`.
 */
module.exports = plugin;

/**
 * Metalsmith plugin to hide drafts from the output.
 *
 * @return {Function}
 */
function plugin() {
  return function(files, metalsmith, done){
    setImmediate(done);
    Object.keys(files).forEach(function(file){
      var data = files[file];
      if (data.draft) delete files[file];
    });
  };
}

4.mikefile文件

学习参考文档 http://www.ruanyifeng.com/blog/2015/03/build-website-with-make.html
学习参考文档 前端入门->makefile https://segmentfault.com/a/1190000004437816

//makefile的基本格式为:
target: prerequisities
[TAB]command
// target就是你要执行的命令套件,prerequisties就是依赖,而command就是实际执行的命令。

5.download

学习文档github https://github.com/kevva/download

//download(url, [destination], [options])
//----Returns both a Promise<Buffer> and a Duplex stream with additional events.
// 内部用的是get-stream
const fs = require('fs');
const download = require('download');

download('http://unicorn.com/foo.jpg', 'dist').then(() => {
	console.log('done!');
});

download('http://unicorn.com/foo.jpg').then(data => {
	fs.writeFileSync('dist/foo.jpg', data);
});

download('unicorn.com/foo.jpg').pipe(fs.createWriteStream('dist/foo.jpg'));

Promise.all([
	'unicorn.com/foo.jpg',
	'cats.com/dancing.gif'
].map(x => download(x, 'dist'))).then(() => {
	console.log('files downloaded!');
});

6.get-stream

学习文档github https://github.com/sindresorhus/get-stream

const fs = require('fs');
const getStream = require('get-stream');
 
(async () => {
    const stream = fs.createReadStream('unicorn.txt');
    console.log(await getStream(stream));
})();
// get-stream部分源码
let len = 0;
const ret = [];
const stream = new PassThrough({ objectMode });
if (encoding) {
    stream.setEncoding(encoding);
}
stream.on('data', chunk => {
    ret.push(chunk);
    if (objectMode) {
        len = ret.length;
    } else {
        len += chunk.length;
    }
});
stream.getBufferedValue = () => {
    if (array) {
        return ret;
    }
    return buffer ? Buffer.concat(ret, len) : ret.join('');
};
stream.getBufferedLength = () => len;

return stream;

6.consolidate

学习文档github https://github.com/tj/consolidate.js

//模板引擎打包库 关于Handlebars的源码
/**
 * Handlebars support.
 */
exports.handlebars = fromStringRenderer('handlebars');

/**
 * Handlebars string support.
 */
exports.handlebars.render = function(str, options, cb) {
  return promisify(cb, function(cb) {
    var engine = requires.handlebars || (requires.handlebars = require('handlebars'));
    try {
      for (var partial in options.partials) {
        engine.registerPartial(partial, options.partials[partial]);
      }
      for (var helper in options.helpers) {
        engine.registerHelper(helper, options.helpers[helper]);
      }
      var tmpl = cache(options) || cache(options, engine.compile(str, options));
      cb(null, tmpl(options));
    } catch (err) {
      cb(err);
    }
  });
};


// 在vue-cli里面使用
const render = require('consolidate').handlebars.render
render(str, metalsmithMetadata, (err, res) => {
    if (err) {
        err.message = `[${file}] ${err.message}`
        return next(err)
    }
    files[file].contents = new Buffer(res)
    next()
})

7.引入的包

const download = require('download-git-repo')  //用于下载远程仓库至本地 支持GitHub、GitLab、Bitbucket
const program = require('commander') //命令行处理工具
const exists = require('fs').existsSync  //node自带的fs模块下的existsSync方法,用于检测路径是否存在。(会阻塞)
const path = require('path') //node自带的path模块,用于拼接路径
const ora = require('ora') //用于命令行上的加载效果
const home = require('user-home')  //用于获取用户的根目录
const tildify = require('tildify') //将绝对路径转换成带波浪符的路径,例如tildify('/Users/sindresorhus/dev');  //=> '~/dev'
const chalk = require('chalk')// 用于高亮终端打印出的信息 console.log('\x1b[91m','message')
const inquirer = require('inquirer') //用于命令行与开发者交互,require('readline')处理
const rm = require('rimraf').sync // 相当于UNIX的“rm -rf”命令
const render = require('consolidate').handlebars.render // 模板引擎集成

// generate.js
const chalk = require('chalk')
const Metalsmith = require('metalsmith') // 静态网站生成器
const Handlebars = require('handlebars') // 知名的模板引擎。
const async = require('async') //非常强大的异步处理工具。
const render = require('consolidate').handlebars.render //支持各种模板引擎的渲染。
const path = require('path')
const multimatch = require('multimatch') // 可以支持多个条件的匹配。
const getOptions = require('./options')//自定义工具-用于获取模板配置。
const ask = require('./ask')//自定义工具-用于询问开发者。
const filter = require('./filter') //自定义工具-用于文件过滤。
const logger = require('./logger')  //自定义工具-用于日志打印。

8.generate.js

module.exports = function generate (name, src, dest, done) {
  const opts = getOptions(name, src)  //获取meta.json,meta.js配置
  const metalsmith = Metalsmith(path.join(src, 'template'))  //初始化Metalsmith对象
  const data = Object.assign(metalsmith.metadata(), {
    destDirName: name,
    inPlace: dest === process.cwd(),
    noEscape: true
  })//添加一些变量至metalsmith中,并获取metalsmith中全部变量
  
  //注册配置对象中的helper
  opts.helpers && Object.keys(opts.helpers).map(key => {
    Handlebars.registerHelper(key, opts.helpers[key])
  })

  const helpers = { chalk, logger }

 //配置对象是否有before函数,是则执行
  if (opts.metalsmith && typeof opts.metalsmith.before === 'function') {
    opts.metalsmith.before(metalsmith, opts, helpers)
  }

  metalsmith.use(askQuestions(opts.prompts))  //询问问题
    .use(filterFiles(opts.filters))  //过滤文件
    .use(renderTemplateFiles(opts.skipInterpolation)) //渲染模板文件


  //配置对象是否有after函数,是则执行
  if (typeof opts.metalsmith === 'function') {
    opts.metalsmith(metalsmith, opts, helpers)
  } else if (opts.metalsmith && typeof opts.metalsmith.after === 'function') {
    opts.metalsmith.after(metalsmith, opts, helpers)
  }

  metalsmith.clean(false) 
    .source('.') // start from template root instead of `./src` which is Metalsmith's default for `source`
    .destination(dest)
    .build((err, files) => {
      done(err)
      if (typeof opts.complete === 'function') {
      //配置对象有complete函数则执行
        const helpers = { chalk, logger, files }
        opts.complete(data, helpers)
      } else {
      //配置对象有completeMessage,执行logMessage函数
        logMessage(opts.completeMessage, data)
      }
    })

  return data
}

/**
 * Create a middleware for asking questions.
 *
 * @param {Object} prompts
 * @return {Function}
 */

function askQuestions (prompts) {
  return (files, metalsmith, done) => {
    ask(prompts, metalsmith.metadata(), done)
  }
}

/**
 * Create a middleware for filtering files.
 *
 * @param {Object} filters
 * @return {Function}
 */

function filterFiles (filters) {
  return (files, metalsmith, done) => {
    filter(files, filters, metalsmith.metadata(), done)
  }
}

/**
 * Template in place plugin.
 *
 * @param {Object} files
 * @param {Metalsmith} metalsmith
 * @param {Function} done
 */

function renderTemplateFiles (skipInterpolation) {
  skipInterpolation = typeof skipInterpolation === 'string'
    ? [skipInterpolation]
    : skipInterpolation    //保证skipInterpolation是一个数组
  return (files, metalsmith, done) => {
    const keys = Object.keys(files) //获取files的所有key
    const metalsmithMetadata = metalsmith.metadata() //获取metalsmith的所有变量
    async.each(keys, (file, next) => { //异步处理所有files
      // skipping files with skipInterpolation option  
      // 跳过符合skipInterpolation的要求的file
      if (skipInterpolation && multimatch([file], skipInterpolation, { dot: true }).length) {
        return next()
      }
      //获取文件的文本内容
      const str = files[file].contents.toString()
      // do not attempt to render files that do not have mustaches
      //跳过不符合handlebars语法的file
      if (!/\{\{([^{}]+)\}\}/g.test(str)) {  
        return next()
      }
      //渲染文件
      render(str, metalsmithMetadata, (err, res) => {
        if (err) {
          err.message = `[${file}] ${err.message}`
          return next(err)
        }
        files[file].contents = new Buffer(res)
        next()
      })
    }, done)
  }
}
/**
 * Display template complete message.
 *
 * @param {String} message
 * @param {Object} data
 */

function logMessage (message, data) {
  if (!message) return  //没有message直接退出函数
  render(message, data, (err, res) => {
    if (err) {
      console.error('\n   Error when rendering template complete message: ' + err.message.trim())  //渲染错误打印错误信息
    } else {
      console.log('\n' + res.split(/\r?\n/g).map(line => '   ' + line).join('\n'))
      //渲染成功打印最终渲染的结果
    }
  })
}