rollup简介

首先,rollup.js是一个JavaScript 模块打包器
能够将咱们本人编写的js代码与第三方模块打包在一起,也能够将小块代码编译成大块简单的代码,例如 library 或应用程序。
rollup间接反对tree shaking只有ES模块才反对,在打包构建时,会对编译的代码进行动态剖析,打包后果只蕴含应用到的代码,这样能够大幅精简代码量。
和咱们相熟的webpack相比,它更专一于js类库打包,而webpack更偏差于利用打包。

Rollup根本利用

从0应用Rollup实现一个根底应用案例, 全程应用npm操作,yarn同理(论团队对立npm和yarn以及对应版本对立的重要性

初始化操作

npm i rollup -gmkdir roolup-democd roolup-demonpm initmkdir srcvim src/name.js// i 输出const name = 'Ada'export default name// esc :wqvim src/main.js// 输出import name from './name.js'export default function() {  console.log(name)}

预览打包后源码

rollup src/main.js -f es

输入

解释一下命令参数
-f --format的缩写,代表生成代码的格局,amd就是AMD规范,cjs是commonjs,es就是es规范

上面咱们输入一下构建后的文件

rollup src/main.js -f es -o dist/output.js

-o 等同output.file

输入文件:

再来输入一个commonjs格调文件

rollup src/main.js --format cjs --o dist/output-cjs.js

test

$ node> const fn = require('./dist/index-es.js')> fn()Ada

拓展node 不反对ES规范 如何解决

所以默认以上代码应用node执行es版本会报错,咱们能够应用babel-node解决

装置如下

npm i @babel/core @babel/node @babel/cli -g

而后创立babel的配置文件.babelrc

touch .babelrc// 内容{  "presets": ["@babel/preset-env"]}

装置依赖

npm i -D @babel/core @babel/preset-env

而后通过babel编译代码

babel dist/output.js$ babel-node > require('./dist/output.js'){ default: [Function: main] }> require('./dist/output.js').default()Ada

这里babel认为default是function name

上面再介绍一下rollup.js配置文件

// 创立配置文件touch rollup.config.js

内容如下

export default {  input: './src/main.js',  output: [{    file: './dist/index-cjs.js',    format: 'cjs',    banner: '// cjs banner',    footer: '// cjs footer'  }, {    file: './dist/index-es.js',    format: 'es',    banner: '// es test banner',    footer: '// es test footer'  }]}

rollup的配置文件阐明

rollup的配置文件须要采纳ES模块规范编写
input示意入口文件的门路

output示意输入文件的内容:

  • output.file:输入文件的门路(老版本为dest,曾经废除)
  • output.format:输入文件的格局
  • output.banner:文件头部增加的内容
  • output.footer:文件开端增加的内容

有了配置文件就能够间接应用rollup -c进行打包了

最初咱们来实现一个简略地build代码

npm i -D rolluptouch rollup-build.jstouch rollup-input.jstouch rollup-output.js// 内容// inputmodule.exports = {  input: './src/main.js'}// outputmodule.exports = [{  file: './dist/index-cjs.js',  format: 'cjs',  banner: '// hello',  footer: '// bye'},{  file: './dist/index-es.js',  format: 'es',  banner: '// hello A',  footer: '// bye A'},{  file: './dist/index-amd.js',  format: 'amd',  banner: '// hello B',  footer: '// bye B'},{  file: './dist/index-umd.js',  format: 'umd',  name: 'umd',  banner: '// hello C',  footer: '// bye C'}]// build // 通过rollup.rollup(input)获取输出// 通过bundle.write(output)输入文件const rollup = require('rollup')const inputOptions = require('./rollup-input')const outputOptions = require('./rollup-output')async function rollupBuild(input, output) {  const bundle = await rollup.rollup(input) // 依据input配置进行打包  console.log(`current:${output.file}`)  await bundle.write(output) // 依据output配置输入文件  console.log(`${output.file} success!`)}(async function () {  for (let i = 0; i < outputOptions.length; i++) {    await rollupBuild(inputOptions, outputOptions[i])  }})()

执行测试一下

node rollup-build.js

根底拓展:watch 监听文件变动主动build

touch rollup-watch-build.jstouch rollup-watch.jsmodule.exports = {  include: 'src/**', // 监听的文件夹  exclude: 'node_modules/**' // 排除监听的文件夹}

watch build

const rollup = require('rollup')const inputOptions = require('./rollup-input')const outputOptions = require('./rollup-output')const watchOptions = require('./rollup-watch')const options = {  ...inputOptions,  output: outputOptions,  watchOptions}const watcher = rollup.watch(options) // 调用rollup的api启动监听watcher.on('event', event => {  console.log('从新打包...', event.code)})

这样当咱们批改src目录文件后就会主动从新打包
平时咱们也能够脚本间接写 rollup -wc 命令

rollup插件扫盲 不便浏览vue构建源码

插件形容
resolve插件集成内部模块
commonjs插件反对CommonJS模块
babel插件编译ES6语法,兼容低版本浏览器
commonjs插件反对CommonJS模块
uglify插件代码最小化打包
json插件反对json

插件的应用都很简略,大家能够按需进行尝试
上面咱们进入vue build局部,去剖析一下在vue源码中build做了那些事。

vue build 剖析

间接从源码动手,以下代码外围局部已削减对应备注。

1.首先将下载源码到本地装置依赖

git clone https://github.com/vuejs/vue.git"cd vuejsnpm i

2.咱们来看一下 package.json 咱们以后须要关怀的局部

"scripts": {  "build": "node scripts/build.js",  "build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",  "build:weex": "npm run build -- weex"  ...},"devDependencies": {  "rollup": "^1.0.0",  "rollup-plugin-alias": "^1.3.1",  "rollup-plugin-buble": "^0.19.6",  "rollup-plugin-commonjs": "^9.2.0",  "rollup-plugin-flow-no-whitespace": "^1.0.0",  "rollup-plugin-node-resolve": "^4.0.0",  "rollup-plugin-replace": "^2.0.0",  ...}

由依赖能够看出vue有应用rollup

上面咱们依据 build scripts去看一下构建脚本都做了什么事

3.scripts/build.js

正文曾经退出源码之中 咱们间接来看源码 一个惯例vue源码构建咱们次要看以下三个文件就能够 build.js config.js alias.js

build 执行大抵能够分为以下5局部

以下为build.js 源码请联合正文进行查看

const fs = require('fs') // 文件解决const path = require('path') // 本地门路解析const zlib = require('zlib') // gzip压缩const rollup = require('rollup') // 打包工具const terser = require('terser') // 代码压缩// 1、创立dist目录// 判断dist 是否存在 不存在进行创立if (!fs.existsSync('dist')) {  fs.mkdirSync('dist')}// 2、通过 config 生成rollup配置let builds = require('./config').getAllBuilds()// 3、rollup配置文件过滤。// 依据传入的参数,对rollup配置文件的内容进行过滤,排除不必要的打包我的项目// filter builds via command line argconsole.log('process.argv', red(process.argv))if (process.argv[2]) {  const filters = process.argv[2].split(',')  console.log('filters', red(filters))  builds = builds.filter(b => {    return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1)  })} else {  // filter out weex builds by default  builds = builds.filter(b => {    return b.output.file.indexOf('weex') === -1  })}// 4.rollup打包build(builds)function build (builds) {  let built = 0  // 以后打包项序号  const total = builds.length // // 须要打包的总次数  const next = () => {    buildEntry(builds[built]).then(() => {      built++      if (built < total) {        next() // 如果打包序号小于打包总次数,则继续执行next()函数      }    }).catch(logError)  }  next()}// 打包执行外围函数function buildEntry (config) {  // 获取rollup 配置信息  const output = config.output  const { file, banner } = output  const isProd = /(min|prod)\.js$/.test(file) //是否压缩 min结尾 标识  return rollup.rollup(config)    .then(bundle => bundle.generate(output))    .then(({ output: [{ code }] }) => {      if (isProd) {        // 最小化打包        const minified = (banner ? banner + '\n' : '') + terser.minify(code, {          toplevel: true,          output: {            ascii_only: true          },          compress: {            pure_funcs: ['makeMap']          }        }).code        return write(file, minified, true)      } else {        return write(file, code)      }    })}// 5. 文件输入 zlib.gzip压缩// dest 门路 code源码function write (dest, code, zip) {  return new Promise((resolve, reject) => {    function report (extra) {      console.log(blue(path.relative(process.cwd(), dest)) + ' ' + getSize(code) + (extra || ''))      resolve()    }        fs.writeFile(dest, code, err => {      if (err) return reject(err)      if (zip) {        zlib.gzip(code, (err, zipped) => {          if (err) return reject(err)          report(' (gzipped: ' + getSize(zipped) + ')')        })      } else {        report()      }    })  })}// 获取文件大小function getSize (code) {  return (code.length / 1024).toFixed(2) + 'kb'}function logError (e) {  console.log(e)}// 生成蓝色文本 ANSI 本义码 \x1b[(文字装璜);(颜色代码):function blue (str) {  return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m'}// 同理咱们实现一个 输出红色文本function red (str) {  return '\x1B[31m' + str + '\x1B[39m'}

config.js 选取局部内容

const path = require('path') // 本地门路解析const buble = require('rollup-plugin-buble') // es6+ 语法转为es5const alias = require('rollup-plugin-alias') // 替换模块别名const cjs = require('rollup-plugin-commonjs') // 反对commonjs模块const replace = require('rollup-plugin-replace') // 替换代码中变量为指定值const node = require('rollup-plugin-node-resolve') //内部模块集成const flow = require('rollup-plugin-flow-no-whitespace') // 去除flow动态类型查看const version = process.env.VERSION || require('../package.json').version // 获取以后版本const weexVersion = process.env.WEEX_VERSION || require('../packages/weex-vue-framework/package.json').version// weex版本 Weex 是一款轻量级的挪动端跨平台动态性技术解决方案 The Weex podling retired on 2021-05-14const featureFlags = require('./feature-flags') // 增加2个构建常量// 打包后 bannerconst banner =  '/*!\n' +  ` * Vue.js v${version}\n` +  ` * (c) 2014-${new Date().getFullYear()} Evan You\n` +  ' * Released under the MIT License.\n' +  ' */'// 打包weex-factory应用const weexFactoryPlugin = {  intro () {    return 'module.exports = function weexFactory (exports, document) {'  },  outro () {    return '}'  }}const aliases = require('./alias') // 别名及其对应的绝对路径const resolve = p => {  const base = p.split('/')[0]  // aliases 匹配 name是否存在  if (aliases[base]) {    return path.resolve(aliases[base], p.slice(base.length + 1))  } else {    // 不存在则合并根门路和传入    return path.resolve(__dirname, '../', p)  }}// build 配置const builds = {  // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify  'web-runtime-cjs-dev': {    entry: resolve('web/entry-runtime.js'),    dest: resolve('dist/vue.runtime.common.dev.js'),    format: 'cjs',    env: 'development',    banner  },  'web-runtime-cjs-prod': {    entry: resolve('web/entry-runtime.js'),    dest: resolve('dist/vue.runtime.common.prod.js'),    format: 'cjs',    env: 'production',    banner  },  // 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',    alias: { he: './entity-decoder' },    banner  },  'web-full-cjs-prod': {    entry: resolve('web/entry-runtime-with-compiler.js'),    dest: resolve('dist/vue.common.prod.js'),    format: 'cjs',    env: 'production',    alias: { he: './entity-decoder' },    banner  },  // Runtime only ES modules build (for bundlers)  'web-runtime-esm': {    entry: resolve('web/entry-runtime.js'),    dest: resolve('dist/vue.runtime.esm.js'),    format: 'es',    banner  },  ...}// 依据name生成对应环境的rollup配置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)      }    }  }  // built-in vars  const vars = {    __WEEX__: !!opts.weex,    __WEEX_VERSION__: weexVersion,    __VERSION__: version  }  // feature flags  Object.keys(featureFlags).forEach(key => {    vars[`process.env.${key}`] = featureFlags[key]  })  // build-specific env  if (opts.env) {    vars['process.env.NODE_ENV'] = JSON.stringify(opts.env)  }  config.plugins.push(replace(vars))  if (opts.transpile !== false) {    config.plugins.push(buble())  }  Object.defineProperty(config, '_name', {    enumerable: false,    value: name  })  // console.log('vars', vars)  // console.log('config', JSON.stringify(config, null, 2))  return config}// 判断环境变量TARGET是否定义 存在即输入if (process.env.TARGET) {  module.exports = genConfig(process.env.TARGET)} else {  exports.getBuild = genConfig  exports.getAllBuilds = () => Object.keys(builds).map(genConfig)}

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

以上就是本次分享的内容,心愿大家能够一步一步跟着去操作,进行实际。