rollup打包一个rem库


环境初始化

1. npm init -y

2.在当前根目录下新建 src 目录和 scripts 目录,在 src 新建 index.js、在 scripts 里面新建 rollup.config.js 如下

|-- src
  |--index.js
|--scripts
  | --rollup.config.js
|-- package.json

配置 rollup.config.js 如 babel

1.使用 Babel 和 Rollup 的最简单方法是使用 @rollup/plugin-babel。 首先,安装插件

npm i -D @rollup/plugin-babel @rollup/plugin-node-resolve

2.然后在 rollup.config.js 添加如下配置

import resolve from '@rollup/plugin-node-resolve'
import babel from '@rollup/plugin-babel'

export default {
  input: 'src/index.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [
    resolve(),
    babel({
      babelHelpers: 'bundled',
      exclude: 'node_modules/**' // 只编译我们的源代码
    })
  ]
}

3.安装 babel-core and the env preset,同时在根目录下新建.babelrc 进行配置

npm i -D @babel/core @babel/preset-env
{
  "presets": [
    ["@babel/preset-env", {
      "modules": false
    }]
  ]
}

配置 npm scripts

1.执行 rollup,看看输出结果

npx rollup -c './scripts/rollup.config.js'

执行完会在当前根目录下输出一个 bundle.js,如果想实时更改编译输出执行

npx rollup -w -c './scripts/rollup.config.js'

2.修改 rollup.config.js 的输出格式

output: [
  {
    file: './lib/index.iife.js',
    name: 'taorem',
    format: 'iife'
  },
  {
    file: './lib/index.umd.js',
    name: 'taorem',
    format: 'umd'
  },
  {
    file: './lib/index.esm.js',
    format: 'esm'
  }
]

3.在 package.json 配置 scripts

"scripts": {
  "build": "rollup -c './scripts/rollup.config.js'",
  "build:watch": "rollup -w -c './scripts/rollup.config.js'"
},

执行 npm run build,此时会报错 sh: rollup: command not found,这是因为 rollup 还没有安装,一般我们不会安装在本地全局

npm i -D rollup

再次执行npm run build,输出如下

本地测试打包结果

1.在根目录新建 examples 文件夹,里面建一个 index.html,引入我们的打包过后文件夹的文件。

<script src="../lib/index.iife.js"></script>

2.我们本地需要启动一个服务,可用rollup-plugin-serve或者browser-sync,常用插件可以从这里了解。这里我们用 browser-sync

npm install -D browser-sync

3.配置 Browsersync options

npx browser-sync init

执行完会在根目录下创建一个 bs-config.js,内容大概如下

/*
 |--------------------------------------------------------------------------
 | Browser-sync config file
 |--------------------------------------------------------------------------
 |
 | For up-to-date information about the options:
 |   http://www.browsersync.io/docs/options/
 |
 | There are more options than you see here, these are just the ones that are
 | set internally. See the website for more info.
 |
 |
 */
module.exports = {
  ui: {
    port: 3001
  },
  files: false,
  watchEvents: ['change'],
  watch: false,
  ignore: [],
  single: false,
  watchOptions: {
    ignoreInitial: true
  },
  server: false,
  proxy: false,
  port: 3000,
  middleware: false,
  serveStatic: [],
  ghostMode: {
    clicks: true,
    scroll: true,
    location: true,
    forms: {
      submit: true,
      inputs: true,
      toggles: true
    }
  },
  logLevel: 'info',
  logPrefix: 'Browsersync',
  logConnections: false,
  logFileChanges: true,
  logSnippet: true,
  rewriteRules: [],
  open: 'local',
  browser: 'default',
  cors: false,
  xip: false,
  hostnameSuffix: false,
  reloadOnRestart: false,
  notify: true,
  scrollProportionally: true,
  scrollThrottle: 0,
  scrollRestoreTechnique: 'window.name',
  scrollElements: [],
  scrollElementMapping: [],
  reloadDelay: 0,
  reloadDebounce: 500,
  reloadThrottle: 0,
  plugins: [],
  injectChanges: true,
  startPath: null,
  minify: true,
  host: null,
  localOnly: false,
  codeSync: true,
  timestamps: true,
  clientEvents: [
    'scroll',
    'scroll:element',
    'input:text',
    'input:toggles',
    'form:submit',
    'form:reset',
    'click'
  ],
  socket: {
    socketIoOptions: {
      log: false
    },
    socketIoClientConfig: {
      reconnectionAttempts: 50
    },
    path: '/browser-sync/socket.io',
    clientPath: '/browser-sync',
    namespace: '/browser-sync',
    clients: {
      heartbeatTimeout: 5000
    }
  },
  tagNames: {
    less: 'link',
    scss: 'link',
    css: 'link',
    jpg: 'img',
    jpeg: 'img',
    png: 'img',
    svg: 'img',
    gif: 'img',
    js: 'script'
  },
  injectNotification: false
}

将此文件移动到 scripts 文件夹,加入 const path = require(‘path’)
const rootDir = path.resolve(‘../‘)
修改 options 的 files 为files: [path.join(rootDir, ‘./lib’), path.join(rootDir, ‘./examples’)] 修改 serveStatic 为 serveStatic: [path.join(rootDir, ‘./lib’), path.join(rootDir, ‘./examples’)] server 为server: true startPath 为 startPath: ‘./examples/index.html’ 完整的如下

const path = require('path')
const rootDir = path.resolve('../')
/*
 |--------------------------------------------------------------------------
 | Browser-sync config file
 |--------------------------------------------------------------------------
 |
 | For up-to-date information about the options:
 |   http://www.browsersync.io/docs/options/
 |
 | There are more options than you see here, these are just the ones that are
 | set internally. See the website for more info.
 |
 |
 */
module.exports = {
  ui: {
    port: 3001
  },
  files: [path.join(rootDir, './lib'), path.join(rootDir, './examples')],
  watchEvents: ['change'],
  watch: false,
  ignore: [],
  single: false,
  watchOptions: {
    ignoreInitial: true
  },
  server: true,
  proxy: false,
  port: 3000,
  middleware: false,
  serveStatic: [path.join(rootDir, './lib'), path.join(rootDir, './examples')],
  ghostMode: {
    clicks: true,
    scroll: true,
    location: true,
    forms: {
      submit: true,
      inputs: true,
      toggles: true
    }
  },
  logLevel: 'info',
  logPrefix: 'Browsersync',
  logConnections: false,
  logFileChanges: true,
  logSnippet: true,
  rewriteRules: [],
  open: 'local',
  browser: 'default',
  cors: false,
  xip: false,
  hostnameSuffix: false,
  reloadOnRestart: false,
  notify: true,
  scrollProportionally: true,
  scrollThrottle: 0,
  scrollRestoreTechnique: 'window.name',
  scrollElements: [],
  scrollElementMapping: [],
  reloadDelay: 0,
  reloadDebounce: 500,
  reloadThrottle: 0,
  plugins: [],
  injectChanges: true,
  startPath: './examples/index.html',
  minify: true,
  host: null,
  localOnly: false,
  codeSync: true,
  timestamps: true,
  clientEvents: [
    'scroll',
    'scroll:element',
    'input:text',
    'input:toggles',
    'form:submit',
    'form:reset',
    'click'
  ],
  socket: {
    socketIoOptions: {
      log: false
    },
    socketIoClientConfig: {
      reconnectionAttempts: 50
    },
    path: '/browser-sync/socket.io',
    clientPath: '/browser-sync',
    namespace: '/browser-sync',
    clients: {
      heartbeatTimeout: 5000
    }
  },
  tagNames: {
    less: 'link',
    scss: 'link',
    css: 'link',
    jpg: 'img',
    jpeg: 'img',
    png: 'img',
    svg: 'img',
    gif: 'img',
    js: 'script'
  },
  injectNotification: false
}

在 package.json 里面配置 scripts

"scripts": {
  "server": "browser-sync start -c './scripts/bs-config.js'",
},

为了方便调试我们再增加两个 scripts

  "start": "npm run server",
  "dev": "npm run build:watch & npm run server"

开始编写 rem 代码

html 中的 font-size 作为一个视觉与实际显示宽度作为缩放因子,并乘以 100 (为提供精度,部分浏览器 font-size 忽略小数点);转换公式如下:

rootFontSize = (designWidth / viewportWidth) * 100
remValue = designX / 100

const win = window
const doc = document
const taorem = (options = {}) => {
  const normalFontSize = 14
  const { designWidth = 750, maxClientWidth = 1024, bodyFontSize = normalFontSize } = options

  const docEl = doc.documentElement
  const dpr = win.devicePixelRatio || 1
  const headEl = doc.head
  let rem = 16

  // adjust body font size
  function setBodyFontSize() {
    const bodyEl = doc.body
    if (bodyEl) {
      bodyEl.style.fontSize = `${bodyFontSize}px`
    }
  }

  // set 1rem = viewWidth / 10
  function setRemUnit() {
    rem = (Math.min(docEl.clientWidth, maxClientWidth) / designWidth) * 100
    docEl.style.fontSize = `${rem}px`

    /* 针对系统自定义字号导致页面错乱问题修复 */
    const styles = window.getComputedStyle(docEl)
    const fontSize = parseFloat(styles.fontSize)
    // rem单位进行四舍五入保留4位小数再比较,因为系统仅保留4位
    if (fontSize !== Math.round(rem * 10000) / 10000) {
      rem = rem * (rem / fontSize) // 根据实际fontSize尺寸得出缩放比例,计算出页面需要的fontSize
      docEl.style.fontSize = `${rem}px`
    }
  }

  setRemUnit()

  // reset rem unit on page resize
  window.addEventListener('resize', setRemUnit)
  window.addEventListener('pageshow', function (e) {
    if (e.persisted) {
      setRemUnit()
    }
  })

  if (doc.body) {
    setBodyFontSize()
  } else {
    doc.addEventListener('DOMContentLoaded', setBodyFontSize)
  }

  return {
    rem,
    dpr,
    setRemUnit,
    rem2px: (d) => {
      const val = parseFloat(d) * rem
      if (/^([0-9.]+)rem$/.test(d)) {
        return `${val}px`
      }
      return val
    },
    px2rem: (d) => {
      const val = parseFloat(d) / rem
      if (/^([0-9]+)px$/.test(d)) {
        return `${val}rem`
      }
      return val
    }
  }
}

const scripts = doc.scripts
const currentScript = scripts[scripts.length - 1]
const taoremEnable = currentScript && currentScript.getAttribute('taorem')

if (taoremEnable !== null || taoremEnable !== undefined) {
  const dataset = { ...currentScript.dataset }
  taorem(dataset)
}

export default taorem

examples index.html 如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>测试</title>
    <script taorem src="../lib/index.iife.js"></script>
  </head>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    html,
    body {
      width: 100%;
      height: 100%;
    }
    .container {
      display: block;
      width: 100%;
      height: 5rem;
      overflow: hidden;
    }
    .aside {
      display: block;
      float: left;
      width: 2rem;
      height: 5rem;
      background: yellow;
    }
    .content {
      display: block;
      overflow: hidden;
      height: 5rem;
      background: red;
    }
    .hairline {
      margin: 1rem auto;
      border: #fff 0.5px solid;
    }
  </style>
  <body>
    <h1>taorem测试方案</h1>
    <div class="container">
      <aside class="aside"></aside>
      <content class="content">
        <div class="hairline"></div>
      </content>
    </div>
  </body>
  <script></script>
</html>

发布到 npm

在 package.json 添加配置


  "main": "lib/index.umd.js",
  "module": "lib/index.esm.js",
  "browser": "lib/index.iife.js",


  "files": [
    "src",
    "lib",
    "examples"
  ],

发布到 npm,注意需要将包名改下,不然发布会报错

npm login
npm publish

添加 eslint

代码添加 eslint,rollup-plugin-eslint

npm install eslint rollup-plugin-eslint --save-dev

在 rollup.config.js 添加配置

import { eslint } from 'rollup-plugin-eslint'
plugins: [eslint()]

然后建立.eslintrc.js 来根据自己风格配置具体检测

module.exports = {
  env: {
    browser: true,
    es6: true,
    node: true
  },
  extends: 'eslint:recommended',
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module'
  }
}

添加发布信息

在 rollup.config.js 添加

import dayjs from 'dayjs'
import pkg from '../package.json'

const banner = `/**
 * REM布局方案
 * @version: ${pkg.version}
 * @author: wutao
 * @date: ${dayjs().format('YYYY/MM/DD')}
 **/`

 output: [
    {
      file: './lib/index.iife.js',
      name: 'taorem',
      format: 'iife',
      banner
    },
    {
      file: './lib/index.umd.js',
      name: 'taorem',
      format: 'umd',
      banner
    },
    {
      file: './lib/index.esm.js',
      format: 'esm',
      banner
    }
  ],

  //同时安装依赖
  npm install -D dayjs

评论
 本篇
rollup打包一个rem库 rollup打包一个rem库
环境初始化1. npm init -y 2.在当前根目录下新建 src 目录和 scripts 目录,在 src 新建 index.js、在 scripts 里面新建 rollup.config.js 如下 |-- src |--in
2021-08-31 Tao
下一篇 
javaScript常用工具方法封装 javaScript常用工具方法封装
前言总结了工作中经常用的方法 JavaScripttype 类型判断isString (o) { //是否字符串 return Object.prototype.toString.call(o).slice(8, -1) === '
2019-01-28
  目录