rollup 打造自己的 javascript 武器库

作者: tww844475003 分类: 前端开发 发布时间: 2022-06-07 22:43

初始化项目

mkdir ife-utils
cd ife-utils
npm init -y

rollup安装与使用

npm i rollup -g        # 全局安装
npm i rollup -D        # 项目本地安装

javascript 武器库

// src/index.js

/**
 * @desc 判断两个数组是否相等
 * @param {Array} arr1 
 * @param {Array} arr2 
 * @return {Boolean}
 */
export const arrayEqual = (arr1, arr2) => {
  if (arr1 === arr2) return true;
  if (arr1.length != arr2.length) return false;
  for (let i = 0; i < arr1.length; ++i) {
    if (arr1[i] !== arr2[i]) return false;
  }
  return true;
}

/**
 * @desc 判断元素是否有某个class
 * @param {HTMLElement} ele 
 * @param {String} cls 
 * @return {Boolean}
 */
export const hasClass = (ele, cls) => {
  return (new RegExp('(\\s|^)' + cls + '(\\s|$)')).test(ele.className);
}

/**
 * @desc   为元素添加class
 * @param  {HTMLElement} ele 
 * @param  {String} cls 
 */
export const addClass = (ele, cls) => {
  if (!hasClass(ele, cls)) {
    ele.className += ' ' + cls;
  }
}

/**
 * @desc 为元素移除class
 * @param {HTMLElement} ele 
 * @param {String} cls 
 */
export const removeClass = (ele, cls) => {
  if (hasClass(ele, cls)) {
    const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
    ele.className = ele.className.replace(reg, ' ');
  }
}

/**
 * @desc 根据name读取cookie
 * @param  {String} name 
 * @return {String}
 */
export const getCookie = (name) => {
  const arr = document.cookie.replace(/\s/g, '').split(';');
  for (let i = 0; i < arr.length; i++) {
    const tempArr = arr[i].split('=');
    if (tempArr[0] == name) {
      return decodeURIComponent(tempArr[1]);
    }
  }
  return '';
}

/**
 * @desc 根据name删除cookie
 * @param  {String} name 
 */
export const removeCookie = (name) => {
  // 设置已过期,系统会立刻删除cookie
  setCookie(name, '1', -1);
}

/**
 * @desc  设置Cookie
 * @param {String} name 
 * @param {String} value 
 * @param {Number} days 
 */
export const setCookie = (name, value, days) => {
  const date = new Date();
  date.setDate(date.getDate() + days);
  document.cookie = name + '=' + value + ';expires=' + date;
}

/**
 * @desc 获取浏览器类型和版本
 * @return {String} 
 */
export const getExplore = () => {
  let sys = {},
    ua = navigator.userAgent.toLowerCase(),
    s;
  (s = ua.match(/rv:([\d.]+)\) like gecko/)) ? sys.ie = s[1] :
    (s = ua.match(/msie ([\d\.]+)/)) ? sys.ie = s[1] :
      (s = ua.match(/edge\/([\d\.]+)/)) ? sys.edge = s[1] :
        (s = ua.match(/firefox\/([\d\.]+)/)) ? sys.firefox = s[1] :
          (s = ua.match(/(?:opera|opr).([\d\.]+)/)) ? sys.opera = s[1] :
            (s = ua.match(/chrome\/([\d\.]+)/)) ? sys.chrome = s[1] :
              (s = ua.match(/version\/([\d\.]+).*safari/)) ? sys.safari = s[1] : 0;
  // 根据关系进行判断
  if (sys.ie) return ('IE: ' + sys.ie)
  if (sys.edge) return ('EDGE: ' + sys.edge)
  if (sys.firefox) return ('Firefox: ' + sys.firefox)
  if (sys.chrome) return ('Chrome: ' + sys.chrome)
  if (sys.opera) return ('Opera: ' + sys.opera)
  if (sys.safari) return ('Safari: ' + sys.safari)
  return 'Unkonwn'
}

/**
 * @desc 获取操作系统类型
 * @return {String} 
 */
export const getOS = () => {
  const userAgent = 'navigator' in window && 'userAgent' in navigator && navigator.userAgent.toLowerCase() || '';
  const vendor = 'navigator' in window && 'vendor' in navigator && navigator.vendor.toLowerCase() || '';
  const appVersion = 'navigator' in window && 'appVersion' in navigator && navigator.appVersion.toLowerCase() || '';
  if (/mac/i.test(appVersion)) return 'MacOSX'
  if (/win/i.test(appVersion)) return 'windows'
  if (/linux/i.test(appVersion)) return 'linux'
  if (/iphone/i.test(userAgent) || /ipad/i.test(userAgent) || /ipod/i.test(userAgent)) 'ios'
  if (/android/i.test(userAgent)) return 'android'
  if (/win/i.test(appVersion) && /phone/i.test(userAgent)) return 'windowsPhone'
}

/**
 * @desc 获取滚动条距顶部的距离
 */
 export const getScrollTop = () => {
  return (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
}

/**
 * @desc  获取一个元素的距离文档(document)的位置,类似jQ中的offset()
 * @param {HTMLElement} ele 
 * @returns { {left: number, top: number} }
 */
export const offset = (ele) => {
  const pos = {
    left: 0,
    top: 0
  };

  while (ele) {
    pos.left += ele.offsetLeft;
    pos.top += ele.offsetTop;
    ele = ele.offsetParent;
  };
  return pos;
}

const requestAnimationFrame = (function () {
  return window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    function (callback) {
      window.setTimeout(callback, 1000 / 60);
    };
})();

/**
* @desc  在${duration}时间内,滚动条平滑滚动到${to}指定位置
* @param {Number} to 
* @param {Number} duration 
*/
export const scrollTo = (to, duration) => {
  if (duration < 0) {
    setScrollTop(to);
    return
  }

  const diff = to - getScrollTop();
  if (diff === 0) return
  const step = diff / duration * 10;
  requestAnimationFrame(
    function () {
      if (Math.abs(step) > Math.abs(diff)) {
        setScrollTop(getScrollTop() + diff);
        return;
      }
      setScrollTop(getScrollTop() + step);
      if (diff > 0 && getScrollTop() >= to || diff < 0 && getScrollTop() <= to) {
        return;
      }
      scrollTo(to, duration - 16);
    });
}

/**
 * @desc 设置滚动条距顶部的距离
 */
export const setScrollTop = (value) => {
  window.scrollTo(0, value);
  return value;
}

/**
 * @desc 深拷贝,支持常见类型
 * @param {Any} values
 */
export const deepClone = (values) => {
  let copy;

  // Handle the 3 simple types, and null or undefined
  if (null == values || 'object' != typeof values) return values;

  // Handle Date
  if (values instanceof Date) {
    copy = new Date();
    copy.setTime(values.getTime());
    return copy;
  }

  // Handle Array
  if (values instanceof Array) {
    copy = [];
    for (let i = 0, len = values.length; i < len; i++) {
      copy[i] = deepClone(values[i]);
    }
    return copy;
  }

  // Handle Object
  if (values instanceof Object) {
    copy = {};
    for (let attr in values) {
      if (values.hasOwnProperty(attr)) copy[attr] = deepClone(values[attr]);
    }
    return copy;
  }
  throw new Error('Unable to copy values! Its type isn\'t supported.');
}

/**
 * @desc   判断`obj`是否为空
 * @param  {Object} obj
 * @return {Boolean}
 */
export const isEmptyObject = (obj) => {
  if (!obj || typeof obj !== 'object' || Array.isArray(obj))
    return false
  return !Object.keys(obj).length
}

rollup 配置

  • rollup.config.js
import pkg from './package.json';

export default {
  input: './src/index.js',
  output: [
    {
      file: pkg.browser,
      format: 'umd',
      name: 'ifeUtils',
    },
    {
      file: pkg.module,
      format: 'es',
    },
    {
      file: pkg.main,
      format: 'cjs',
    }
  ],
}
  • package.json 配置
{
  "main": "dist/ife-utils.cjs.js",
  "module": "dist/ife-utils.esm.js",
  "browser": "dist/ife-utils.umd.js",
  "scripts": {
    "dev": "cross-env NODE_ENV=development rollup -wc",
    "build": "cross-env NODE_ENV=production rollup -c",
    "upload": "npm run build && npm publish"
  }
}

由于 window 环境支持 ”环境变量“ 需要依赖 cross-env

npm i -D cross-env

rollup 环境变量配置

import replace from 'rollup-plugin-replace';

const NODE_ENV = process.env.NODE_ENV
const isProd = NODE_ENV === 'production'

export default {
  ...
  plugins: [
    replace({
      'process.env.NODE_ENV': JSON.stringify(NODE_ENV)
    }),
  ],
}
  • rollup-plugin-babel es6 语法支持
npm i rollup-plugin-babel @babel/core @babel/preset-env --D
import replace from 'rollup-plugin-babel';

export default {
  ...
  plugins: [
    babel({
      exclude: 'node_modules/**'
    }),
  ],
}

.babelrc

{
  "presets": [
    [
      "@babel/preset-env"
    ]
  ]
}
  • rollup-plugin-commonjs

rollup默认是不支持CommonJS模块的,自己写的时候可以尽量避免使用CommonJS模块的语法,但有些外部库的是cjs或者umd(由webpack打包的),所以使用这些外部库就需要支持CommonJS模块。

npm i -D rollup-plugin-commonjs
import replace from 'rollup-plugin-commonjs';

export default {
  ...
  plugins: [
    commonjs(),
  ],
}
  • rollup-plugin-node-resolve

node 模块路由解析

npm i -D rollup-plugin-node-resolve
import nodeResolve from 'rollup-plugin-node-resolve';

export default {
  ...
  plugins: [
    nodeResolve(),
  ],
}
  • serve 启动及热刷新
npm i -D rollup-plugin-livereload rollup-plugin-serve

import livereload from 'rollup-plugin-livereload';
import serve from 'rollup-plugin-serve';

export default {
  ...
  plugins: [
    livereload(),
    serve({
      open: true,
      openPage: '/public/index.html',
      port: 3000,
      contentBase: './'
    }),
  ],
}

public/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>ife-utils</title>
</head>
<body>
<script src="../dist/ife-utils.umd.js"></script>
<script>
  const isEmptyObject = ifeUtils.isEmptyObject({});
  console.log(isEmptyObject);
</script>
</body>
</html>
  • 代码压缩
npm i -D rollup-plugin-terser

import { terser } from 'rollup-plugin-terser';

export default {
  ...
  plugins: [
    terser(),
  ],
}
  • 构建文件大小输出
npm i -D rollup-plugin-filesize

import filesize from 'rollup-plugin-filesize';

export default {
  ...
  plugins: [
    filesize(),
  ],
}

综合 rollup.config.js 配置

import babel from 'rollup-plugin-babel';
import commonjs from 'rollup-plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import nodeResolve from 'rollup-plugin-node-resolve';
import replace from 'rollup-plugin-replace';
import livereload from 'rollup-plugin-livereload';
import serve from 'rollup-plugin-serve';
import filesize from 'rollup-plugin-filesize';
import pkg from './package.json';

const NODE_ENV = process.env.NODE_ENV
const isProd = NODE_ENV === 'production'

let envPlugins = []
if (isProd) {
  envPlugins = [
    terser(),
    filesize(),
  ]
} else {
  envPlugins = [
    livereload(),
    serve({
      open: true,
      openPage: '/public/index.html',
      port: 3000,
      contentBase: './'
    }),
  ]
}


export default {
  input: './src/index.js',
  output: [
    {
      file: pkg.browser,
      format: 'umd',
      name: 'ifeUtils',
    },
    {
      file: pkg.module,
      format: 'es',
    },
    {
      file: pkg.main,
      format: 'cjs',
    }
  ],
  plugins: [
    babel({
      exclude: 'node_modules/**'
    }),
    nodeResolve(),
    commonjs(),
    replace({
      'process.env.NODE_ENV': JSON.stringify(NODE_ENV)
    }),
    ...envPlugins,
  ],
}

源码下载

文档说明

https://www.npmjs.com/package/ife-utils

前端开发那点事
微信公众号搜索“前端开发那点事”

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注