プログラミング

Web Development for Beginners を読む:レッスン12, 13, 14

投稿日:2020年12月10日 更新日:

web development

1. はじめに

Web Development for Beginners の3つのレッスンをやっていきます。

3日分のレッスンを通して、ブラウザの拡張機能を作成します。

※ 本記事は、ほぼ「個人的なメモ」です。

2. Lesson 12: Browser Extension Project Part 1: All about Browsers

最初に載っている画像がよい。ブラウザでWebサイトを見る際に、内部ではどんな処理が行われているかが描かれている。これ↓

Browser sketchnote
Sketchnote by Wassim Chegham

Introduction

ブラウザの拡張機能を作る前に、ブラウザの動作について学ぶ。

About the browser

この「Browser Extension Project」ではブラウザの拡張機能を作るのだが、このパートでは、ブラウザの動作と拡張機能の準備について学ぶ。

ブラウザの動作をかなり簡単に説明している。

Browser extensions

ブラウザの拡張機能は便利だよという話。

Installing extensions

拡張機能のインストール方法は、どのブラウザもだいたい似通っている。

ブラウザによって少し異なるところはあるが、拡張機能をビルドして、ブラウザにデプロイするプロセスはおおよそ以下である。

  1. npm build コマンドを実行して、拡張機能をビルドする。
  2. ブラウザの拡張機能ページに移動する。
  3. 新規インストールの場合は、[展開して読み込み] を選択して、ビルドフォルダー(この場合は / dist)から新しい拡張機能をアップロードする。
  4. もしくは、すでにインストールされている拡張機能をリロードする場合は、[再読み込み]をクリックする。

Get Started

あなたの地域の二酸化炭素排出量を表示する拡張機能を開発する。

必要なもの

(1) APIキー:CO2 Signal にアクセスし、メールアドレスを入力して送信すると送られてくる。

(2) electricityMap | ライブ二酸化炭素排出地図 における、あなたの地域のコード

コード のリンク先に、いろいろなコードが載っているが、日本に関連する部分を以下に抜粋する。

"JP-CB":{"countryName":"Japan","zoneName":"Chūbu"},
"JP-CG":{"countryName":"Japan","zoneName":"Chūgoku"},
"JP-HKD":{"countryName":"Japan","zoneName":"Hokkaidō"},
"JP-HR":{"countryName":"Japan","zoneName":"Hokuriku"},
"JP-KN":{"countryName":"Japan","zoneName":"Kansai"},
"JP-KY":{"countryName":"Japan","zoneName":"Kyūshū"},
"JP-ON":{"countryName":"Japan","zoneName":"Okinawa"},
"JP-SK":{"countryName":"Japan","zoneName":"Shikoku"},
"JP-TH":{"countryName":"Japan","zoneName":"Tōhoku"},
"JP-TK":{"countryName":"Japan","zoneName":"Tōkyō"},

例えば、東京 (Tokyo) なら “JP-TK” がコードとなる。

(3) スターターコードmicrosoft/Web-Dev-For-Beginners を自分のGitHubアカウントでフォークし、それをローカルに git clone すれば、5-browser-extension フォルダ内に startフォルダがあるのでこれをコピーして使う。これを元にして、拡張機能を作っていく。

(4) NPM:Node.js のパッケージマネージャ。Node.js をインストールすれば入っている。

元の文書では、あまり詳しく説明されていないが、start フォルダに入っている npm のプロジェクトは、webpack を使ってビルドするようになっている。但し、webpack の設定ファイル である webpack.config.js ファイルは使用しておらず、デフォルトのフォルダ構成を利用している。

.
├─ src/ 
│   └─ index.js(入力ファイル、エントリポイント)
└─ dist/ 
     └─ main.js(出力ファイル)

dist フォルダ以下のファイル構成は以下。これが拡張機能となる。

.
├─ manifest.json
├─ index.html
├─ main.js
├─ background.js
├─ style.css
└─ images/plants-people.png

Build the HTML for the extension

dist/index.html に以下を記述する。

<form class="form-data" autocomplete="on">
	<div>
		<h2>New? Add your Information</h2>
	</div>
	<div>
		<label for="region">Region Name</label>
		<input type="text" id="region" required class="region-name" />
	</div>
	<div>
		<label for="api">Your API Key from tmrow</label>
		<input type="text" id="api" required class="api-key" />
	</div>
	<button class="search-btn">Submit</button>
</form>

その下には、以下を記述する。

<div class="result"> 
	<div class="loading">loading...</div> 
	<div class="errors"></div> 
	<div class="data"></div> 
	<div class="result-container"> 
		<p><strong>Region: </strong><span class="my-region"></span></p> 
		<p><strong>Carbon Usage: </strong><span class="carbon-usage"></span></p> 
		<p><strong>Fossil Fuel Percentage: </strong><span class="fossil-fuel"></span></p> 
	</div> 
	<button class="clear-btn">Change region</button> 
</div>

以下のコマンドを実行してビルドする。

npm install

/dist/main.js が上書きされる。

この時点で、この拡張機能をEdge に導入した。手順は以下。

(1) Edge を起動し、拡張機能の画面で「開発者モード」をオンにする。

開発者モードをオンにする
開発者モードをオンにする

(2) [展開して読み込み] をクリックして、dist フォルダーを指定する。

[展開して読み込み]

(3) 拡張機能がインストールされる。

インストールされた拡張機能
拡張機能がインストールされた

ツールバー上にこの拡張機能のアイコンが表示されるので、クリックすると小さなウィンドウが開く。そこの内容はまだ途中の状態。

3. Lesson 13: Browser Extension Project Part 2: Call an API, use Local Storage

Introduction

このレッスンでは、ブラウザー拡張機能のフォームを送信してAPIを呼び出し、結果をブラウザー拡張機能に表示します。 さらに、将来の参照と使用のために、ブラウザのローカルストレージにデータを保存する方法について学習します。

Set up the elements to manipulate in the extension

/src/index.js に以下を記述する。

// form fields
const form = document.querySelector('.form-data');
const region = document.querySelector('.region-name');
const apiKey = document.querySelector('.api-key');

// results
const errors = document.querySelector('.errors');
const loading = document.querySelector('.loading');
const results = document.querySelector('.result-container');
const usage = document.querySelector('.carbon-usage');
const fossilfuel = document.querySelector('.fossil-fuel');
const myregion = document.querySelector('.my-region');
const clearBtn = document.querySelector('.clear-btn');

CSSクラスを指定して要素を取得している。

Add listeners

フォームやボタンのためのイベントリスナーを記述する。

form.addEventListener('submit', (e) => handleSubmit(e));
clearBtn.addEventListener('click', (e) => reset(e));
init();

Build out the init() function and the reset() function

拡張機能の初期化関数を記述する。

function init() {
	//if anything is in localStorage, pick it up
	const storedApiKey = localStorage.getItem('apiKey');
	const storedRegion = localStorage.getItem('regionName');

	//set icon to be generic green
	//todo

	if (storedApiKey === null || storedRegion === null) {
		//if we don't have the keys, show the form
		form.style.display = 'block';
		results.style.display = 'none';
		loading.style.display = 'none';
		clearBtn.style.display = 'none';
		errors.textContent = '';
	} else {
        //if we have saved keys/regions in localStorage, show results when they load
        displayCarbonUsage(storedApiKey, storedRegion);
		results.style.display = 'none';
		form.style.display = 'none';
		clearBtn.style.display = 'block';
	}
};

function reset(e) {
	e.preventDefault();
	//clear local storage for region only
	localStorage.removeItem('regionName');
	init();
}

この関数でやっている処理についての説明。

LocalStorage  についての説明。

今回のような練習ではなく、実際に Webアプリケーションを開発する場合は、LocalStorage に機密情報を保存してはいけない(セキュリティ)。

元のページには書いなかったが、拡張機能が LocalStorage に保存した値を ブラウザの開発者ツールで確認するには、以下のように、目的の拡張機能の右側にある [バックグラウンド ページ] を開き、こちらのウィンドウを使う。

バックグラウンド ページ
拡張機能画面

そして [Application] パネル – [Storage] – [Local Storage] をたどっていけばよい(つまり、拡張機能には専用のスコープが使われる)。

バックグラウンド ページから開発者ツールを使う

Handle the form submission

サブミットの処理を記述する。

function handleSubmit(e) {
	e.preventDefault(); // ブラウザ規定のサブミット処理を止める
	setUpUser(apiKey.value, region.value);
}

Set up the user

Local Storage に値を保存する処理を記述する。

function setUpUser(apiKey, regionName) {
	localStorage.setItem('apiKey', apiKey);
	localStorage.setItem('regionName', regionName);
	loading.style.display = 'block';
	errors.textContent = '';
	clearBtn.style.display = 'block';
	//make initial call
	displayCarbonUsage(apiKey, regionName);
}

Display Carbon Usage(炭素使用量を表示する)

API という言葉の意味についての説明。有名な API の1つに、REST API がある。

async についての簡単な説明(非同期)。

Create a new function to query the C02Signal API:

import axios from '../node_modules/axios';

async function displayCarbonUsage(apiKey, region) {
	try {
		await axios
			.get('https://api.co2signal.com/v1/latest', {
				params: {
					countryCode: region,
				},
				headers: {
					'auth-token': apiKey,
				},
			})
			.then((response) => {
				let CO2 = Math.floor(response.data.data.carbonIntensity);

				//calculateColor(CO2);

				loading.style.display = 'none';
				form.style.display = 'none';
				myregion.textContent = region;
				usage.textContent =
					Math.round(response.data.data.carbonIntensity) + ' grams (grams C02 emitted per kilowatt hour)';
				fossilfuel.textContent =
					response.data.data.fossilFuelPercentage.toFixed(2) +
					'% (percentage of fossil fuels used to generate electricity)';
				results.style.display = 'block';
			});
	} catch (error) {
		console.log(error);
		loading.style.display = 'none';
		results.style.display = 'none';
		errors.textContent = 'Sorry, we have no data for the region you have requested.';
	}
}

ビルドする。

npm run build
  • これだと production 用にビルドされる。
  • 開発用ビルドは、「webpack -d」を実行する。

ブラウザの拡張機能画面上から、この拡張機能のところにある [再読み込み] をクリックし、再度動かしてみる。(ブラウザの画面をリロードする必要はない)

拡張機能を実行した画面
ちゃんと動いた

4. Lesson 14: Browser Extension Project Part 3: Learn about Background Tasks and Performance

Introduction

ここでは、Background Tasks と Performance について考えますよ。

Web Performance Basics

“Website performance is about two things: how fast the page loads, and how fast the code on it runs.” — Zack Grossbart

「ウェブサイトのパフォーマンスは、ページの読み込み速度とコードの実行速度の2つです。」– Zack Grossbart

サイトについてのパフォーマンスに関するデータを集める(パフォーマンスを測定する)。

Profiling checks

注意すべきポイント

Asset sizes:ここ数年でウェブは「重く」なってきており、遅くなってきた。この重みの一部は、画像の使用に関係している。

DOM traversals:ブラウザは、記述したコードに基づいてドキュメント オブジェクト モデルを構築する必要があるため、HTMLタグを最小限に抑える。

JavaScript:DOMに対する処理を行う JavaScriptファイルを読み込む場合は、defer の利用を検討する。

Create a function to calculate color

/src/index.js において、DOMにアクセスするために設定した一連の const変数の後に、 calculateColor()という関数を追加する。

function calculateColor(value) {
	let co2Scale = [0, 150, 600, 750, 800];
	let colors = ['#2AA364', '#F5EB4D', '#9E4229', '#381D02', '#381D02'];

	let closestNum = co2Scale.sort((a, b) => {
		return Math.abs(a - value) - Math.abs(b - value);
	})[0];
	console.log(value + ' is closest to ' + closestNum);
	let num = (element) => element > closestNum;
	let scaleIndex = co2Scale.findIndex(num);

	let closestColor = colors[scaleIndex];
	console.log(scaleIndex, closestColor);

	chrome.runtime.sendMessage({ action: 'updateIcon', value: { color: closestColor } });
}

前回のレッスンで実装したAPIで取得した値(炭素強度?)を渡してから、その値が色配列で表示されるインデックスにどれだけ近いかを計算する。次に、その最も近い色の値をchromeランタイムに送信する。

chrome.runtimeには、あらゆる種類のバックグラウンドタスクを処理する API があり、拡張機能はそれを活用している。

Set a default icon color

ここで、 init() 関数で、拡張機能のアイコンを一般的な緑色に設定して、chromeの `updateIcon`アクションを再度呼び出すことから始める。

chrome.runtime.sendMessage({
	action: 'updateIcon',
		value: {
			color: 'green',
		},
});

Call the function, execute the call

次に、C02SignalAPI から返される promise オブジェクトを、calculateColor() に渡す。

//let CO2...
calculateColor(CO2);

そして最後に、 /dist/background.jsで、これらのバックグラウンドアクション呼び出しのリスナーを追加する。

chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
	if (msg.action === 'updateIcon') {
		chrome.browserAction.setIcon({ imageData: drawIcon(msg.value) });
	}
});
//borrowed from energy lollipop extension, nice feature!
function drawIcon(value) {
	let canvas = document.createElement('canvas');
	let context = canvas.getContext('2d');

	context.beginPath();
	context.fillStyle = value.color;
	context.arc(100, 100, 50, 0, 2 * Math.PI);
	context.fill();

	return context.getImageData(50, 50, 100, 100);
}

次に、拡張機能を再構築し( npm run build)、拡張機能を更新して起動し、色の変化を確認する。

ブラウザのツールバー上に表示される、本拡張機能のアイコンの色が変わる。

5. おわりに

3つレッスンを通して、ブラウザの拡張機能を作成しました。

シンプルな拡張機能ではありますが、JavaScript の非同期処理, npm, webpack, 外部のAPI, 開発者ツールなどを利用しており、本当の初心者にはかなりハードルが高いのではないかと思います。ある程度のところまでは分かっている人向けなのかもしれません。

📂-プログラミング

執筆者:labo


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

Web Components

Web Components: 組み込み要素を拡張する方法

目次1. はじめに2. 組み込みHTML要素を拡張する方法3. おわりに4. 参考 1. はじめに Web Components では、HTMLElement を継承してカスタムクラスを定義することも …

Web

OpenID Connect の処理フロー

OpenID Connect の処理フローを図にしました。

Web Programming

Puppeteer を使った画像遅延読み込みテストを試してみました

目次1. はじめに2. Puppeteer とは?3. テストするテスト1(問題ないページ)テスト2(問題があるページ)テスト3(問題があるページ)4. おわりに 1. はじめに Puppeteer …

JavaScript

WebSocket を使ったサンプルプログラムを作りました

目次1. はじめに2. スクリーンショット3. 動作4. 大まかな動作イメージ 1. はじめに WebSocket を使ったサンプルページを作りました。Node.js を使っています。 ソースコードは …

React Redux

React と Redux を使ったウェブページのサンプルその1

React と Redux を使ってウェブページのサンプルを作ってみました。以下のURLからアクセスすることがでます。 デモページ 🔗 React + Redux のサンプル002 こ …