手把手搭建脚之原理剖析

112 阅读4分钟

下面文字很枯燥,请耐心读完,它对你后面的学习非常有用

什么是脚手架?

对于前端的同学来说,脚手架并不陌生,尽管你不知道脚手架是如何运行,但你一定会使用,例如你的vue项目react项目,当你新建一个vue项目你一定会使用下面这个命令让脚手架帮你,而不是自己手动去创建文件来组合成一个项目.

vue create hello-world

当你在终端输入上面的命令时,脚手架就会帮助我们去初始化一个项目,生成项目结构。整个项目的初始化可能不到一分钟,而如果你是手动去创建可能半个小时还不一定能完成,甚至项目无法启动。这就是脚手架的魅力,当然脚手架的魅力远不止这里,当公司业务越来越多,会有多个项目,此事一定有一些是通用的功能,例如一些工具代码、网络框架代码等等,传统做法就是新增一个项目就把这些共有的逻辑拷贝一份。如果要增加某个功能,或者某个功能有bug,这个时候你需要去维护多套代码。这时候如果公司开发一套自己的脚手架来做,那么问题可能就迎刃而解。

哪些人需要脚手架

  • 公司业务线较多,公共代码在多个项目里引用
  • 想进阶成为架构师
  • 公司项目进行工程化
  • 想要升职加薪

从原理入手,胜过一切

首先,回忆一下你怎么使用vue-cli脚手架的?

  1. 安装脚手架 npm install -g @vue/cli
  2. 终端输入 vue create hello-world 回车
  3. npm install
  4. npm run start

此时,眼前一亮,一个vue项目就正式运行起来了,那么以上4步到底发生了什么?

通过which vue看一下这个vue是执行的哪个文件

image.png

通过上面的可以看出在终端输入vue其实就是执行了../lib/node_modules/@vue/cli/bin/vue.js, 可以通过上面的截图知道,包括npm、npx都是通过软链,通过软链关联到真正的执行文件。vue的执行文件是vue.js

部分vue.js节选代码

const fs = require('fs')
const path = require('path')
const slash = require('slash')
const minimist = require('minimist')

// enter debug mode when creating test repo
if (
  slash(process.cwd()).indexOf('/packages/test') > 0 && (
    fs.existsSync(path.resolve(process.cwd(), '../@vue')) ||
    fs.existsSync(path.resolve(process.cwd(), '../../@vue'))
  )
) {
  process.env.VUE_CLI_DEBUG = true
}

const program = require('commander')
const loadCommand = require('../lib/util/loadCommand')

program
  .version(`@vue/cli ${require('../package').version}`)
  .usage('<command> [options]')

program
  .command('create <app-name>')
  .description('create a new project powered by vue-cli-service')
  .option('-p, --preset <presetName>', 'Skip prompts and use saved or remote preset')
  .option('-d, --default', 'Skip prompts and use default preset')
  .option('-i, --inlinePreset <json>', 'Skip prompts and use inline JSON string as preset')
  .option('-m, --packageManager <command>', 'Use specified npm client when installing dependencies')
  .option('-r, --registry <url>', 'Use specified npm registry when installing dependencies (only for npm)')
  .option('-g, --git [message]', 'Force git initialization with initial commit message')
  .option('-n, --no-git', 'Skip git initialization')
  .option('-f, --force', 'Overwrite target directory if it exists')
  .option('--merge', 'Merge target directory if it exists')
  .option('-c, --clone', 'Use git clone when fetching remote preset')
  .option('-x, --proxy <proxyUrl>', 'Use specified proxy when creating project')
  .option('-b, --bare', 'Scaffold project without beginner instructions')
  .option('--skipGetStarted', 'Skip displaying "Get started" instructions')
  .action((name, cmd) => {
    const options = cleanArgs(cmd)

    if (minimist(process.argv.slice(3))._.length > 1) {
      console.log(chalk.yellow('\n Info: You provided more than one argument. The first one will be used as the app\'s name, the rest are ignored.'))
    }
    // --git makes commander to default git to true
    if (process.argv.includes('-g') || process.argv.includes('--git')) {
      options.forceGit = true
    }
    require('../lib/create')(name, options)
  })

这部分代码就是去做命令解析的,然后根据终端不同的命令执行不同的逻辑,vue.js也引用了commander来处理命令,后面会有一篇专栏讲解commander的使用。

你可能还有个疑问,在安装vue-cli的时候并没有创建软链指向vue.js啊,其实这是npm在下载安装包后去分析package.json里面有没有下面代码,如果有就会自动帮你创建软链

"bin": {
    "vue": "bin/vue.js"
  },

vue package.json

讲完了上面的相信你谜团都解开了,下面说一下npm包管理器下载的包是怎么处理,安装在什么位置的:

npm包管理器

当我们在电脑上安装了node.js后,默认也会装上npm, 此时就可以在终端输入npm命令进行包管理的一系列操作。 npm install <packagename> 会从npm下载安装包到本地,那么这个包会安装到哪里呢,

  • 本地安装 默认情况下,当输入 npm install 命令时,例如:
npm install lodash

软件包会被安装到当前文件树中的 node_modules 子文件夹下。

在这种情况下,npm 还会在当前文件夹中存在的 package.json 文件的 dependencies 属性中添加 lodash 条目。

  • 全局安装 使用 -g 标志可以执行全局安装:
npm install -g lodash

在这种情况下,npm 不会将软件包安装到本地文件夹下,而是使用全局的位置。

全局的位置到底在哪里?

npm root -g 命令会告知其在计算机上的确切位置。

在 macOS 或 Linux 上,此位置可能是 /usr/local/lib/node_modules。 在 Windows 上,可能是 C:\Users\YOU\AppData\Roaming\npm\node_modules

但是,如果使用 nvm 管理 Node.js 版本,则该位置会有所不同。

例如,使用 nvm,则软件包的位置可能为 /Users/joe/.nvm/versions/node/v8.9.0/lib/node_modules