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"><<</span>
<span class="picker-btn picker-prev-month"><</span>
<span class="picker-date">${this.showDate.year} 年 ${this.showDate.month + 1} 月</span>
<span class="picker-btn picker-next-month">></span>
<span class="picker-btn picker-next-year">>></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
项目源码
版权声明:本文为weixin_49809163原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。