vue的源碼解析

一、目錄結構

 

1、src

├── compiler        # 編譯相關 

├── core            # 核心代碼 

├── platforms       # 不同平臺的支持

├── server          # 服務端渲染

├── sfc             # .vue 文件解析

├── shared          # 共享代碼

 

core 目錄:包含了 Vue.js 的核心代碼,包括內置組件、全局 API 封裝,Vue 實例化、觀察者、虛擬 DOM、工具函數等等。這裏的代碼可謂是 Vue.js 的靈魂

 

platform目錄:Vue.js 是一個跨平臺的 MVVM 框架,它可以跑在 web 上,也可以配合 weex 跑在 natvie 客戶端上。platform 是 Vue.js 的入口,2 個目錄代表 2 個主要入口,分別打包成運行在 web 上和 weex 上的 Vue.js。比如現在比較火熱的mpvue框架其實就是在這個目錄下面多了一個小程序的運行平臺相關內容。

 

2、new Vue() --->inti---$mount---compile---render---vnode---patch---DOM

 

3、當調用new Vue的時候,事實上就調用的Vue原型上的_init方法.

 

4、vue中的雙向數據是通過數據劫持(Object.defineProperty())

 結合發佈者-訂閱者模式來實現的,Object.defineProperty()方法會直接在一個對象上定義一個新屬性,

 或者修改一個已經存在的屬性, 並返回這個對象。

二、文件說明

1、.circleci 持續部署  持續集成

2、benchmarks 性能監控

3、dist 最終打包出來的結果

4、examples 例子

5、flow vue2採用的是flow 裏面有很多的declare,就是flow的聲明 安裝flow language support 插件

6、pageages 主要就是vue裏面寫的幾個包,

  服務端渲染-vue-server-renderer

  模板渲染 vue-server-renderer 把模板編譯成render函數

  weex-template-compiler

  weex-vue-framework

7、secript 主要的作用是構建使用的 比如rollup vue內容也是用rollup來進行打包的

8、src 源碼

9、test 測試  unit單元測試  modules 工具方法 observer

10、types 是ts的中type的聲明文件

 

 三、src

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

"build": "node scripts/build.js",   //打包命令文件 這個就是vue打包執行的語句 核心是node

// rollup 打包,打包工具只能打包webpack  打包內庫

 

四、build.js

   vue-dev\scripts\build.js

const fs = require('fs')
const path = require('path')
const zlib = require('zlib')
const rollup = require('rollup')
const terser = require('terser')

//創建dist目錄   1、查看打包vue的時候,是以那個文件作爲入口的。
if (!fs.existsSync('dist')) {  // 創建一個輸出目錄
  fs.mkdirSync('dist')   
}

// 通過配置來進行打包
// 獲取所有的配置項  是一個通過自定義的對象生成的roll up配置
let builds = require('./config').getAllBuilds()

// filter builds via command line arg
if (process.argv[2]) { //過濾生產的配置和參數

  // web-runtime-cjs,web-server-renderer  逗號分隔,filters 過濾
  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
  })
}

build(builds)   // 


// 異步遞歸方法next  這個以後會用的很多的哦!!!!
// 循環調用roll up打包
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|prod)\.js$/.test(file)
  return rollup.rollup(config)
    .then(bundle => bundle.generate(output))
    .then(({ output: [{ code }] }) => {
      if (isProd) {
        // minified 壓縮
        const minified = (banner ? banner + '\n' : '') + terser.minify(code, {
          toplevel: true,
          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'
}

五、config.js

 vue-dev\scripts\config.js

const path = require('path')
const buble = require('rollup-plugin-buble')
const alias = require('rollup-plugin-alias')
const cjs = require('rollup-plugin-commonjs')
const replace = require('rollup-plugin-replace')
const node = require('rollup-plugin-node-resolve')
const flow = require('rollup-plugin-flow-no-whitespace')
const version = process.env.VERSION || require('../package.json').version
const weexVersion = process.env.WEEX_VERSION || require('../packages/weex-vue-framework/package.json').version
const featureFlags = require('./feature-flags')

const banner =
  '/*!\n' +
  ` * Vue.js v${version}\n` +
  ` * (c) 2014-${new Date().getFullYear()} Evan You\n` +
  ' * Released under the MIT License.\n' +
  ' */'

const weexFactoryPlugin = {
  intro () {
    return 'module.exports = function weexFactory (exports, document) {'
  },
  outro () {
    return '}'
  }
}

// web/entry-runtime-with-compiler.js
const aliases = require('./alias')
const resolve = p => {   // resolve 方法,根據'/'來分割
  const base = p.split('/')[0]   
  if (aliases[base]) {  // aliases[base] 中去取web

   // aliases 中去取web
    return path.resolve(aliases[base], p.slice(base.length + 1))
  } else {
    return path.resolve(__dirname, '../', p)
  }
}

// builds 是自己維護的一個打包對象,不是rollup打包的配置
//builds 代表的是rollup的配置, 這裏有n個配置,
// filter \ builds \ via \ common and arg 過濾要打包的結果
const builds = {
  // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify

  //  cjs---CommonJS  vue 分爲兩種類型 Runtime only();  這裏不寫  體積小
  //    runtime-with-compiler 用戶可以寫template  編譯原理
  // .vue template 中vue-loader來編譯的。
  // 打包平臺的不同 web week
  // compiler+runtime=full;
  // runtime 裏面沒有compiler, 在開發的時候不能使用template語法 <template></template> 
  // .vue文件 使用vue-loader來處理的。 和compiler 無關
  // compiler 可以將模板轉化成render函數
  // esm:es6的模塊 umd:是amd和common.js的集合體   common規範:
  // 三個不同:平臺不同(web和week)、打包出來的結果不同(compiler和runtime)、打包的格式不同(common.js,esm,umd)
  // entry-runtime.js

  'web-runtime-cjs-dev': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.dev.js'),
    format: 'cjs',
    env: 'development',
    banner
  },

  
  'web-runtime-cjs-prod': {
    entry: resolve('web/entry-runtime.js'),  // 打包前的路徑
    dest: resolve('dist/vue.runtime.common.prod.js'),  // 打包後的路徑
    format: 'cjs', //打包後的格式
    env: 'production',   // 生成還是開發環境
    banner  //標識而已
  },
  // Runtime+compiler CommonJS build (CommonJS)
  'web-full-cjs-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.common.dev.js'),
    format: 'cjs',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },
  'web-full-cjs-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.common.prod.js'),
    format: 'cjs',
    env: 'production',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime only ES modules build (for bundlers)
  'web-runtime-esm': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.esm.js'),
    format: 'es',
    banner
  },
  // Runtime+compiler ES modules build (for bundlers)
//  

  'web-full-esm': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.js'),
    format: 'es',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime+compiler ES modules build (for direct import in browser)
  'web-full-esm-browser-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.browser.js'),
    format: 'es',
    transpile: false,
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime+compiler ES modules build (for direct import in browser)
  'web-full-esm-browser-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.browser.min.js'),
    format: 'es',
    transpile: false,
    env: 'production',
    alias: { he: './entity-decoder' },
    banner
  },
  // runtime-only build (Browser)
  'web-runtime-dev': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.js'),
    format: 'umd',   // 統一模塊規範,  包括commonJS amd和cmd
    env: 'development',
    banner
  },
  // runtime-only production build (Browser)
  'web-runtime-prod': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.min.js'),
    format: 'umd',
    env: 'production',
    banner
  },
  // 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
  },
  // Web compiler (CommonJS).
  'web-compiler': {
    entry: resolve('web/entry-compiler.js'),
    dest: resolve('packages/vue-template-compiler/build.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/vue-template-compiler/package.json').dependencies)
  },
  // Web compiler (UMD for in-browser use).
  'web-compiler-browser': {
    entry: resolve('web/entry-compiler.js'),
    dest: resolve('packages/vue-template-compiler/browser.js'),
    format: 'umd',
    env: 'development',
    moduleName: 'VueTemplateCompiler',
    plugins: [node(), cjs()]
  },
  // Web server renderer (CommonJS).
  'web-server-renderer-dev': {
    entry: resolve('web/entry-server-renderer.js'),
    dest: resolve('packages/vue-server-renderer/build.dev.js'),
    format: 'cjs',
    env: 'development',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  'web-server-renderer-prod': {
    entry: resolve('web/entry-server-renderer.js'),
    dest: resolve('packages/vue-server-renderer/build.prod.js'),
    format: 'cjs',
    env: 'production',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  'web-server-renderer-basic': {
    entry: resolve('web/entry-server-basic-renderer.js'),
    dest: resolve('packages/vue-server-renderer/basic.js'),
    format: 'umd',
    env: 'development',
    moduleName: 'renderVueComponentToString',
    plugins: [node(), cjs()]
  },
  'web-server-renderer-webpack-server-plugin': {
    entry: resolve('server/webpack-plugin/server.js'),
    dest: resolve('packages/vue-server-renderer/server-plugin.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  'web-server-renderer-webpack-client-plugin': {
    entry: resolve('server/webpack-plugin/client.js'),
    dest: resolve('packages/vue-server-renderer/client-plugin.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  // Weex runtime factory
  'weex-factory': {
    weex: true,
    entry: resolve('weex/entry-runtime-factory.js'),
    dest: resolve('packages/weex-vue-framework/factory.js'),
    format: 'cjs',
    plugins: [weexFactoryPlugin]
  },
  // Weex runtime framework (CommonJS).
  'weex-framework': {
    weex: true,
    entry: resolve('weex/entry-framework.js'),
    dest: resolve('packages/weex-vue-framework/index.js'),
    format: 'cjs'
  },
  // Weex compiler (CommonJS). Used by Weex's Webpack loader.
  'weex-compiler': {
    weex: true,
    entry: resolve('weex/entry-compiler.js'),
    dest: resolve('packages/weex-template-compiler/build.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/weex-template-compiler/package.json').dependencies)
  }
}

function genConfig (name) {
  const opts = builds[name]  // 獲取到每個key對應的配置。 獲取聲明的對象
  const config = {  // rollup的真正的配置,input、output rollup的真實配置,
    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
  })

  // 做了一堆配置,最好把config返回了。
  return config
}

if (process.env.TARGET) {
  module.exports = genConfig(process.env.TARGET)
} else {
  exports.getBuild = genConfig

  // 獲取所有打包的配置
  //將對象所有的key 轉化成數組   map方法返回的是一個數組。
  // genConfig 生成rollup的配置文件
  exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章