美文网首页
vue源码构建

vue源码构建

作者: Amfishers | 来源:发表于2020-03-31 12:31 被阅读0次

    本次学习版本是 vue 2.6.11

    首先下载vue源码 **https://github.com/vuejs/vue.git** 然后优先看根目录下的 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",
        "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.js",
        "test:ssr": "npm run build:ssr && jasmine JASMINE_CONFIG_PATH=test/ssr/jasmine.js",
        "test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2",
        "test:types": "tsc -p ./types/test/tsconfig.json",
        "lint": "eslint 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"
      },
    
    

    主要分析我们日常开发熟悉的 "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev" 这一段,这里用到了 rollup (https://github.com/rollup/rollup) 构建,这里暂时不管,只知道是一个类似 webpack 的打包工具。

    往后查找 scripts/config.js 以及 TARGET为 web-full-dev 的内容

    大概所略为

    const version = process.env.VERSION || require('../package.json').version
    const banner =
      '/*!\\n' +
      ` * Vue.js v${version}\\n` +
      ` * (c) 2014-${new Date().getFullYear()} Evan You\\n` +
      ' * Released under the MIT License.\\n' +
      ' */'
    
    const builds = {
    // 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
      },
    }
    
    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)
          }
        }
      }
        // 中间被我删掉一些
        ...
      return config
    }
    
    if (process.env.TARGET) {
      module.exports = genConfig(process.env.TARGET)
    } else {
      exports.getBuild = genConfig
      exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
    }
    
    

    到最下面,我们看到它调用了genConfig(process.env.TARGET) ,getConfig用于生成rollup的配置文件。builds是一个对象,获取它的process.env.TARGET值,在package.json中,我们看到dev中有TARGET:web-full-dev参数,即上面我留下的那一段配置。这样入口文件我们就找到了,也就是根目录下 src\platforms\web\entry-runtime-with-compiler.js

    找到 entry-runtime-with-compiler.js 就看到

    import Vue from './runtime/index' 
    
    

    Vue 是从 './runtime/index' 文件引入,然后在改文件进行加工 原型上挂载 $mount 以及 添加 compile 方法。

    打开 './runtime/index'

    第一行就是 import Vue from 'core/index' 所以该文件的vue也还是从另一个地方的 vue 引进来而进行拓展。该文件 引入 patch 以及 对vue原型关注了 $mount 所以该文件是比较重要,以后会分析

    接下来找到 'core/index' 文件

    第一行也是从其他文件引入 import Vue from './instance/index' 是真有点深

    找到 ./instance/index 文件,终于看到了光明。

    整个过程是

    src/platforms/web/entry-runtime-with-compiler.js
    ->
    src/platforms/web/runtime/index.js
    ->
    src/core/index.js
    ->
    src/core/instance/index.js
    
    

    找到 src/core/instance/index.js 终于找到定义 Vue 对象的所在之处,它是一个构造函数

    function Vue (options) {
      if (process.env.NODE_ENV !== 'production' &&
        !(this instanceof Vue)
      ) {
        warn('Vue is a constructor and should be called with the `new` keyword')
      }
      this._init(options)
    }
    
    

    首先判断如果不是生产环境,且不是通过 new 构造出来的实例对象话,就在控制台打印 warn ,来看下这个 warn 定义

    是在 src\core\util\debug.js 文件里面,

    export let generateComponentTrace = (noop: any) // work around flow check
    
    warn = (msg, vm) => {
    
        const trace = vm ? generateComponentTrace(vm) : ''
            // config.warnHandler 默认是 null 
        if (config.warnHandler) {
          config.warnHandler.call(null, msg, vm, trace)
        } else if (hasConsole && (!config.silent)) {
          console.error(`[Vue warn]: ${msg}${trace}`)
        }
      }
    
    

    接下来调用 this._init(options) ,接着 就对 Vue.prototype 原型上绑定了一些实例,后面看到源码有总结慢慢进行补充

    // _init
    initMixin(Vue)  
    // $set、$delete、$watch
    stateMixin(Vue)
    // $on、$once、$off、$emit
    eventsMixin(Vue)
    // _update、$forceUpdate、$destroy
    lifecycleMixin(Vue)
    // $nextTick、_render、以及多个内部调用的方法
    renderMixin(Vue)
    
    

    接着我们按照查找的倒叙看回 src/core/index.js

    initGlobalAPI(Vue)
    
    Object.defineProperty(Vue.prototype, '$isServer', {
      get: isServerRendering
    })
    
    Object.defineProperty(Vue.prototype, '$ssrContext', {
      get () {
        /* istanbul ignore next */
        return this.$vnode && this.$vnode.ssrContext
      }
    })
    
    // expose FunctionalRenderContext for ssr runtime helper installation
    Object.defineProperty(Vue, 'FunctionalRenderContext', {
      value: FunctionalRenderContext
    })
    
    Vue.version = '__VERSION__'
    
    

    这个文件也可以一目了然,首先调用了 initGlobalAPI(Vue) , initGlobalAPI 方法引自src\core\global-api\index.js

    export function initGlobalAPI (Vue: GlobalAPI) {
      // config
      const configDef = {}
      configDef.get = () => config
      if (process.env.NODE_ENV !== 'production') {
        configDef.set = () => {
          warn(
            'Do not replace the Vue.config object, set individual fields instead.'
          )
        }
      }
      Object.defineProperty(Vue, 'config', configDef)
    
      // exposed util methods.
      // NOTE: these are not considered part of the public API - avoid relying on
      // them unless you are aware of the risk.
      Vue.util = {
        warn,
        extend,
        mergeOptions,
        defineReactive
      }
    
      Vue.set = set
      Vue.delete = del
      Vue.nextTick = nextTick
    
      // 2.6 explicit observable API
      Vue.observable = <T>(obj: T): T => {
        observe(obj)
        return obj
      }
    
      Vue.options = Object.create(null)
    
        // ASSET_TYPES  ->  'component','directive','filter'
    
      ASSET_TYPES.forEach(type => {
        Vue.options[type + 's'] = Object.create(null)
      })
    
      // this is used to identify the "base" constructor to extend all plain-object
      // components with in Weex's multi-instance scenarios.
      Vue.options._base = Vue
    
      // Vue.options.components.KeepAlive
      extend(Vue.options.components, builtInComponents)
    
        // Vue.use
      initUse(Vue)
        // Vue.mixin
      initMixin(Vue)
        // Vue.extend
      initExtend(Vue)
        // Vue.component、Vue.directive、Vue.filter
      initAssetRegisters(Vue)
    }
    
    

    从上面代码可以看出,它是对Vue对象添加了一些静态方法和属性

    /src/core/index.js文件中,还添加了一个Vue.prototype.$isServer属性,用于判断是不是服务端渲染,以及 $ssrContext (待补充) 还有 FunctionalRenderContext 用来 SSR 运行时帮助安装, 还有一个就是Vue.version。

    接着就是 src/platforms/web/runtime/index.js

    // install platform specific utils
    Vue.config.mustUseProp = mustUseProp
    Vue.config.isReservedTag = isReservedTag
    Vue.config.isReservedAttr = isReservedAttr
    Vue.config.getTagNamespace = getTagNamespace
    Vue.config.isUnknownElement = isUnknownElement
    
    // install platform runtime directives & components
    extend(Vue.options.directives, platformDirectives)
    extend(Vue.options.components, platformComponents)
    
    // install platform patch function
    Vue.prototype.__patch__ = inBrowser ? patch : noop
    
    // public mount method
    Vue.prototype.$mount = function (
      el?: string | Element,
      hydrating?: boolean
    ): Component {
      el = el && inBrowser ? query(el) : undefined
      return mountComponent(this, el, hydrating)
    }
    
    // devtools global hook
    /* istanbul ignore next */
    if (inBrowser) {
      setTimeout(() => {
        if (config.devtools) {
          if (devtools) {
            devtools.emit('init', Vue)
          } else if (
            process.env.NODE_ENV !== 'production' &&
            process.env.NODE_ENV !== 'test'
          ) {
            console[console.info ? 'info' : 'log'](
              'Download the Vue Devtools extension for a better development experience:\\n' +
              '<https://github.com/vuejs/vue-devtools>'
            )
          }
        }
        if (process.env.NODE_ENV !== 'production' &&
          process.env.NODE_ENV !== 'test' &&
          config.productionTip !== false &&
          typeof console !== 'undefined'
        ) {
          console[console.info ? 'info' : 'log'](
            `You are running Vue in development mode.\\n` +
            `Make sure to turn on production mode when deploying for production.\\n` +
            `See more tips at <https://vuejs.org/guide/deployment.html`>
          )
        }
      }, 0)
    }
    
    export default Vue
    
    

    我们先看看 src/platforms/ 里面有 web 和 weex 文件夹,所以源码里把两个平台不同的内容单独提取出来了,这种目录思维非常值得学习借鉴。

    我们这里只谈 web ,首先,在Vue.config上添加了几个平台相关的方法,扩展了Vue.options.directives(model和show)和Vue.options.components(Transition和TransitionGroup)。在Vue.prototype上添加了__patch__(虚拟dom相关)和$mount(挂载元素)。

    最后是/src/entries/web-runtime-with-compiler.js,该文件主要干了两件事,一个是定义了一个方法Vue.prototype.$mount,另一个是将compileToFunctions挂在到Vue.compile上。

    总结

    entry-runtime-with-compiler.js

    一个是定义了一个方法Vue.prototype.$mount,另一个是将compileToFunctions挂在到Vue.compile上

    src/platforms/web/runtime/index.js

    首先,在Vue.config上添加了几个平台相关的方法,扩展了Vue.options.directives(model和show)和Vue.options.components(Transition和TransitionGroup)。在Vue.prototype上添加了__patch__(虚拟dom相关)和$mount(挂载元素)。

    src/core/index.js

    添加了一些静态方法和属性

    Vue.use、 Vue.mixin、 Vue.extend、[ Vue.component、Vue.directive、Vue.filter ] Vue.options.components.KeepAlive

    还添加了一个Vue.prototype.$isServer属性,用于判断是不是服务端渲染,以及 $ssrContext (待补充) 还有 FunctionalRenderContext 用来 SSR 运行时帮助安装, 还有一个就是Vue.version。

    src/core/instance/index.js

    vue 构造函数,以及一些静态方法和实例

    _init initMixin(Vue)
    $set$delete$watch stateMixin(Vue)
    $on$once$off$emit eventsMixin(Vue)
    _update、$forceUpdate$destroy lifecycleMixin(Vue)
    $nextTick、_render、以及多个内部调用的方法 renderMixin(Vue)

    相关文章

      网友评论

          本文标题:vue源码构建

          本文链接:https://www.haomeiwen.com/subject/pglwuhtx.html