最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue源码解读(Rollup篇)

    正文概述 掘金(Husky-Yellow)   2021-02-14   662

    Rollup基础知识

    Vue.js通过rollup构建工具进行构建,它是一个类似于webpack的打包工具,区别于webpack它更适合一个Library库的打包。在学习Vue.js源码之前,我们有必要知道Vue.js是如何构建不同版本的。

    核心概念

    webpack一样,rollup也有以下几大核心概念:

    • input:入口文件,类比于webpackentry,它指明了我们库文件入口位置。
    • output:输出位置,它指明了打包后的输出信息,包括:输出目录,打包文件名等。
    • plugins插件rollup在构建过程中,插件可提供一些辅助功能,例如:alias别名解析、转义ES6等。
    • external:当我们的库依赖于其它第三方库时,我们不需要把这些第三方库一起打包,而是应该把依赖写在external里面。

    webpack一样,rollup同样适合使用配置文件的做法来配置打包的选项,例如:

    // rollup.config.js
    export default {
      input: 'src/main.js',
      output: [
        { file: 'dist/vue.js', format: 'umd', name: 'Vue' },
        { file: 'dist/vue.common.js', format: 'cjs', name: 'Vue' },
        { file: 'dist/vue.esm.js', format: 'es', name: 'Vue' }
      ]
    }
    

    构建版本说明:

    • umd:此选项构建出来的库文件主要适用于Web端,可以通过不同的方式去使用:script标签引入,ES Module规范引入和CommonJs规范引入等。
    • cjs: 此选项构建出来的库文件主要为CommonJs规范,可在Node环境中使用。
    • es:此版本构建出来的库文件主要为ES Module规范,可在支持ES Module也就是import/export的环境中使用。

    有了以上配置文件,我们可以在package.json中进行如下修改:

    {
      "name": "Vue",
      "version": "1.0.0",
      "scripts": {
        "dev": "rollup -w -c scripts/rollup.config.dev.js",
        "build": "rollup -c scripts/rollup.config.prod.js"
      }
    }
    

    参数说明:

    • -c:为--config的缩写,表示设置rollup打包的配置。
    • -w:为--watch的缩写,在本地开发环境添加-w参数可以监控源文件的变化,自动重新打包。

    常用插件

    rollup并不像webpack那样强大,它需要和其它插件配合使用才能完成特定的功能,常用的插件有:

    • @rollup/plugin-json: 支持从.json读取信息,配合rollupTree Shaking可只打包.json文件中我们用到的部分。
    • @rollup/plugin-commonjs:将CommonJs规范的模块转换为ES6提供rollup使用。
    • @rollup/plugin-node-resolve:与@rollup/plugin-commonjs插件一起使用,配合以后就可以使用node_modules下的第三方模块代码了。
    • @rollup/plugin-babel:把ES6代码转义成ES5代码,需要同时安装@babel/core@babel/preset-env插件。注意:如果使用了高于ES6标准的语法,例如async/await,则需要进行额外的配置。
    • rollup-plugin-terser:代码压缩插件,另外一种方案是rollup-plugin-uglify + uglify-es进行代码压缩,不过更推荐第一种方案。

    以上插件使用方式如下:

    // rollup.config.js
    import commonjs from '@rollup/plugin-commonjs'
    import json from '@rollup/plugin-json'
    import resolve from '@rollup/plugin-node-resolve'
    import babel from '@rollup/plugin-babel'
    import { terser } from 'rollup-plugin-terser'
    
    const config =  {
      input: 'src/index.js',
      output: [
        { file: 'dist/vue.js', format: 'umd', name: 'Vue' },
        { file: 'dist/vue.common.js', format: 'cjs', name: 'Vue', exports: 'auto' },
        { file: 'dist/vue.esm.js', format: 'es', name: 'Vue', exports: 'auto' }
      ],
      plugins: [
        json(),
        resolve(),
        babel(),
        commonjs(),
        terser()
      ]
    }
    
    export default config
    

    区分生产环境和开发环境

    正如你在上面看到的那样,我们可以像webpack一样进行开发环境和生产环境的配置区分,我们把和rollup构建相关的文件都放在scripts目录下:

    |-- scripts
    |   |-- rollup.config.base.js      # 公共配置
    |   |-- rollup.config.dev.js       # 开发环境配置
    |   |-- rollup.config.prod.js      # 生产环境配置
    

    根据我们的拆分逻辑,rollup.config.base.js代码如下:

    import commonjs from '@rollup/plugin-commonjs'
    import json from '@rollup/plugin-json'
    import resolve from '@rollup/plugin-node-resolve'
    import babel from '@rollup/plugin-babel'
    
    const config =  {
      input: 'src/index.js',
      plugins: [
        json(),
        resolve(),
        babel(),
        commonjs()
      ]
    }
    
    export default config
    

    rollup.config.dev.js代码如下:

    import baseConfig from './rollup.config.base.js'
    import serve from 'rollup-plugin-serve'
    import { name } from '../package.json'
    
    const config =  {
      ...baseConfig,
      output: [
        { file: 'dist/vue.js', format: 'umd', name },
        { file: 'dist/vue.common.js', format: 'cjs', name, exports: 'auto' },
        { file: 'dist/vue.esm.js', format: 'es', name, exports: 'default' }
      ],
      plugins: [
        ...baseConfig.plugins,
        serve({
          open: true,
          port: '4300',
          openPage: '/example/index.html',
          contentBase: ''
        })
      ]
    }
    export default config
    

    配置说明:本地开发环境下,我们可以有选择的添加rollup-plugin-serve插件,它类似于webpack-dev-server,能在开发环境下起一个服务方便我们进行开发和代码调试。

    rollup.config.prod.js代码如下:

    import baseConfig from './rollup.config.base.js'
    import { terser } from 'rollup-plugin-terser'
    import { name } from '../package.json'
    const config =  {
      ...baseConfig,
      output: [
        { file: 'dist/vue.min.js', format: 'umd', name },
        { file: 'dist/vue.common.min.js', format: 'cjs', name, exports: 'auto' },
        { file: 'dist/vue.esm.min.js', format: 'es', name, exports: 'default' }
      ],
      plugins: [
        ...baseConfig.plugins,
        terser()
      ]
    }
    
    export default config
    

    配置说明:生产环境下,我们需要对代码进行压缩处理,对ES ModuleCommonJsUMD等规范分别生成其对应的压缩文件。

    分别运行npm run devnpm run build之后,我们可以得到如下的目录:

    |-- dist
    |   |-- vue.js            # UMD未压缩版本
    |   |-- vue.min.js        # UMD压缩版本
    |   |-- vue.esm.js        # ES Module未压缩版本
    |   |-- vue.esm.min.js    # ES Module压缩版本
    |   |-- vue.common.js     # CommonJs未压缩版本
    |   |-- vue.common.min.js # CommonJs压缩版本
    

    最后,如果我们像Vue.js一样构建的是一个库文件,那么我们还需要在package.json进行如下配置:

    {
      "main": "dist/vue.common.js",
      "module": "dist/vue.esm.js"
    }
    

    Vue中的Rollup构建

    在阅读Vue.js源码时,我们首先应该去看其package.json文件内容,在Vue.js项目中其精简掉与compilerweexssr相关的内容以后,如下所示:

    {
      "name": "vue",
      "version": "2.6.11",
      "main": "dist/vue.runtime.common.js",
      "module": "dist/vue.runtime.esm.js",
      "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:ssr": "rollup -w -c scripts/config.js --environment TARGET:web-server-renderer",
        "build": "node scripts/build.js"
      }
    }
    

    我们可以从上面很容易的发现,其精简后的内容和我们在rollup基础知识里面的配置十分相似,其构建脚本同样放置在scripts目录下。在scripts目录下,我们需要重点关注下面几个文件:

    • alias.js:与rollup构建别名相关的配置。
    • config.js:与rollup构建不同版本相关的代码。
    • build.jsrollup构建不同压缩版本Vue.js文件相关代码。

    alias别名

    我们在开发Vue应用时,经常会用到@别名,其中@代表src目录:

    // 使用别名
    import HelloWorld from '@/components/HelloWorld.vue'
    
    // 相当于
    import HelloWorld from 'src/components/HelloWorld.vue'
    

    scripts/alias.js中,我们可以发现其别名配置代码如下:

    const path = require('path')
    const resolve = p => path.resolve(__dirname, '../', p)
    
    module.exports = {
      vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
      compiler: resolve('src/compiler'),
      core: resolve('src/core'),
      shared: resolve('src/shared'),
      web: resolve('src/platforms/web'),
      weex: resolve('src/platforms/weex'),
      server: resolve('src/server'),
      sfc: resolve('src/sfc')
    }
    

    core别名为例,在Vue.js源码中,我们通过别名进行如下引入:

    // 使用core别名
    import Vue from 'core/instance/index.js'
    
    // 相当于
    import Vue from 'src/core/instance/index.js'
    

    其中alias.js文件是在config.js中引入并使用的:

    // config.js文件
    import alias from 'rollup-plugin-alias'
    import aliases from './alias.js'
    
    function genConfig () {
      const config = {
        plugins: [
          alias(Object.assign({}, aliases))
        ])
      }
      return config
    }
    

    注意:由于Vue.js中使用rollup主版本以及其周边插件的版本较低,如果你使用了最新的rollup版本或者其周边的插件,需要按照最新插件的配置要求来,这里以最新的@rollup/plugin-alias插件为例:

    const path = require('path')
    const resolve = p => path.resolve(__dirname, '../', p)
    
    module.exports = [
      { file: 'vue', replacement: resolve('src/platforms/web/entry-runtime-with-compiler') },
      { file: 'compiler', replacement: resolve('src/compiler') },
      { file: 'core', replacement: resolve('src/core') },
      { file: 'shared', replacement: resolve('src/shared') },
      { file: 'web', replacement: resolve('src/platforms/web' },
      { file: 'weex', replacement: resolve('src/platforms/weex') },
      { file: 'server', replacement: resolve('src/server') },
      { file: 'sfc', replacement: resolve('src/sfc') }
    ]
    

    其在config.js新的使用方式同样需要做调整,如下:

    // config.js文件
    import alias from '@rollup/plugin-alias'
    import aliases from './alias.js'
    
    function genConfig () {
      const config = {
        plugins: [
          alias({ entries: aliases })
        ])
      }
      return config
    }
    

    config.js

    首先我们从package.json打包命令中可以看到,在development环境下它通过-c指定了rollup的配置文件,所以会使用到scripts/config.js文件,并且打包命令还提供了一个叫做TARGET的环境变量:

    {
      "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",
      }
    }
    

    那么在scripts/config.js文件下,我们可以看到它是通过module.exports导出的一个对象:

    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,
          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)
    }
    

    在以上代码中,我们可以看到module.exports导出的对象,主要是通过genConfig()函数返回的,其中这个函数接受的参数正是我们在打包命令中提供的环境变量TARGET。我们再来粗略的看一下genConfig()函数,它的主要作用依然是生成rollup几大核心配置,然后返回配置完毕后的对象。

    我们再来看一个叫做builds的对象,由于在源码中它的内容非常多,为了节省篇幅我们精简后其代码如下:

    const builds = {
      // 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',
      },
      'web-full-cjs-prod': {
        entry: resolve('web/entry-runtime-with-compiler.js'),
        dest: resolve('dist/vue.common.prod.js'),
        format: 'cjs',
        env: 'production'
      },
      // 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'
      },
      // 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'
      },
      // 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'
      }
    }
    

    我们可以发现它的键名正好是我们打包命令中提供的环境变量TARGET的值,这里以web-full-dev为例,它通过web-full-dev这个键可以得到一个对象:

    {
      entry: resolve('web/entry-runtime-with-compiler.js'),
      dest: resolve('dist/vue.js'),
      format: 'umd',
      env: 'development'
    }
    

    然后配合resolve函数和上面我们已经提到过的别名配置,就可以构造下面这样的rollup配置对象:

    {
      // 省略其它
      input: 'src/platforms/web/entry-runtime-with-compiler.js',
      output: {
        dest: 'dist/vue.js',
        format: 'umd',
        name: 'Vue'
      }
    }
    

    build.js

    srcipts/build.js文件的作用就是通过配置然后生成不同版本的压缩文件,其中它获取配置的方式同样是在scripts/config.js文件中,其中关键代码为:

    // config.js中导出
    exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
    
    // build.js中引入
    let builds = require('./config').getAllBuilds()
    

    起源地下载网 » Vue源码解读(Rollup篇)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元