我是靠谱客的博主 寂寞花生,这篇文章主要介绍从零开始编写一个js插件,现在分享给大家,希望可以做个参考。

什么是js插件

首先我们必须要明白什么是js插件,说的简单点,类似于

复制代码
1
2
function add(a, b){ return a+b; }

这就是一种插件的雏形,因为它还没有封装起来, 它会影响到工作区的其他参与者(函数),也就是说它有可能会影响到其他成员,这样并不好。

封装

想要插件不影响本身的命名空间的话,使用匿名函数是最好的方式之一,由于JavaScript本身是基于函数作用域的,也就是说只要是在自身的函数范围内,那么便不会与其他同名变量或者函数造成冲突。

匿名函数本身连函数名都可以不存在的(匿名函数同样可以命名,这可能多少有点矛盾,但的确如此),如此一来,我们的函数只要声明在一个匿名函数之下的话,那么就不可能会对其他作用域造成影响,如下:

复制代码
1
2
3
4
(function(global){ //some code... }(this))

一些必要的讲解

  1. 这里的匿名函数传入的变量是this,而非window, 这里主要是为了兼容到服务端,在node.js中,全局变量的名称为global,而this本身是基于调用上下文,所以传入this即可完成兼容。
  2. 此外传入this的最重要原因是为了能通过原型链prototype继承到全局变量this里边,进而可以在其他作用域中调用插件中的方法。

可拓展性

当我们有了自己的命名空间以后,我们自然是需要一些相应的代码来填充进去

不过由于插件本身也是一个function,但是他必须兼顾到可拓展可配置, 所以如果使用普通函数的传参方式在实际拓展起来会较为麻烦, 所以这里推荐的是使用一个Object作为参数, 并且在函数内部声明一个常用的默认配置项

js插件其实有两个最为基础而且重要的内部函数,那就是config()init(),一个是基本的配置,另一个则是初始化插件状态。这两个内部函数实际上不是非得要这么写, 只是如果不使用这两个函数的话, 在实际拓展和使用起来或许会较为困难

首先来看看config()的基本写法,很简单,只要传入一个opts参数,表示由用户配置的基本参数,然后再自己内部添加自己的默认参数,再与用户的参数进行比较,一旦用户有配置的参数便使用用户参数,反之则使用默认参数,最后输出即可。

这里其实就是函数式编程的一个体现,用户传入数据,函数返回数据,相同输入相同输出,没有任何的不确定性

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
// 该方法有问题的, 这里只是做一个示例 function config(opts, options) { //默认参数 if (!opts) return options; for (var key in opts) { if (!!opts[key]) { options[key] = opts[key]; } } return options; }

初始化状态(即最基本的API)

在这里直接贴出代码估计也能看的懂,不过多少还需要再讲解下。

  1. 首先需要获取到自己在config()中配置的参数
  2. 其次就是根据获取的参数进行操作
  3. 作为入口函数使用

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/** * @method 初始化 * @param { object } 由@method config() 提供的配置参数 */ init: function (opts) { var _this = this; var option = config(opts, this.options);//用户配置 var _elems = document.getElementsByClassName(option.elem); var _elemsLength = _elems.length; var index = null; initPreviewArea(_elems); var yPreviewImage = document.getElementById('yPreviewImage'); var yPreviewArea = document.getElementById('yPreviewArea'); for (var i = 0; i < _elemsLength; i++) { _elems[i].setAttribute('data-id', i); _elems[i].style.cursor = 'pointer'; _elems[i].addEventListener('click', function () { var src = this.getAttribute('src'); yPreviewImage.setAttribute('src', src); yPreviewImage.setAttribute('data-id', this.getAttribute('data-id')); show(yPreviewArea); }) } }

架构

现在我们已经有了国土(匿名函数), 资源(用户参数)和建筑(基本的API),那么还缺什么?没错,怎么把他们串联在一起。其实非常简单,只需要通过原型链继承即可。

在这里实现的主要步骤有这么几步

  1. 确定作用域,即匿名函数
  2. 使用严格模式
  3. 设置插件函数
  4. 配置函数原型链
  5. 将插件函数注册到全局参数中
  6. 恭喜你,你已经可以在你的js中调用该插件了
  7. 更加具体的请查看下方注释

2019.07.19

在这里更新一个新的用该方法写的一个基于localStorage的前端数据库插件的地址, 有兴趣的朋友可以参考一下

https://github.com/LElysion/nothing/tree/master/yDB

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
"use strict"; var yPreview = function () { } // 有必要调用到插件本身this的, 就放在prototype上边, 功能函数尽量放在下方工具中 yPreview.prototype = { options: { name: 'yPreview', elem: 'preview-images' }, index: null, /** * @method 初始化 * @param { object } 由@method config() 提供的配置参数 */ init: function (opts) { var _this = this; var option = config(opts, this.options);//用户配置 var _elems = document.getElementsByClassName(option.elem); var _elemsLength = _elems.length; var index = null; initPreviewArea(_elems); var yPreviewImage = document.getElementById('yPreviewImage'); var yPreviewArea = document.getElementById('yPreviewArea'); for (var i = 0; i < _elemsLength; i++) { _elems[i].setAttribute('data-id', i); _elems[i].style.cursor = 'pointer'; _elems[i].addEventListener('click', function () { var src = this.getAttribute('src'); yPreviewImage.setAttribute('src', src); yPreviewImage.setAttribute('data-id', this.getAttribute('data-id')); show(yPreviewArea); }) } } } global.yPreview = yPreview;//注册到全局中, 届时可以直接new yPreview() 实例化对象

调用

复制代码
1
2
3
4
5
6
let yn = new yPreview(); yn.init({ name: 'yourNewName', elem: 'preview-image' });

用处

市面上已经有大量的插件了,为什么我们还要自己造轮子?答案很简单,市面上再好的插件他们也不是根据你的需求所定制的,灵活性和可拓展性自然有所缺陷,而自己配置一个插件库便可以完全的为需求所服务,甚至可以将其更加完善从而更好的适应其他需求。

最后

上方的一个图片预览插件,不完善, 但是单用作学习已经足够了
index.html

复制代码
1
2
3
4
5
6
7
8
9
10
11
<style> .wrap{ max-width: 750px; margin: 0 auto; } .preview-image { width: 120px; color:rgb(89, 21, 21);} </style> <div class="wrap"> <img src="img/1.jpg" alt="" class="preview-image"> <img src="img/2.jpg" alt="" class="preview-image"> <img src="img/3.jpg" alt="" class="preview-image"> </div>

preview.js

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
let yn = new yPreview(); yn.init({ name: 'yourNewName', elem: 'preview-image' }); (function (global) { "use strict"; var yPreview = function () { } // 有必要调用到插件本身this的, 就放在prototype上边, 功能函数尽量放在下方工具中 yPreview.prototype = { options: { name: 'yPreview', elem: 'preview-images' }, index: null, /** * @method 初始化 * @param { object } 由@method config() 提供的配置参数 */ init: function (opts) { var _this = this; var option = config(opts, this.options);//用户配置 var _elems = document.getElementsByClassName(option.elem); var _elemsLength = _elems.length; var index = null; initPreviewArea(_elems); var yPreviewImage = document.getElementById('yPreviewImage'); var yPreviewArea = document.getElementById('yPreviewArea'); for (var i = 0; i < _elemsLength; i++) { _elems[i].setAttribute('data-id', i); _elems[i].style.cursor = 'pointer'; _elems[i].addEventListener('click', function () { var src = this.getAttribute('src'); yPreviewImage.setAttribute('src', src); yPreviewImage.setAttribute('data-id', this.getAttribute('data-id')); show(yPreviewArea); }) } } } function hide(elem) { elem.style.display = 'none'; } function show(elem) { elem.style.display = 'block'; } function setStyle(elem, styles) { for (var key in styles) { elem.style[key] = styles[key] } } function initPreviewArea(elems) { var imgsrcs = []; var elemsLength = elems.length; for (var i = 0; i < elemsLength; i++) { imgsrcs.push({ src: elems[i].getAttribute('src'), id: i }) } var div = document.createElement('div'); div.setAttribute('id', 'yPreviewArea'); var divStyle = { position: 'fixed', top: 0, left: 0, width: '100%', height: window.innerHeight + 'px', background: 'rgba(0, 0, 0, .6)', display: 'none', userSelect: 'none' } var img = document.createElement('img'); img.setAttribute('id', 'yPreviewImage'); var imgStyle = { position: 'relative', top: '50%', left: '50%', maxHeight: window.innerHeight + 'px' } setStyle(img, imgStyle); img.style.transform = 'translate(-50%, -50%)'; // img.addEventListener('click', function () { // div.style.display = 'none'; // }) div.appendChild(img); var next = document.createElement('a'); next.innerText = '>'; var nextStyle = { position: 'absolute', top: '50%', right: 0, margin: '12px', width: '42px', height: '42px', lineHight: '42px', display: 'block', background: '#1E67B9', cursor: 'pointer', color: '#fff', textAlign: 'center', fontSize: '27px', borderRadius: '50%' } setStyle(next, nextStyle); next.addEventListener('click', function () { var idx = img.getAttribute('data-id'); if ((elemsLength - 1) > idx) { var _idx = parseInt(idx); img.setAttribute('src', elems[_idx + 1].getAttribute('src')); img.setAttribute('data-id', elems[_idx + 1].getAttribute('data-id')); } else { img.setAttribute('src', elems[0].getAttribute('src')); img.setAttribute('data-id', elems[0].getAttribute('data-id')); } }) div.appendChild(next); var prev = document.createElement('a'); prev.innerText = '<'; var prevStyle = { position: 'absolute', top: '50%', left: 0, margin: '12px', width: '42px', height: '42px', lineHight: '42px', display: 'block', background: '#1E67B9', cursor: 'pointer', color: '#fff', textAlign: 'center', fontSize: '27px', borderRadius: '50%' } setStyle(prev, prevStyle); prev.addEventListener('click', function () { var idx = img.getAttribute('data-id'); if (idx != 0) { var _idx = parseInt(idx); console.log(elems[_idx - 1].getAttribute('src')) img.setAttribute('src', elems[_idx - 1].getAttribute('src')); img.setAttribute('data-id', elems[_idx - 1].getAttribute('data-id')); } else { img.setAttribute('src', elems[elemsLength - 1].getAttribute('src')); img.setAttribute('data-id', elems[elemsLength - 1].getAttribute('data-id')); } }) div.appendChild(prev); var close = document.createElement('a'); close.innerText = '×'; var closeStyle = { position: 'absolute', top: '12px', right: '12px', display: 'block', fontSize: '32px', color: '#fff', cursor: 'pointer', zIndex: 99 } close.addEventListener('click', function () { hide(div); }) setStyle(close, closeStyle); div.appendChild(close); setStyle(div, divStyle); document.body.appendChild(div) } function getNodeClass(className) { var _elems = document.getElementsByClassName(className); return _elems } // 工具函数 // 检查非空 function isEmpty(val) { return val != '' && val != null && val != undefined ? false : true; } /** * @method 配置 * @param opts { object } 用户提供的参数,在没有提供参数的情况下使用默认参数 * @param options { object } 默认参数 * @return options { object } 返回一个配置对象 */ function config(opts, options) { //默认参数 if (!opts) return options; for (var key in opts) { if (!!opts[key]) { options[key] = opts[key]; } } return options; } global.yPreview = yPreview;//注册到全局中, 届时可以直接new yPreview() 实例化对象 }(this))

转自https://www.jianshu.com/p/0a3127d301b1

 

最后

以上就是寂寞花生最近收集整理的关于从零开始编写一个js插件的全部内容,更多相关从零开始编写一个js插件内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部