想阅读源码又无从下手?给你一张寻宝图

80 阅读2分钟

大型源码与业务代码不同,并没有显而易见的入口,本文记录如何理清大型文章脉络,快速开启对源码的阅读。

要了解目录结构,从package.json入手。
正常情况下,main代表着入口文件。然而,在vue2源码中,main对应着dist下的文件,我们不得不将视野转向别的方向。

"main": "dist/vue.runtime.common.js",

在根据main寻找线索无果的情况下,我们将视线转移至命令行,rollup的打包方式拥有入口的配置文件,我们由此向下寻找

"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
"build": "node scripts/build.js",

在build.js中,我们发现了两行目标代码

let builds = require('./config').getAllBuilds()
build(builds)

尽管我们没有直接找到入口文件,但通过第一行语句,我们找到了配置项。第二行build正是依据此配置项进行打包,顺理成章的进入config.js,我们发现这正是命令行dev的配置项,此时run和build的配置项统一了,我们的方向是正确的。

面对配置项,首先去找exports,也就是这个文件暴露了什么东西到外面。

if (process.env.TARGET) {
  module.exports = genConfig(process.env.TARGET)
} else {
  exports.getBuild = genConfig
  exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}

随即,找到genConfig方法。

function genConfig (name) {
  const opts = builds[name]
  const config = {
    input: opts.entry,
    external: opts.external,
    plugins: [
      flow(),
      alias(Object.assign({}, aliases, opts.alias))
    ].concat(opts.plugins || []),
    output: {
      file: opts.dest,
      format: opts.format,
      banner: opts.banner,
      name: opts.moduleName || 'Vue'
    },
    onwarn: (msg, warn) => {
      if (!/Circular/.test(msg)) {
        warn(msg)
      }
    }
  }

  // built-in vars
  const vars = {
    __WEEX__: !!opts.weex,
    __WEEX_VERSION__: weexVersion,
    __VERSION__: version
  }
  // feature flags
  Object.keys(featureFlags).forEach(key => {
    vars[`process.env.${key}`] = featureFlags[key]
  })
  // build-specific env
  if (opts.env) {
    vars['process.env.NODE_ENV'] = JSON.stringify(opts.env)
  }
  config.plugins.push(replace(vars))

  if (opts.transpile !== false) {
    config.plugins.push(buble())
  }

  Object.defineProperty(config, '_name', {
    enumerable: false,
    value: name
  })

  return config
}

genConfig方法return了config对象,而config对象依赖常量builds,dev命令执行索引web-full-dev,其配置如下

// Runtime+compiler development build (Browser)
  'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },

自此,找到了入口entry,发现entry并不直接是有效地址,而是方法,寻找方法

const aliases = require('./alias')
const resolve = p => {
  const base = p.split('/')[0]
  if (aliases[base]) {
    return path.resolve(aliases[base], p.slice(base.length + 1))
  } else {
    return path.resolve(__dirname, '../', p)
  }
}

方法依赖alias,查找alias兑换到entry的真正路径:src/platforms/web/entry-runtime-with-compiler.js