美文网首页
Vue.js编译功能的实现

Vue.js编译功能的实现

作者: zacone | 来源:发表于2022-08-30 00:01 被阅读0次

    打开package.json文件,可以看到编译的脚本为

       "build": "node scripts/build.js",
    

    是通过node执行build.js实现的,他的核心是buildEntry方法

    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(async ({ output: [{ code }] }) => {
          if (isProd) {
            const {code: minifiedCode} =  await terser.minify(code, {
              toplevel: true,
              compress: {
                pure_funcs: ['makeMap'],
              },
              format: {
                ascii_only: true,
              }
            });
            const minified = (banner ? banner + '\n' : '') + minifiedCode
            return write(file, minified, true)
          } else {
            return write(file, code)
          }
        })
    }
    

    将读取到的config传给rollup执行,将生成的js产物通过node提供的fsAPI写入到dist目录下,再看看scripts/config.js中vue都配置了哪些编译方案

    const builds = {
      // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
      'runtime-cjs-dev': {
        entry: resolve('web/entry-runtime.ts'),
        dest: resolve('dist/vue.runtime.common.dev.js'),
        format: 'cjs',
        env: 'development',
        banner
      },
      'runtime-cjs-prod': {
        entry: resolve('web/entry-runtime.ts'),
        dest: resolve('dist/vue.runtime.common.prod.js'),
        format: 'cjs',
        env: 'production',
        banner
      },
      // Runtime+compiler CommonJS build (CommonJS)
      'full-cjs-dev': {
        entry: resolve('web/entry-runtime-with-compiler.ts'),
        dest: resolve('dist/vue.common.dev.js'),
        format: 'cjs',
        env: 'development',
        alias: { he: './entity-decoder' },
        banner
      },
      'full-cjs-prod': {
        entry: resolve('web/entry-runtime-with-compiler.ts'),
        dest: resolve('dist/vue.common.prod.js'),
        format: 'cjs',
        env: 'production',
        alias: { he: './entity-decoder' },
        banner
      },
      // Runtime only ES modules build (for bundlers)
      'runtime-esm': {
        entry: resolve('web/entry-runtime-esm.ts'),
        dest: resolve('dist/vue.runtime.esm.js'),
        format: 'es',
        banner
      },
      // Runtime+compiler ES modules build (for bundlers)
      'full-esm': {
        entry: resolve('web/entry-runtime-with-compiler-esm.ts'),
        dest: resolve('dist/vue.esm.js'),
        format: 'es',
        alias: { he: './entity-decoder' },
        banner
      },
      // Runtime+compiler ES modules build (for direct import in browser)
      'full-esm-browser-dev': {
        entry: resolve('web/entry-runtime-with-compiler-esm.ts'),
        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)
      'full-esm-browser-prod': {
        entry: resolve('web/entry-runtime-with-compiler-esm.ts'),
        dest: resolve('dist/vue.esm.browser.min.js'),
        format: 'es',
        transpile: false,
        env: 'production',
        alias: { he: './entity-decoder' },
        banner
      },
      // runtime-only build (Browser)
      'runtime-dev': {
        entry: resolve('web/entry-runtime.ts'),
        dest: resolve('dist/vue.runtime.js'),
        format: 'umd',
        env: 'development',
        banner
      },
      // runtime-only production build (Browser)
      'runtime-prod': {
        entry: resolve('web/entry-runtime.ts'),
        dest: resolve('dist/vue.runtime.min.js'),
        format: 'umd',
        env: 'production',
        banner
      },
      // Runtime+compiler development build (Browser)
      'full-dev': {
        entry: resolve('web/entry-runtime-with-compiler.ts'),
        dest: resolve('dist/vue.js'),
        format: 'umd',
        env: 'development',
        alias: { he: './entity-decoder' },
        banner
      },
      // Runtime+compiler production build  (Browser)
      'full-prod': {
        entry: resolve('web/entry-runtime-with-compiler.ts'),
        dest: resolve('dist/vue.min.js'),
        format: 'umd',
        env: 'production',
        alias: { he: './entity-decoder' },
        banner
      },
      // Web compiler (CommonJS).
      compiler: {
        entry: resolve('web/entry-compiler.ts'),
        dest: resolve('packages/template-compiler/build.js'),
        format: 'cjs',
        external: Object.keys(
          require('../packages/template-compiler/package.json').dependencies
        )
      },
      // Web compiler (UMD for in-browser use).
      'compiler-browser': {
        entry: resolve('web/entry-compiler.ts'),
        dest: resolve('packages/template-compiler/browser.js'),
        format: 'umd',
        env: 'development',
        moduleName: 'VueTemplateCompiler',
        plugins: [node(), cjs()]
      },
      // Web server renderer (CommonJS).
      'server-renderer-dev': {
        entry: resolve('packages/server-renderer/src/index.ts'),
        dest: resolve('packages/server-renderer/build.dev.js'),
        format: 'cjs',
        env: 'development',
        external: [
          'stream',
          ...Object.keys(
            require('../packages/server-renderer/package.json').dependencies
          )
        ]
      },
      'server-renderer-prod': {
        entry: resolve('server/index.ts'),
        dest: resolve('packages/server-renderer/build.prod.js'),
        format: 'cjs',
        env: 'production',
        external: [
          'stream',
          ...Object.keys(
            require('../packages/server-renderer/package.json').dependencies
          )
        ]
      },
      'server-renderer-basic': {
        entry: resolve('server/index-basic.ts'),
        dest: resolve('packages/server-renderer/basic.js'),
        format: 'umd',
        env: 'development',
        moduleName: 'renderVueComponentToString',
        plugins: [node(), cjs()]
      },
      'server-renderer-webpack-server-plugin': {
        entry: resolve('server/webpack-plugin/server.ts'),
        dest: resolve('packages/server-renderer/server-plugin.js'),
        format: 'cjs',
        external: Object.keys(
          require('../packages/server-renderer/package.json').dependencies
        )
      },
      'server-renderer-webpack-client-plugin': {
        entry: resolve('server/webpack-plugin/client.ts'),
        dest: resolve('packages/server-renderer/client-plugin.js'),
        format: 'cjs',
        external: Object.keys(
          require('../packages/server-renderer/package.json').dependencies
        )
      },
      'compiler-sfc': {
        entry: resolve('packages/compiler-sfc/src/index.ts'),
        dest: resolve('packages/compiler-sfc/dist/compiler-sfc.js'),
        format: 'cjs',
        external: Object.keys(
          require('../packages/compiler-sfc/package.json').dependencies
        ),
        plugins: [
          node({ preferBuiltins: true }),
          cjs({
            ignore: [
              ...Object.keys(require(consolidatePath).devDependencies),
              'vm',
              'crypto',
              'react-dom/server',
              'teacup/lib/express',
              'arc-templates/dist/es5',
              'then-pug',
              'then-jade'
            ]
          })
        ]
      }
    }
    

    核心的几个entry都在src/platforms/web下的,分别提供生产环境、开发环境下的cjsesumd规范产物

    • entry-runtime.ts
    import Vue from './runtime/index'
    import * as vca from 'v3'
    import { extend } from 'shared/util'
    
    extend(Vue, vca)
    
    export default Vue
    
    • entry-runtime-esm.ts
    import Vue from './runtime/index'
    
    export default Vue
    
    export * from 'v3'
    
    • runtime-with-compiler.ts
    import config from 'core/config'
    import { warn, cached } from 'core/util/index'
    import { mark, measure } from 'core/util/perf'
    
    import Vue from './runtime/index'
    import { query } from './util/index'
    import { compileToFunctions } from './compiler/index'
    import {
      shouldDecodeNewlines,
      shouldDecodeNewlinesForHref
    } from './util/compat'
    import type { Component } from 'types/component'
    import type { GlobalAPI } from 'types/global-api'
    
    const idToTemplate = cached(id => {
      const el = query(id)
      return el && el.innerHTML
    })
    
    const mount = Vue.prototype.$mount
    Vue.prototype.$mount = function (
      el?: string | Element,
      hydrating?: boolean
    ): Component {
      el = el && query(el)
    
      /* istanbul ignore if */
      if (el === document.body || el === document.documentElement) {
        __DEV__ &&
          warn(
            `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
          )
        return this
      }
    
      const options = this.$options
      // resolve template/el and convert to render function
      if (!options.render) {
        let template = options.template
        if (template) {
          if (typeof template === 'string') {
            if (template.charAt(0) === '#') {
              template = idToTemplate(template)
              /* istanbul ignore if */
              if (__DEV__ && !template) {
                warn(
                  `Template element not found or is empty: ${options.template}`,
                  this
                )
              }
            }
          } else if (template.nodeType) {
            template = template.innerHTML
          } else {
            if (__DEV__) {
              warn('invalid template option:' + template, this)
            }
            return this
          }
        } else if (el) {
          // @ts-expect-error
          template = getOuterHTML(el)
        }
        if (template) {
          /* istanbul ignore if */
          if (__DEV__ && config.performance && mark) {
            mark('compile')
          }
    
          const { render, staticRenderFns } = compileToFunctions(
            template,
            {
              outputSourceRange: __DEV__,
              shouldDecodeNewlines,
              shouldDecodeNewlinesForHref,
              delimiters: options.delimiters,
              comments: options.comments
            },
            this
          )
          options.render = render
          options.staticRenderFns = staticRenderFns
    
          /* istanbul ignore if */
          if (__DEV__ && config.performance && mark) {
            mark('compile end')
            measure(`vue ${this._name} compile`, 'compile', 'compile end')
          }
        }
      }
      return mount.call(this, el, hydrating)
    }
    
    /**
     * Get outerHTML of elements, taking care
     * of SVG elements in IE as well.
     */
    function getOuterHTML(el: Element): string {
      if (el.outerHTML) {
        return el.outerHTML
      } else {
        const container = document.createElement('div')
        container.appendChild(el.cloneNode(true))
        return container.innerHTML
      }
    }
    
    Vue.compile = compileToFunctions
    
    export default Vue as GlobalAPI
    

    从几个entry看,Vue的实现最终都指向了src/platforms/web/runtime/index.ts

    相关文章

      网友评论

          本文标题:Vue.js编译功能的实现

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