立诚勿怠,格物致知
It's all about connecting the dots

Vue2.5.16源码解读一:确定阅读入口

准备粗略地阅读下Vue,跟网上别人家的源码解读文章不同的是,这个系列是完全以第一次去读代码的视角带大家一起去读的,而非看完源码后以上帝的总结视角来写的系列文章。

要看我们就直接找比较新的版本看,fork vue代码到我们自己的仓库上,clone到本地后,执行git tag 发现最新的版本是2.5.16,所以在本地新开一个tag-v2.5.16分支方便阅读以及后续commit(如果要commit的话):

git checkout -b tag-v2.5.16

执行完,我们的本地项目就已经切换到新的tag-v2.5.16 分支上了。

拿到一个项目,我们首先看一下package.json 文件里的scripts 字段:

"scripts": {
  "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
  "dev:cjs": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-cjs",
  "dev:esm": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-esm",
  "dev:test": "karma start test/unit/karma.dev.config.js",
  "dev:ssr": "rollup -w -c scripts/config.js --environment TARGET:web-server-renderer",
  "dev:compiler": "rollup -w -c scripts/config.js --environment TARGET:web-compiler ",
  "dev:weex": "rollup -w -c scripts/config.js --environment TARGET:weex-framework",
  "dev:weex:factory": "rollup -w -c scripts/config.js --environment TARGET:weex-factory",
  "dev:weex:compiler": "rollup -w -c scripts/config.js --environment TARGET:weex-compiler ",
  "build": "node scripts/build.js",
  "build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
  "build:weex": "npm run build -- weex",
  "test": "npm run lint && flow check && npm run test:types && npm run test:cover && npm run test:e2e -- --env phantomjs && npm run test:ssr && npm run test:weex",
  "test:unit": "karma start test/unit/karma.unit.config.js",
  "test:cover": "karma start test/unit/karma.cover.config.js",
  "test:e2e": "npm run build -- web-full-prod,web-server-basic-renderer && node test/e2e/runner.js",
  "test:weex": "npm run build:weex && jasmine JASMINE_CONFIG_PATH=test/weex/jasmine.json",
  "test:ssr": "npm run build:ssr && jasmine JASMINE_CONFIG_PATH=test/ssr/jasmine.json",
  "test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2",
  "test:types": "tsc -p ./types/test/tsconfig.json",
  "lint": "eslint --fix src scripts test",
  "flow": "flow check",
  "sauce": "karma start test/unit/karma.sauce.config.js",
  "bench:ssr": "npm run build:ssr && node benchmarks/ssr/renderToString.js && node benchmarks/ssr/renderToStream.js",
  "release": "bash scripts/release.sh",
  "release:weex": "bash scripts/release-weex.sh",
  "release:note": "node scripts/gen-release-note.js",
  "commit": "git-cz"
},

mmp,但是英雄无所畏惧,因为我打算只看build 命令,所以我们的入口文件就是scripts/build.js 这个文件了。

接下来我们采用调试代码的方式进行阅读。我用的是webstorm这个IDE,先点右上角edit configuration,然后按下图进行配置:

保存后再点右上角的甲壳虫进行debug。

scripts/build.js 文件开头部分内容如下:

const fs = require('fs')
const path = require('path')
const zlib = require('zlib')
const rollup = require('rollup')
const uglify = require('uglify-js')

if (!fs.existsSync('dist')) {
  fs.mkdirSync('dist')
}

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

// filter builds via command line arg
if (process.argv[2]) {
  const filters = process.argv[2].split(',')
  builds = builds.filter(b => {
    return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1)
  })
} else {
  // filter out weex builds by default
  builds = builds.filter(b => {
    return b.output.file.indexOf('weex') === -1
  })
}

我们就将断点打到if (!fs.existsSync(‘dist’)) { 这一行,然后开始往下一点点走(step over)。

下面就单独对这个文件添加一些注释方便读者阅读:

const fs = require('fs')
const path = require('path')
const zlib = require('zlib')
const rollup = require('rollup')
const uglify = require('uglify-js')

// 如果项目根目录下不存在dist目录,则同步创建dist目录
if (!fs.existsSync('dist')) {
  fs.mkdirSync('dist')
}

// 拿到各种build情况对应的配置参数
let builds = require('./config').getAllBuilds()

// 通过命令行参数来判断需要针对哪些build情况进行打包,直接`node scripts/build.js`的话,`process.argv`拿到的数组长度为2,故下面的判断流程会进入else分支
if (process.argv[2]) {
  const filters = process.argv[2].split(',')
  builds = builds.filter(b => {
    return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1)
  })
} else {
  // 直接`node scripts/build.js`会进入这个分支,默认会过滤掉weex相关的build,不对weex相关的build情况进行打包
  builds = builds.filter(b => {
    return b.output.file.indexOf('weex') === -1
  })
}

// 根据过滤后剩下的build案例对应的配置参数,使用rollup库进行打包
build(builds)

function build (builds) {
  let built = 0
  const total = builds.length
  const next = () => {
    buildEntry(builds[built]).then(() => {
      built++
      if (built < total) {
        next()
      }
    }).catch(logError)
  }

  next()
}

function buildEntry (config) {
  const output = config.output
  const { file, banner } = output
  const isProd = /min\.js$/.test(file)
  return rollup.rollup(config)
    .then(bundle => bundle.generate(output))
    .then(({ code }) => {
      if (isProd) {
        var minified = (banner ? banner + '\n' : '') + uglify.minify(code, {
          output: {
            ascii_only: true
          },
          compress: {
            pure_funcs: ['makeMap']
          }
        }).code
        return write(file, minified, true)
      } else {
        return write(file, code)
      }
    })
}

function write (dest, code, zip) {
  return new Promise((resolve, reject) => {
    function report (extra) {
      console.log(blue(path.relative(process.cwd(), dest)) + ' ' + getSize(code) + (extra || ''))
      resolve()
    }

    fs.writeFile(dest, code, err => {
      if (err) return reject(err)
      if (zip) {
        zlib.gzip(code, (err, zipped) => {
          if (err) return reject(err)
          report(' (gzipped: ' + getSize(zipped) + ')')
        })
      } else {
        report()
      }
    })
  })
}

function getSize (code) {
  return (code.length / 1024).toFixed(2) + 'kb'
}

function logError (e) {
  console.log(e)
}

function blue (str) {
  return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m'
}

 

好了,大概的打包流程是这样的,anyway我们是要读源码,所以我们的关注点应该是这里打包时对应的入口文件,从那个文件开始着手阅读代码。

根据let builds = require(‘./config’).getAllBuilds() 这行代码,我们跳转到config.js 文件,发现:

// 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
},
// Runtime+compiler production build  (Browser)
'web-full-prod': {
  entry: resolve('web/entry-runtime-with-compiler.js'),
  dest: resolve('dist/vue.min.js'),
  format: 'umd',
  env: 'production',
  alias: { he: './entity-decoder' },
  banner
},

因为我们如果直接通过在html里用script标签引用vue.js进行开发的时候,一般都是引用vue.js的,所以这里我们就看这个web-full-dev 就可以了(它的dest 字段对应的是vue.js 文件)。然后我们通过entry 字段判断我们要看的入口文件为:src/platforms/web/entry-runtime-with-compiler.js 这个文件。

赞(6) 打赏
文章名称:《Vue2.5.16源码解读一:确定阅读入口》
文章链接:https://www.orzzone.com/vue2-source-code-analysis.html
商业联系:yakima.public@gmail.com

本站大部分文章为原创或编译而来,对于本站版权文章,未经许可不得用于商业目的,非商业性转载请以链接形式标注原文出处。
本站内容仅供个人学习交流,不做为任何投资、建议的参考依据,因此产生的问题需自行承担。

评论 抢沙发

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力提供更多优质内容!

支付宝扫一扫打赏

微信扫一扫打赏

登录

找回密码

注册