zoukankan      html  css  js  c++  java
  • [转] 编译输出文件的区别

    Vue 源码是选用了 rollup 作为 bundler ,看 Vue 的源码时发现: npm script 对应了不同的构建选项。这也对应了最后打包构建后产出的不同的包。

    不同于其他的 library , Vue 为什么要在最后的打包构建环节输出不同类型的包呢?接下来我们通过 Vue 的源码以及对应的构建配置中简单的去分析下。

    由于 Vue 是基于 rollup 进行构建的,我们先来简单了解下 rollup 这个 bundler : rollup 是默认使用 ES Module 规范而非 CommonJS ,因此如果你在你的项目中使用 rollup 作为构建工具的话,那么可以放心的使用 ES Module 规范,但是如果要引入只遵循了 CommonJs 规范的第三包的话,还需要使用相关的插件,插件会帮你将 CommonJs 规范的代码转为 ES Module 。得益于 ES Module , rollup 在构建前进行静态分析,进行 tree-shaking 。关于 tree-shaking 的描述 请戳我 。在构建输出环节, rollup 提供了多种文件输出类型:

    • iife : 立即执行函数

    • cjs : 遵循 CommonJs Module 规范的文件输出

    • amd : 遵循 AMD Module 规范的文件输出

    • umd : 支持 外链 / CommonJs Module / AMD Module 规范的文件输出

    • es : 将多个遵循 ES6 Module 的文件编译成1个 ES6 Module

    接下来我们就看看 Vue 的使用 rollup 进行构建的几个不同的版本(使用于 browser 的版本)。

    npm run dev 对应
    
    rollup -w -c build/config.js --environment TARGET:web-full-dev

    rollup 对应的配置信息为:

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

    开发环境下输出的 umd 格式的代码,入口文件是 runtime-with-compiler.js ,这个入口文件中是将 Vue 的 构建时 和 运行时 的代码都统一进行打包了,通过查看这个入口文件,我们注意到

    ...
    import { compileToFunctions } from './compiler/index'
    ...
    
    const mount = Vue.prototype.$mount
    Vue.prototype.$mount = function () {
        
    }

    我们发现,这个文件当中,首先将原来定义的 Vue.prototype.$mount 方法缓存起来,然后将这个方法进行重写,重写后的方法当中,首先判断是否有自定义的 render 函数,如果有自定义的 render 函数的话, Vue 不会通过自带的 compiler 对模板进行编译并生成 render 函数。但是如果没有自定义的 render 函数,那么会调用 compiler 对你定义的模板进行编译,并生成 render 函数,所以通过这个 rollup 的配置构建出来的代码既支持自定义 render 函数,又支持 template 模板编译:

    // 将模板编译成render函数,并挂载到vm实例的options属性上
          const { render, staticRenderFns } = compileToFunctions(template, {
            shouldDecodeNewlines,
            delimiters: options.delimiters
          }, this)
          options.render = render
          options.staticRenderFns = staticRenderFns
          
          ...
        // 调用之前缓存的mount函数,TODO: 关于这个函数里面发生了什么请戳我
        return mount.call(this, el, hydrating)

    接下来看第二种构建方式:

    npm run dev:cjs 对应的构建脚本
    
    rollup -w -c build/config.js --environment TARGET:web-runtime-cjs

    rollup 对应的配置信息为:

    // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
      'web-runtime-cjs': {
        entry: resolve('web/runtime.js'),
        dest: resolve('dist/vue.runtime.common.js'),
        format: 'cjs',
        banner
      }

    最后编译输出的文件是遵循 CommonJs Module 同时只包含 runtime 部分的代码,它能直接被 webpack 1.x 和 Browserify 直接 load 。它对应的入口文件是 runtime.js :

    import Vue from './runtime/index'
    
    export default Vue

    这里没有重写 Vue.prototye.$mount 方法,因此在 vm 实例的生命周期中,进行到 beforeMount 阶段时:

    // vm挂载的根元素
        if (vm.$options.el) {
          vm.$mount(vm.$options.el)
        }
    export function mountComponent (
      vm: Component,
      el: ?Element,
      hydrating?: boolean
    ): Component {
      // vm.$el为真实的node
      vm.$el = el
      // 如果vm上没有挂载render函数
      if (!vm.$options.render) {
        // 空节点
        vm.$options.render = createEmptyVNode
        if (process.env.NODE_ENV !== 'production') {
          /* istanbul ignore if */
          if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
            vm.$options.el || el) {
            warn(
              'You are using the runtime-only build of Vue where the template ' +
              'compiler is not available. Either pre-compile the templates into ' +
              'render functions, or use the compiler-included build.',
              vm
            )
          } else {
            warn(
              'Failed to mount component: template or render function not defined.',
              vm
            )
          }
        }
      }
      // 钩子函数
      callHook(vm, 'beforeMount')
    
      let updateComponent
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        updateComponent = () => {
          const name = vm._name
          const id = vm._uid
          const startTag = `vue-perf-start:${id}`
          const endTag = `vue-perf-end:${id}`
    
          mark(startTag)
          const vnode = vm._render()
          mark(endTag)
          measure(`${name} render`, startTag, endTag)
    
          mark(startTag)
          vm._update(vnode, hydrating)
          mark(endTag)
          measure(`${name} patch`, startTag, endTag)
        }
      } else {
        // updateComponent为监听函数, new Watcher(vm, updateComponent, noop)
        updateComponent = () => {
          // Vue.prototype._render 渲染函数
          // vm._render() 返回一个VNode
          // 更新dom
          // vm._render()调用render函数,会返回一个VNode,在生成VNode的过程中,会动态计算getter,同时推入到dep里面
          // 在非ssr情况下hydrating为false
          vm._update(vm._render(), hydrating)
        }
      }
    
      // 新建一个_watcher对象
      // vm实例上挂载的_watcher主要是为了更新DOM
      // 在实例化watcher的过程中,就会执行updateComponent,完成对依赖的变量的收集过程
      // vm/expression/cb
      vm._watcher = new Watcher(vm, updateComponent, noop)
      hydrating = false
    
      // manually mounted instance, call mounted on self
      // mounted is called for render-created child components in its inserted hook
      if (vm.$vnode == null) {
        vm._isMounted = true
        callHook(vm, 'mounted')
      }
      return vm
    }

    首先判断 vm 实例上是否定义了 render 函数。如果没有,那么就会新建一个新的空 vnode 并挂载到 render 函数上。此外,如果页面的渲染是通过传入 根节点 的形式:

    new Vue({
          el: '#app'
        })

    Vue 便会打出 log 信息:

    warn(
              'You are using the runtime-only build of Vue where the template ' +
              'compiler is not available. Either pre-compile the templates into ' +
              'render functions, or use the compiler-included build.'

    意思就是你当前使用的是只包含 runtime 打包后的代码,模板的编译器(即构建时)的代码并不包含在里面。因此,你不能通过挂根节点或者是声明式模板的方式去组织你的 html 内容,而只能使用 render 函数去书写模板内容。不过报错信息里面也给出了提示信息就是,你还可以选择 pre-compile 预编译工具去将 template 模板编译成 render 函数( vue-loader 就起到了这个作用)或者是使用包含了 compiler 的输出包,也就是上面分析的即包含 compiler ,又包含 runtime 的包。

    第三种构建方式:

    npm run dev:esm 对应的构建脚本为:
    
    rollup -w -c build/config.js --environment TARGET:web-runtime-esm

    入口文件及最后构建出来的代码内容和第二种一样,只包含 runtime 部分的代码,但是输出代码是遵循 ES Module 规范的。可以被支持 ES Module 的 bundler 直接加载,如 webpack2 和 rollup 。

    第四种构建方式:

    npm run dev:compiler 对应的构建脚本为:
    
    rollup -w -c build/config.js --environment TARGET:web-compiler

    不同于前面3种构建方式:

    // Web compiler (CommonJS).
    'web-compiler': {
        entry: resolve('web/compiler.js'),
        dest: resolve('packages/vue-template-compiler/build.js'),
        format: 'cjs',
        external: Object.keys(require('../packages/vue-template-compiler/package.json').dependencies)
      },

    这一构建对应于将关于 Vue 模板编译的成 render 函数的 compiler.js 单独进行打包输出。最后输出的 packages/vue-template-compiler/build.js 文件会单独作为一个 node_modules 进行发布,在你的开发过程中,如果使用了 webpack 作为构建工具,以及 vue-loader ,在开发构建环节, vue-loader 便会通过 web compiler 去处理你的 *.vue 文件中的模板 <template> 当中的内容,将这些模板字符串编译为 render 函数。

  • 相关阅读:
    android之手机震动Vibrate
    Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现) .
    用AutoCompleteTextView实现历史记录提示
    AutoCompleteTextView
    android学习笔记7--------MVC设计模式
    Android之MVC模式
    Android中常见的MVC模式
    //11个整数的手机号码正则式 phoneNumber(String number)
    YuvImage知识点总结
    运行项目的时候出现missing frature:WATCH
  • 原文地址:https://www.cnblogs.com/chris-oil/p/8855893.html
Copyright © 2011-2022 走看看