JS 开发工具库

前言

  • 日常开发中我们总会需要封装各种各样的函数,有的函数在项目中用到多次,有的甚至在很多项目中都要用到
  • 这就需要我们将封装的函数打包成库,方便下次使用
  • 本文将是使用 JavaScript 开发函数,并上传到 npm

一、开发环境准备

1、安装 Node

  • node.js 安装很简单,网上很多教程

2、创建项目

  • 在自己平常放置项目代码的文件夹下创建一个项目文件夹(建议别使用中文名称)
  • 然后在新创建的项目文件夹下分别创建两个文件,一个用于测试的 html,一个用于开发的 js(就是正常开发项目时的样子)
    在这里插入图片描述
  • 然后就可以正常开发了

二、工具函数的封装

1、Ajax 的封装

/**
 * Ajax 封装
 */
 
function ajax({ method, url, params, data }) {
  // 将 method 转为大写 toUpperCase()
  method = method.toUpperCase()

  return new Promise((resolve, reject) => {
    /** 
     * 1、创建 XMLHttpRequest 对象
     **/
    let xhr = null
    if (window.XMLHttpRequest) {
      // 这种方式是对于使用这几种浏览器的情况
      // IE7+,Firefox,Chrome,Opera,Safari ...
      xhr = new XMLHttpRequest();
    } else {
      // 这种方式是针对低版本浏览器:IE6,IE5
      xhr = new ActiveObject("Microsoft.XMLHTTP");
    }

    /**
     * 2、初始化
     **/
    // 需要将 params: { a: 100, b:200 } 转为 a=100&b=200 的形式
    let str = ""
    for (let k in params) {
      str += `${k}=${params[k]}&`
    }
    // 对最后一个参数后面的 & 截掉
    str = str.slice(0, -1)

    xhr.open(method, url + '?' + str)

    /**
     * 3、发送
     */
    if (method === 'POST' || method === 'PUT' || method === 'DELETE' || method === 'PATCH') {
      // 设置请求头信息
      xhr.setRequestHeader('Content-type', 'application/json')
      // 设置请求体
      xhr.send(JSON.stringify(data))
    } else {
      // method === 'GET'
      xhr.send()
    }

    /**
     * 4、处理响应
     */
    // 设置响应结果的类型为 JSON
    xhr.responseType = 'json'
    // 处理函数
    xhr.onreadystatechange = function () {
      // readyState 这个状态的变化值从 0~4 表示 5 种状态
      // 0:请求还未初始化。尚未调用 open()方法
      // 1:启动(请求已建立)。已经调用 open()方法,但未调用 send() 方法
      // 2:请求已发送。已经调用 send() 方法,但未接收到响应
      // 3:接收。开始接收响应数据
      // 4:接收完成。已经全部接收完响应数据,可以在浏览器中使用了
      // status == 200 表示请求成功
      if (xhr.readyState == 4) {
        // 判断响应的状态码是否是 2xx
        if (xhr.status >= 200 && xhr.status < 300) {
          // 成功
          resolve({
            status: xhr.status,
            message: xhr.statusText,
            data: xhr.response
          })
        } else {
          reject(new Error('请求失败,失败的状态码为:' + xhr.status))
        }
      }
    }
  })
}

ajax.get = function (url, options) {
  // 直接使用 ajax 请求 GET 方法
  let config = Object.assign(options, { method: 'GET', url: url })

  return ajax(config)
}

ajax.post = function (url, options) {
  // 直接使用 ajax 请求 GET 方法
  let config = Object.assign(options, { method: 'POST', url: url })

  return ajax(config)
}

ajax.put = function (url, options) {
  // 直接使用 ajax 请求 GET 方法
  let config = Object.assign(options, { method: 'PUT', url: url })

  return ajax(config)
}

ajax.delete = function (url, options) {
  // 直接使用 ajax 请求 GET 方法
  let config = Object.assign(options, { method: 'DELETE', url: url })

  return ajax(config)
}

ajax.patch = function (url, options) {
  // 直接使用 ajax 请求 GET 方法
  let config = Object.assign(options, { method: 'PATCH', url: url })

  return ajax(config)
}

2、datePicker

  • 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>Document</title>
  <link rel="stylesheet" href="./index.css">
</head>

<body>
  <div id="date-picker">
  </div>
  <script src="./index.js"></script>
  <script>
    calender.init({
      el: document.getElementById('date-picker'),
      getDate: function (date) {
        console.log("回调=>", date)
      }
    })
  </script>
</body>

</html>
  • css
.picker-input {
  position: relative;
}
.picker-input input {
  display: inline-block;
  height: 40px;
  line-height: 40px;
  color: #606266;
  padding: 0 30px;
  border: 1px solid #dcdef6;
  border-radius: 4px;
  outline: none;
}
.picker-input .picker-prefix {
  position: absolute;
  left: 5px;
  display: inline-block;
  width: 25px;
  height: 40px;
  background-color: red;
}

.calender {
  width: 322px;
  height: 329px;
  border: 1px solid #e4e4e4;
  border-radius: 4px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  margin-top: 5px;
  user-select: none;
}

.calender .header {
  padding: 15px 0 10px;
  text-align: center;
}

.calender .picker-btn {
  display: inline-block;
  width: 12px;
  height: 12px;
  margin: 0 5px;
  /* background-color: green; */
  cursor: pointer;
}

.calender .picker-btn.picker-prev-year {
  color: #606266;
  font-size: 14px;
}
.calender .picker-btn.picker-prev-month {
  color: #606266;
  font-size: 14px;
}
.calender .picker-btn.picker-next-month {
  color: #606266;
  font-size: 14px;
}
.calender .picker-btn.picker-next-year {
  color: #606266;
  font-size: 14px;
}
.calender .picker-date {
  color: #606266;
  font-size: 15px;
  font-weight: 500;
  margin: 0 50px;
}

.calender .content {
  padding: 0 10px 10px 10px;
  color: #606266;
}

.calender .picker-weeks {
  height: 40px;
  line-height: 40px;
  font-size: 14px;
  border-bottom: 1px solid #ebeef5;
}
.calender .picker-weeks div,
.calender .picker-days div {
  float: left;
  width: 30px;
  height: 30px;
  line-height: 30px;
  text-align: center;
  margin: 4px 6px;
}
.calender .picker-days div {
  font-size: 13px;
  cursor: pointer;
}
.calender .picker-days div:hover {
  color: #409eff;
}

.calender .picker-days div.is-today {
  color: #409eff;
  font-weight: 700;
}
.calender .picker-days div.other-month {
  color: #c0c4cc;
}
.calender .picker-days div.is-select {
  color: #fff;
  background: #409eff;
  border-radius: 50%;
}
  • js

var calender = {
  date: new Date(),
  weeks: ['日', '一', '二', '三', '四', '五', '六'],
  showDate: {
    year: 0,
    month: 0,
    day: 0
  },
  showDays: [],
  showCalender: false,
  init: function (options) {
    this.initData(options)
    this.render()
    this.handler()
  },
  initData: function (options) {
    this.el = options.el
    this.getDate = options.getDate
    this.showDate = this.getCalender(this.date)
    this.showDays = this.getDays()
    this.selectDay = this.getSelectDay(this.showDate)
  },
  getCalender: function (date) {
    var year = date.getFullYear()
    var month = date.getMonth()
    var day = date.getDate()
    return {
      year: year,
      month: month,
      day: day
    }
  },
  getDays: function () {
    var arr = []
    // 获取某月的第一天
    var firstDay = new Date(this.showDate.year, this.showDate.month, 1)
    // 获取某年某月的第一天对应星期的位置
    let weekIndex = firstDay.getDay()
    // 计算在 6*7 的面板中的第一天,即上个月的多少号是在当前月对应面板的第一天
    let startDay = firstDay - weekIndex * 24 * 60 * 60 * 1000
    for (let i = 0; i < 42; i++) {
      arr[i] = new Date(startDay + i * 24 * 60 * 60 * 1000)
    }
    return arr
  },
  getSelectDay: function (date) {
    if (date instanceof Date) {
      return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
    }
    return `${date.year}-${date.month + 1}-${date.day}`
  },
  render: function () {
    this.el.innerHTML = this.renderInput() + this.renderCalender()
  },
  renderInput: function () {
    return `<div class="picker-input">
    <span class="picker-prefix"></span>
    <input type="text" value="${this.selectDay}">
  </div>`
  },
  renderCalender: function () {
    return `<div class="calender" style="display: ${this.showCalender ? 'block' : 'none'}">
      ${this.renderHeader()}
      ${this.renderContent()}
    </div>`
  },
  renderHeader: function () {
    return `<div class="header">
    <span class="picker-btn picker-prev-year">&lt;&lt;</span>
    <span class="picker-btn picker-prev-month">&lt;</span>
    <span class="picker-date">${this.showDate.year} 年 ${this.showDate.month + 1}</span>
    <span class="picker-btn picker-next-month">&gt;</span>
    <span class="picker-btn picker-next-year">&gt;&gt;</span>
  </div>`
  },
  renderContent: function () {
    return `
    <div class="picker-weeks">${this.renderWeeks()}</div>
    <div class="picker-days">${this.renderDays()}</div>`
  },
  renderWeeks: function () {
    var template = ''
    for (var i = 0; i < this.weeks.length; i++) {
      template += `<div>${this.weeks[i]}</div>`
    }
    return template
  },
  renderDays: function () {
    var template = ''
    for (var i = 0; i < 42; i++) {
      var date = this.showDays[i]
      var isCurMonth = this.isCurrentMonth(date)
      template += `
        <div
          class="
            ${isCurMonth.month ? '' : 'other-month'}
            ${isCurMonth.today ? 'is-today' : ''}
            ${isCurMonth.select ? 'is-select' : ''}"
          data-index=${i}
        >${date.getDate()}</div>`
    }
    return template
  },
  isCurrentMonth: function (date) {
    // date 是不是当前月
    // 如果 date 所属的年月 和 当前 showDate 所属的年月一样
    var year = this.getCalender(date).year
    var month = this.getCalender(date).month
    var day = this.getCalender(date).day

    var showDate = this.showDate
    var curYear = showDate.year
    var curMonth = showDate.month

    var todayDate = this.getCalender(this.date)
    var todayYear = todayDate.year
    var todayMonth = todayDate.month
    var todayDay = todayDate.day

    var selectDate = this.getCalender(new Date(this.selectDay))
    var selectYear = selectDate.year
    var selectMonth = selectDate.month
    var chooseDay = selectDate.day

    return {
      month: year === curYear && month === curMonth,
      today: year === todayYear && month === todayMonth && day === todayDay,
      select: year === selectYear && month === selectMonth && day === chooseDay
    }
  },
  handler: function () {
    var that = this
    document.onclick = function (e) {
      var dom = e.target
      var isElChild = that.el.contains(dom) && dom !== that.el

      if (isElChild && !that.showCalender) {
        that.changeCalender(true)
      } else if (!isElChild && that.showCalender) {
        that.changeCalender(false)
      }

      if (isElChild) {
        var isDay = dom.parentNode.classList.contains('picker-days')
        var isBtn = dom.classList.contains('picker-btn')
        var isYearBtn = isBtn && dom.getAttribute('class').includes('-year')
        var isMonthBtn = isBtn && dom.getAttribute('class').includes('-month')

        if (isDay) {
          that.handleDay(dom)
        } else if (isYearBtn) {
          that.handleYear(dom)
        } else if (isMonthBtn) {
          that.handleMonth(dom)
        }
      }
    }
  },
  changeCalender: function (isShowCalender) {
    this.showCalender = isShowCalender
    var calenderDom = this.el.getElementsByClassName('calender')[0]
    calenderDom.style.display = isShowCalender ? 'block' : 'none'
  },
  handleDay: function (dom) {
    // 获取当前点击日期的索引对应的日期对象
    var index = dom.dataset.index
    var date = this.showDays[index]
    var selectMonth = date.getMonth()
    // 得到选择的日期
    this.selectDay = this.getSelectDay(date)

    if (selectMonth !== this.showDate.month) {
      this.showDate.month = selectMonth
      this.showDays = this.getDays()
    }
    // 重新渲染之前将日历面板关闭
    this.showCalender = false
    this.getDate(this.selectDay)
    // 重新渲染
    this.render()
  },
  handleMonth: function (dom) {
    var isPrev = dom.getAttribute('class').includes('prev')
    var moveMonth = isPrev ? -1 : +1
    var showDate = new Date(this.showDate.year, this.showDate.month, this.showDate.day)
    showDate.setMonth(this.showDate.month + moveMonth)
    this.showDate.month = showDate.getMonth()
    this.showDate.year = showDate.getFullYear()
    this.showDays = this.getDays()
    this.render()
  },
  handleYear: function (dom) {
    var isPrev = dom.getAttribute('class').includes('prev')
    var moveYear = isPrev ? -1 : +1
    this.showDate.year += moveYear
    this.showDays = this.getDays()
    this.render()
  }
}

3、文件导出功能的实现

3-1 导出 Word

import docxtemplater from 'docxtemplater';
import { saveAs } from 'file-saver';
import JSZipUtils from 'jszip-utils';
import PizZip from 'pizzip';

/**
 * 导出 Word.docx
 * @param {Object} templateDocxPath 模板文件路径
 * @param {Object} exportWordData 导出数据
 * @param {Object} outFileName 导出文件名
 */
export const exportWord = (templateDocxPath, exportWordData, outFileName) => {

  // 读取并获得模板文件的二进制内容
  JSZipUtils.getBinaryContent(templateDocxPath, function (error, content) {

    if (error) {
      throw error;
    }

    // 创建一个PizZip实例,内容为模板的内容
    let zip = new PizZip(content);
    // 创建并加载docxtemplater实例对象
    let doc = new docxtemplater();
    doc.loadZip(zip);

    doc.setData(exportWordData);

    try {
      // 用模板变量的值替换所有模板变量
      doc.render();
    } catch (error) {
      // 抛出异常
      let e = {
        message: error.message,
        name: error.name,
        stack: error.stack,
        properties: error.properties
      };
      console.log(JSON.stringify({
        error: e
      }));
      throw error;
    }

    // 生成一个代表docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
    let out = doc.getZip().generate({
      type: "blob",
      mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    });
    // 将目标文件对象保存为目标类型的文件,并命名
    saveAs(out, outFileName);
  });
}

三、项目打包

1、打包前准备

1.1 初始化项目

  • 安装 Node 之后,进入项目目录下,初始化项目
// 进入项目
// cd lgk-js-tools
// 初始化项目
npm init -y

1.2 下载依赖

  • 下载 webpack 和 webpack-cli 的依赖
  • 如果你对别的打包工具比较熟的话,也可以换成其他打包工具
npm install webpack webpack-cli

1.3 配置 Webpack

  • 需要在项目目录下创建一个 webpack.config.js 文件
const path = require('path')

module.exports = {
  // 模式:development/production
  mode: 'production',
  // 入口
  entry: './src/index.js',
  // 出口
  output: {
    // 打包后存放的目录
    path: path.resolve(__dirname, 'dist'),
    // 打包后输出的文件名称
    filename: 'lgk-js-tools.js',
    // 向外暴露的对象名称,用于通过对象调用的方式调用封装的函数
    library: 'Utils',
    // 可以让打包生成的库可以通过 esmodule/commonjs/requirejs 的语法引入
    libraryTarget: 'umd'
  }
}

1.4 配置打包命令

  • 在 package.json 文件中的 scripts 属性添加 打包命令 “build”: “webpack --watch”
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --watch"
  },

1.5 项目打包

npm run build

1.6 测试

  • 在 src/index.js 中添加如下测试函数
export function test() {
  console.log("这是一个测试方法!!")
}
  • 在 test/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>Document</title>
</head>

<body>
  <!-- 引入打包后的 JS 文件 -->
  <script src="./../dist/lgk-js-tools.js"></script>
  <script>
    // 使用暴露的对象名称 Utils 调用暴露的测试函数
    console.log(Utils.test())
  </script>
</body>

</html>

在这里插入图片描述

2、暴露工具函数

2.1 在工具函数中使用 export 导出

  • 例如:
/**
 * Ajax 封装
 */

export function ajax({ method, url, params, data }) {
  // 将 method 转为大写 toUpperCase()
  method = method.toUpperCase()

  return new Promise((resolve, reject) => {
    /** 
     * 1、创建 XMLHttpRequest 对象
     **/
    let xhr = null
    if (window.XMLHttpRequest) {
      // 这种方式是对于使用这几种浏览器的情况
      // IE7+,Firefox,Chrome,Opera,Safari ...
      xhr = new XMLHttpRequest();
    } else {
      // 这种方式是针对低版本浏览器:IE6,IE5
      xhr = new ActiveObject("Microsoft.XMLHTTP");
    }

    /**
     * 2、初始化
     **/
    // 需要将 params: { a: 100, b:200 } 转为 a=100&b=200 的形式
    let str = ""
    for (let k in params) {
      str += `${k}=${params[k]}&`
    }
    // 对最后一个参数后面的 & 截掉
    str = str.slice(0, -1)

    xhr.open(method, url + '?' + str)

    /**
     * 3、发送
     */
    if (method === 'POST' || method === 'PUT' || method === 'DELETE' || method === 'PATCH') {
      // 设置请求头信息
      xhr.setRequestHeader('Content-type', 'application/json')
      // 设置请求体
      xhr.send(JSON.stringify(data))
    } else {
      // method === 'GET'
      xhr.send()
    }

    /**
     * 4、处理响应
     */
    // 设置响应结果的类型为 JSON
    xhr.responseType = 'json'
    // 处理函数
    xhr.onreadystatechange = function () {
      // readyState 这个状态的变化值从 0~4 表示 5 种状态
      // 0:请求还未初始化。尚未调用 open()方法
      // 1:启动(请求已建立)。已经调用 open()方法,但未调用 send() 方法
      // 2:请求已发送。已经调用 send() 方法,但未接收到响应
      // 3:接收。开始接收响应数据
      // 4:接收完成。已经全部接收完响应数据,可以在浏览器中使用了
      // status == 200 表示请求成功
      if (xhr.readyState == 4) {
        // 判断响应的状态码是否是 2xx
        if (xhr.status >= 200 && xhr.status < 300) {
          // 成功
          resolve({
            status: xhr.status,
            message: xhr.statusText,
            data: xhr.response
          })
        } else {
          reject(new Error('请求失败,失败的状态码为:' + xhr.status))
        }
      }
    }
  })
}

ajax.get = function (url, options) {
  // 直接使用 ajax 请求 GET 方法
  let config = Object.assign(options, { method: 'GET', url: url })

  return ajax(config)
}

ajax.post = function (url, options) {
  // 直接使用 ajax 请求 GET 方法
  let config = Object.assign(options, { method: 'POST', url: url })

  return ajax(config)
}

ajax.put = function (url, options) {
  // 直接使用 ajax 请求 GET 方法
  let config = Object.assign(options, { method: 'PUT', url: url })

  return ajax(config)
}

ajax.delete = function (url, options) {
  // 直接使用 ajax 请求 GET 方法
  let config = Object.assign(options, { method: 'DELETE', url: url })

  return ajax(config)
}

ajax.patch = function (url, options) {
  // 直接使用 ajax 请求 GET 方法
  let config = Object.assign(options, { method: 'PATCH', url: url })

  return ajax(config)
}

2.2 在入口函数引入

  • 在 src/index.js 中引入
/**
 * 将工具函数文件引入,再通过 src/index.js 暴露
 */

export { ajax } from "./ajax/index.js"

// export function test() {
//   console.log("这是一个测试方法!!")
// }

2.3 测试使用

  • 使用 npm install 的方式安装使用
// 安装
npm install lgk-js-tools
// 引入
import * as Utils from "lgk-js-tools"
  • 在 test/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>Document</title>
</head>

<body>
  <!-- 引入 -->
  <script src="./../dist/lgk-js-tools.js"></script>
  <script>
    // console.log(Utils.test())
    // Utils.ajax({
    //   url: '这里填URL',
    //   method: 'post',
    //   // params 里面是请求参数
    //   params: {
    //     a: 100
    //   },
    //   // data 里面的是请求体
    //   data: {
    //     b: 200
    //   }
    // })
    Utils.ajax.get('这里填URL', {
      // params 里面是请求参数
      params: {
        a: 100
      },
    })
      .then(res => {
        console.log("res=>", res)
      }).catch(err => {
        console.log("err=>", err)
      })
  </script>
</body>

</html>

四、项目发布

1、注册 npm 账号

  • 直接注册即可,没有什么特别的要求

2、修改 package.json 配置

  • name 属性值必须唯一,在发布之前将想发布的名称到 npm 仓库搜一下看有没有
  • version 版本号每次发布必须与之前发布的不相同,否则会报错
  • main 属性值必须指定为打包生成的 js 文件
  • author 属性用于指定作者(可以不添加)
  • description 属性用于描述这个工具库的作用(可以不添加)
  • keywords 属性用于填写关键词,方便别人检索(可以不添加)
{
  "name": "lgk-js-tools",
  "version": "1.0.1",
  "main": "./dist/lgk-js-tools.js",
  "description": "",
  "author": {
    "name": "lgk"
  },
  "keywords": [
    "ajax",
    "js",
    "lgk",
    "utils"
  ],
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --watch"
  },
  "license": "ISC",
  "dependencies": {
    "webpack": "^5.71.0",
    "webpack-cli": "^4.9.2"
  }
}

3、切换 npm 源

  • 查看当前所使用的源
npm get registry
  • 检查现在所使用的 npm 源,如果不是 npm 源,需要切换回来
nrm ls

在这里插入图片描述

  • 切换 npm 源为 https://registry.npmjs.org/
// 切换为 npm 源
npm config set registry https://registry.npmjs.org/
// 切换为 taobao 源
npm config set registry http://registry.npm.taobao.org/

4、登录与发布

  • ⚠️注意:一定要先进入到项目目录下
cd lgk-js-tools
  • 登录
npm login

// 或者
npm addUser
  • 填写登录信息:用户名、密码、邮箱、验证码
    在这里插入图片描述
  • 发布
npm publish
  • 需要等几分钟后就可以在 npm 查看了
    在这里插入图片描述

5、强制删除已发布的库

  • ⚠️注意:必须在 72 小时之内,否则不能再被删除
  • 执行删除命令
npm unpublish --force

项目源码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值