vue cli3源码解析

vue-cli3 源码解析

脚手架代码入口点

从package.json文件中可以看到"vue-cli-service": "bin/vue-cli-service.js"
目录结构

Service.js 指令、配置文件管理类 new Service(context)

constructor (context, { plugins, pkg, inlineOptions, useBuiltIn } = {}){
    // ...
}

PluginAPI.js 指令api,new PluginAPI(id, service)

//service是Service.js导出的实例对象
constructor (id, service) {
    this.id = id
    this.service = service
}
// 在init方法中初始化PluginAPI的方法从这段代码中可以看出循环初始化PluginAPI的参数service都是同一个this
// 使用了单例模式,使多个PluginAPI实例的service指向同一个Service实例
this.plugins.forEach(({ id, apply }) => {
    apply(new PluginAPI(id, this), this.projectOptions)
})

options.js 参数配置项

commands

// 指令目录这个目录下返回的都是一个函数function(api,options) api是PluginAPI实例,options是项目配置
module.exports = (api, options) =>{
    //...
}
// 指令对应的webpack模式
module.exports.defaultModes = {
  inspect: 'development'
}

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

this.modes = this.plugins.reduce((modes, { apply: { defaultModes }}) => {
    return Object.assign(modes, defaultModes)
}, {})

build
help.js
inspect.js
serve.js

config 各种开发模式下需要配置的的插件

// 指令目录这个目录下返回的都是一个函数function(api,options) api是PluginAPI实例,options是项目配置
module.exports = (api, options) =>{
    //...
}
app.js
base.js
css.js
dev.js
index-default.html
prod.js
terserOptions.js

使用npm进行调用时执行的代码

#!/usr/bin/env node

const semver = require('semver')
const { error } = require('@vue/cli-shared-utils')
const requiredVersion = require('../package.json').engines.node

if (!semver.satisfies(process.version, requiredVersion)) {
  error(
    `You are using Node ${process.version}, but vue-cli-service ` +
    `requires Node ${requiredVersion}.\nPlease upgrade your Node version.`
  )
  process.exit(1)
}

const Service = require('../lib/Service')
const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd())

const rawArgv = process.argv.slice(2)
const args = require('minimist')(rawArgv, {
  boolean: [
    // build
    'modern',
    'report',
    'report-json',
    'watch',
    // serve
    'open',
    'copy',
    'https',
    // inspect
    'verbose'
  ]
})
const command = args._[0]

service.run(command, args, rawArgv).catch(err => {
  error(err)
  process.exit(1)
})

架构初始化操作

  1. 构造函数中加载指令和参数配置项信息
  2. 构造函数中
  3. 初始化环境变量
  4. 加载用户配置项
  5. 将用户配置项与默认选项合并
  6. 初始化指令,环境配置选项
  7. 处理合并后的chainWebpack链路配置与configureWebpack选项
init (mode = process.env.VUE_CLI_MODE) {
    if (this.initialized) {
      return
    }
    this.initialized = true
    this.mode = mode

    // load mode .env
    if (mode) {
      this.loadEnv(mode)
    }
    // load base .env
    this.loadEnv()

    // load user config
    const userOptions = this.loadUserOptions()
    this.projectOptions = defaultsDeep(userOptions, defaults())

    debug('vue:project-config')(this.projectOptions)

    // apply plugins.
    this.plugins.forEach(({ id, apply }) => {
      apply(new PluginAPI(id, this), this.projectOptions)
    })

    // apply webpack configs from project config file
    if (this.projectOptions.chainWebpack) {
      this.webpackChainFns.push(this.projectOptions.chainWebpack)
    }
    if (this.projectOptions.configureWebpack) {
      this.webpackRawConfigFns.push(this.projectOptions.configureWebpack)
    }
}

环境变量加载顺序


loadEnv (mode) {
    const logger = debug('vue:env')
    const basePath = path.resolve(this.context, `.env${mode ? `.${mode}` : ``}`)
    const localPath = `${basePath}.local`

    const load = path => {
      try {
        const env = dotenv.config({ path, debug: process.env.DEBUG })
        dotenvExpand(env)
        logger(path, env)
      } catch (err) {
        // only ignore error if file is not found
        if (err.toString().indexOf('ENOENT') < 0) {
          error(err)
        }
      }
    }

    load(localPath)
    load(basePath)

    // ...
}

内置环境变量

  1. NODE_ENV 用于判断使用那些插件,设置那种webpack模式,这个变量不要随意修改
    process.env.NODE_ENV !== 'production' vue cli是根据这个变量来做webpack优化操作的,
// config/prod.js配置文件中的部分代码
if (process.env.NODE_ENV === 'production'){
    //...
}
  1. BASE_URL 内置的默认变量,同时也会注入到目标文件中,值为vue.config.js文件中的publicPath值
  2. VUE_CLI_SERVICE_CONFIG_PATH 用户配置文件地址绝对路径不能使用文件名或相对路径,不然会出问题
//获取用户配置文件代码
const configPath = (
    process.env.VUE_CLI_SERVICE_CONFIG_PATH ||
    path.resolve(this.context, 'vue.config.js')
)
  1. VUE_CLI_CONTEXT 执行js脚本的路径,这个地址和DllReferencePlugin插件的地址有一定的关联
const Service = require('../lib/Service')
const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd())
  1. VUE_CLI_MODE build指令选项中的 --mode
  2. VUE_CLI_BUILD_TARGET build指令下的 --target选项
  3. VUE_CLI_MODERN_MODE、VUE_CLI_MODERN_BUILD 现代化的脚本属性对应的插件ModernModePlugin.js
    主要的作用是在引入的js脚本中追加后修改link属性
    //use instead of
const isLegacyBundle = process.env.VUE_CLI_MODERN_MODE && !process.env.VUE_CLI_MODERN_BUILD
  1. VUE_CLI_SERVICE 指向指令管理实例 构造函数第一行代码process.VUE_CLI_SERVICE = this

常用的webpack插件

  1. HotModuleReplacementPlugin热加载模块用于development模式下,同时使用热加载模块时webpack的out.filename不能使用chunkhash或contenthash作为输出文件名只能使用hash或者不用hash来生成文件
  2. webpack.DefinePlugin这个插件用于将用户定义的变量切入到打包的目标文件中使用
// resolveClientEnv.js文件处理客户端的环境变量问题V以 UE_APP_开头的变量会切入到客户端的目标文件中
const prefixRE = /^VUE_APP_/
module.exports = function resolveClientEnv (options, raw) {
  const env = {}
  Object.keys(process.env).forEach(key => {
    if (prefixRE.test(key) || key === 'NODE_ENV') {
      env[key] = process.env[key]
    }
  })
  env.BASE_URL = options.publicPath

  if (raw) {
    return env
  }

  for (const key in env) {
    env[key] = JSON.stringify(env[key])
  }
  return {
    'process.env': env
  }
}
  1. webpack.DllPlugin和webpack.DllReferencePlugin这两个插件成对使用,可以将webpack中的模块从原有的依赖关系中抽离出来形成一个静态的模块资源

你可能感兴趣的:(前端框架,vue,cli3)