1

本文同时发布于知乎专栏:前端指南
转载需提前联系作者,未经允许不得转载。

最近比较忙,这两天终于抽时间写了我们系列的第二篇文章,大家有什么意见和建议欢迎评论

本来是想再给大家详细介绍一下chrome扩展的许多文档细节和一些定义,后来考虑到这个扩展本身的意义就是在于做出应用,过多的介绍API反而会让大家失去兴趣,故而,今天给大家带来就是一个基于Chrome的一个ToDoList的纯前端小玩具。

同学们在跟着我把所有代码都实现之后,会对js的ES6、基本DOM操作,事件监听,逻辑数据分离思想以及Chrome的本地存储都会有一个比较大的进步

本系列课程源码都在我的github上:miaoihan/chrome-extensions

前期准备

项目目录:
图片描述

上节课已经介绍过改文件里各个文件的作用了,这次的应用并没有多出任何的文件,都是在基础之上做的代码的扩充。

  • app.js:TodoList的主文件

  • background.js:后台数据通信(后来版本中用chrome.storage替换了background的数据传递)

  • Task.js:task类,包含task属性和方法

在介绍代码之前,我们先理一下我们的思路,我们要实现一个可以增删查改的任务清单,最后实现类似于滴答清单这样的一个效果。
图片描述

我们需要做的功能:

  • 在打开该应用之前,需要获取所有task信息,并分别展现到两个列表里(查)

  • 在文本框能输入需要完成的任务,回车后保存任务信息到浏览器中,任务跳到清单列表,状态是未完成(增)

  • 在勾选清单列表里的某一项后,该任务跳转到已完成列表,状态是已完成(改)

  • 同样在勾选已完成列表中某项,可以跳到清单列表。

  • 可以修改未完成的任务内容和删除内容(删、改)
    这样一个看似很简单的应用做起来其实背后也有一套挺复杂的思想,我们再不借用任何外部库的情况下独立完成该应用。

起步

  1. 编写todo.html 前端页面
    页面部分没啥好讲的,大家自己动手,先简单布局一个版本,类似于这样:

图片描述

当前版本并不支持在html页面里写js代码,不是不推荐,是不支持,所以大家必须把文件单独出来在页面中引入

  1. 编写app.js 主文件
    首先我们完成文本框回车添加的功能,代码如下

// 监听回车
$('task-input').addEventListener('keyup', function(event) {
    if (event.keyCode == "13") {
        let content = $('task-input').value
        let taskItemHtml = 
            `<div class="task-item">
              <input type="checkbox">
              <span class="task-content" style="font-size: 14px;">${task}</span>
            </div>`
            $('task-list').innerHTML += taskItemHtml
        $('task-input').value = ''
    }
})

这里keyCode是是数字键盘的键码值,13对应回车,JavaScript Event KeyCodes 这个网站你可以找到所有的键盘对应值。

这里的代码同学们可能会问了,你不是说不用任何库么,怎么又有“$‘’了,其实这个‘’$‘’是我自己定义的简版选择器,只是方便id选择的。

let $ = function(id){
    return document.getElementById(id)
}
$('task-input').addEventListener('keyup', function)

这里的addEventListener是添加监听器的意思,当我们发生了'keyup'即键盘抬起事件后就会触发一个function,我们这里暂时的思路是直接操作视图,这里的${}这种写法是ES6的字面量的写法,免去了各种加号拼接字符串了,非常方便。在我们成功插入了一个html片段之后,我们再把input里的内容给清空,最后的效果是这样的:
图片描述

然后我们打开控制台,发现div已经插入进去了
图片描述

那么我们前面为什么要说是暂时的思路呢,因为现阶段已经不推荐直接操作DOM了,现在的大部分框架已经不用去关系操作DOM,数据和view是实时更新的,即双向的数据流动。

在完成了视图操作之后,我们添加的数据并没有保存下来,在一般的应用中,这时候的数据保存是和后端服务器进行交互的,也就是刚刚添加的任务会保存在数据库中,我们这个应用不涉及后端部分,所以我们使用HTML5的API localStorage进行数据的存取,但是localStorage并不直接支持对象的存取,所以在操作之前我们需要对数据进行JSON.stringify 和 JSON.parse操作进行格式的转换。

let task = {}; let taskList = []
task.content = content; task.isFinish = false
taskList.push(task)
// 将对象转换成字符串后保存
localStorage.taskList = JSON.stringify(taskList)

正常情况下我们这样存储就已经可以了,再通过taskList = JSON.parse(localStorage.taskList)就可以获取到task对象了,但是。。。localStroage的存储是基于域名的,所以我们的扩展如果直接这样使用,在切换网页的时候域名不一样这个存储空间就是失效了,那这样当然不行了,那么怎么解决呢?

两种方案
一种还是用localStorage,app.js通过runtime.sendMessage和background通信,由background读写扩展所在域(通常是chrome-extension://extension-id/)的localStorage,这时候域相同localStorage值有效,然后再传递给app.js。

app.js:

chrome.runtime.sendMessage('fetchData', function(res){
     // res.taskList 即传回来的list
     console.log(res);
});

background.js:

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
    if(message == 'fetchData'){
        let taskList = JSON.parse(localStorage.taskList)
        sendResponse(taskList);
    }
});

通过上面两个文件之间的通信,我们就可以完成数据的存储。

另一种是通过使用chrom.storage这个API完成对数据存储的操作,他的好处有几点:

  • 用户数据可以自动与Chrome同步(使用storage.sync)同步。

  • content script可以直接访问数据,不需要通过background中转

  • 无痕模式,用户数据也能保存下来

  • 因为是异步操作,所以比localStorage更快

  • 可以直接存储对象

说这么多好处,其实就是让你去用它。使用Chrome存储API必须要在Manifest的permissions中声明"storage",之后才有权限调用。

{
    "name": "todo",
    ...
    "permissions": [
      "storage"
    ],
    ...
  }

对于每种储存区域,Chrome又提供了5个方法,分别是get、getBytesInUse、set、remove和clear。

chrome.storage.sync.get('taskList', function(res){
    updateView(res.taskList)
});

Chrome存储API提供了2种储存区域,分别是sync和local。两种储存区域的区别在于,sync储存的区域会根据用户当前在Chrome上登陆的Google账户自动同步数据,当无可用网络连接可用时,sync区域对数据的读写和local区域对数据的读写行为一致。
好,那么现在我们的存储逻辑就可以写出来了

chrome.storage.sync.get('taskList', function(res) {
    let taskList = res.taskList || []
    taskList.push(obj)
    chrome.storage.sync.set({taskList:taskList});
});

这里我们先get了一下,因为直接set的话,会把原数据覆盖,这里API并没有类似sql的update操作,所以只能取出来,push进去,再set。
好了,到目前为止,我们已经不怎么优雅的实现了数据的添加和视图的展现,接下来我们,接下来我们。。。下一期见

下期预告:
使用ES6 class分离数据逻辑的处理,把一些公共模块抽象出来。
完成清单的勾选,完成和未完成任务的处理


sunny
256 声望8 粉丝