Bu adımda şunları öğreneceksiniz:
- Mevcut bir web uygulamasını Chrome Uygulamaları platformuna uyarlama.
- Uygulamanızın komut dosyalarını İçerik Güvenliği Politikası'na (İGP) uygun hale getirme
- chrome.storage.local'den yararlanarak yerel depolama nasıl uygulanır?
Bu adımın tamamlanması için tahmini süre: 20 dakika.
.
Bu adımda tamamlayacaklarınızı önizlemek için bu sayfanın en altına gidin ↓.
Mevcut bir Todo uygulamasını içe aktarma
Başlangıç olarak, yaygın bir karşılaştırma olan TodoMVC'nin vanilya JavaScript sürümünü içe aktarın. projenize ekleyin.
TodoMVC uygulamasının bir sürümünü, todomvc içindeki referans kodu posta kodu bölümüne ekledik. tıklayın. todomvc dosyasındaki tüm dosyaları (klasörler dahil) proje klasörünüze kopyalayın.
index.html'yi değiştirmeniz istenir. Devam edin ve kabul edin.
Uygulama klasörünüzde artık aşağıdaki dosya yapısına sahip olmanız gerekir:
Maviyle vurgulanan dosyalar todomvc klasöründendir.
Uygulamanızı şimdi yeniden yükleyin (sağ tıklayın > Uygulamayı Yeniden Yükle). Temel kullanıcı arayüzünü görürsünüz ancak çok iyi olur.
Komut dosyalarını İçerik Güvenliği Politikası'na (İGP) uygun hale getirin
Geliştirici Araçları Konsolu'nu açın (sağ tıklayın > Öğeyi İncele'yi, ardından Konsol sekmesini seçin). Siz satır içi komut dosyasını çalıştırmayı reddetmeyle ilgili bir hata mesajı görür:
Uygulamayı İçerik Güvenliği Politikası'na uygun hale getirerek bu hatayı düzeltelim. Google'ın en büyük
yaygın CSP uyumsuzlukları satır içi JavaScript'ten kaynaklanır. Satır içi JavaScript örnekleri aşağıdakileri içerir:
DOM özellikleri (ör. <button onclick=''>
) ve içeriği olan <script>
etiketleri olarak etkinlik işleyiciler
içine gireceğim.
Çözüm basittir: satır içi içeriği yeni bir dosyaya taşıyın.
1. index.html sayfasının alt kısmına yakın bir yerde, satır içi JavaScript'i kaldırın ve bunun yerine js/bootstrap.js:
<script src="bower_components/director/build/director.js"></script>
<script>
// Bootstrap app data
window.app = {};
</script>
<script src="js/bootstrap.js"></script>
<script src="js/helpers.js"></script>
<script src="js/store.js"></script>
2. js klasöründe bootstrap.js adlı bir dosya oluşturun. Önceki satır içi kodu taşıyın şu dosyada yer almalıdır:
// Bootstrap app data
window.app = {};
Uygulamayı şimdi yeniden yüklerseniz ancak çalışmaya yaklaştığınızda çalışmaya devam eder.
localStorage'ı chrome.storage.local olarak dönüştür
Geliştirici Araçları Konsolu'nu şimdi açarsanız önceki hata kaybolacaktır. Yeni bir hata var,
ancak, window.localStorage
adlı müşterinin mevcut olmaması hakkında:
localStorage
eşzamanlı olduğundan Chrome Uygulamaları localStorage
uygulamasını desteklemez. Eşzamanlı erişim
tek iş parçacıklı bir çalışma zamanında kaynakları (G/Ç) engellemek, uygulamanızın yanıt vermemesine neden olabilir.
Chrome Uygulamaları, nesneleri eşzamansız olarak depolayabilen eşdeğer bir API'ye sahiptir. Bu sayede karşılaşabileceğiniz nesne->string->nesne serileştirme işlemi bazen maliyetli olur.
Uygulamamızdaki hata mesajıyla ilgilenmek için localStorage
öğesini şuna dönüştürmeniz gerekiyor:
chrome.storage.local gibi daha fazla depolama alanı kullanabilirsiniz.
Uygulama izinlerini güncelleme
chrome.storage.local
ürününü kullanmak için storage
iznini istemeniz gerekir. İçinde
manifest.json dosyası için permissions
dizisine "storage"
ekleyin:
"permissions": ["storage"],
local.storage.set() ve local.storage.get() hakkında bilgi edinin.
Yapılacak işler öğelerini kaydetmek ve almak için öğelerin set()
ve get()
yöntemleri hakkında bilgi sahibi olmanız gerekir.
chrome.storage
API.
set() yöntemi, ilk parametresi olarak anahtar/değer çiftleri nesnesini kabul eder. İsteğe bağlı geri çağırma işlevi ikinci parametredir. Örneğin:
chrome.storage.local.set({secretMessage:'Psst!',timeSet:Date.now()}, function() {
console.log("Secret message saved");
});
get() yöntemi, istediğiniz veri deposu anahtarları için isteğe bağlı bir ilk parametreyi kabul eder. geri dönelim. Tek bir anahtar bir dize olarak iletilebilir; birden fazla anahtar, dizeler veya sözlük nesnesi oluşturur.
Gerekli olan ikinci parametre, bir geri çağırma işlevidir. Döndürülen nesnede, şunu kullanın: depolanan değerlere erişmek için ilk parametrede istenen anahtar sayısı. Örneğin:
chrome.storage.local.get(['secretMessage','timeSet'], function(data) {
console.log("The secret message:", data.secretMessage, "saved at:", data.timeSet);
});
Şu anda chrome.storage.local
içinde olan her şeyi get()
eklemek istiyorsanız ilkini atlayın
parametresi:
chrome.storage.local.get(function(data) {
console.log(data);
});
localStorage
hizmetinin aksine, Yerel olarak depolanan öğeleri Geliştirici Araçları'nı kullanarak inceleyemezsiniz.
Kaynaklar paneli. Bununla birlikte, chrome.storage
ile JavaScript Konsolu'ndan şunun gibi etkileşimde bulunabilirsiniz:
dolayısıyla:
Gerekli API değişikliklerini önizleyin
Todo uygulamasını dönüştürmeyle ilgili geri kalan adımların çoğu, API çağrılarında yapılan küçük değişikliklerdir. Değiştiriliyor
localStorage
ürününün şu anda kullanıldığı tüm yerler; zaman alıcı ve hataya açık olsa da,
gereklidir.
localStorage
ile chrome.storage
arasındaki temel farklar,
chrome.storage
:
localStorage
ile basit ödevler istemek yerine İsteğe bağlı geri çağırma işlevleriylechrome.storage.local.set()
.var data = { todos: [] }; localStorage[dbName] = JSON.stringify(data);
karşı
var storage = {}; storage[dbName] = { todos: [] }; chrome.storage.local.set( storage, function() { // optional callback });
localStorage[myStorageName]
hizmetine doğrudan erişmek yerinechrome.storage.local.get(myStorageName,function(storage){...})
ve ardından döndürülenstorage
nesnesini içeriyor.var todos = JSON.parse(localStorage[dbName]).todos;
karşı
chrome.storage.local.get(dbName, function(storage) { var todos = storage[dbName].todos; });
.bind(this)
işlevi, tüm geri çağırmalarda kullanılır. Böylecethis
,this
Store
prototipi. (Bağlı işlevler hakkında daha fazla bilgi MDN belgelerinde bulunabilir: Function.prototype.bind().)function Store() { this.scope = 'inside Store'; chrome.storage.local.set( {}, function() { console.log(this.scope); // outputs: 'undefined' }); } new Store();
karşı
function Store() { this.scope = 'inside Store'; chrome.storage.local.set( {}, function() { console.log(this.scope); // outputs: 'inside Store' }.bind(this)); } new Store();
bu bölümde bulabilirsiniz.
Yapılacaklar listesi öğelerini alma
Yapılacaklar listesi öğelerini almak için Todo uygulamasını güncelleyelim:
1. Store
oluşturucu yöntemi, Todo uygulamasını mevcut tüm
öğeler atayacağım. Yöntem ilk olarak veri deposunun olup olmadığını kontrol eder. Aksi takdirde
boş bir todos
dizisi oluşturup çalışma zamanı okuma hatası olmaması için bunu veri deposuna kaydedin.
js/store.js'de, oluşturucu yöntemde localStorage
kullanımını bunun yerine
chrome.storage.local
:
function Store(name, callback) {
var data;
var dbName;
callback = callback || function () {};
dbName = this._dbName = name;
if (!localStorage[dbName]) {
data = {
todos: []
};
localStorage[dbName] = JSON.stringify(data);
}
callback.call(this, JSON.parse(localStorage[dbName]));
chrome.storage.local.get(dbName, function(storage) {
if ( dbName in storage ) {
callback.call(this, storage[dbName].todos);
} else {
storage = {};
storage[dbName] = { todos: [] };
chrome.storage.local.set( storage, function() {
callback.call(this, storage[dbName].todos);
}.bind(this));
}
}.bind(this));
}
2. Modelden yapılacak işler okunurken find()
yöntemi kullanılır. Döndürülen sonuçlar şuna göre değişir:
"Tümü", "Etkin" veya "Tamamlandı" ölçütüne göre filtreleme yaptığınızdan emin olun.
find()
değerini, chrome.storage.local
kullanacak şekilde dönüştürün:
Store.prototype.find = function (query, callback) {
if (!callback) {
return;
}
var todos = JSON.parse(localStorage[this._dbName]).todos;
callback.call(this, todos.filter(function (todo) {
chrome.storage.local.get(this._dbName, function(storage) {
var todos = storage[this._dbName].todos.filter(function (todo) {
for (var q in query) {
return query[q] === todo[q];
}
});
callback.call(this, todos);
}.bind(this));
}));
};
3. find()
özelliğine benzer şekilde, findAll()
tüm yapılacak işleri Model'den alır. findAll()
metnini kullanmak için dönüştürün
chrome.storage.local
:
Store.prototype.findAll = function (callback) {
callback = callback || function () {};
callback.call(this, JSON.parse(localStorage[this._dbName]).todos);
chrome.storage.local.get(this._dbName, function(storage) {
var todos = storage[this._dbName] && storage[this._dbName].todos || [];
callback.call(this, todos);
}.bind(this));
};
Yapılacaklar listesi öğelerini kaydet
Mevcut save()
yöntemi bazı zorluklar içeriyor. İki eşzamansız işleme (al ve ayarla) bağlıdır
monolitik JSON depolama alanının tamamında çalışan uygulamalar. Birden fazla cihazdaki toplu güncellemeler
"yapılacak tüm işleri tamamlandı olarak işaretle" gibi bir işlem öğesi,
Yazma Sonrası Okuma. Daha uygun bir veri depolama alanı kullanıyor olsaydık bu sorun
ancak bu codelab için dönüşüm çabasını en aza indirmeye çalışıyoruz.
Bu sorunu düzeltmenin birkaç yolu olduğundan bu fırsatı değerlendirerek save()
ile ilgili
aynı anda güncellenecek bir dizi yapılacaklar listesi öğesi alır:
1. Başlamak için zaten save()
içindeki her şeyi bir chrome.storage.local.get()
ile sarmalayın
geri arama:
Store.prototype.save = function (id, updateData, callback) {
chrome.storage.local.get(this._dbName, function(storage) {
var data = JSON.parse(localStorage[this._dbName]);
// ...
if (typeof id !== 'object') {
// ...
}else {
// ...
}
}.bind(this));
};
2. Tüm localStorage
örneklerini chrome.storage.local
ile dönüştürün:
Store.prototype.save = function (id, updateData, callback) {
chrome.storage.local.get(this._dbName, function(storage) {
var data = JSON.parse(localStorage[this._dbName]);
var data = storage[this._dbName];
var todos = data.todos;
callback = callback || function () {};
// If an ID was actually given, find the item and update each property
if ( typeof id !== 'object' ) {
// ...
localStorage[this._dbName] = JSON.stringify(data);
callback.call(this, JSON.parse(localStorage[this._dbName]).todos);
chrome.storage.local.set(storage, function() {
chrome.storage.local.get(this._dbName, function(storage) {
callback.call(this, storage[this._dbName].todos);
}.bind(this));
}.bind(this));
} else {
callback = updateData;
updateData = id;
// Generate an ID
updateData.id = new Date().getTime();
localStorage[this._dbName] = JSON.stringify(data);
callback.call(this, [updateData]);
chrome.storage.local.set(storage, function() {
callback.call(this, [updateData]);
}.bind(this));
}
}.bind(this));
};
3. Ardından, tek bir öğe yerine bir dizide çalışacak şekilde mantığı güncelleyin:
Store.prototype.save = function (id, updateData, callback) {
chrome.storage.local.get(this._dbName, function(storage) {
var data = storage[this._dbName];
var todos = data.todos;
callback = callback || function () {};
// If an ID was actually given, find the item and update each property
if ( typeof id !== 'object' || Array.isArray(id) ) {
var ids = [].concat( id );
ids.forEach(function(id) {
for (var i = 0; i < todos.length; i++) {
if (todos[i].id == id) {
for (var x in updateData) {
todos[i][x] = updateData[x];
}
}
}
});
chrome.storage.local.set(storage, function() {
chrome.storage.local.get(this._dbName, function(storage) {
callback.call(this, storage[this._dbName].todos);
}.bind(this));
}.bind(this));
} else {
callback = updateData;
updateData = id;
// Generate an ID
updateData.id = new Date().getTime();
todos.push(updateData);
chrome.storage.local.set(storage, function() {
callback.call(this, [updateData]);
}.bind(this));
}
}.bind(this));
};
Yapılacak öğeleri tamamlandı olarak işaretle
Uygulama artık diziler üzerinde çalıştığına göre Tamamlananları temizle (#) düğmesi:
1. controller.js'de, toggleAll()
öğesini bir diziyle yalnızca bir kez toggleComplete()
çağrısı yapacak şekilde güncelleyin.
tek tek tamamlandı olarak işaretlemek yerine, yapılacak işler listesine ekleyeceksiniz. _filter()
adlı kişiye yapılan aramayı da sil
çünkü toggleComplete
_filter()
ayarını yapacaksınız.
Controller.prototype.toggleAll = function (e) {
var completed = e.target.checked ? 1 : 0;
var query = 0;
if (completed === 0) {
query = 1;
}
this.model.read({ completed: query }, function (data) {
var ids = [];
data.forEach(function (item) {
this.toggleComplete(item.id, e.target, true);
ids.push(item.id);
}.bind(this));
this.toggleComplete(ids, e.target, false);
}.bind(this));
this._filter();
};
2. Şimdi toggleComplete()
uygulamasını hem tek bir yapılacak işi hem de bir dizi yapılacak işi kabul edecek şekilde güncelleyin. Buna şunlar dâhildir:
filter()
, dışarı değil update()
içinde olacak şekilde taşınıyor.
Controller.prototype.toggleComplete = function (ids, checkbox, silent) {
var completed = checkbox.checked ? 1 : 0;
this.model.update(ids, { completed: completed }, function () {
if ( ids.constructor != Array ) {
ids = [ ids ];
}
ids.forEach( function(id) {
var listItem = $$('[data-id="' + id + '"]');
if (!listItem) {
return;
}
listItem.className = completed ? 'completed' : '';
// In case it was toggled from an event and not by clicking the checkbox
listItem.querySelector('input').checked = completed;
});
if (!silent) {
this._filter();
}
}.bind(this));
};
Count todo items
After switching to async storage, there is a minor bug that shows up when getting the number of todos. You'll need to wrap the count operation in a callback function:
1. In model.js, update getCount()
to accept a callback:
Model.prototype.getCount = function (callback) {
var todos = {
active: 0,
completed: 0,
total: 0
};
this.storage.findAll(function (data) {
data.each(function (todo) {
if (todo.completed === 1) {
todos.completed++;
} else {
todos.active++;
}
todos.total++;
});
if (callback) callback(todos);
});
return todos;
};
2. Back in controller.js, update _updateCount()
to use the async getCount()
you edited in
the previous step:
Controller.prototype._updateCount = function () {
var todos = this.model.getCount();
this.model.getCount(function(todos) {
this.$todoItemCounter.innerHTML = this.view.itemCounter(todos.active);
this.$clearCompleted.innerHTML = this.view.clearCompletedButton(todos.completed);
this.$clearCompleted.style.display = todos.completed > 0 ? 'block' : 'none';
this.$toggleAll.checked = todos.completed === todos.total;
this._toggleFrame(todos);
}.bind(this));
};
You are almost there! If you reload the app now, you will be able to insert new todos without any console errors.
Remove todos items
Now that the app can save todo items, you're close to being done! You still get errors when you attempt to remove todo items:
1. In store.js, convert all the localStorage
instances to use chrome.storage.local
:
a) To start off, wrap everything already inside remove()
with a get()
callback:
Store.prototype.remove = function (id, callback) {
chrome.storage.local.get(this._dbName, function(storage) {
var data = JSON.parse(localStorage[this._dbName]);
var todos = data.todos;
for (var i = 0; i < todos.length; i++) {
if (todos[i].id == id) {
todos.splice(i, 1);
break;
}
}
localStorage[this._dbName] = JSON.stringify(data);
callback.call(this, JSON.parse(localStorage[this._dbName]).todos);
}.bind(this));
};
b) Then convert the contents within the get()
callback:
Store.prototype.remove = function (id, callback) {
chrome.storage.local.get(this._dbName, function(storage) {
var data = JSON.parse(localStorage[this._dbName]);
var data = storage[this._dbName];
var todos = data.todos;
for (var i = 0; i < todos.length; i++) {
if (todos[i].id == id) {
todos.splice(i, 1);
break;
}
}
localStorage[this._dbName] = JSON.stringify(data);
callback.call(this, JSON.parse(localStorage[this._dbName]).todos);
chrome.storage.local.set(storage, function() {
callback.call(this, todos);
}.bind(this));
}.bind(this));
};
2. The same Read-After-Write data hazard issue previously present in the save()
method is also
present when removing items so you will need to update a few more places to allow for batch
operations on a list of todo IDs.
a) Still in store.js, update remove()
:
Store.prototype.remove = function (id, callback) {
chrome.storage.local.get(this._dbName, function(storage) {
var data = storage[this._dbName];
var todos = data.todos;
var ids = [].concat(id);
ids.forEach( function(id) {
for (var i = 0; i < todos.length; i++) {
if (todos[i].id == id) {
todos.splice(i, 1);
break;
}
}
});
chrome.storage.local.set(storage, function() {
callback.call(this, todos);
}.bind(this));
}.bind(this));
};
b) In controller.js, change removeCompletedItems()
to make it call removeItem()
on all IDs
at once:
Controller.prototype.removeCompletedItems = function () {
this.model.read({ completed: 1 }, function (data) {
var ids = [];
data.forEach(function (item) {
this.removeItem(item.id);
ids.push(item.id);
}.bind(this));
this.removeItem(ids);
}.bind(this));
this._filter();
};
c) Finally, still in controller.js, change the removeItem()
to support removing multiple items
from the DOM at once, and move the _filter()
call to be inside the callback:
Controller.prototype.removeItem = function (id) {
this.model.remove(id, function () {
var ids = [].concat(id);
ids.forEach( function(id) {
this.$todoList.removeChild($$('[data-id="' + id + '"]'));
}.bind(this));
this._filter();
}.bind(this));
this._filter();
};
Yapılacak tüm öğeleri bırak
store.js'de localStorage
kullanan bir yöntem daha vardır:
Store.prototype.drop = function (callback) {
localStorage[this._dbName] = JSON.stringify({todos: []});
callback.call(this, JSON.parse(localStorage[this._dbName]).todos);
};
Bu yöntem mevcut uygulamada çağrılmıyor. Daha fazla sorgulamak isterseniz
uygulayıp ona göre hareket edebilirsiniz. İpucu: chrome.storage.local.clear()
sitesine göz atın.
Tamamlanmış Todo uygulamanızı başlatın
2. adımı tamamladınız! Uygulamanızı yeniden yüklediğinizde Chrome'un tam olarak çalışan bir paket sürümüne sahip olmanız gerekir. .
Daha fazla bilgi için
Bu adımda tanıtılan API'lerin bazıları hakkında daha ayrıntılı bilgi için aşağıdaki konulara bakın:
- İçerik Güvenliği Politikası ↑
- İzin beyan etme ↑
- chrome.storage ↑
- chrome.storage.local.get() ↑
- chrome.storage.local.set() ↑
- chrome.storage.local.remove() ↑
- chrome.storage.local.clear() ↑
Bir sonraki adıma geçmeye hazır mısınız? 3. Adım - Alarm ve bildirim ekleme » bölümüne gidin.