通过在页面应用使用 localStorage 提升用户使用体验。

背景

首先,来说两个关于用户体验的经历。
大学在校期间,经常被安排在教学系统上填写各种信息采集表,教学系统是学校找外面外包公司做的,给的钱多,出的活却很烂,信息采集的表单是很老旧的 table 布局,一页一个表单,一个表单有几十个项,当你辛辛苦苦填完了整个表单,点击提交按钮页面却没有响应,系统卡了?你抱着试一试的心态刷新了页面,恭喜你,之前填的数据全部丢失,像是卷入了一个黑洞,你又从第一项开始填。这样的系统毫无用户体验可言,不是强制要求,没人愿意打开这样的网站。
到了大三找实习,投简历到阿里,需要在系统上填一些信息,对应的也是一个表单很多项,巧的是,提交时页面卡住了,虽然脑海中重现了被学校系统支配的恐惧,但除了刷新页面别无选择,刷新之后,页面回来了,之前填写的数据竟然原封不动的回来了,当时很菜,不知道是怎么实现,就觉得背后的技术肯定很厉害,后面才去了解这方面的知识。
后来了解了 localStorage 之后,才意识到表单数据保存背后的技术很简单,但是用在了相应的场景,给用户带来的是非常棒的用户体验,从那之后,我对阿里校招页面一直很有好感。

关于 localStorage

容量

localStorage 的容量是相对其作用域而言,而 localStorage 的作用域是 协议 + 主机名 + 端口,目前业界默认同一作用域下拥有 5M 的容量,手机端和 PC 端标准略有不同。
数据以键值对方式存储,同一作用域下的数据可以被相互访问,并且不允许跨域。
注意: localStorage 存储只支持存储字符串,因此在存储对象时需要使用 JSON.stringify() 以及 JSON.parse() 进行序列化以及解析。

API

1
2
3
4
5
localStorage.setItem("name","leeon");   //设置 name 值 为 leeon
localStorage.getItem("name"); //获取 name 的值
localStorage.key(0); //获取第 0 个数据项的 key 名
localStorage.removeItem("name"); //移除 name 对应的键值对
localStorage.clear(); //清除当前作用域下的所有localstorage数据

应用场景

上面提到的表单数据保存是最常见的场景,避免信息的丢失,实现方式是在 input 标签 blur(失焦) 后,触发 localStorage 存储,页面加载之后,从 localStorage 读取已存储的数据,表单提交成功后,移除存储的数据。

还有一种情况,类似于博客系统内置的评论功能,页面可以参见下图,这样的场景,没有登陆,用户信息不能持久化保存,每次评论都需要再次填写基础信息

应用 localStorage 保存用户基础信息,我用 React 写了一个评论框输入组件,包含的 _saveUsername_loadUsername 方法对应着用户信息的存储以及获取,分别会在输入框失焦以及页面渲染时被调用,体验更好的方式则是提供一个保存基本信息的选项,用户在勾选后,信息被保存。

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
class CommentInput extends Component {
constructor(props) {
super(props);
this.state = {
username: '',
content: ''
}
}
static PropType = {
onSubmit: PropType.func
}
componentDidMount() {
this.textarea.focus()
}
componentWillMount() {
this._loadUsername()
}
_saveUsername (username) {
localStorage.setItem('username', username)
}
_loadUsername () {
const username = localStorage.getItem('username')
if(username) {
this.setState({username})
}
}
handleUserNameBlur (event) {
this._saveUsername(event.target.value)
}
handleUsernameChange (event) {
this.setState({
username: event.target.value
})
}
handleContentChange (event) {
this.setState({
content: event.target.value
})
}
handleSubmit () {
if(this.props.onSubmit) {
const {username, content} = this.state
this.props.onSubmit({
username: this.state.username,
content: this.state.content,
createdTime: +new Date()
})
}
this.setState({content: ''})
}
render () {
return (
<div className="comment-input">
<div className="comment-field">
<span className="comment-field-name">userName:</span>
<div className="comment-field-input">
<input
onBlur={this.handleUserNameBlur.bind(this)}
value={this.state.username}
onChange={this.handleUsernameChange.bind(this)} />
</div>
</div>
<div className="comment-field">
<span className="comment-field-name">Comment:</span>
<div className="comment-field-input">
<textarea
ref={(textarea) => {this.textarea = textarea}}
value={this.state.content}
onChange={this.handleContentChange.bind(this)}
></textarea>
</div>
</div>
<div className="comment-field-button">
<button onClick={this.handleSubmit.bind(this)} >comment</button>
</div>
</div>
)
}
}

关于用户体验

因为做的工作的原因,遇到一些很棒的网站第一反应是打开控制台看代码,每每看到那些巧妙的代码都赞叹不已,少量的代码带来用户体验的大幅度提升,这是具有很高性价比的一件事。遇到一些烂糟糟的设计,会考虑烂的原因,以及如何去改进,比如渣浪的微博。