优化图片上传预览框代码
孙泽辉

项目用jQuery写的,一些操作dom逻辑混杂在一起,不够清晰,于是重构了一下,把逻辑分离出来,方便以后维护。

实现思路

类似pubsub,监听attr变化,更新dom
而数据处理部分仅仅需要往attr上写数据,不需要关心dom如何渲染

实现代码

稍微展示一下重点代码

看看界面长什么样吧!

未选中状态

image

选中状态

image

可以看到,未选中状态需要展示文本“添加自定义图片”,选中状态需要展示图片和删除按钮
若是直接编写代码,采用命令式的方式,需要判断当前状态,然后根据状态来决定如何渲染dom
这会导致状态切换逻辑混杂DOM操作,使代码不够清晰,不利于维护

而采用数据驱动的方式,只需要往attr上写数据,然后监听attr变化,根据attr的值来渲染dom,这样就可以把状态切换逻辑和DOM操作分离开来,使代码更加清晰

这是DOM结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div class="editor-image-choice">
<div class="editor-image--view rounded" style="display: none;">
<img src="" class="image-view__img" />
<div class=" init-text text-gray text-xs image-view__text">
<p>请选择图片</p>
<p>支持JPG,PNG,GIF</p>
</div>
<div class="del-icon image-view__del">
<img src="/edit3d/assets/image/icon/del.png" alt="" >
</div>
</div>
<div class="editor-image--content">
<div class="editor-image--text">
<div class="text text-xs text-gray">建议尺寸:50 x 50</div>
</div>
<button class="btn btn-primary editor-image--btn choice-logo-image-btn">
<span class="text-sm">添加图片</span>
</button>
</div>
</div>

这里是配合该组件的脚本

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
/**
* @typedef Config
* @property {string} url 初始图片链接
* @property {Function} onChange 图片链接变更回调
*/
/**
* @description 实现图片上传选择组件
* 思路:初始化为容器设置当前选中的链接属性,删除后清空链接属性,上传后设置链接属性
* 元素属性变更监听器:
* 判断当前元素属性值
* 1. 有值:显示图片,隐藏文字
* 2. 无值:显示文字,隐藏图片
*
*
* @param {string} selector 图片容器元素选择器
* @param {Config} cfg
**/
export const useImagePicker = (selector, cfg = {}) => {
const $container = $(selector)
const $viewContainer = $container.find('.editor-image--view')
const $viewImg = $container.find('.image-view__img')
const $viewText = $container.find('.image-view__text')
const $delIcon = $container.find('.image-view__del')
const $uploadBtn = $container.find('.editor-image--content .editor-image--btn')

function setImageUrl(url) {
$container.attr('image-url', url)
}
function isImgSetted() {
return $container.attr('image-url') !== ''
}
function setError() {
$viewContainer.addClass('error')
}
function removeError() {
$viewContainer.removeClass('error')
}
// 监听attr变更
tools.observeDomAttribute(selector, 'image-url', function (url) {
$viewImg.attr('src', tools.urlResolve(url))
if (url === '') {
$viewImg.hide()
$viewText.show()
$delIcon.hide()
} else {
$viewImg.show()
$viewText.hide()
$delIcon.show()
// 设置了图片后要清除错误状态
removeError()
}
$viewContainer.show()
// 执行change回调
cfg.onChange && cfg.onChange(url)
});

// 初始化
setImageUrl(cfg.url || '')
// 上传按钮点击后打开媒体库选择图片,选择后设置当前图片链接状态
$uploadBtn.click(function () {
openMediaSourceDialog('image', function (url) {
console.log('selected', url)
setImageUrl(url)
})
})
// 删除按钮点击后清空当前图片链接状态
$delIcon.on('click', function () {
setImageUrl('')
})

return {
setImageUrl,
setError,
isImgSetted
}
}
// 使用时,只需传入选择器,并且在图片变更时做外部的操作即可
let imgPicker = null
async function initImageView() {
const imgUrl = await getImgUrl()
imgPicker = useImagePicker(".editor-image-choice", {
url: imgUrl,
onChange(url) {
// dosomething(url) ...
},
});
}
// 初始化图片预览框
window.onload = function () {
initImageView()
}
// 提交时判断是否设置了图片
function onSubmit(){
const isImgSet = imgPicker?.isImgSetted()
if (!isImgSet) {
imgPicker.setError()
layer.msg("请上传图片")
return;
}
// submit...
}

基本就是这样,感觉很简单有没有?这就是数据驱动的魅力!

 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
This site is deployed on
Total words 89.4k