SlideShare a Scribd company logo
1 of 71
Chrome Extensionsの基本
とデザインパターン
Chrome+HTML5 Developers Live Japan #1
Chrome Extension
使ってますか?
Chrome Extensionで
何ができるの?
Browser Actions
Page Actions
Context Menus
Desktop Notifications
Options Pages
Options Pages
APIs
alarms, bookmarks, browserAction, browsingData,
commands, contentSettings, contextMenus, cookies,
debugger, declarativeWebRequest,
devtools.network, devtools.inspectedWindow,
devtools.panels, downloads, events, extension,
fileBrowserHandler, fontSettings, history, i18n, idle,
input.ime, management, omnibox, pageAction,
pageCapture, permissions, privacy, proxy,
pushMessaging, runtime, scriptBadge, storage, tabs,
topSites, tts, ttsEngine, types, webNavigation,
webRequest, webstore, windows
APIs
Chrome Extensionは
何でできてるの?
Technologies
Web pages + JavaScript API
Structure
マニフェストファイル(manifest.json)
1つ以上のHTMLファイル
(Optional) 1つ以上のJavaScriptファイル
(Optional) 必要となる他のファイル
- 画像ファイルなど
これらをzipファイルにまとめてChromeウェブ
ストアにアップロードする
Minimum Extension
manifest.json
{
"manifest_version": 2,
"name": "Minimum Extension",
"version": "0.0.1",
"browser_action": {
"default_popup": "popup.html"
}
}
Minimum Extension
popup.html
<!DOCTYPE html>
<html>
<head></head>
<body>
<div>Hello, Chrome extension!</div>
</body>
</html>
Minimum Extension
Minimum Extension
Minimum Extension
Minimum Extension
Minimum Extension
オレ流
Chrome Extension
デザインパターン
Structure
manifest.json analytics.json background.js
options.html
options.js
popup.html
popup.js
My design pattern for Chrome Extension
manifest.json
{ ...
"version": "バージョン番号(XX.XX.XX形式)",
"browser_action": {
"default_icon": "./icon_**.png (**はサイズ)",
"default_popup": "./popup.html",
...
},
"content_security_policy": "script-src 'self'
https://ssl.google-analytics.com; object-src 'self'",
"background": {
"scripts": [ "./jquery-min.js", "./background.js" ],
"persistent": false,
...
},
"options_page": "./options.html",
"default_locale": "en",
...
}
{ ...
"version": "バージョン番号(XX.XX.XX形式)",
"browser_action": {
"default_icon": "./icon_**.png (**はサイズ)",
"default_popup": "./popup.html",
...
},
"content_security_policy": "script-src 'self'
https://ssl.google-analytics.com; object-src 'self'",
"background": {
"scripts": [ "./jquery-min.js", "./background.js" ],
"persist": false,
...
},
"options_page": "./options.html",
"default_locale": "en",
...
}
Browser/Page actionのコンテ
ンツは「popup.html」という
ファイル名にする
{ ...
"version": "バージョン番号(XX.XX.XX形式)",
"browser_action": {
"default_icon": "./icon_**.png (**はサイズ)",
"default_popup": "./popup.html",
...
},
"content_security_policy": "script-src 'self'
https://ssl.google-analytics.com; object-src 'self'",
"background": {
"scripts": ["./jquery-min.js","./background.js" ],
"persistent": false,
...
},
"options_page": "./options.html",
"default_locale": "en",
...
}
Background Pageは必ず作成
し「background.js」というフ
ァイル名とする
軽いExtensionとするために
Event pageとする
{ ...
"version": "バージョン番号(XX.XX.XX形式)",
"browser_action": {
"default_icon": "./icon_**.png (**はサイズ)",
"default_popup": "./popup.html",
...
},
"content_security_policy": "script-src 'self'
https://ssl.google-analytics.com; object-src 'self'",
"background": {
"scripts": [ "./jquery-min.js", "./background.js" ],
"persistent": false,
...
},
"options_page": "./options.html",
"default_locale": "en",
...
}
設定ページが必要な場合は
「options.html」というファ
イル名で作成する
{ ...
"version": "バージョン番号(XX.XX.XX形式)",
"browser_action": {
"default_icon": "./icon_**.png (**はサイズ)",
"default_popup": "./popup.html",
...
},
"content_security_policy": "script-src 'self'
https://ssl.google-analytics.com; object-src 'self'",
"background": {
"scripts": [ "./jquery-min.js", "./background.js" ],
"persistent": false,
...
},
"options_page": "./options.html",
"default_locale": "en",
...
}
どんなに小さなExtensionで
も国際化しておく
最低限enとjaをサポートする
{ ...
"version": "バージョン番号(XX.XX.XX形式)",
"browser_action": {
"default_icon": "./icon_**.png (**はサイズ)",
"default_popup": "./popup.html",
...
},
"content_security_policy": "script-src 'self'
https://ssl.google-analytics.com; object-src 'self'",
"background": {
"scripts": [ "./jquery-min.js", "./background.js" ],
"persistent": false,
...
},
"options_page": "./options.html",
"default_locale": "en",
...
}
利用状況を把握するために
Google Analyticsを仕込んでおく
Google Analyticsサーバと通信す
るためにCSPに記載しておく
Google Analytics
ポリシー:
「Google Analyticsを使っていることを開示し、
トラッキングデータの収集のためにCookieが使
われていることも明記する」
ChromeウェブストアのページやExtension内に
てちゃんとユーザに説明しましょう。
My design pattern for Chrome Extension
background.js
popup.html
popup.js
options.html
options.js
background.js Backend server
UI処理に専念
UI処理に専念
Ajaxでの通信
設定値出し入れ
イベント処理
var Background = function() {
this.assignEventHandlers();
};
Background.prototype = {
assignEventHandlers: function() {...},
load***: function(callbacks) {...},
get***Config: function() {...},
set***Config: function() {...}
...
};
var bg = new Background();
var Background = function() {
this.assignEventHandlers();
};
Background.prototype = {
assignEventHandlers: function() {...},
load***: function(callbacks) {...},
get***Config: function() {...},
set***Config: function() {...}
...
};
var bg = new Background();
各種イベントハンドラを登録する
処理を「assignEventHandlers()」
関数にまとめる
var Background = function() {
this.assignEventHandlers();
};
Background.prototype = {
assignEventHandlers: function() {...},
load***: function(callbacks) {...},
get***Config: function() {...},
set***Config: function() {...}
...
};
var bg = new Background();
assignEventHandlers: function() {
// タブの変更監視
chrome.tab.onActivated.addListener(
function(activeInfo) {
...
}
);
// Chromeの起動監視
chrome.runtime.onStartup.addListener(
function() {
// Google Analyticsに記録など
_gaq.push(["_trackEvent", "..", ".."]);
}
);
...
},
var Background = function() {
this.assignEventHandlers();
};
Background.prototype = {
assignEventHandlers: function() {...},
load***: function(callbacks) {...},
get***Config: function() {...},
set***Config: function() {...}
...
};
var bg = new Background();
Ajax処理は「load***()」という関
数名にする
Ajax処理をbackground.jsに集めて
おく(Debugしやすくなるため)
var Background = function() {
this.assignEventHandlers();
};
Background.prototype = {
assignEventHandlers: function() {...},
load***: function(callbacks) {...},
get***Config: function() {...},
set***Config: function() {...}
...
};
var bg = new Background();
load***: function(callbacks) {
$.ajax({
url: this.getServerUrl + "ajax/get_***"
})
.done(function(data) {
callbacks.onSuccess(data);
});
},
getServerUrl: function() {
return "http://backend.server.name/";
},
var Background = function() {
this.assignEventHandlers();
};
Background.prototype = {
assignEventHandlers: function() {...},
load***: function(callbacks) {...},
get***Config: function() {...},
set***Config: function() {...}
...
};
var bg = new Background();設定ページで設定されるような動
的な設定値の格納と取得処理も
background.jsにまとめておく
var Background = function() {
this.assignEventHandlers();
};
Background.prototype = {
assignEventHandlers: function() {...},
load***: function(callbacks) {...},
get***Config: function() {...},
set***Config: function() {...}
...
};
var bg = new Background();
get***Config: function() {
var value = localStorage["***"];
if (value) {
return value;
} else {
return "初期値";
}
},
set***Config: function() {
localStorage["***"] = value;
},
popup.html
popup.js
options.html
options.js
background.js Backend server
chrome.runtime.getBackgroundPage()
でアクセス可能
Chrome DevTools
My design pattern for Chrome Extension
options.html
Options Pages
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="./jquery-min.js">
<script type="text/javascript" src="./options.js">
</head>
<body>
<script type="text/javascript" src="./analytics.js">
...
<div><span id="opt***"></span></div>
<table>
<tr>
<td> <input type="checkbox" id="***" /> </td>
<td> <span id="opt***"></span> </td>
</tr>
...
</table>
...
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="./jquery-min.js">
<script type="text/javascript" src="./options.js">
</head>
<body>
<script type="text/javascript" src="./analytics.js">
...
<div><span id="opt***"></span></div>
<table>
<tr>
<td> <input type="checkbox" id="***" /> </td>
<td> <span id="opt***"></span> </td>
</tr>
...
</table>
...
Google Analyticsで設定ページへの
アクセスを計測できるようにする
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-xxxxxx-xx']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = 'https://ssl.google-analytics.com/ga.js';
var s = document.getElementByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="./jquery-min.js">
<script type="text/javascript" src="./options.js">
</head>
<body>
<script type="text/javascript" src="./analytics.js">
...
<div><span id="opt***"></span></div>
<table>
<tr>
<td> <input type="checkbox" id="***" /> </td>
<td> <span id="opt***"></span> </td>
</tr>
...
</table>
...
表示文字列はhtmlに
一切記載しない
id属性はしっかり
書いておく
My design pattern for Chrome Extension
options.js
(function() {
var Options = function() {
this.assignMessages();
this.assignEventHandlers();
this.restoreConfigurations();
};
Options.prototype = {
assignMessages: function() { ... },
assignEventHandlers: function() { ... },
restoreConfigurations: function() { ... },
};
window.addEventListener(
"load", function(evt) { new Options(); });
})();
(function() {
var Options = function() {
this.assignMessages();
this.assignEventHandlers();
this.restoreConfigurations();
};
Options.prototype = {
assignMessages: function() { ... },
assignEventHandlers: function() { ... },
restoreConfigurations: function() { ... },
};
window.addEventListener(
"load", function(evt) { new Options(); });
})();
onloadイベント発生に応じて初期
化処理が動くようにしておく
(function() {
var Options = function() {
this.assignMessages();
this.assignEventHandlers();
this.restoreConfigurations();
};
Options.prototype = {
assignMessages: function() { ... },
assignEventHandlers: function() { ... },
restoreConfigurations: function() { ... },
};
window.addEventListener(
"load", function(evt) { new Options(); });
})();
各種初期化処理を呼び出す
他のjsと関数名を揃えておく
(function() {
var Options = function() {
this.assignMessages();
this.assignEventHandlers();
this.restoreConfigurations();
});
};
Options.prototype = {
assignMessages: function() { ... },
assignEventHandlers: function() { ... },
restoreConfigurations: function() { ... },
};
window.addEventListener(
"load", function(evt) { new Options(); });
})();
assignMessages: function() {
var hash = {
"opt***" : "opt***",
...
};
for (var key in hash) {
$("#" + key).text(
chrome.i18n.getMessage(hash[key]));
}
},
HTMLの各プレースホルダ(id属性を振っ
た要素)に対して、メッセージリソース
から得た国際化文字列をセットする
(function() {
var Options = function() {
this.assignMessages();
this.assignEventHandlers();
this.restoreConfigurations();
});
};
Options.prototype = {
assignMessages: function() { ... },
assignEventHandlers: function() { ... },
restoreConfigurations: function() { ... },
};
window.addEventListener(
"load", function(evt) { new Options(); });
})();
restoreConfigurations: function() {
chrome.runtime.getBackgroundPage(
function(bg) {
$("#***").val(bg.get***Config());
...
}
);
}; Background pageの設定値取得
関数を使って、せっせとUIに値
をセットしていく
(function() {
var Options = function() {
this.assignMessages();
this.assignEventHandlers();
this.restoreConfigurations();
});
};
Options.prototype = {
assignMessages: function() { ... },
assignEventHandlers: function() { ... },
restoreConfigurations: function() { ... },
};
window.addEventListener(
"load", function(evt) { new Options(); });
})();
assignEventHandlers: function() {
$("#***").on("click", $.proxy(
function(evt) {
this.onClick***(evt);
}, this
));
},
onClick***: function(evt) {
chrome.runtime.getBackgroundPage(
function(bg) {
var value = $("#***").val();
bg.set***Config(value);
}
);
}, Background pageの設定値格納
関数を使って設定値を保存する
(function() {
var Options = function() {
this.assignMessages();
this.assignEventHandlers();
this.restoreConfigurations();
});
};
Options.prototype = {
assignMessages: function() { ... },
assignEventHandlers: function() { ... },
restoreConfigurations: function() { ... },
};
window.addEventListener(
"load", function(evt) { new Options(); });
})();
assignEventHandlers: function() {
$("#***").on("click", $.proxy(
function(evt) {
this.onClick***(evt);
}, this
));
},
onClick***: function(evt) {
chrome.runtime.getBackgroundPage(
function(bg) {
var value = $("#***").val();
bg.set***Config(value);
}
);
},
イベントハンドラの名前は
「on+イベント種別+UI項目名」
とする
My design pattern for Chrome Extension
popup.html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="./jquery-min.js">
<script type="text/javascript" src="./popup.js">
</head>
<body>
<script type="text/javascript" src="./analytics.js">
<span id="***"></span>
...
options.jsとほぼ一緒
My design pattern for Chrome Extension
popup.js
(function() {
var Popup = function() {
this.assignMessages();
this.assignEventHandlers();
};
Popup.prototype = {
assignMessages: function() { ... },
assignEventHandlers: function() { ... },
...
};
window.addEventListener(
"load", function(evt) { new Popup(); });
})();
options.jsとほぼ一緒
(function() {
var Popup = function() {
this.assignMessages();
this.assignEventHandlers();
};
Popup.prototype = {
assignMessages: function() { ... },
assignEventHandlers: function() { ... },
...
};
window.addEventListener(
"load", function(evt) { new Popup(); });
})();
assignEventHandlers: function() {
$("#***").on("click", $.proxy(function(evt) {
this.onClick***(evt);
}, this));
},
onClick***: function(evt) {
chrome.runtime.getBackgroundPage(
function(bg) {
// 設定値を取得
var config =bg.get***Config();
// Ajax通信
bg.load***({
onSuccess: function(data) { ... }
});
}
);
},
Background pageが提
供する関数を利用
My design pattern
ロジックを整理&集中させる
- Background pageをうまく使うこと
統一感を作り出す
- assign***() , onClick***() , set/get***Config()
国際化を徹底させる
- id属性, assignMessages()
利用者が増えるのを
楽しむには?
http://developer.chrome.com/extensions/

More Related Content

What's hot

Shibuya Pm Tt08 Advanced Mogilefs
Shibuya Pm Tt08 Advanced MogilefsShibuya Pm Tt08 Advanced Mogilefs
Shibuya Pm Tt08 Advanced Mogilefsguest172cfb
 
Tide - SmalltalkでSPA
Tide - SmalltalkでSPATide - SmalltalkでSPA
Tide - SmalltalkでSPAMasashi Umezawa
 
About GStreamer 1.0 application development for beginners
About GStreamer 1.0 application development for beginnersAbout GStreamer 1.0 application development for beginners
About GStreamer 1.0 application development for beginnersShota TAMURA
 
[Tokyo NodeFest 2015] Hardware Hacking for Javascript Developers
[Tokyo NodeFest 2015] Hardware Hacking for Javascript Developers[Tokyo NodeFest 2015] Hardware Hacking for Javascript Developers
[Tokyo NodeFest 2015] Hardware Hacking for Javascript DevelopersTomomi Imura
 
第8回KPF発表資料
第8回KPF発表資料第8回KPF発表資料
第8回KPF発表資料cryks
 
Custom Scan API - PostgreSQL Unconference #3 (18-Jan-2014)
Custom Scan API - PostgreSQL Unconference #3 (18-Jan-2014)Custom Scan API - PostgreSQL Unconference #3 (18-Jan-2014)
Custom Scan API - PostgreSQL Unconference #3 (18-Jan-2014)Kohei KaiGai
 
TDC20111031_Groovy_Geb
TDC20111031_Groovy_GebTDC20111031_Groovy_Geb
TDC20111031_Groovy_GebNobuhiro Sue
 
Lisp Tutorial for Pythonista : Day 4
Lisp Tutorial for Pythonista : Day 4Lisp Tutorial for Pythonista : Day 4
Lisp Tutorial for Pythonista : Day 4Ransui Iso
 
JavaでインメモリSQLエンジンを作ってみた
JavaでインメモリSQLエンジンを作ってみたJavaでインメモリSQLエンジンを作ってみた
JavaでインメモリSQLエンジンを作ってみたJustSystems Corporation
 
PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!Shohei Okada
 
10分で作る Node.js Auto Scale 環境 with CloudFormation
10分で作る Node.js Auto Scale 環境 with CloudFormation10分で作る Node.js Auto Scale 環境 with CloudFormation
10分で作る Node.js Auto Scale 環境 with CloudFormationKazuyuki Honda
 
Pycon2014 django performance
Pycon2014 django performancePycon2014 django performance
Pycon2014 django performancehirokiky
 
VarnishではじめるESI
VarnishではじめるESIVarnishではじめるESI
VarnishではじめるESIIwana Chan
 
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall博文 斉藤
 
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in TokyoGrails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in TokyoTsuyoshi Yamamoto
 
Gradle a new Generation Build Tool
Gradle a new Generation Build ToolGradle a new Generation Build Tool
Gradle a new Generation Build ToolShinya Mochida
 
社内勉強会資料(Varnish Module)
社内勉強会資料(Varnish Module)社内勉強会資料(Varnish Module)
社内勉強会資料(Varnish Module)Iwana Chan
 
EWD 3トレーニングコース#19 JavaScriptからGlobalストレジにアクセスする
EWD 3トレーニングコース#19 JavaScriptからGlobalストレジにアクセスするEWD 3トレーニングコース#19 JavaScriptからGlobalストレジにアクセスする
EWD 3トレーニングコース#19 JavaScriptからGlobalストレジにアクセスするKiyoshi Sawada
 

What's hot (20)

Shibuya Pm Tt08 Advanced Mogilefs
Shibuya Pm Tt08 Advanced MogilefsShibuya Pm Tt08 Advanced Mogilefs
Shibuya Pm Tt08 Advanced Mogilefs
 
Tide - SmalltalkでSPA
Tide - SmalltalkでSPATide - SmalltalkでSPA
Tide - SmalltalkでSPA
 
About GStreamer 1.0 application development for beginners
About GStreamer 1.0 application development for beginnersAbout GStreamer 1.0 application development for beginners
About GStreamer 1.0 application development for beginners
 
[Tokyo NodeFest 2015] Hardware Hacking for Javascript Developers
[Tokyo NodeFest 2015] Hardware Hacking for Javascript Developers[Tokyo NodeFest 2015] Hardware Hacking for Javascript Developers
[Tokyo NodeFest 2015] Hardware Hacking for Javascript Developers
 
Nodejs
NodejsNodejs
Nodejs
 
第8回KPF発表資料
第8回KPF発表資料第8回KPF発表資料
第8回KPF発表資料
 
Custom Scan API - PostgreSQL Unconference #3 (18-Jan-2014)
Custom Scan API - PostgreSQL Unconference #3 (18-Jan-2014)Custom Scan API - PostgreSQL Unconference #3 (18-Jan-2014)
Custom Scan API - PostgreSQL Unconference #3 (18-Jan-2014)
 
TDC20111031_Groovy_Geb
TDC20111031_Groovy_GebTDC20111031_Groovy_Geb
TDC20111031_Groovy_Geb
 
Lisp Tutorial for Pythonista : Day 4
Lisp Tutorial for Pythonista : Day 4Lisp Tutorial for Pythonista : Day 4
Lisp Tutorial for Pythonista : Day 4
 
JavaでインメモリSQLエンジンを作ってみた
JavaでインメモリSQLエンジンを作ってみたJavaでインメモリSQLエンジンを作ってみた
JavaでインメモリSQLエンジンを作ってみた
 
PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!
 
10分で作る Node.js Auto Scale 環境 with CloudFormation
10分で作る Node.js Auto Scale 環境 with CloudFormation10分で作る Node.js Auto Scale 環境 with CloudFormation
10分で作る Node.js Auto Scale 環境 with CloudFormation
 
Pycon2014 django performance
Pycon2014 django performancePycon2014 django performance
Pycon2014 django performance
 
VarnishではじめるESI
VarnishではじめるESIVarnishではじめるESI
VarnishではじめるESI
 
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
 
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in TokyoGrails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
 
HTML5&API総まくり
HTML5&API総まくりHTML5&API総まくり
HTML5&API総まくり
 
Gradle a new Generation Build Tool
Gradle a new Generation Build ToolGradle a new Generation Build Tool
Gradle a new Generation Build Tool
 
社内勉強会資料(Varnish Module)
社内勉強会資料(Varnish Module)社内勉強会資料(Varnish Module)
社内勉強会資料(Varnish Module)
 
EWD 3トレーニングコース#19 JavaScriptからGlobalストレジにアクセスする
EWD 3トレーニングコース#19 JavaScriptからGlobalストレジにアクセスするEWD 3トレーニングコース#19 JavaScriptからGlobalストレジにアクセスする
EWD 3トレーニングコース#19 JavaScriptからGlobalストレジにアクセスする
 

Similar to Chrome Extensionsの基本とデザインパターン

G*workshop 2011/11/22 Geb+Betamax
G*workshop 2011/11/22 Geb+BetamaxG*workshop 2011/11/22 Geb+Betamax
G*workshop 2011/11/22 Geb+BetamaxNobuhiro Sue
 
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみたYuki Takei
 
Selenium webdriver使ってみようず
Selenium webdriver使ってみようずSelenium webdriver使ってみようず
Selenium webdriver使ってみようずOda Shinsuke
 
初めての Data API CMS どうでしょう - 仙台編 -
初めての Data API   CMS どうでしょう - 仙台編 -初めての Data API   CMS どうでしょう - 仙台編 -
初めての Data API CMS どうでしょう - 仙台編 -Yuji Takayama
 
ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用Yatabe Terumasa
 
Data api workshop at Co-Edo
Data api workshop at Co-EdoData api workshop at Co-Edo
Data api workshop at Co-EdoYuji Takayama
 
エンタープライズ分野での実践AngularJS
エンタープライズ分野での実践AngularJSエンタープライズ分野での実践AngularJS
エンタープライズ分野での実践AngularJSAyumi Goto
 
ASP.NET シングル ページ アプリケーション (SPA) 詳説
ASP.NET シングル ページ アプリケーション (SPA) 詳説ASP.NET シングル ページ アプリケーション (SPA) 詳説
ASP.NET シングル ページ アプリケーション (SPA) 詳説Akira Inoue
 
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話Akira Inoue
 
【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へ【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へDevelopers Summit
 
Html5 Web Applications
Html5  Web ApplicationsHtml5  Web Applications
Html5 Web Applicationstotty jp
 
Express Web Application Framework
Express Web Application FrameworkExpress Web Application Framework
Express Web Application FrameworkLearningTech
 
Android Architecture Componentsの新機能
Android Architecture Componentsの新機能Android Architecture Componentsの新機能
Android Architecture Componentsの新機能Damper Matsu
 
アプリコンテスト
アプリコンテストアプリコンテスト
アプリコンテストTomonori Yamada
 
JavaScriptでWebDriverのテストコードを書きましょ
JavaScriptでWebDriverのテストコードを書きましょJavaScriptでWebDriverのテストコードを書きましょ
JavaScriptでWebDriverのテストコードを書きましょKohki Nakashima
 
ヒカルのGo 資料 Webアプリケーションの作り方
ヒカルのGo 資料 Webアプリケーションの作り方ヒカルのGo 資料 Webアプリケーションの作り方
ヒカルのGo 資料 Webアプリケーションの作り方Yosuke Furukawa
 
EC-CUBEプラグイン講義
EC-CUBEプラグイン講義EC-CUBEプラグイン講義
EC-CUBEプラグイン講義ria1201
 

Similar to Chrome Extensionsの基本とデザインパターン (20)

G*workshop 2011/11/22 Geb+Betamax
G*workshop 2011/11/22 Geb+BetamaxG*workshop 2011/11/22 Geb+Betamax
G*workshop 2011/11/22 Geb+Betamax
 
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
 
Selenium webdriver使ってみようず
Selenium webdriver使ってみようずSelenium webdriver使ってみようず
Selenium webdriver使ってみようず
 
Try Jetpack
Try JetpackTry Jetpack
Try Jetpack
 
初めての Data API CMS どうでしょう - 仙台編 -
初めての Data API   CMS どうでしょう - 仙台編 -初めての Data API   CMS どうでしょう - 仙台編 -
初めての Data API CMS どうでしょう - 仙台編 -
 
ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用
 
Data api workshop at Co-Edo
Data api workshop at Co-EdoData api workshop at Co-Edo
Data api workshop at Co-Edo
 
エンタープライズ分野での実践AngularJS
エンタープライズ分野での実践AngularJSエンタープライズ分野での実践AngularJS
エンタープライズ分野での実践AngularJS
 
Ajax 応用
Ajax 応用Ajax 応用
Ajax 応用
 
ASP.NET シングル ページ アプリケーション (SPA) 詳説
ASP.NET シングル ページ アプリケーション (SPA) 詳説ASP.NET シングル ページ アプリケーション (SPA) 詳説
ASP.NET シングル ページ アプリケーション (SPA) 詳説
 
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話
 
【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へ【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へ
 
Html5 Web Applications
Html5  Web ApplicationsHtml5  Web Applications
Html5 Web Applications
 
Express Web Application Framework
Express Web Application FrameworkExpress Web Application Framework
Express Web Application Framework
 
Android Architecture Componentsの新機能
Android Architecture Componentsの新機能Android Architecture Componentsの新機能
Android Architecture Componentsの新機能
 
Ajax basic
Ajax basicAjax basic
Ajax basic
 
アプリコンテスト
アプリコンテストアプリコンテスト
アプリコンテスト
 
JavaScriptでWebDriverのテストコードを書きましょ
JavaScriptでWebDriverのテストコードを書きましょJavaScriptでWebDriverのテストコードを書きましょ
JavaScriptでWebDriverのテストコードを書きましょ
 
ヒカルのGo 資料 Webアプリケーションの作り方
ヒカルのGo 資料 Webアプリケーションの作り方ヒカルのGo 資料 Webアプリケーションの作り方
ヒカルのGo 資料 Webアプリケーションの作り方
 
EC-CUBEプラグイン講義
EC-CUBEプラグイン講義EC-CUBEプラグイン講義
EC-CUBEプラグイン講義
 

More from Yoichiro Tanaka

Navigate users from assistant app to android app
Navigate users from assistant app to android appNavigate users from assistant app to android app
Navigate users from assistant app to android appYoichiro Tanaka
 
Chrome Extensionsから見るWebExtensions
Chrome Extensionsから見るWebExtensionsChrome Extensionsから見るWebExtensions
Chrome Extensionsから見るWebExtensionsYoichiro Tanaka
 
みんなの知らないChrome appsの世界
みんなの知らないChrome appsの世界みんなの知らないChrome appsの世界
みんなの知らないChrome appsの世界Yoichiro Tanaka
 
Chromeウェブストア - Html5とか勉強会42
Chromeウェブストア - Html5とか勉強会42Chromeウェブストア - Html5とか勉強会42
Chromeウェブストア - Html5とか勉強会42Yoichiro Tanaka
 
SocialWeb-Japan Vol.2 20090428
SocialWeb-Japan Vol.2 20090428SocialWeb-Japan Vol.2 20090428
SocialWeb-Japan Vol.2 20090428Yoichiro Tanaka
 
JavaEdge第3回ライブセッション
JavaEdge第3回ライブセッションJavaEdge第3回ライブセッション
JavaEdge第3回ライブセッションYoichiro Tanaka
 
maven2+aptで楽々ドキュメント
maven2+aptで楽々ドキュメントmaven2+aptで楽々ドキュメント
maven2+aptで楽々ドキュメントYoichiro Tanaka
 
丸山先生レクチャーシリーズ2007-2008
丸山先生レクチャーシリーズ2007-2008丸山先生レクチャーシリーズ2007-2008
丸山先生レクチャーシリーズ2007-2008Yoichiro Tanaka
 
Sun Tech Days 2007 Mash up
Sun Tech Days 2007 Mash upSun Tech Days 2007 Mash up
Sun Tech Days 2007 Mash upYoichiro Tanaka
 
体操競技のルール改正と今後の日本の方向性
体操競技のルール改正と今後の日本の方向性体操競技のルール改正と今後の日本の方向性
体操競技のルール改正と今後の日本の方向性Yoichiro Tanaka
 
世間の荒波を乗りこなせ!
世間の荒波を乗りこなせ!世間の荒波を乗りこなせ!
世間の荒波を乗りこなせ!Yoichiro Tanaka
 

More from Yoichiro Tanaka (15)

Navigate users from assistant app to android app
Navigate users from assistant app to android appNavigate users from assistant app to android app
Navigate users from assistant app to android app
 
Chrome Extensionsから見るWebExtensions
Chrome Extensionsから見るWebExtensionsChrome Extensionsから見るWebExtensions
Chrome Extensionsから見るWebExtensions
 
みんなの知らないChrome appsの世界
みんなの知らないChrome appsの世界みんなの知らないChrome appsの世界
みんなの知らないChrome appsの世界
 
Chromeウェブストア - Html5とか勉強会42
Chromeウェブストア - Html5とか勉強会42Chromeウェブストア - Html5とか勉強会42
Chromeウェブストア - Html5とか勉強会42
 
Info scoop opensource
Info scoop opensourceInfo scoop opensource
Info scoop opensource
 
Yapc
YapcYapc
Yapc
 
SocialWeb-Japan Vol.2 20090428
SocialWeb-Japan Vol.2 20090428SocialWeb-Japan Vol.2 20090428
SocialWeb-Japan Vol.2 20090428
 
JRuby on Rails
JRuby on RailsJRuby on Rails
JRuby on Rails
 
JavaEdge第3回ライブセッション
JavaEdge第3回ライブセッションJavaEdge第3回ライブセッション
JavaEdge第3回ライブセッション
 
maven2+aptで楽々ドキュメント
maven2+aptで楽々ドキュメントmaven2+aptで楽々ドキュメント
maven2+aptで楽々ドキュメント
 
丸山先生レクチャーシリーズ2007-2008
丸山先生レクチャーシリーズ2007-2008丸山先生レクチャーシリーズ2007-2008
丸山先生レクチャーシリーズ2007-2008
 
Sun Tech Days 2007 Mash up
Sun Tech Days 2007 Mash upSun Tech Days 2007 Mash up
Sun Tech Days 2007 Mash up
 
体操競技のルール改正と今後の日本の方向性
体操競技のルール改正と今後の日本の方向性体操競技のルール改正と今後の日本の方向性
体操競技のルール改正と今後の日本の方向性
 
Wicket勉強会2
Wicket勉強会2Wicket勉強会2
Wicket勉強会2
 
世間の荒波を乗りこなせ!
世間の荒波を乗りこなせ!世間の荒波を乗りこなせ!
世間の荒波を乗りこなせ!
 

Chrome Extensionsの基本とデザインパターン

  • 3.
  • 4.
  • 5.
  • 6.
  • 14. APIs alarms, bookmarks, browserAction, browsingData, commands, contentSettings, contextMenus, cookies, debugger, declarativeWebRequest, devtools.network, devtools.inspectedWindow, devtools.panels, downloads, events, extension, fileBrowserHandler, fontSettings, history, i18n, idle, input.ime, management, omnibox, pageAction, pageCapture, permissions, privacy, proxy, pushMessaging, runtime, scriptBadge, storage, tabs, topSites, tts, ttsEngine, types, webNavigation, webRequest, webstore, windows
  • 15. APIs
  • 17. Technologies Web pages + JavaScript API
  • 18. Structure マニフェストファイル(manifest.json) 1つ以上のHTMLファイル (Optional) 1つ以上のJavaScriptファイル (Optional) 必要となる他のファイル - 画像ファイルなど これらをzipファイルにまとめてChromeウェブ ストアにアップロードする
  • 19. Minimum Extension manifest.json { "manifest_version": 2, "name": "Minimum Extension", "version": "0.0.1", "browser_action": { "default_popup": "popup.html" } }
  • 28. My design pattern for Chrome Extension manifest.json
  • 29. { ... "version": "バージョン番号(XX.XX.XX形式)", "browser_action": { "default_icon": "./icon_**.png (**はサイズ)", "default_popup": "./popup.html", ... }, "content_security_policy": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'", "background": { "scripts": [ "./jquery-min.js", "./background.js" ], "persistent": false, ... }, "options_page": "./options.html", "default_locale": "en", ... }
  • 30. { ... "version": "バージョン番号(XX.XX.XX形式)", "browser_action": { "default_icon": "./icon_**.png (**はサイズ)", "default_popup": "./popup.html", ... }, "content_security_policy": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'", "background": { "scripts": [ "./jquery-min.js", "./background.js" ], "persist": false, ... }, "options_page": "./options.html", "default_locale": "en", ... } Browser/Page actionのコンテ ンツは「popup.html」という ファイル名にする
  • 31. { ... "version": "バージョン番号(XX.XX.XX形式)", "browser_action": { "default_icon": "./icon_**.png (**はサイズ)", "default_popup": "./popup.html", ... }, "content_security_policy": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'", "background": { "scripts": ["./jquery-min.js","./background.js" ], "persistent": false, ... }, "options_page": "./options.html", "default_locale": "en", ... } Background Pageは必ず作成 し「background.js」というフ ァイル名とする 軽いExtensionとするために Event pageとする
  • 32. { ... "version": "バージョン番号(XX.XX.XX形式)", "browser_action": { "default_icon": "./icon_**.png (**はサイズ)", "default_popup": "./popup.html", ... }, "content_security_policy": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'", "background": { "scripts": [ "./jquery-min.js", "./background.js" ], "persistent": false, ... }, "options_page": "./options.html", "default_locale": "en", ... } 設定ページが必要な場合は 「options.html」というファ イル名で作成する
  • 33. { ... "version": "バージョン番号(XX.XX.XX形式)", "browser_action": { "default_icon": "./icon_**.png (**はサイズ)", "default_popup": "./popup.html", ... }, "content_security_policy": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'", "background": { "scripts": [ "./jquery-min.js", "./background.js" ], "persistent": false, ... }, "options_page": "./options.html", "default_locale": "en", ... } どんなに小さなExtensionで も国際化しておく 最低限enとjaをサポートする
  • 34. { ... "version": "バージョン番号(XX.XX.XX形式)", "browser_action": { "default_icon": "./icon_**.png (**はサイズ)", "default_popup": "./popup.html", ... }, "content_security_policy": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'", "background": { "scripts": [ "./jquery-min.js", "./background.js" ], "persistent": false, ... }, "options_page": "./options.html", "default_locale": "en", ... } 利用状況を把握するために Google Analyticsを仕込んでおく Google Analyticsサーバと通信す るためにCSPに記載しておく
  • 36. My design pattern for Chrome Extension background.js
  • 38. var Background = function() { this.assignEventHandlers(); }; Background.prototype = { assignEventHandlers: function() {...}, load***: function(callbacks) {...}, get***Config: function() {...}, set***Config: function() {...} ... }; var bg = new Background();
  • 39. var Background = function() { this.assignEventHandlers(); }; Background.prototype = { assignEventHandlers: function() {...}, load***: function(callbacks) {...}, get***Config: function() {...}, set***Config: function() {...} ... }; var bg = new Background(); 各種イベントハンドラを登録する 処理を「assignEventHandlers()」 関数にまとめる
  • 40. var Background = function() { this.assignEventHandlers(); }; Background.prototype = { assignEventHandlers: function() {...}, load***: function(callbacks) {...}, get***Config: function() {...}, set***Config: function() {...} ... }; var bg = new Background(); assignEventHandlers: function() { // タブの変更監視 chrome.tab.onActivated.addListener( function(activeInfo) { ... } ); // Chromeの起動監視 chrome.runtime.onStartup.addListener( function() { // Google Analyticsに記録など _gaq.push(["_trackEvent", "..", ".."]); } ); ... },
  • 41. var Background = function() { this.assignEventHandlers(); }; Background.prototype = { assignEventHandlers: function() {...}, load***: function(callbacks) {...}, get***Config: function() {...}, set***Config: function() {...} ... }; var bg = new Background(); Ajax処理は「load***()」という関 数名にする Ajax処理をbackground.jsに集めて おく(Debugしやすくなるため)
  • 42. var Background = function() { this.assignEventHandlers(); }; Background.prototype = { assignEventHandlers: function() {...}, load***: function(callbacks) {...}, get***Config: function() {...}, set***Config: function() {...} ... }; var bg = new Background(); load***: function(callbacks) { $.ajax({ url: this.getServerUrl + "ajax/get_***" }) .done(function(data) { callbacks.onSuccess(data); }); }, getServerUrl: function() { return "http://backend.server.name/"; },
  • 43. var Background = function() { this.assignEventHandlers(); }; Background.prototype = { assignEventHandlers: function() {...}, load***: function(callbacks) {...}, get***Config: function() {...}, set***Config: function() {...} ... }; var bg = new Background();設定ページで設定されるような動 的な設定値の格納と取得処理も background.jsにまとめておく
  • 44. var Background = function() { this.assignEventHandlers(); }; Background.prototype = { assignEventHandlers: function() {...}, load***: function(callbacks) {...}, get***Config: function() {...}, set***Config: function() {...} ... }; var bg = new Background(); get***Config: function() { var value = localStorage["***"]; if (value) { return value; } else { return "初期値"; } }, set***Config: function() { localStorage["***"] = value; },
  • 46. My design pattern for Chrome Extension options.html
  • 48. <!DOCTYPE html> <html> <head> <script type="text/javascript" src="./jquery-min.js"> <script type="text/javascript" src="./options.js"> </head> <body> <script type="text/javascript" src="./analytics.js"> ... <div><span id="opt***"></span></div> <table> <tr> <td> <input type="checkbox" id="***" /> </td> <td> <span id="opt***"></span> </td> </tr> ... </table> ...
  • 49. <!DOCTYPE html> <html> <head> <script type="text/javascript" src="./jquery-min.js"> <script type="text/javascript" src="./options.js"> </head> <body> <script type="text/javascript" src="./analytics.js"> ... <div><span id="opt***"></span></div> <table> <tr> <td> <input type="checkbox" id="***" /> </td> <td> <span id="opt***"></span> </td> </tr> ... </table> ... Google Analyticsで設定ページへの アクセスを計測できるようにする
  • 50. var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-xxxxxx-xx']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = 'https://ssl.google-analytics.com/ga.js'; var s = document.getElementByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
  • 51. <!DOCTYPE html> <html> <head> <script type="text/javascript" src="./jquery-min.js"> <script type="text/javascript" src="./options.js"> </head> <body> <script type="text/javascript" src="./analytics.js"> ... <div><span id="opt***"></span></div> <table> <tr> <td> <input type="checkbox" id="***" /> </td> <td> <span id="opt***"></span> </td> </tr> ... </table> ... 表示文字列はhtmlに 一切記載しない id属性はしっかり 書いておく
  • 52. My design pattern for Chrome Extension options.js
  • 53. (function() { var Options = function() { this.assignMessages(); this.assignEventHandlers(); this.restoreConfigurations(); }; Options.prototype = { assignMessages: function() { ... }, assignEventHandlers: function() { ... }, restoreConfigurations: function() { ... }, }; window.addEventListener( "load", function(evt) { new Options(); }); })();
  • 54. (function() { var Options = function() { this.assignMessages(); this.assignEventHandlers(); this.restoreConfigurations(); }; Options.prototype = { assignMessages: function() { ... }, assignEventHandlers: function() { ... }, restoreConfigurations: function() { ... }, }; window.addEventListener( "load", function(evt) { new Options(); }); })(); onloadイベント発生に応じて初期 化処理が動くようにしておく
  • 55. (function() { var Options = function() { this.assignMessages(); this.assignEventHandlers(); this.restoreConfigurations(); }; Options.prototype = { assignMessages: function() { ... }, assignEventHandlers: function() { ... }, restoreConfigurations: function() { ... }, }; window.addEventListener( "load", function(evt) { new Options(); }); })(); 各種初期化処理を呼び出す 他のjsと関数名を揃えておく
  • 56. (function() { var Options = function() { this.assignMessages(); this.assignEventHandlers(); this.restoreConfigurations(); }); }; Options.prototype = { assignMessages: function() { ... }, assignEventHandlers: function() { ... }, restoreConfigurations: function() { ... }, }; window.addEventListener( "load", function(evt) { new Options(); }); })(); assignMessages: function() { var hash = { "opt***" : "opt***", ... }; for (var key in hash) { $("#" + key).text( chrome.i18n.getMessage(hash[key])); } }, HTMLの各プレースホルダ(id属性を振っ た要素)に対して、メッセージリソース から得た国際化文字列をセットする
  • 57. (function() { var Options = function() { this.assignMessages(); this.assignEventHandlers(); this.restoreConfigurations(); }); }; Options.prototype = { assignMessages: function() { ... }, assignEventHandlers: function() { ... }, restoreConfigurations: function() { ... }, }; window.addEventListener( "load", function(evt) { new Options(); }); })(); restoreConfigurations: function() { chrome.runtime.getBackgroundPage( function(bg) { $("#***").val(bg.get***Config()); ... } ); }; Background pageの設定値取得 関数を使って、せっせとUIに値 をセットしていく
  • 58. (function() { var Options = function() { this.assignMessages(); this.assignEventHandlers(); this.restoreConfigurations(); }); }; Options.prototype = { assignMessages: function() { ... }, assignEventHandlers: function() { ... }, restoreConfigurations: function() { ... }, }; window.addEventListener( "load", function(evt) { new Options(); }); })(); assignEventHandlers: function() { $("#***").on("click", $.proxy( function(evt) { this.onClick***(evt); }, this )); }, onClick***: function(evt) { chrome.runtime.getBackgroundPage( function(bg) { var value = $("#***").val(); bg.set***Config(value); } ); }, Background pageの設定値格納 関数を使って設定値を保存する
  • 59. (function() { var Options = function() { this.assignMessages(); this.assignEventHandlers(); this.restoreConfigurations(); }); }; Options.prototype = { assignMessages: function() { ... }, assignEventHandlers: function() { ... }, restoreConfigurations: function() { ... }, }; window.addEventListener( "load", function(evt) { new Options(); }); })(); assignEventHandlers: function() { $("#***").on("click", $.proxy( function(evt) { this.onClick***(evt); }, this )); }, onClick***: function(evt) { chrome.runtime.getBackgroundPage( function(bg) { var value = $("#***").val(); bg.set***Config(value); } ); }, イベントハンドラの名前は 「on+イベント種別+UI項目名」 とする
  • 60. My design pattern for Chrome Extension popup.html
  • 61.
  • 62. <!DOCTYPE html> <html> <head> <script type="text/javascript" src="./jquery-min.js"> <script type="text/javascript" src="./popup.js"> </head> <body> <script type="text/javascript" src="./analytics.js"> <span id="***"></span> ... options.jsとほぼ一緒
  • 63. My design pattern for Chrome Extension popup.js
  • 64. (function() { var Popup = function() { this.assignMessages(); this.assignEventHandlers(); }; Popup.prototype = { assignMessages: function() { ... }, assignEventHandlers: function() { ... }, ... }; window.addEventListener( "load", function(evt) { new Popup(); }); })(); options.jsとほぼ一緒
  • 65. (function() { var Popup = function() { this.assignMessages(); this.assignEventHandlers(); }; Popup.prototype = { assignMessages: function() { ... }, assignEventHandlers: function() { ... }, ... }; window.addEventListener( "load", function(evt) { new Popup(); }); })(); assignEventHandlers: function() { $("#***").on("click", $.proxy(function(evt) { this.onClick***(evt); }, this)); }, onClick***: function(evt) { chrome.runtime.getBackgroundPage( function(bg) { // 設定値を取得 var config =bg.get***Config(); // Ajax通信 bg.load***({ onSuccess: function(data) { ... } }); } ); }, Background pageが提 供する関数を利用
  • 66. My design pattern ロジックを整理&集中させる - Background pageをうまく使うこと 統一感を作り出す - assign***() , onClick***() , set/get***Config() 国際化を徹底させる - id属性, assignMessages()
  • 68.
  • 69.
  • 70.