Rollup Basics

Vue.js is built with the Rollup build tool, which is a packaging tool similar to WebPack, but is more suitable for a Library’s packaging than WebPack. Before looking at the vue.js source code, it is important to know how different versions of vue.js are built.

The core concept

Like WebPack, Rollup has the following core concepts:

  • input: Entry file, analogous towebpacktheentry, which indicates our library file entry location.
  • output: Output location, which indicates the output information after packaging, including: output directory, package file name, etc.
  • plugins: the plugin.rollupDuring the build process, plug-ins can provide some accessibility functions, such as:aliasAlias parsing and escapingES6And so on.
  • externalWhen our libraries depend on other third-party libraries, we don’t need to package them together, but write the dependencies inexternalThe inside.

Like Webpack, Rollup is also suitable for configuring packaged options using configuration files, such as:

// 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'}}]Copy the code

Build version notes:

  • umd: This option builds libraries that are primarily applicable toWebEnds can be used in different ways:scriptTag is introduced,ES ModuleSpecification introduction andCommonJsSpecification introduction, etc.
  • cjs: This option builds libraries that areCommonJsSpecifications are available atNodeEnvironment.
  • es: The library files built for this version areES ModuleSpecifications are available in supportES ModuleThat isimport/exportIs used in the environment.

With the above configuration files, we can make the following changes in 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"}}Copy the code

Parameter description:

  • -c: in order to--configAbbreviation for settingrollupPackaged configuration.
  • -w: in order to--watchIs added in the local development environment-wParameters can monitor changes in source files and automatically repackage.

Common plug-in

Rollup is not as powerful as WebPack. It needs to be used in conjunction with other plug-ins to perform specific functions. Common plug-ins include:

  • @rollup/plugin-json: support from.jsonRead the information, cooperaterolluptheTree ShakingCan pack.jsonThe part of the file that we use.
  • @rollup/plugin-commonjsWill:CommonJsModule of theES6providerollupUse.
  • @rollup/plugin-node-resolveAnd:@rollup/plugin-commonjsPlugins are used together, and you can use them laternode_modulesUnder the third party module code.
  • @rollup/plugin-babel:ES6The code escapes toES5Code that needs to be installed at the same time@babel/coreand@babel/preset-envThe plug-in. Note: If used aboveES6Standard syntax, for exampleasync/await, you need to perform additional configuration.
  • rollup-plugin-terser: code compression plug-in, another solution isrollup-plugin-uglify + uglify-esCompress the code, but the first option is preferred.

The above plug-ins are used as follows:

// 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
Copy the code

Distinguish between production and development environments

As you can see above, we can differentiate between development and production configuration just as we do with WebPack, by keeping files related to the rollup build in the scripts directory:

|-- scripts
|   |-- rollup.config.base.js      # Public configuration
|   |-- rollup.config.dev.js       Development environment configuration
|   |-- rollup.config.prod.js      Production environment configuration
Copy the code

According to our split logic, the rollup.config.base.js code looks like this:

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
Copy the code

The code of rollup.config.dev.js is as follows:

import baseConfig from './rollup.config.base.js'
import serve from 'rollup-plugin-serve'
import { name } from '.. /package.json'

constconfig = { ... 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
Copy the code

Configuration description: In the local development environment, we can optionally add the rollup-plugin-serve plug-in, which is similar to webpack-dev-server, to set up a service in the development environment to facilitate development and code debugging.

The code of rollup.config.prod.js is as follows:

import baseConfig from './rollup.config.base.js'
import { terser } from 'rollup-plugin-terser'
import { name } from '.. /package.json'
constconfig = { ... 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
Copy the code

Configuration description: In the production environment, we need to compress the code and generate corresponding compressed files for ES Module, CommonJs and UMD specifications respectively.

After running NPM run dev and NPM run build separately, we can get the following directories:

|-- dist
|   |-- vue.js            # UMD uncompressed version
|   |-- vue.min.js        # UMD compressed version
|   |-- vue.esm.js        # ES Module uncompressed version
|   |-- vue.esm.min.js    # ES Module compressed version
|   |-- vue.common.js     # CommonJs uncompressed version
|   |-- vue.common.min.js # CommonJs compressed version
Copy the code

Finally, if we were building a library file like viee. js, we would need to configure package.json as follows:

{
  "main": "dist/vue.common.js"."module": "dist/vue.esm.js"
}
Copy the code

The Rollup build in Vue

When reading the vue. js source code, we should first look at the package.json file content. In the vue. js project, compiler, weex, and SSR are removed, as follows:

{
  "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"}}Copy the code

As you can easily see from the above, the condensed content is very similar to the configuration in the Rollup basics, and the build scripts are also placed in the scripts directory. In the scripts directory, we need to focus on the following files:

  • alias.jsAnd:rollupBuild the alias related configuration.
  • config.jsAnd:rollupBuild code related to different versions.
  • build.js:rollupBuild different compressed versionsVue.jsFile related code.

Alias alias

When developing Vue applications, we often use the @ alias, where @ stands for the SRC directory:

// Use an alias
import HelloWorld from '@/components/HelloWorld.vue'

/ / equivalent to
import HelloWorld from 'src/components/HelloWorld.vue'
Copy the code

In scripts/alias.js, we can find the alias configuration code as follows:

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')}Copy the code

For example, in the vue.js source code, we introduce the alias as follows:

// Use the core alias
import Vue from 'core/instance/index.js'

/ / equivalent to
import Vue from 'src/core/instance/index.js'
Copy the code

Where alias.js is introduced and used in config.js:

/ / config. Js file
import alias from 'rollup-plugin-alias'
import aliases from './alias.js'

function genConfig () {
  const config = {
    plugins: [
      alias(Object.assign({}, aliases))
    ])
  }
  return config
}
Copy the code

Note: If you are using the latest rollup version or plugins, you will need to follow the latest configuration requirements of the plugins. The latest @rollup/plugin-alias plugin is used here as an example:

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')}]Copy the code

Its new use in config.js also needs to be adjusted, as follows:

/ / config. Js file
import alias from '@rollup/plugin-alias'
import aliases from './alias.js'

function genConfig () {
  const config = {
    plugins: [
      alias({ entries: aliases })
    ])
  }
  return config
}
Copy the code

config.js

The package.json command specifies the rollup configuration file via -c in the development environment, so the scripts/config.js file is used. The package.json command also provides an environment variable called 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",}}Copy the code

Under the scripts/config.js file, we can see that it is an exported object via 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)
}
Copy the code

In the code above, we can see that module.exports export objects are mainly returned via genConfig(), which takes the environment variable TARGET that we provided in the packaging command. Take a cursory look at the genConfig() function, which is still used to rollup the core configuration and return the configured object.

Let’s take a look at an object called Builds. Since it has a lot of content in the source code, we simplified it to save space:

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'}}Copy the code

We can see that its key is the value of the environment variable TARGET provided in our package command. In this case, it can get an object by using the key web-full-dev:

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

Then, with the resolve function and the alias configuration mentioned above, we can construct a rollup configuration object that looks like this:

{
  // Omit the others
  input: 'src/platforms/web/entry-runtime-with-compiler.js'.output: {
    dest: 'dist/vue.js'.format: 'umd'.name: 'Vue'}}Copy the code

build.js

The srcipts/build.js file is used to generate different versions of the compressed files. It is also used to obtain the configuration in the scripts/config.js file, where the key code is:

// export from config.js
exports.getAllBuilds = () = > Object.keys(builds).map(genConfig)

// build. Js
let builds = require('./config').getAllBuilds()
Copy the code