20210802のJavaScriptに関する記事は20件です。

【JavaScript】Dateオブジェクトの使い方

はじめに アウトプットが苦手な自分が練習の一環として記事を書いてみる。 第二回目です。 以前Dateオブジェクトを使った日付の加算で少し躓いたところがあったので、 それについて記事を書こうと思ったのですが、 その前にまずはDateオブジェクトの簡単な使い方について書いていきたいと思います。 現在の日時を取得する new Date()を使用することでDateオブジェクトを生成します。 引数を指定しない場合、現在の日時でオブジェクトが生成されます。 現在日時を取得 const date = new Date(); console.log(date); // 実行結果 // 2021-08-02T03:46:44.601Z 任意の日時を取得する 先ほどのnew Date()に引数を指定して生成します。 指定の仕方は以下の3種類あります。 年月日時分秒ミリ秒(年月日のみ指定など省略可) 文字列 1970年1月1日(UTC)からの経過ミリ秒数 任意の日時を取得 // 年月日時分で指定 const date1 = new Date(2021,7,1,10,00,5,300); // 文字列で指定 const date2 = new Date('2021-08-01 12:30'); // ミリ秒で指定 const date3 = new Date(1627797600000); console.log(date1); console.log(date2); console.log(date3); // 実行結果 // 2021-08-01T10:00:05.300Z // 2021-08-01T12:30:00.000Z // 2021-08-01T06:00:00.000Z ※月を指定する場合、値は0から始まるので注意が必要です。(1月を指定する場合、値は「0」となる) 日時の値ををそれぞれ取得する Dateオブジェクトにはオブジェクトが持つ日時の値をそれぞれ取得するメソッドがあります。 メソッド 返値 getFullYear() 年の値 getMonth() 月の値 getDate() 日の値 getDay() 曜日の値 getHours() 時の値 getMinutes() 分の値 getSeconds() 秒の値 getMilliseconds() ミリ秒の値 日時をそれぞれ取得 const date = new Date(2021,7,1,10,20,5,300); console.log(date.getFullYear()); // 年の値を取得 console.log(date.getMonth()); // 月の値を取得 console.log(date.getDate()); // 日の値を取得 console.log(date.getDay()); // 曜日の値を取得 console.log(date.getHours()); // 時の値を取得 console.log(date.getMinutes()); // 分の値を取得 console.log(date.getSeconds()); // 秒の値を取得 console.log(date.getMilliseconds()); // ミリ秒の値を取得 // 実行結果 // 2021 // 7 // 1 // 0 // 10 // 20 // 5 // 300 ※曜日の値も月と同様に値が0から始まります。(日曜日の場合は「0」となる) 日時の再設定 先ほどはオブジェクトの値を取得しましたが、オブジェクトの値を再設定するメソッドもそれぞれ用意されてます。 メソッド 引数 setFullYear() 「年」を表す整数の値 setMonth() 「月」を表す整数の値 setDate() 「日」を表す整数の値 setHours() 0から23までの間の整数値 setMinutes() 0から59までの間の整数値 setSeconds() 0から59までの間の整数値 setMilliseconds() 0から999までの間の整数値 日時の再設定 const date = new Date(2021,7,1,10,20,5,300); date.setFullYear(2020); date.setMonth(9); date.setDate(20); date.setHours(11); date.setMinutes(22); date.setSeconds(33); date.setMilliseconds(44); console.log(date); // 実行結果 // 2020-10-20T11:22:33.044Z まとめ Dateオブジェクトの使い方については以上です。 月と曜日の値が0から始まることに注意すれば簡単な日付の取得と設定はできると思いますので、 参考にしていただければ幸いです。 参考サイト https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

正しいuseCallback()の使い方

今回、useCallbackを理解する上で、参考にさせていただいた記事がこちらです。 Your Guide to React.useCallback() こちらを翻訳してまとめたものになります。掲載許可済みです。 Dmitri Pavlutinさん、ご協力ありがとうございます? 「Good luck in your journey to mastering Frontend development!」 と、とても優しい方で、すっかりファンになってしまった。 その前に、関数の等価性チェックを理解する。 function factory() { return (a, b) => a + b; } const sum1 = factory(); const sum2 = factory(); sum1(1, 2); // => 3 sum2(1, 2); // => 3 sum1 === sum2; // => false sum1 === sum1; // => true 例えばfactory()から生成されたsum1とsum2は異なる関数オブジェクトであることがわかる。 sum1 === sum2 // => false sum1 === sum1 // => true 全てのオブジェクトは、それ自身としか等しくない。 useCallbackの目的 const MyComponent = () => { // handleClick is re-created on each render const handleClick = () => { console.log('Clicked!'); }; // ... } このhandleClick関数は、コンポーネントが再レンダリングされるたびに再生成されます。 そのため、レンダリングごとに異なるオブジェクトになります。 インライン機能は安価な(軽い?)なので、レンダリングごとに機能を作り直すことは問題になりません。 コンポーネントごとに数個のインライン関数があれば問題ありません。 ※インライン関数とは、名前のついた無名関数のこと。たとえば以下のような関数のこと。 const handleClick = () => { console.log('Clicked!'); }; しかし、場合によってはレンダリング間で1つの関数インスタンスを維持しておく必要があります。 React.memo()でラップされた機能コンポーネントが、関数オブジェクトpropを受けとっている場合。 useEffect(..., [callback])のように、関数オブジェクトが他のフックに依存している場合。 関数が何らかの内部状態を持っているとき、例えば関数がデバウンスやスロットルされているとき。 useCallback(callbackFun, deps)が役に立つのは以上3つのとき。 同じ依存関係の値(deps)が与えられると、hookはレンダリングの間に関数インスタンスを返す。 import { useCallback } from 'react'; const MyComponent = () => { // handleClick is the same function object const handleClick = useCallback(() => { console.log('Clicked!'); }, []); // ... } handleClickは、MyComponentがレンダリングされる間、常に同じコールバック関数オブジェクトを保持するようになります。 良い使い方 例えば、とても大量のitemリストをレンダリングするコンポーネントがあったとします。 import useSearch from './fetch-items'; const MyBigList = ({ term, onItemClick }) => { const items = useSearch(term); const map = item => <div onClick={onItemClick}>{item}</div>; return <div>{items.map(map)}</div>; } export default React.memo(MyBigList); リストはとても大きく、数百のアイテムがあるかもしれません。無駄なリストの再レンダリングを防ぐために、React.memo()でラップしています。 import { useCallback } from 'react'; export default const MyParent = ({ term }) => { const onItemClick = useCallback(event => { console.log('You clicked ', event.currentTarget); }, [term]); return ( <MyBigList term={term} onItemClick={onItemClick} /> ); } onItemClickをuseCallbackにてラップしています。 useCallback(callbackFun, term)のtermが同じであれば、useCallbackは同じ関数オブジェクトを返します。 このような使い方をすれば、MyParentコンポーネントが再レンダリングされても、onItemClick関数オブジェクトは同じままで、MyBigListのメモ化が壊れることはありません。 悪い使い方 import { useCallback } from 'react'; const MyComponent = () => { // Contrived use of `useCallback()` const handleClick = useCallback(() => { // handle the click event }, []); return <MyChild onClick={handleClick} />; } const MyChild = ({ onClick }) => { return <button onClick={onClick}>I am a child</button>; } このコンポーネントでは、useCallbackを使う意味があるのでしょうか? MyChildコンポーネントは軽く、その再レンダリングはパフォーマンスの問題を引き起こさないのでほとんどの場合には意味がないと思ってよいです。 useCallbackフックは、MyComponentがレンダリングされるたびに呼びされます。 useCallbackが同じ関数オブジェクトを返したとしても、再レンダリングのたびにインライン関数が再作成されます。(useCallbackはそのインライン関数を返すことをスキップするだけです。) また、useCallbackを使うことで、コードの複雑性も増します。useCallback(..., deps)のdepsをメモライズされたコールバックの内部で使用しているものと同期させておく必要があります。 結論として、最適化は、最適化を行わないよりもコストがかかることになります。 このような場合には、新しい関数が作成されることを許容するべきです。 まとめ 最適化を行うと、複雑さが増します。 最適化をするのが早すぎると、最適化されたコードが何度も変更される可能性があるため、リスクを伴います。 useCallbackを適切に使用するタイミングは、メモライズされた重たい子コンポーネントに供給されるコールバック関数をメモ化することです。 また、パフォーマンスの向上を数値化し、そのパフォーマンスの向上は複雑さの増加と比較して、useCallbackを使う価値があるかを判断しましょう。 紹介 私が所属している「もりけん塾」は、もりけん先生が運営するJavaScriptに特化したコミュニティーです。(とは言ってもいろんな方がいらっしゃいます。) フロントエンドエンジニアになりたい方にむけて、先生が道標となって初学者が迷わないように導いてくれます。コードの書き方、自走力を身に着けるにはとても良い環境です。 毎月1日に募集をかけていたのですが、ここ最近は募集かけなくても入塾したいとDMがくるそうです...!!(大人気) 先生のブログ 先生のTwitter
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript オブジェクトの参照とコピーについて

挨拶 @hakeと申します。初投稿です! オブジェクトの参照とコピーについて勉強し直したので記事にしました! 知っている方も復習する気持ちで読んでいただけると嬉しいです! 以下のコードには問題があります! Index.tsx const [team, setTeam] = useState({ engineer: { reader: "kazu", member: ["eita", "yasu"] }, designer: { reader: "satoshi", member: ["keita", "tadashi"] }, }); // bad const setEngeneerReader = () => { // スプレッド構文でteamをコピーする const newTeam = { ...team }; // 深くネストされた値を変更する newTeam.engineer.reader = "koki"; console.log( "prevReader", team.engineer.reader, "newReader", newTeam.engineer.reader ); // →出力:prevReader koki newReader koki // この時点でコピー元であるteam.engineer.readerが変更されている // →意図していない挙動 setTeam(newTeam); }; stateを直接変更しては行けないというルールがあるにもかかわらず、変更しちゃってますね。 https://ja.reactjs.org/docs/state-and-lifecycle.html#using-state-correctly こういったミスをしないためにも参照やコピーについて理解しておきましょう!!! 参照とは 他の場所にあるデータを指している情報を含む小さなオブジェクトであり、それ自身の中に(指している)データ自体を含まない。 名前の例 名前を識別子とすると、実際の「人」が値で、値に情報がたくさん詰まっている。 →(例)「のび太」という名前はのび太くん本体(値)を参照していると言える。 のび太くん本体 ← のび太 のび太くん本体 ← のびくん のび太くん本体 ← のびちゃん といったように複数の名前(あだ名)がのび太くん本体を参照している 参照によるメリット 複数のコードが参照によってひとつのデータを共有することができ、メモリの節約になる コピーの種類 シャローコピー(浅いコピー) オブジェクトの参照をコピーする コピー元とコピー先で同じ値を参照する ディープコピー(深いコピー) オブジェクトの値をコピー コピー元とコピー先で別の値を参照する プリミティブ値のコピー Index.tsx let a = "a"; let b = "b"; a = b; b = "c"; console.log(a); // →出力:"b" console.log(b); // →出力:"c" プリミティブ値の場合は値がコピーされる →Another worksの値を変更してもbの値は変更されない オブジェクトのコピー Index.tsx let obj = {p1: "dora", p2: "nobi", p3: "shizu"} let obj2 = obj obj2.p1 = "sune" console.log(obj.p1) // →出力:"sune" console.log(obj2.p1) // →出力:"sune" オブジェクトの場合は値への参照がコピーされる →obj2.p1を変更すると元の値が変更されてしまうので注意が必要! とはいっても、コピー元を変更せずにコピー先を変更したいとき、ありますよね? コピー元を変更せずにコピー先を変更する方法 パターン1:Object.assign Index.tsx let obj = {p1: "dora", p2: "nobi", p3: "shizu"} let obj2 = Object.assign({}, obj) obj2.p1 = "sune" console.log(obj.p1); // →出力:"dora" console.log(obj2.p1); // →出力:"sune" パターン2:スプレッド演算子 Index.tsx let obj = {p1: "dora", p2: "nobi", p3: "shizu"} let obj2 = {...obj} obj2.p1 = "sune" console.log(obj.p1); // →出力:"dora" console.log(obj2.p1); // →出力:"sune" 問題点 ①②の問題点:プロパティにオブジェクトがネストされている場合、そのオブジェクトは参照がコピーされる。 Index.tsx let obj = { p1: "nobi", p2: "shizu", robot: { r1: "doraemon", r2: "dorami" } }; let obj2 = { ...obj }; obj2.robot.r1 = "roomba"; console.log(obj.robot.r1); // →出力:"roomba" // 同じ値を参照しているので元の値も変更されてしまう console.log(obj2.robot.r1); // →出力:"roomba" つまり、上記のパターンはシャローコピー(浅いコピー)!!! →深いネストのオブジェクトをコピーした場合はコビー元も変更されてしまう!! ディープコピーしたい場合 パターン1:一度json文字列に変換する Index.tsx let obj = {p1: "nobita", p2: "shizu", robot: {r1: "doraemon", r2: "dorami"}} let obj2 = Object.assign({}, JSON.parse(JSON.stringify(obj))) obj2.robot.r1 = "roomba" console.log(obj.robot.r1); // →出力:"doraemon" console.log(obj2.robot.r1); // →出力:"roomba" 以下のオブジェクトはdeepCopyできないのであまり推奨されていない ・Dateオブジェクト ・function ・undefined パターン2:lodashのcloneDeep Index.tsx let obj = { p1: "nobi", p2: "shizu", robot: { r1: "doraemon", r2: "dorami" } }; let obj2 = _.cloneDeep(obj); obj2.robot.r1 = "roomba"; console.log(obj.robot.r1); // →出力:"doraemon" console.log(obj2.robot.r1); // →出力:"roomba" 問題のコードをもう一度見てみよう! Index.tsx const [team, setTeam] = useState({ engineer: { reader: "kazu", member: ["eita", "yasu"] }, designer: { reader: "satoshi", member: ["keita", "tadashi"] }, }); // bad const setEngeneerReader = () => { // teamをディープコピーする const newTeam = _.cloneDeep(team); // 深くネストされた値を変更する newTeam.engineer.reader = "koki"; console.log( "prevReader", team.engineer.reader, "newReader", newTeam.engineer.reader ); // →出力:prevReader koki newReader koki // この時点でコピー元であるteam.engineer.readerが変更されている // →意図していない挙動 setTeam(newTeam); }; とは言っても、なるべくネストしないように定義するべきですね!!! おまけ 分割代入 Index.tsx const obj = { p: "dora", }; let { p } = obj; p = "nobi"; console.log(obj.p, p); // →出力:"dora" "nobi" const obj2 = { p1: { p2: "dora", }, }; let { p1 } = obj2; p1.p2 = "nobi"; console.log(obj2.p1.p2, p1.p2); // →出力:"nobi" "nobi" 分割代入の場合オブジェクトがネストされている場合、参照がコピーされるので注意!! 新しくインスタンス化 Index.tsx class Team { public id: number = 0; public engineer: { reader: string; front: string[]; back: string[] } = { reader: "", front: [], back: [], }; public designer: { reader: string; member: string[] } = { reader: "", member: [], }; constructor(init?: Partial<Team>) { // assign Object.assign(this, init); } } const team1 = new Team({ id: 1, engineer: { reader: "dora", front: ["atom", "haro"], back: ["r2", "c3"], }, designer: { reader: "roomba", member: ["braaba", "eufy"], }, }); const team2 = new Team(team1); team2.engineer.reader = "ashimo"; console.log(team1.engineer.reader, team2.engineer.reader); // →出力:"ashimo" "ashimo" newでのインスタンス化はシャローコピーになる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでプレビュー付きの画像アップロードを作った。

はじめに おはようございます。こんにちは。こんばんは。 Watatakuです。 今回はVue.jsでプレビュー付きのよくある画像アップローダーの作り方です。 ⬇︎画像の選択後 対象者 Vue.js初学者。 環境 node.js: v15.11.0 npm: v7.6.0 @vue/cli: v4.5.13 vue: ^2.6.11 作り方 <template> <div class="imgContent"> <div class="imagePreview"> <img :src="this.uploadedImage" width="50" height="50" alt /> </div> <div class="module--spacing--largeSmall"></div> <label for="corporation_file" class="btn btn-success"> 画像を設定する <input type="file" class="file_input" style="display:none;" id="corporation_file" mulitple="multiple" @change="onDrop" /> </label> </div> </template> <script> export default { data() { return { uploadedImage: "", }; }, methods: { onDrop(event) { //画像データ取得 const fileList = event.target.files; this.previewImage(fileList[0]); }, // アップロードした画像を表示 previewImage(file) { let reader = new FileReader(); reader.onload = (e) => { this.uploadedImage = e.target.result; }; reader.readAsDataURL(file); }, }, }; </script> <style scoped> /* 略 */ </style> 解説 画像が選択されるとonDrop()が発火し、選択されたファイルの情報を取得し、 previewImage()で画像を表示させている流れ。 最後に ぜひ、参考にして見てください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

n回ループする処理をJS/TSで書く

[...Array(n)] n に要素数を入れます。 これ(↓)で、要素数が 3 で中身が空(undefined)の配列が出来ます。 > [...Array(3)].length 3 > [...Array(3)] [ undefined, undefined, undefined ] map で使う。 > [...Array(3)].map((val, index) => index) [ 0, 1, 2 ] forEach で使う。 > [...Array(3)].forEach((val, index) => console.log(index)) 0 1 2
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Chrome92からalert,confirmの動作の仕様変更でGASのWEBアプリで困った件

0.初めに 私は、非エンジニアのドシロウトです。 私にとっては、けっこう影響大なのですが、騒いでる人が少ないので、需要ない情報かもしれません。 今朝、週に4,5回使う、自作のGoogle Apps ScriptのWEBアプリを触っていて「あれっ?」となりました。 元々、JavaScriptのAlertで「○○します」とダイアログ表示して「OK」を押すと処理されるWEBアプリでしたが、ダイアログが一切表示されませんでした。 ネットでググると、海外の人には、困ってる人もいそうでした。 google chrome - JavaScript dialogs alert(), confirm() and prompt() in cross origin iframe does not work any longer - Stack Overflow https://stackoverflow.com/questions/68492434/javascript-dialogs-alert-confirm-and-prompt-in-cross-origin-iframe-does-n 一部を機械翻訳すると以下です。 「これは、クロスオリジンiframeのalert()、confirm()、prompt()を削除するというGoogleの不条理で主観的な決定です。そして彼らはそれを「機能」と呼んだ。そして、正当化は非常に貧弱です…コミュニティと開発者は抗議する必要があります!」 ちなみにChromeサイドのこの件に関する発表は以下。 Remove alert(), confirm(), and prompt for cross origin iframes - Chrome Platform Status この記事を見てもわかる通り、Google Apps Scriptの問題ではなくChromeでJavaScriptを実行した場合に発生する事象になります。 私は、ifreme内でJSのalert,confirmを使うことを、まぁまぁしていたので困った訳です。 そこでQiitaを探すと、1件記事がありました。 安定稼働(放置)していたGASのWebアプリがある日動かないと言われた(長編ポエム) 分かりやすく書いてくれていてありがたかったのですが、confirmで処理を分けていたのをやめる、alertをコメントアウトするという、一時的な回避策の記事になっていました。 もちろんポエムなので、それだけで十分だと思います。 しかし私は、少なくともalert,confirmについては、代替策が必要だったのでいくつか試してうまく行ったことを記事に残そうと思います。 1.toast.js alertの代替として、ただメッセージをダイアログで表示すれば良い場合は、toast.jsを使いました。 Super Simple JavaScript Message Toaster - toast.js 実際の使い方はこんな感じです。 HTMLサンプル <script src="https://cdn.jsdelivr.net/gh/mlcheng/js-toast@5e5bb39b9f68938c44663dd2594eddc7434a01b7/toast.js"></script> JSサンプル var iqoptions = { settings: { duration: 2000 } }; iqwerty.toast.Toast("なにかメッセージ",iqoptions); ダイアログ表示の終わりまで処理を待たせたいニーズがなければ、これで十分です。 2.Cute Alert legant Alert/Confirm/Toast Dialog Box In JavaScript 私はconfirmの代替で使いましたが、alertでも使えます。 ダイアログ表示の終わりまで処理を待たせたいニーズがあれば、こちらでalert表示すれば良いと思います。 HTMLサンプル <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/gustavosmanc/cute-alert@master/style.css" /> <script src="https://cdn.jsdelivr.net/gh/gustavosmanc/cute-alert@master/cute-alert.js"></script> JSサンプル function delmemo(memonodel){ cuteAlert({ type: "question", title: "文書の削除", message: "メモを削除しますか", confirmText: "削除", cancelText: "やめる" }).then((e)=>{ if ( e == ("confirm")){ // 削除処理 console.log('削除が完了しました'); } else { // やめる return false; }; }) } 3.終わりに 上記1、2は、GASのWEBアプリ上のJSで動作確認しました。 おそらく普通のifreme内のJSでも動作すると思いますが、試してません。 私の勝手な想像だと、もっと全然簡単方法があるんじゃないかと… ドシロウトのいい加減な記事ではない、詳しい方のコメント、別記事をお待ちしてます。 以 上 補足 対応策が書いてあったのでリンク Chrome92からalertやconfirmが動かなくなりました officeの杜
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vueのコンポーネントを学ぶ

はじめに vue.jsをまた基礎から学びなおしているのですが、コンポーネントがなかなか理解した!と自信を持って言えないところです。 公式ドキュメントに沿って覚えたことを自分用解説つきで書いていきます。 コンポーネントとは コンポーネントシステムは Vue.js におけるもうひとつの重要な抽象概念です。「小さく、自己完結的で、(多くの場合)再利用可能なコンポーネント」を組み合わせることで、大規模アプリケーションを構築することが可能になります。アプリケーションのインターフェイスについて考えてみると、ほぼすべてのタイプのインターフェイスはコンポーネントツリーとして抽象化することができます 例えば、webページに共通のヘッダーフッターをすべてのページに出すとき、すべてのファイルにHTML分ちまちま書いたりしたくないですよね。このヘッダーフッターを再利用できるパーツ(再利用可能なコンポーネント)にしちゃおう!という感じです。 簡単なコンポーネントを作って表示してみる 文字読んでてもよくわからないので一旦作ってみます。 サイトでよく見る、会社名と選択中のタイトル(見出し)を表示するやつをコンポーネント化してみます。 ※コンポーネントの理解が主目的なので文言やデザインは超適当です。 componentsの直下にHeader.vueを作成します Header.vue <template> <div class="header"> <h1>会社名</h1> <span>見出し</span> </div> </template> <script> export default { name: 'Header', } </script> <style scoped> .header { background-color: #2fa16e; color: aliceblue; } </style> この作ったコンポーネントを使用してみます。今回はIndex.vueというファイルで使用します Index.vue <template> <div id="app"> <Header/> // 3 </div> </template> <script> import Header from './components/Header.vue' // 1 export default { name: "Index", components: { Header // 2 }, } </script> 1.Header.vueをimportします。importしたものをここではHeaderと呼ぶよ。という記述 2.Headerをコンポーネントとして扱ういますという記述 3.実際に使用 という感じです。 親:Index.vue 子:Header.vue の関係になります。 実行してIndex.vueを表示した結果 作成したHeader.vueコンポーネントが表示されました。 使用するコンポーネント名を変えたいとき 先ほどの書き方ではHeader.vueをHeaderという名前としてimportして使用しましたが、<>内での名称を変更することができます。 Index.vue <template> <div id="app"> <HeaderName/> </div> </template> <script> import Header from './components/Header.vue' export default { name: "Index", components: { "HeaderName": Header //ここを変更 }, } </script> Headerをコンポーネントとして使用するときはHeaderNameという名前とするよ、という記述です。 それ以外の関数でHeader.vue使用する場合はそのままHeaderとなります。 プロパティを受け取れるようにする【props】 先ほどの例では「会社名」「見出し」の文言が固定でしたが、文言をページごとに変えたい場合も多いと思います。 propsを使用することで親のスコープから子コンポーネントへとデータを渡せるようにできます。 試しに先ほどの例の「見出し」を変えられるようにしてみます。 子コンポーネントの書き方 Header.vue <template> <div class="header"> <h1>会社名</h1> <span>{{ title }}</span> </div> </template> <script> export default { name: 'Header', props: { title: String } } </script> ---以下略--- {{ title }}でtitleにセットされた値を受け取れるようにします。 では実際titleに何をいれるのか? 通常dataにtitleを定義しますが、今回titleに何が入るか決めるのは親でないといけません。そのため親からデータをもらえるようpropsを使用します。今回は文字列をもらうのでtitle: Stringとしています。 補足1 以下のように書いても同じ動きになります。 Header.vue <script> export default { name: 'Header', props: { title: { type: String } } } </script> 補足2 補足1のような書き方をすることで他の項目も定義できます。 Header.vue <script> export default { name: 'Header', props: { title: { type: String, default: 'デフォルト', //デフォルト値 required: true //必須 } } } </script> 親コンポーネントの書き方 Index.vue <template> <div id="app"> <HeaderName title="見出し1"/> <HeaderName title="見出し2"/> </div> </template> <script> import HeaderName from './components/Header.vue' export default { name: "Index", components: { HeaderName }, } </script> title="見出し1"と渡す値を指定することができます。 わかりやすいように2つ並べてみました。 実行結果 プロパティに変数を渡す【props + v-bind】 propsで値を受け取れるようにしましたが、固定でなく変数を渡したい場合は以下のようにv-bindを使用します。 子コンポーネントはそのままで問題ありません。親を修正します。 Index.vue <template> <div id="app"> <HeaderName title="見出し1"/> <HeaderName title="見出し2"/> <HeaderName v-bind:title="title"/> --追加 </div> </template> <script> import HeaderName from './components/Header.vue' export default { name: "Index", components: { HeaderName }, data() { return { title: "見出し3", } }, } </script> v-bind:prop名="データ名"で変数を渡せます。 省略した場合は:prop名="データ名"となります。 実行結果 v-forを使用するとき v-forでちょっと悩んだので書き方メモ。 子コンポーネントの書き方 Header.vue <template> <div> <li>{{ menu.name }}</li> </div> </template> <script> export default { name: 'Header', props: { menu: Object } } </script> 親コンポーネントの書き方 Index.vue <template> <div id="app"> <Header v-for="menu in menus" :key="menu.name" :menu="menu"/> </div> </template> <script> import Header from './components/Header.vue' export default { name: "Index", components: { Header }, data() { return { menus: [ { name: "メニュー1" }, { name: "メニュー2" }, { name: "メニュー3" }, ] } }, } </script> さいごに ドキュメントよみながら思い出しながら進めているので補足などあれば随時更新していきます。 また、なにか間違いやこうしたほうがいいよ等何かあればコメントいただけると嬉しいです。 最後まで読んでいただきありがとうございます!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フロントエンドの慣習のまとめ

1. はじめに フロントエンドを始めたばかりの頃、参考書や参考サイトで当然のように使われているものの、特に說明がないような「慣習」や「テクニック」に困惑したり、その理由が気になった経験を思い出したので、その一部を簡単にまとめてみました。 2. HTML 2-1. iタグでアイコン <i class="icon"></i>テキスト HTML の興味深いテキスト要素 (<i>)は、何らかの理由で他のテキストと区別されるテキストの範囲を表します。例えば、技術用語、外国語のフレーズ、架空の人物の思考などです。英文においてはよく斜体で表示されるものです。 <i>: 興味深いテキスト要素 - HTML: HyperText Markup Language | MDN 仕様上は適切な使用方法ではなさそうですが、「Bootstrap Icons」や「Font Awesome」などでも採用されているように、普及している印象があります。 ちなみにHTML4.01ではテキストを斜体(イタリック)にするものとして定義されていました。 2-2. ボタンのマークアップ <a href="#">こちらをクリック!</a> <button type="button">こちらをクリック!</button> そのボタンがリンク機能を持つ場合はaタグで、それ以外はbuttonタグを使用することが多いです。 inputタグにもtype="button"がありますが、デザイン調整のしづらさ(疑似要素が使えない)からあまり使われることはありません。 divやspanでも作成できますが、フォーカスが効かない、エンターキーやスペースキーでの操作ができない、スクリーンリーダーに対応していないなどのデメリットがあり、別途対応の手間がかかってしまいます。 ちなみにbuttonタグのtypeを明記しない場合はsubmitが既定値になります。 ボタンは<button>で実装しよう - Qiita 2-3. 一部分だけタグが改行されていない or タグ間の謎のコメントアウト <ul> <li>hoge</li><li>fuga</li><li>piyo</li> </ul> <ul> <li>hoge</li><!-- --><li>fuga</li><!-- --><li>piyo</li> </ul> display:inline-block;は横並び時に不要な隙間ができてしまうので、それを防止するテクニックのひとつです。 他者が作成した上記のようなHTMLをメンテする際、その意味を確認せず整形したりコメントアウトを除去してしまうと、デザイン崩れを引き起こす可能性があります。 そのため基本的にはCSSでの対応がおすすめです。 inline-blockの隙間をなくす方法 - Qiita 2.4. scriptタグをbody閉じタグ直前に設置 <body> ... <script src="hoge.js"> </body> scriptタグはhtmlのどこにでも記述可能ですが、JavaScriptの読み込み中はレンダリングがストップしてしまい表示が遅くなってしまうということと、JavaScriptでDOM操作をする場合、DOM構築が完了している必要があるため、scriptタグをbody閉じタグ直前に設置するという慣習が生まれました。 ちなみにheadタグ内にscriptタグを記述した場合でもdefer属性をつけることで上記の問題は解決できます。 <script>: スクリプト要素 - HTML: HyperText Markup Language | MDN 3. CSS 3-1. クリアフィックス .clearfix::after { display: block; clear: both; content: ""; } 最近では要素の横並びはFlexboxを使用する機会が増え、floatを使うことは少なくなりましたが、これはfloatを解除するためのテクニックです。 float適用要素もしくはそのラッパー要素のafter疑似要素で解除することで、次に続く要素の先頭での解除を不要にします。 overflow: hidden;でも解除できますが、内包要素が親要素からはみだすようなデザインの場合には不向きです。 IEが非対応ならdisplay: flow-root;という方法もあります。 Clearfix · Bootstrap v5.0 3-2. 表示上は何も変わらないtransform設定 transform: translate3d(0,0,0); transform: translateZ(0); backface-visibility: hidden; これらの設定によりCSSアニメーションにGPU処理が取り入れられるようになり、アニメーションがスムーズになるケースがあります。 また、最近では以下のようなプロパティによる設定方法もあります。 will-change - CSS: カスケーディングスタイルシート | MDN しかしこれらはいずれも乱用すべきテクニックではないため使用には注意が必要です。 CSS アニメーションについて深く知る - Qiita 3-3. ズーム率を等倍 zoom: 1; IE7までは内部にhaslayoutという値をもっており、これがデフォルトでfalseだったため、レイアウトが崩れるという事象がありました。 上記コードによりtrueへと変更することで、レイアウト崩れを直せるケースがありました。 目にすることはあっても、もう自分で書く機会はないと思います。 4. JavaScript 4-1. for文のカウンタ変数「i」 for (let i = 0; i < 9; i++) { console.log(i); } FORTRANという世界最初の高水準プログラミング言語があり、その言語では変数名は一文字だけしか使用できず、そのうち整数をあらわすものはI~Nの6個だけだったことによる慣習らしいです。 Index,Iterator,Integerなどの頭文字というわけではなかったんですね。 iのほかにもそれに続くj,kなどが使用されたりします。 なぜループカウンタ変数のほとんどに “i”が使用されるのか? - Qiita 4-2. $から始まる変数 const $hoge = $('.hoge'); 先頭に「$」を付与することで、ひと目でjQueryオブジェクトが格納されていることがわかります。 4-3. that, _this, selfという変数名 const that = this; thisの参照を保存するためによく使われる変数名ですが、Airbnbのスタイルガイドでは アロー関数かFunction#bindを利用すること が推奨されています。 4-4. アンダースコアから始まる変数・関数名 const _hoge = 'hoge'; function _doSomething() {…}; JavaScriptには仕様上、アクセス修飾子(public,private,protected,static)がないため、プライベート(ローカルスコープ内だけでの使用を想定)であることを明示したいときなどに使われることがあります。 また「_this」のように予約語を回避するために使用されることもあります。 ちなみにAirbnb JavaScript スタイルガイドでは、 JavaScriptは、プロパティやメソッドについてのプライバシーの概念を持っていません。先頭のアンダースコアは「非公開」を意味する一般的な規則ですが、実際にはこれらのプロパティは完全に公開されているため公開APIの一部です。この規約により、開発者は変更が壊れているとは見なされない、またはテストが必要ではないと誤って考える可能性があります。 として、「末尾または先頭のアンダースコアを避ける」ことが推奨されていました。 4-5. クラス名はアッパーキャメル class HogeFuga {...} const hogeFuga = new HogeFuga(); ちなみにAirbnb JavaScript スタイルガイドにも オブジェクト、関数、インスタンスにはキャメルケース(小文字から始まる)を使用すること。 クラスやコンストラクタにはパスカルケース(大文字から始まる)を使用すること。 と明記されています。 クラス名をアッパーキャメル(パスカルケース)にしておくと、インスタンス化する時にローワーキャメル(キャメルケース)にするだけでいいという利便性もあります。 4-6. 定数は大文字 const HOGE_FUGA = 42; 定数は大文字をアンダースコアでつないだアッパースネークケースで書かれることがあります。 MDNには 定数は大文字または小文字で宣言することができますが、すべて大文字で宣言するのが慣例です。 と書かれておりますが、Airbnb JavaScript スタイルガイドでは、 ファイル内の定数には大文字を使用しないでください。ただしエクスポートされた定数には使用する必要があります。 となっています。 4-7. 即時関数によるスコープ ;(function() {...})(); 即時関数(即時に実行される関数式)により、スコープを作るテクニックです。 スコープとは「変数の名前や関数などの参照できる範囲を決めるもの」です。 functionの前に「(」があるのは、これがないと関数式ではなく関数宣言だと構文解析されエラーになってしまうから(+や!などが使われているパターンも)。 先頭に「;」が付けられていることもありますが、これはファイル結合された場合、直前のファイルの最後にセミコロンが抜けていると構文エラーになってしまうことを防止するためです。 (function($){…})(jQuery);のように引数が渡されている場合もありますが、これは変数$の衝突を回避するためです。 4-8. 名前空間パターン const Hoge = window.Hoge || {}; Hoge.Fuga = function() {...}: グローバル空間に公開するオブジェクトを最小限にし、予期せぬ名前の衝突を防ぐテクニックです。 「||」は論理和演算子で、左辺と右辺のいずれかがtrueの場合trueとなり、その値を返却します。 つまり「もし変数Hogeがすでに設定されていたならそれを流用し、未使用なら新規オブジェクトを代入する」ことにより、すでにある変数の上書きを回避します。 5. その他 5-1. ほげ const ary = ['hoge','fuga','piyo']; メタ構文変数(メタこうぶんへんすう、metasyntactic variable)はプログラミング言語の記述で使われる識別子の一種。サンプルプログラムなどで意味のない名前が必要な場合に利用される、「意味のない名前」であることが広く知られた識別子のことである。 メタ構文変数 - Wikipedia https://ja.wikipedia.org/wiki/%E3%83%A1%E3%82%BF%E6%A7%8B%E6%96%87%E5%A4%89%E6%95%B0#hoge%E3%81%A8piyo 日本で主に使用されているメタ構文変数です。 由来についてはいくつか仮説があるものの、よくわかっていないようです。 使用する順番が決まっており、たとえばいきなりfugaから使用するということはありません。 ちなみに英語圏ではfoo,bar,bazなどが採用されています。 5-2. 英文のダミーテキスト「Lorem ipsum」 <p>Lorem ipsum dolor sit amet, consectetur adipisci elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </p> lorem ipsum(ロレム・イプサム、略してリプサム lipsum ともいう)とは、出版、ウェブデザイン、グラフィックデザインなどの諸分野において使用されている典型的なダミーテキスト。書籍やウェブページや広告などのデザインのプロトタイプを制作したり顧客にプレゼンテーションしたりする際に、まだ正式な文章の出来上がっていないテキスト部分の書体(フォント)、タイポグラフィ、レイアウトなどといった視覚的なデザインを調整したりわかりやすく見せるために用いられる。 lorem ipsum - Wikipedia https://ja.wikipedia.org/wiki/Lorem_ipsum もとは古代ローマの政治家・哲学者キケロの『De finibus bonorum et malorum』(善と悪の究極について)という著作から取られているそうです。 デザインよりも文章の内容に意識が集中しないようにするための工夫です。 ちなみにHTMLやCSSを省略記法で簡潔に記述するためのツール「Emmet」に対応したエディタで、「lorem」と入力し変換すると、毎回、ランダムなlorem ipsumを生成してくれるので便利です。 5-3. TODOというコメントアウト // TODO: totalはオプションパラメータとして設定されるべき。 // FIXME: グローバル変数を使用するべきではない。 アノテーションコメントといい、問題を指摘して再考を促す場合や、問題の解決策を提案する場合に使用されます。 上記以外にも種類があり、エディタによってはこれらをハイライトや一覧で見ることができます。 TODO: 以外のアノテーションコメントをまとめた - Qiita 5-4. READMEファイル名を大文字にする README.md リードミー(Readme)とは、ソフトウェアを配布する際の添付文書のひとつ。配布物の一般的な情報を記載したファイルである。多くの場合、そのソフトウェアをインストールし使用する前に読むべきものとされている。 リードミー - Wikipedia https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%BC%E3%83%89%E3%83%9F%E3%83%BC なぜ大文字なのかということについては、「目立つから」「並び順が小文字よりも先になるから」といった理由が散見されました。 6. おわりに まだまだ挙げればキリがありませんが、あらためて感じたことは、たいていの慣習にはなんらかの理由や目的があるということでした。 このような慣習に対する深い理解は、応用や発展に向けて有用だと思いました。 7. 参考サイト Bootstrap · 世界で最も人気のあるフロントエンドフレームワーク 開発者向けのウェブ技術 | MDN Airbnb JavaScript スタイルガイド() { | javascript-style-guide javascriptイディオム集 JavaScript イディオム集
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

kintone Tips まとめ(私が書いたもののまとめ)

今までカキカキしてきたkintone関連記事がたまってきたので、まとめてみました。 (2021/08/03の時点で93件) 記事が増えるたびにコチラに追加していこうと思います。 ?標準機能 ?関数 ?実務で使えそうな関数Tips IF関数を試してみよう(IF関数) IF関数で場合分けしよう(IF関数) チェックボックスで特定の値だけが選択されているとき~をベン図とIF関数とCONTAINS関数を使ってやってみる(IF関数、CONTAINS関数) 標準機能で「年度」を計算する 標準機能で、休憩時間を除いた労働時間を計算する ?数学の計算をしてみたもの 標準機能だけで平方根の計算をしてみる 標準機能だけで2次方程式を解くアプリ 標準機能でsinX、cosX、tanXを求める ?通知・アクセス権 レコード追加で「通知」を送りたいとき アプリのレコードにコメントを書いたときの通知 スレッドに書き込んだときの通知 アクセス権を設定して表示する一覧を制限する ?小技 日付でルックアップしたいときの小技 ?表とグラフ 表やグラフを使いこなすための第1歩 日付や時刻でデータを仲間分けして表やグラフにする(分類する項目の〇〇単位) 表とグラフの「集計方法」の設定 人毎、時系列の棒グラフを描く 人毎、時系列の折れ線グラフを描く クロス集計表を作ってみる(その1) クロス集計表を作ってみる(その2) 定期レポートを使ってみる 目標と実績の比較棒グラフを描いてみよう 数学で使うグラフを試してみた いろいろなグラフ(集計方法が1つの場合) 積み上げと100%積み上げのグラフ グラフの小技 チェックボックスを使ったグラフ グラフ機能でサブテーブルのフィールドを使う ?‍?JavaScriptカスタマイズ kintone カスタマイズチュートリアルを一通り済ませた方を対象に書いております? まだ終わってないよという方は先にチュートリアルを一通りやってみてね? →kintoneカスタマイズ チュートリアル ?kintone JavaScript API の基本 アプリの「スペース」フィールドにボタンを設置する ボタンをクリックしてフィールドの値を書き換えてみよう! ルックアップを便利にするキーを作るカスタマイズ テーブルの値を変更してみる(超初心者向け) 30分前リマインダー条件通知ができるようにするカスタマイズ フィールド値更新でハマりがちなこと ?テーブル(サブテーブル)の操作 テーブルの中で入力禁止にしたりフィールドにエラー出したりの小技 for-ofやforEachでテーブルの値を覗いてみる テーブルの行を並び替える(ソートする) サブテーブルの行追加時に上の行をコピーする 詳細画面でサブテーブルの値を変える 詳細画面のサブテーブルのDOM操作※Zennの記事 チェックボックスで選択したものをサブテーブルにセットする サブテーブルの行を入れ替える技 ?kintone REST API ?非同期処理 async/await を使って他のアプリからデータを取得する kintone.Promise.all で他アプリからデータを取得して計算しよう async/await で順番通りにデータを取得する ?レコードの登録・更新 kintone REST API でレコードをたくさん(100件以上)登録する方法 UPSERTしてみる ?グラフAPI グラフAPIを試してみた(GET) グラフAPIでGETしたグラフの情報をアプリに表示する グラフ設定API(PUT)でグラフの更新をする グラフ設定APIで分類する項目を変更する ?Googleフォーム Googleフォームからレコードを登録する Googleフォームからレコードを編集(更新)する ?M5StickC-Plus M5StickC-Plus物理的にボタンぽちっとしてレコードを登録してみる M5StickC-Plus×UIFlowでHttpRequestが飛ばなかったのを解決した話 ?その他 ログインボーナスみたいなものを実装する JavaScriptでレコード一覧をCSVファイルで出力してみよう 私っぽいつぶやきをつくるアプリ 英語勉強ツールを作る Party Parrot を飾る ?kintone REST API Client ?kintone REST API Client サンプル kintone REST API Client でレコード1件操作する方法 kintone REST API Client でレコード複数件を操作する方法 kintone REST API Client でレコード全件処理する方法 フィールドコードやフィールド名を書き換えるカスタマイズ ?ファイル操作 テンプレートのExcelに書き込んでダウンロードする 添付Excelファイルを書き換えてアップロードする ?テーブルから別アプリにレコードを登録するシリーズ テーブルのレコードを別アプリのレコードに登録する テーブルでアプリのレコードの登録・更新・削除をする(1) テーブルでアプリのレコードの登録・更新・削除をする(2) テーブルでアプリのレコードの登録・更新・削除をする(3) テーブルでアプリのレコードの登録・更新・削除をする(4) ?kintone UI Component kintone UI Componentのドロップダウンの文字色を変える kintone UI Component v1でドロップダウンを作る kintone UI Component v1で連携するドロップダウンを作る 標準機能のドロップダウンとkintone UI Component v1のドロップダウンを連携させる ルックアップとkintone UI Component v1 のドロップダウンを連携させる kintone UI Component v1 のチェックボックス kintone UI Component v1 でラジオボタンを設置する ?kintone Portal Designer Kintone Portal Designer と kintone UI Component v1でポータルにボタンを設置する Kintone Portal Designer でサンプルテンプレートを編集してオリジナルポータルを作成する コピペでOK!Kintone Portal Designer のサンプルデザインの使い方 ポータルのファビコンを無理やり変えるカスタマイズ ?画像 新規登録画面で落書きした画像を添付ファイルに保存する お絵描きして添付ファイルに保存する お絵描きして添付ファイルに保存する(改) kintone上で座席表を作る ?音楽・音声 添付の音楽ファイルに音楽プレイヤーつける ボタンクリックで音を鳴らしてみる ?その他 レコード詳細画面にQRコードを表示する Vue.jsを使ってみる 表示上の値を変える方法 クリスマスわくわくアプリを作ろう Advent Calendarで作った最強のアプリ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コードネームのテキストを移調するアプリを作った

コードネームのテキストを、簡単に移調するウェブアプリを作りました。 機能 入力したコード進行を、任意のキーやディグリーネームへ一瞬で書き換えられます。 想定している用途 ・コード進行メモを別のキーへ変更する →オリジナル曲などのキーを変更したくなったとき、いちいち書き直す必要はありません。 テキストを貼り付けてキーを設定するだけでOKです。 ・コード進行のアイデアメモをディグリーネームへ変換する →良さそうなコード進行をディグリーネームでメモしたいときありますよね。 変換後のキーを「ディグリーネーム表記」にすれば簡単に変換できます。 製作の背景 以前作った「読みやすさチェッカー」↓の仕組みを応用すればできそう…と思ってやってみたらできました。 大雑把な仕組みは、コードネームの音名部分をJavaScriptのreplaceメソッドで置換しています。 (だから、英文を入力するのはオススメしません笑)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コードネームを移調するアプリを作ったら便利すぎたwwwwwwwwwww

前に作った「読みやすさチェッカー」を応用して コードネームのテキストを、簡単に移調するウェブアプリを作りました! 機能 入力したコード進行を、任意のキーやディグリーネームへ一瞬で書き換えられます。 想定している用途 ・コード進行メモを別のキーへ変更する →オリジナル曲などのキーを変更したくなったとき、いちいち書き直す必要はありません。 テキストを貼り付けてキーを設定するだけでOKです。 ・コード進行のアイデアメモをディグリーネームへ変換する →良さそうなコード進行をディグリーネームでメモしたいときありますよね。 変換後のキーを「ディグリーネーム表記」にすれば簡単に変換できます。 便利過ぎて、草。 今までの人生で、コード進行のメモを別のキーへちまちま書き換える作業を何度やったか分かりません。 でも、もうやる必要は無くなりました。これを使えば一瞬で終わるので!! ぜひ使ってみてください!(๑˃̵ᴗ˂̵)و
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Salesforce】VisualforceページでLightning ExperienceとSalesforce Classicの判別をする

VisualforceページでLightning ExperienceとSalesforce Classicの判別をする Visualforceページを作っていて、Lightning ExperienceとSalesforce Classicでコードの処理内容を分岐させたいと思った事があるのではないだろうか。今回はJavaScriptを用いてユーザーがどちらのUIを使用しているのかを判別する方法を記述する。 IsLightningOrSalesforceClassic.vfp <apex:page > <apex:includeScript value="https://code.jquery.com/jquery-3.2.1.min.js"/> <script type="text/javascript" import="true"> function IsLightningOrSalesforceClassic() { return((typeof sforce != 'undefined') && sforce && (!!sforce.one)); } function checkExperience(){ if(IsLightningOrSalesforceClassic()) { alert('LightningExperienceです'); } else { alert('ClassicExperienceです'); } } $(function(){ checkExperience(); }); </script> </apex:page> 説明 内容としては簡単である。"sforceオブジェクトが定義されているか"そして定義されている場合はoneプロパティが有効化されているかどうかを確認している。 sforceオブジェクトが定義されていない場合はClassic UIで、かつsforceオブジェクトが定義されつつも.oneプロパティが有効になっている場合はSalesforceアプリ(Salesforce1アプリ)であると判断出来る。 Salesforceオブジェクトが定義されており、かつ.oneプロパティが無効になっている場合はLightning Experienceであると判断できるという仕組みである。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptを基本からまとめてみた【8】【addEventListener関数】【随時更新】

はじめに 学習するに至った経緯 2020年より、未経験からエンジニアへの転職を目指し、某プログラミングスクールへ通う。 入学後、『Ruby』を未経験から学ぶ人が多いのと『Ruby』の求人が思っていた以上に少ないので、 卒業後、フロントエンドのエンジニアを目指す事に。 Javascriptの学習した事を言語化し、認識の深化による備忘録として記載。 addEventListener関数を使い方 削除ボタンを押したときに何かの処理を実行したいときは addEventListener関数を使う。 第一引数にはイベント(クリック,スクロールなど)を第二引数には実行したい関数を渡す。 削除ボタンが押されたときにタスクを削除したいので 『deleteButton』がクリックされたときにdeleteTaskを呼び出して欲しいとする。 関数の定義方法はいくつか種類があるが、一番良く使用されているアロー関数を使用。 deleteButton.addEventListener('click', () => deleteTask(taskId)) 次にdeleteTask関数を作成。 deleteTaskの引数には削除したいタスクのIDを渡す。 remove関数を呼び出してタスク(テーブルの一行の要素)を削除する。 const deleteTask = (taskId) => { const task = document.getElementById(taskId) task.remove() }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascriptの豆知識(...について)

背景 最近のJavascriptのコードを見たら、よく...を見ます。私は2014以降では業務上でjavascriptを触っていないため、最初...を見る時に、不思議でした。 役割 残余引数(rest parameters) ES6以降では、...を残余引数として使えるようになりました。 const test = (a,...args) => { console.log(a) console.log(args) } test("a",1,2,3,4,5) a <- 1番目の引数です [ 1, 2, 3, 4, 5 ] <- ...argsです、一つの配列にまとめています。 ...argsは残余引数になります。 注意点としては、 ...argsを引数の最後に位置付ける必要があります ...argsにデフォルト値を与えることはできないです ...argsの可能な値は配列です、undefinedまた単一な値になることはないです 参考リンク https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/rest_parameters スプレッド構文 残余引数はたくさんの引数を一つの配列にまとめることです スプレッド構文は逆の役割をもっています。一つの配列を一つずつの関数の引数や要素に展開します。 const arr = [1,2,3] console.log(arr) console.log(...arr) console.log(["a",...arr]) [ 1, 2, 3 ] <- 元な配列 1 2 3 <- 展開した配列 [ 'a', 1, 2, 3 ] <- 展開した配列 参考リンク https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascriptの豆知識(let,var,クロージャーに関する面接問題)

背景 皆さんはjavascriptのletとvarのスコープ(変数の有効範囲)の違いがご存知だと思います。その違いを表せる一つの例を紹介します。この例はよく面接にも出ます。 varを使う場合1 コード const main = () => { const funcs = [] for (var i=0;i<5;i++) { <--ここのiをvarで定義しています funcs.push(()=>{ console.log(i) }) } for (let func of funcs){ func() } } main() 結果 5 5 5 5 5 letを使う場合 コード const main = () => { const funcs = [] for (let i=0;i<5;i++) {  <--ここのiをletで定義しています funcs.push(()=>{ console.log(i) }) } for (let func of funcs){ func() } } main() 0 1 2 3 4 解説 case1とcase2の違いは一箇所だけです。それはforの中,iを宣伝する方法は違います。 case1はvarを使っています。 case2はletを使っています。 そして出力はcase2、for分にletを使っているコードは予想通りに動いていました。 ここで三つの知識点を説明いたします。 1. varを使うと、スコープはfor文ではなく、for分の上のmain関数になります 2. letを使うと、スコープはfor分になり、毎回ループするときに、新しいスコープを作っています 3. クロージャーは直上のスコープにアクセスしています varを使うと、for分ごとにクロージャーを作っていますが、クロージャーからアクセスできる直上のスコープはmain関数のスコープです。main関数スコープのiはfor分のループと共に変化され、最終形態はi=5になります。closures配列にある五つのクロージャーから出力しようとしたiもmain関数スコープにあるiになり、すべてのクロージャーは同じiをアクセスし、すべてのクロージャーは同じ値を出力しています。 letを使うと、新しいスコープもループと共に作成されています。毎回のループにあるiは新しく作成されたスコープにあります。それぞれのクロージャーはアクセスできるスコープは直上のスコープはmain関数のスコープではなく、新しい作成されたfor分のスコープです。closures配列にある五つのクロージャーからアクセスできるスコープも五つにあり、それぞれ独立されています。それぞれ独立されてあるスコープにあるiはそれぞれ異なるため、当然クロージャーからの出力はそれぞれ異なります。 感想 どんな言語も深く考えれば、楽しみを味わえます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

共通テスト「情報」サンプル問題のプログラミング言語を作ってみた

共通テスト「情報」試作問題のプログラミング言語を作ってみた の続編です。共通テスト(旧センター試験)に追加が決まった「情報」の問題で使われている擬似プログラミング言語を実際に実行できる環境を作成しました。言語処理系をイチから作るのではなく JavaScript に変換して実行させることで実装を簡単化しています。実際に動くものは ↓ からどうぞ。 DNCL2- 大学入試共通テスト「情報」問題言語 はじめに 共通テスト「情報」試作問題に続いて、サンプル問題が公開されました。サンプル問題でも、試作問題とほぼ同様の擬似言語が使われています。(どのような擬似言語なのかはサンプルプログラム集のページや、本記事末尾の現在判明している仕様を御覧ください。) これに基づき仕様の変更?に対応したほか、その他いろいろと変更しました。 サンプル問題に基づく変更点 for 相当の繰り返し構文の微変更 試作問題では i を 1 から 100 まで 1 ずつ増やしながら: だったのが、i を 1 から 100 まで 1 ずつ増やしながら繰り返す: と 繰り返す が入りました。以前の構文でも新しい構文でも動くようにしています。 while 相当の繰り返し構文が追加 一般的なプログラミング言語における while に相当する構文として、tosenkei < giseki の間繰り返す: が追加されました。 組み込み関数「切り捨て()」の追加 組み込み関数「表示する()」の可変長引数化 試作問題では 表示する() は 1 引数しかとりませんでしたが、サンプル問題では可変個の引数をとっていました。表示する(1, 2, 3) などと書いた場合、出力は 123\n と最後のみに改行が入る仕様のようです。 配列の自動初期化 今回のサンプル問題にも違和感のある部分がありました。それが以下。 (07) ⎿ Hikaku[m] = Tokuhyo[m] …この行だけ見れば違和感は無いでしょう。ただ問題は、配列 Hikaku はこの行で初登場しており、一切初期化処理が無いという点です。確かに Perl では初期化処理なしにいきなり $Hikaku[0] = 1; と書くことができますが1、JavaScript ではそうした書き方はできません。 よく見ると試作問題でもサンプル問題でも配列の変数名は大文字で始まっているので、「大文字で始まる変数名は配列であり、必要であれば初回アクセス時に自動初期化される。」という言語仕様なのかもしれません。ただ、「JavaScript に変換して実行する」という作成方針の都合上そうした言語仕様を採用してしまうと辛いので、「実行前に配列っぽい変数名を調べ、あらかじめ初期化しておく」という雑な対応をしました。 論理演算子「and」「or」「not」 今回のサンプル問題によると、この言語では論理演算子としてand, or, not を使用するようです。Ruby であればそのままでも大丈夫ですが、JavaScript にはand, or, notは無いので&&, ||, !に変換が必要です。 それ以外の変更点 「配列のすべての要素に代入」を雑な対応に変更 試作問題には配列 Hindo のすべての要素に 0 を代入するという要素数が不定の配列に初期値を設定する謎構文があり、当初はこれに対応するため、上の例であればプログラム中のすべての Hindo[ なんとか ] を Hindo[ なんとか ] == undefined ? Hindo.default : Hindo[ なんとか ] に置き換える、という方法をとりました。 ですが対応方法が複雑で悪影響が起きそうな気がすることと、今回のサンプル問題にはこの構文がなかったことから、「要素数を指定して初期化することもできるのだが、要素数を省略した場合はデフォルトの要素数(試作問題の都合上 26 )で初期化する。」という雑な対応方法に変更しました。 else if 相当の構文を追加 試作・サンプル問題には無いのですが、else if に相当する構文がないと FizzBuzz を書くのが面倒なので、勝手に あるいは ○○ ならば: という構文を追加しました。 エラーを表示するようにした 試作・サンプル問題等を実行するだけであればエラーは発生しないので以前はエラー処理を一切していませんでしたが、自分で新たにプログラムを書く場合のことも考え、エラー発生時にはエラー内容を出力するようにしました。 ですが、どの行でエラーが発生したのかまでは表示しない2のでデバッグは困難ですね…変換後の JavaScript を表示する機能を付けたので、JavaScript が分かる人はそっちでエラー箇所を探すと良いかもしれません。 フォーマッターを追加 近年のプログラミング言語では公式なフォーマッターが存在することが多いので、せっかくだから付けてみました。とはいえ行頭の行番号と|を整える程度ですが。 条件分岐・反復構文の挿入ボタンを追加 i を 1 から 100 まで 1 ずつ増やしながら繰り返す: みたいな構文を間違えることなく入力するのは地味に面倒なので、挿入ボタンを付けました。 試作問題・サンプル問題を別ページに移行 MIT License で公開していたのですが、その中に私が作ったわけではない試作・サンプル問題が含まれているのはマズイのでは?と気付き、それは別のリポジトリに移しました。少し手間が増えてしまいますが、試作問題・サンプル問題を実行してみたい場合はそちらからコピペしてください。 おまけ:現在判明している仕様 基本的な言語仕様 動的型の手続き型言語と思われます。 「関数」という存在はあるものの、自分で定義できるのかは不明です。 オブジェクト指向的な要素は今のところ見当たりません。(例えば Ruby や JavaScript であれば配列の要素数は array.length で調べられますが、この言語では 要素数(Array) と関数を使って調べる必要があります。) 改行は基本的に文の区切りとして認識されるらしく、; 等の区切り文字は行末に不要です。 制御構文は、Python のように末尾に : が付きます。 コードブロックは、行頭の |, ⎿ で表します。 行番号を無視して、|, ⎿ を空白で置き換えてしまえば、まさに Python の構文ですね。このように、全体的には Python が一番近い印象を受けます。 演算子 算術演算子: +, -, /, % 乗算の演算子は不明ですが、普通に考えれば * でしょう。 除算は、3480/6 が 580、1200/580 が 2.068966 になることから、JavaScript のように「整数型」を持たず「浮動小数点型」のみで整数も扱う言語仕様ではないかと思われます。 代入演算子: = sousuu = sousuu + Tokuhyo[m] という式があるのを見ると、 += 等の自己代入演算子は無いのかもしれません。 比較演算子: !=, <, >=, <= 等号が一般的な == なのか、それとも VBA 等で使われている = なのか不明ですが、等号否定が一般的な != (VBA だと <>)であることから == ではないかと思われます。 ところで「公平性を鑑みて独自の日本語表記の疑似言語とした」そうですが、VBA を使って学習した生徒からすると != が説明なしに使われているので少し不公平な気もします。 論理演算子: and, or, not Perl 書きではないので分かりませんが、多分 Perl でも推奨される書き方ではないでしょう。 ↩ どの行でエラーが発生したのかを取得する方法は無いんでしょうか…御存知の方がいましたら教えて下さい。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JS】ディスクリプタを最低限の理解まで持っていく

はじめに まず、ディスクリプタって聞いたことありますか?? 自分は初めて聞く単語で今も戸惑っています。。w 用語の理解と実際の使い方などを最低限のレベルまで持って行けたら良いなと思います! ディスクリプタとは オブジェクトプロパティには、キーバリューストア(key-value)のイメージがあると思いますが、もっと柔軟で強力なものです。ディスクリプタとは、プロパティを細かく制御することが可能で、その制御モードを指示するオブジェクトのことです。 Object.defineProperty(オブジェクト, "属性", { ディスクリプタ }のように設定できる。 // オブジェクトを生成 let obj = {} // 生成したオブジェクトにnameプロパティを設定する Object.defineProperty(obj, "name", { value: 'tanaka', // 実際に持つ値 writable: false // 値の変更可能性 enumerable: false, // 列挙可能性 configurable: false, // 設定変更可能性 } // 以下のようにすることで全てtrueで設定されます let user = { name: "tanaka" }; ゲッターとセッター 値を取得するときや、変更するときの処理をカスタマイズしたい時は、ゲッター(getter)とセッター(setter)を設定します。 class Person { constructor(name, age) { this._name = name; this._age = age; } // name属性のgetterを設定 get name() { return this._name; } // name属性のsetterを設定 set name(val) { this._name = val; } } 使い所は? 値の取得や更新の際に、任意の処理を実行したいとき 更新したと同時に他の値も同時に更新したいときなど 静的メソッドの設定 クラス内で使用する静的メソッドを使用する際には、staticを使用します。これは、インスタンス化せずに使用する事が出来ます。 ※thisは使用できません class Person { constructor(name, age) { this._name = name; this._age = age; } . . . // 静的メソッドhelloを定義 static hello() { console.log('hello') } } Person.hello() // => 'hello' 終わりに 少しだけでもお力添えできたら幸いです。。 参考 JavaScriptのディスクリプタを使い、堅牢なオブジェクトやObservableなオブジェクトを作る JavaScriptのGetter Setterを理解する(入門者向け) プロパティフラグとディスクリプタ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Reactやるなら知っておきたい】関数コンポーネントとクラスコンポーネントの違い

はじめに 僕自身、現在React勉強中なのですが、 クラスコンポーネントと関数コンポーネントの二種類あることを理解していなかったため、 備忘録として残しておきます。 なにから勉強したらいいかわからない人はサラッと読んでみてください! Reactとは facebook社が開発したJSのライブラリ 用途としてはUIのコンポーネントを作成するのに使われる 【本題】関数コンポーネントとクラスコンポーネントの違い 前提 ★現在は主に関数コンポーネントを使用する 〈理由〉 以前は下記の機能がクラスコンポーネントでしか使用出来なかった  ■State(状態管理)  ■Lifecycle Hooks   - Lifecycle とは、Mounting、Updating、Unmountingの一連の流れの事 ・Mounting(マウント):コンポーネントをレンダーする ・Updating(更新):コンポーネントのStateを更新する ・Unmounting(アンマウント):コンポーネントのレンダーが切れる ▶現在はHooks(React 16.8 で追加された新機能)により、どちらも使えるようになった。 違いについて 【関数コンポーネント】 シンプルで、状態(state)を持たない。 渡された値に従って特定の固定要素を描画するだけ。 要はただの「関数」である。 【クラスコンポーネント】 classを用いてコンポーネントを定義する。 クラスコンポーネントは状態(state)を持つことができ、内容を柔軟に変化させることが可能 また、Reactに関するメソッドをクラスに実装することで、より便利な制御が可能となる。 関数コンポーネント使うメリットなくない????? ★そこで考案されたのがHooksである。 状態管理などが関数コンポーネントでも出来るようになった! よってクラスコンポーネントと同等の機能を実現可能になった。 最低限、【useState】 【useEffect】 【useContext】 は習得しよう! 例えばuseStateというフックは関数コンポーネントに状態を持たせることができる。 (※HooksはすべてuseXXXという命名規則) 例 // お約束。フックを使うならそのフックを必ずimportする import React, { useState } from 'react' // set〇〇とキャメルケースで書こう(ちなみにset〇〇は関数です) // useState(0)の(0)は初期値です const App = () => { const [count, setCount] = useState(0) // {count}には、stateで保存されているcountの値が表示されます // {count}の値が変わるたびにレンダリングされます // setCountを使わないと、countの値を変更することはできません // また、setCountの引数に関数を渡すこともできます return ( <> <p>現在、{count} 回押しました</p>             // このボタンがクリックされると、countの値が+1されて、{count}の表示が変わります <button onClick={() => setCount(count + 1)}> 押すとイチ増えるよ </button> </> ) } export default App 同等の機能ならどっちでもよくない????? ★関数コンポーネントの方が、、、 1. thisやbindを多用しなくて良い 2. コードが短くシンプル 3. ロジックを共通化出来る 最後に 今からReactを勉強するなら迷わず関数コンポーネントを使用して勉強しよう。 さらに、Hooksはカスタムフックといってロジックを再利用出来るようになっているのです。 下記のようなカスタムフックを提供するライブラリも出ているので調べてみては。 ・react-use ・react-table ・Formik 引き続き、React関連の記事を書いていきますので宜しくお願いいたします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クロスブラウザなブラウザ拡張を開発・審査・公開するまで(Chrome拡張/Firefoxアドオン/Opera/Edge)

ブラウザ拡張のクロスブラウザ対応には結構手間がかかります。 特にブラウザごとの違いにはなかなか悩まされます。 デバッグ方法 の相違 パッケージ作成方法 の相違 審査方法・基準 の相違 対応する過程で手間取ったり足踏みしたりすることも多いわけですが、「開発から公開まで」について良くまとまっている記事も見当たりません。 そこで本記事は、ブラウザ拡張の開発から公開までのコストを削減できるよう、以下の2点を説明していきます。 開発の工程を減らす クロスブラウザ拡張用パッケージによる開発・デバッグ・パッケージ化 申請・公開をスムーズに 各ベンダーの審査の共通点・相違点をまとめ、準備の負担を軽減 いつか役に立てていただければ 対象の読者 これからChrome拡張などを作りたい方 拡張のクロスブラウザ対応をしたい方 ただし Chrome, Firefox, Opera, Edge に限ります 各ベンダーの審査の違いについて知りたい方 必要な知識 ターミナルコマンドが打てる npmなどが実行できる 拡張に必要なJavaScript, CSSなどが書ける 作るもの 今回は簡単な例として「Qiita記事のヘッダーを色分けする拡張」を作ります。 Qiita記事の各ヘッダーがこのように表示されるようにします。 (自分がこの機能を欲しかっただけです) 実際に公開したものはこちらです。 Chrome: Qiita Rainbow Header - Chrome ウェブストア Firefox: Qiita Rainbow Header – ? Firefox (ja) 向け拡張機能を入手 Opera: Qiita Rainbow Header extension - Opera add-ons Edge: Qiita Rainbow Header – Microsoft Edge Addons 目次 大きく3つのセクションにわけて説明していきます。 拡張機能の開発 審査の準備 審査・公開 1. 拡張機能の開発 Node.js が必要となりますので、もし入っていない方は こちら からインストールお願いします。 クロスブラウザ対応のため webextension-toolbox というパッケージを使っていきます。 ブラウザごとの違いなどを気にせずに、簡単に各ブラウザ用のパッケージを出力できる神ツールです。 サンプルコード 記事で使っているコードの最終形はこちらに公開しています。 https://github.com/yamadashy-sandbox/webextension-toolbox-example また、今回は記事用に極力シンプルにしたものを使っていきますが、実際に使用しているコードはこちらになります。 webpack部分のカスタムに加え、TypeScriptやESLintなどにも対応しています。 https://github.com/yamadashy/qiita-rainbow-header 雛形からコード生成 さっそく開発していきます。 1. 必要なパッケージをインストール yo 雛形作成パッケージ generator-web-extension webextension-toolbox の雛形作成パッケージ $ npm install -g yo generator-web-extension 2. 開発用のフォルダを作成、移動 $ mkdir my-web-extension && cd $_ 3. コード生成 $ yo web-extension 以下の選択肢以外はそのままEnterで大丈夫です Would you like to use UI Action? Content ScriptsだけSpaceで選択しEnter Would you like to install promo images for the Chrome Web Store? yをおしてEnter 拡張機能の実装 いくつかのファイルを書き換えていきます。 1. app/scripts/contentscript.js を書き換え 今回はJavaScriptは必要ないので中身を消してください。 ファイルを消しても大丈夫です。 2. app/styles/contentscript.css を書き換え .allWrapper .it-MdContent h1 { color: #c35c5c; border-bottom-color: #c35c5c; } .allWrapper .it-MdContent h2 { color: #bf905d; border-bottom-color: #bf905d; } .allWrapper .it-MdContent h3 { color: #9b9b4c; } .allWrapper .it-MdContent h4 { color: #4a944a; } .allWrapper .it-MdContent h5 { color: #468a8a; } .allWrapper .it-MdContent h6 { color: #6464a5; } 3. app/_locals/en/messages.json の書き換え en フォルダの名前を ja にして、 message.json を書き換えてください。 (json内のコメントは消してください) { // 拡張機能の表示名 "appName": { "message": "Qiita Rainbow Header" }, // URLなどに使う短縮名 "appShortName": { "message": "qiita-rainbow-header" }, // 拡張の説明 "appDescription": { "message": "Qiita記事のヘッダーの色を段階によって変えます" } } 4. app/manifest.json を書き換え { "manifest_version": 2, // messages.json から自動で取得されます "name": "__MSG_appName__", "short_name": "__MSG_appShortName__", "description": "__MSG_appDescription__", // 自由な値で大丈夫です "version": "0.1.0", "default_locale": "ja", "author": "作者名", // 拡張のアイコン "icons": { "16": "images/icon-16.png", "128": "images/icon-128.png" }, "content_scripts": [ { // 拡張を有効化するURLにマッチする文字列 "matches": [ "https://qiita.com/*" ], "js": [ "scripts/contentscript.js" ], "css": [ "styles/contentscript.css" ], // 実行タイミング。document_startはDOMの読み込み中 "run_at": "document_start" } ] } manifest.json についてはここが参考になるかと思います。 また、run_at についてはこちらを参考に。 これで機能の実装は終わりです。 アイコンの変更などは後ほど説明します。 各ブラウザでデバッグ ブラウザ共通 以下のコマンドで開発用にビルドでき、dist/<ブラウザ名> のフォルダに出力されます。 $ npm run dev <ブラウザ名> # 以下のどれか $ npm run dev chrome $ npm run dev firefox $ npm run dev opera $ npm run dev edge また、この開発用ビルドを終了させない限りは、ファイル変更を検知して自動で再ビルドしてくれます。 Chrome拡張のデバッグ URL欄に chrome://extensions でEnter 右上の「デベロッパーモード」をON dist/chrome フォルダをドラッグ&ドロップ 対象ページを再読み込みして確認 無事、ヘッダーの色が変わることを確認できました。 Firefoxアドオンのデバッグ URL欄に about:debugging#/runtime/this-firefox でEnter 「一時的なアドオンを読み込む...」から dist/firefox/manifest.json を開く 対象ページを再読み込みして確認 Operaアドオンのデバッグ URL欄に opera://extensions でEnter 右上の「開発者モード」をON dist/opera フォルダをドラッグ&ドロップ 対象ページを再読み込みして確認 対応したいブラウザの確認ができたら、審査の準備をしていきます。 Edge拡張のデバッグ URL欄に edge://extensions でEnter 左下の「開発者モード」をON dist/edge フォルダをドラッグ&ドロップ 対象ページを再読み込みして確認 2. 審査の準備 アイコン画像などの用意 各ブラウザで用途などを比較していきます。 アイコン画像 最低限 128x128 が必要です。 各ブラウザごとの違いの表です。 サイズ Chrome Firefox Opera Edge 全般 必須サイズありガイドライン なければデフォルト Chromeと同じ Chromeと同じ 16x16 拡張機能ページのファビコン Chromeと同じ Chromeと同じ 32x32 Windows向けにあると良い 48x48 拡張機能ページ 理想のサイズ Chromeと同じ Chromeと同じ 96x96 表示される実際のサイズ 公式の例で使用 128x128 必須。Chromeウェブストア Chromeと同じ Chromeと同じ 画像が用意できたら app/images フォルダに入れ、 app/manifest.json も用意した画像の分書き換えてください。 以下は 48x48 128x128 を用意したパターンです。 - "16": "images/icon-16.png", - "128": "images/icon-128.png" + "48": "images/icon-48.png", + "128": "images/icon-128.png" 今回はFireAlpacaというペイントソフトで適当にアイコンを作りました。 プロモーション画像 440x280 が必要となります。 スクリーンショットだと弾かれる可能性がありますが、用意が面倒なのでスクショで一旦申請してみても良いと思います。 サイズ Chrome Firefox Opera Edge 全般 画像ありは優先表示 設定不可 未設定だとフィーチャーされない 440x280 必須。Small x x 必須 920x680 Large x x 1400x560 Marquee x x あると良い 300x188 x 設定可能 スクリーンショット画像 1つは必要です。 1280x800 があると良いと思います。 サイズ Chrome Firefox Opera Edge 全般 最低1つ あると良い(?) ないとリジェクトの可能性あり あると良い 640x400 設定可 設定可 1280x800 望ましい 設定可 詳細用の文章の用意 messages.json のdescriptionとは別に、下の赤枠に表示するような詳細な文章が表示できます。 こちらは効率化のためなので任意ですが、コードで管理しておいたほうが多言語化の際に楽になります。 自分の場合は、 app/_locals/<言語コード>/detailed-description.txt に入れています。 プライバシーポリシー アナリティクスなど、個人情報を収集する処理を入れている場合は必要となります。 今回の記事は特にデータ収集などはしていないですが、以下のようなプラポリを設定しています。 https://github.com/yamadashy/qiita-rainbow-header/wiki/Privacy-Policy-JA レビュアー用の説明 レビュアーが拡張機能を試す際に必要な情報を事前にまとめておきます。Firefox, Edgeの申請時に使います。 確認用のURL 確認の手順 SNS用の拡張など、アカウントが必要となる場合はデバッグ用アカウントの情報 この拡張だと以下のようになります。 確認手順 1. 拡張機能をインストール 2. qiita.com を開き、適当な記事に飛びます 3. 記事内のヘッダーの色が変わっていることが確認できます。 Slackの拡張を作るとしたらこんな感じになると思います。 確認用の情報 - Slackワークスペース: 〇〇〇.slack.com - アカウント: 〇〇〇@gmail.com - パスワード: 〇〇〇 確認手順 1. 拡張機能をインストール 2. slackのワークスペースにログイン 3. 〇〇〇が確認できます パッケージ化 審査に提出するためのパッケージを作ります。 以下のコマンドで各ブラウザ向けのパッケージが packages フォルダに出力されます $ npm run build <ブラウザ名> # 以下のどれか $ npm run build chrome $ npm run build firefox $ npm run build opera $ npm run build edge レビュー提出用ソースコード Firefoxのレビューでは必要になります。 git管理している場合は git archive で簡単にzipが作れます。 $ git archive HEAD -o source.zip package.json の scripts に追加しておくと便利です { "scripts": { "archive": "git archive HEAD -o source.zip" } } 3. 審査・公開 いよいよ申請です。 各ベンダーへの審査について、迷いそうな点を重点的に説明していきます。 ブラウザ共通 以下の情報があると良いです。 Webサイトなどは、GitHubのリポジトリをREADMEを置くだけでも良いので作ると便利です。 拡張の情報 Webサイト ... GitHubのURLで大丈夫です サポートページ ... GitHubのissueページ プライバシーポリシー ... GitHubのWikiにページを作って設定 ストアの情報 説明 ... 事前に説明をまとめておきます。 または detailed-description.txt を使います Chrome拡張の申請 公式のガイドライン 1. 開発者アカウント登録 Googleアカウントを作り、デベロッパーとして登録します。 最初だけ500円の支払いが必要です。 https://chrome.google.com/webstore/devconsole/register 2. パッケージのアップロード 以下のページを開き「新しいアイテム」からアップロードできます。 https://chrome.google.com/webstore/devconsole/ 3. 内容を設定 「ストア掲載情報」を設定 説明 ... 事前にまとめた内容か detailed-description.txt を使います カテゴリ選択 画像設定 ... 用意しておいたアイコンとスクショなどを設定 ホームページURL ... GitHubのページなど サポートURL ... GitHubのissueページなど 「プライバシーへの取り組み」を設定 拡張機能の用途 権限が必要な理由 ... なにかしらの権限を要求している場合は、権限ごとにその理由を記載 データ使用 ... 特にデータを使用していない場合は、下のほうにある3つのチェックボックスを選択しておけば大丈夫です 最近のChromeは審査が厳しくなっているので、使用する権限は必要最低限にしたほうが良いです。 4. プライバシーポリシーの設定 ダッシュボードのトップページの「アカウント」から設定します。 プライバシーポリシーを設定しないとリジェクトされる可能性があります。 今回は以下のように記載しています。 ユーザーのデータを収集するか、収集する場合どう扱うかなどを明記していれば大丈夫なようです。 https://github.com/yamadashy/qiita-rainbow-header/wiki/Privacy-Policy-JA 5. 審査待ち https://chrome.google.com/webstore/devconsole 1,2日で審査結果が出ます。 体感的にリジェクトされるときは3,4日経ってからリジェクトされます。 Firefoxアドオンの申請 1. 開発者アカウント登録 開発者アカウント登録します。 https://addons.mozilla.org/ja/developers/ 2. パッケージのアップロード 「新しいアドオンを登録」ページを開き、パッケージをアップロードします。 https://addons.mozilla.org/ja/developers/addon/submit/distribution また、今回のようにwebpackなどを使用している場合は、ソースコードの提出が必要なので、レビュー提出用ソースコード の項を参考にzipを作り提出します。 3. 内容を設定 以下を埋めていきます 概要 ... 特に変更は必要ないです。 説明 ... 用意した内容か detailed-description.txt を使います カテゴリ選択 メールアドレス サポートサイト ... GitHubのissueページなど ライセンス 審査担当者へのメモ ... 拡張機能の説明や動作確認の手順などを書きます 一旦ここで登録が完了し審査が始まりますが、まだアイコンなどを登録していないので編集します。 4. アイコンなどの設定 アドオン管理ページから対象のアドオンのページを開きます。 https://addons.mozilla.org/ja/developers/addons 画像の「編集」ボタンから、アイコンとスクショを設定できます。 5. 審査待ち https://addons.mozilla.org/ja/developers/addons 2,3日で審査結果が出ます。 以前はかなり早かったのですが、コロナによる影響で遅くなっているようです。 Operaアドオンの申請 公式のガイドライン 1. Operaアカウントを作成 以下からOperaのアカウントを作成します。 https://auth.opera.com/account/signup 2. パッケージをアップロード Opera拡張ダッシュボードを開き「Upload new addon」からアップロードします。 https://addons.opera.com/developer/ 3. 内容を設定 「General」の設定 カテゴリ 「I want my extension to be available for auto-publishing.」にチェック。自動で公開するかの設定です。 「Versions」の設定 気づきづらいのですが、Versionsタブ内に表示されている各バージョンをクリックして、パッケージ説明などの編集ができます。 以下の項目を設定します。 1つ設定するごとにチェックマークで保存するのを忘れないようにしてください。 General ウェブサイトURL サポートURL ,,, GitHubのissueページなど ソースコードURL ... GitHubのページで大丈夫です(ソースが公開されている場合) 審査担当者用の情報 ソースコードURL ビルド方法 ... 審査担当者用にコードのビルド方法を記載 ライセンス プライバシーポリシー ... URLだけで良いです。 Media スクショ 1280x800 アイコン 64x64 Translations 用意した内容か detailed-description.txt を使います 記入が終わったらGeneralの「Submit Changes」で保存します。 同時に審査も始まります。 「Promotional Image」の設定 300x188 の画像を設定します 4. 審査待ち 「Conversation」タブで確認できます。 自分の場合は3分で審査が通りました。 よっぽど内容がおかしくない場合は一瞬で通りそうです。 Edge拡張の申請 公式のガイドライン 1. アカウント作成 Microsoftのアカウントを作成し、開発者として登録してください。 https://partner.microsoft.com/dashboard/microsoftedge 2. パッケージのアップロード 以下のページの「拡張機能を作成」からパッケージをアップロードします。 https://partner.microsoft.com/ja-jp/dashboard/microsoftedge/overview 3. 内容を設定 以下のようなページになるので、左側の「プロパティ」などを選択して設定していきます。 「プロパティの設定」を設定 カテゴリ プライバシーポリシー Webサイト サポート連絡先(サポートサイト) ... GitHubのissueページなど 「Store登録情報」を設定 画像のドラッグ&ドロップなどもできず、また多言語対応している場合は1つ1つ設定しないといけないので結構面倒です。 拡張機能の説明 ... message.json と detailed-description.txt をあわせた内容を設定します。 4. 審査用の説明を記載 最後に審査担当者が確認するための説明を記載して申請します。 5. 審査待ち https://partner.microsoft.com/ja-jp/dashboard/microsoftedge/overview 審査結果が出るまで4,5日程度かかります。 7営業日以内には審査結果が出ると公式には書かれています。 公開 無事すべて公開されました。 Chrome: Qiita Rainbow Header - Chrome ウェブストア Firefox: Qiita Rainbow Header – ? Firefox (ja) 向け拡張機能を入手 Opera: Qiita Rainbow Header extension - Opera add-ons Edge: Qiita Rainbow Header – Microsoft Edge Addons 審査は体感だとOperaが一番早くEdgeが一番遅いです。 またChromeが一番厳しいです。 おわりに 要点だけを書きましたが、それでもやること多いですね。 各ベンダーで協力して共通の拡張ストアページ作ってくれないですかね...。 最近のニュースで 大手のベンダー達がブラウザ拡張を改善するワーキンググループを作った みたいな話があるので、改善されていくことを願います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クロスブラウザなブラウザ拡張を開発・審査・公開するまで

ブラウザ拡張のクロスブラウザ対応には結構手間がかかります。 特にブラウザごとの違いにはなかなか悩まされます。 デバッグ方法 の相違 パッケージ作成方法 の相違 審査方法・基準 の相違 対応する過程で手間取ったり足踏みしたりすることも多いわけですが、「開発から公開まで」について良くまとまっている記事も見当たりません。 そこで本記事は、ブラウザ拡張の開発から公開までのコストを削減できるよう、以下の2点を説明していきます。 開発の工程を減らす クロスブラウザ拡張用パッケージによる開発・デバッグ・パッケージ化 申請・公開をスムーズに 各ベンダーの審査の共通点・相違点をまとめ、準備の負担を軽減 いつか役に立てていただければ 対象の読者 これからChrome拡張などを作りたい方 拡張のクロスブラウザ対応をしたい方 ただし Chrome, Firefox, Opera, Edge に限ります 各ベンダーの審査の違いについて知りたい方 必要な知識 ターミナルコマンドが打てる npmなどが実行できる 拡張に必要なJavaScript, CSSなどが書ける 作るもの 今回は簡単な例として「Qiita記事のヘッダーを色分けする拡張」を作ります。 Qiita記事の各ヘッダーがこのように表示されるようにします。 (自分がこの機能を欲しかっただけです) 実際に公開したものはこちらです。 Chrome: Qiita Rainbow Header - Chrome ウェブストア Firefox: Qiita Rainbow Header – ? Firefox (ja) 向け拡張機能を入手 Opera: Qiita Rainbow Header extension - Opera add-ons Edge: Qiita Rainbow Header – Microsoft Edge Addons 目次 大きく3つのセクションにわけて説明していきます。 拡張機能の開発 審査の準備 審査・公開 1. 拡張機能の開発 Node.js が必要となりますので、もし入っていない方は こちら からインストールお願いします。 クロスブラウザ対応のため webextension-toolbox というパッケージを使っていきます。 ブラウザごとの違いなどを気にせずに、簡単に各ブラウザ用のパッケージを出力できる神ツールです。 サンプルコード 記事で使っているコードの最終形はこちらに公開しています。 https://github.com/yamadashy-sandbox/webextension-toolbox-example また、今回は記事用に極力シンプルにしたものを使っていきますが、実際に使用しているコードはこちらになります。 webpack部分のカスタムに加え、TypeScriptやESLintなどにも対応しています。 https://github.com/yamadashy/qiita-rainbow-header 雛形からコード生成 さっそく開発していきます。 1. 必要なパッケージをインストール yo 雛形作成パッケージ generator-web-extension webextension-toolbox の雛形作成パッケージ $ npm install -g yo generator-web-extension 2. 開発用のフォルダを作成、移動 $ mkdir my-web-extension && cd $_ 3. コード生成 $ yo web-extension 以下の選択肢以外はそのままEnterで大丈夫です Would you like to use UI Action? Content ScriptsだけSpaceで選択しEnter Would you like to install promo images for the Chrome Web Store? yをおしてEnter 拡張機能の実装 いくつかのファイルを書き換えていきます。 1. app/scripts/contentscript.js を書き換え 今回はJavaScriptは必要ないので中身を消してください。 ファイルを消しても大丈夫です。 2. app/styles/contentscript.css を書き換え .allWrapper .it-MdContent h1 { color: #c35c5c; border-bottom-color: #c35c5c; } .allWrapper .it-MdContent h2 { color: #bf905d; border-bottom-color: #bf905d; } .allWrapper .it-MdContent h3 { color: #9b9b4c; } .allWrapper .it-MdContent h4 { color: #4a944a; } .allWrapper .it-MdContent h5 { color: #468a8a; } .allWrapper .it-MdContent h6 { color: #6464a5; } 3. app/_locals/en/messages.json の書き換え en フォルダの名前を ja にして、 message.json を書き換えてください。 (json内のコメントは消してください) { // 拡張機能の表示名 "appName": { "message": "Qiita Rainbow Header" }, // URLなどに使う短縮名 "appShortName": { "message": "qiita-rainbow-header" }, // 拡張の説明 "appDescription": { "message": "Qiita記事のヘッダーの色を段階によって変えます" } } 4. app/manifest.json を書き換え { "manifest_version": 2, // messages.json から自動で取得されます "name": "__MSG_appName__", "short_name": "__MSG_appShortName__", "description": "__MSG_appDescription__", // 自由な値で大丈夫です "version": "0.1.0", "default_locale": "ja", "author": "作者名", // 拡張のアイコン "icons": { "16": "images/icon-16.png", "128": "images/icon-128.png" }, "content_scripts": [ { // 拡張を有効化するURLにマッチする文字列 "matches": [ "https://qiita.com/*" ], "js": [ "scripts/contentscript.js" ], "css": [ "styles/contentscript.css" ], // 実行タイミング。document_startはDOMの読み込み中 "run_at": "document_start" } ] } manifest.json についてはここが参考になるかと思います。 また、run_at についてはこちらを参考に。 これで機能の実装は終わりです。 アイコンの変更などは後ほど説明します。 各ブラウザでデバッグ ブラウザ共通 以下のコマンドで開発用にビルドでき、dist/<ブラウザ名> のフォルダに出力されます。 $ npm run dev <ブラウザ名> # 以下のどれか $ npm run dev chrome $ npm run dev firefox $ npm run dev opera $ npm run dev edge また、この開発用ビルドを終了させない限りは、ファイル変更を検知して自動で再ビルドしてくれます。 Chrome拡張のデバッグ URL欄に chrome://extensions でEnter 右上の「デベロッパーモード」をON dist/chrome フォルダをドラッグ&ドロップ 対象ページを再読み込みして確認 無事、ヘッダーの色が変わることを確認できました。 Firefoxアドオンのデバッグ URL欄に about:debugging#/runtime/this-firefox でEnter 「一時的なアドオンを読み込む...」から dist/firefox/manifest.json を開く 対象ページを再読み込みして確認 Operaアドオンのデバッグ URL欄に opera://extensions でEnter 右上の「開発者モード」をON dist/opera フォルダをドラッグ&ドロップ 対象ページを再読み込みして確認 対応したいブラウザの確認ができたら、審査の準備をしていきます。 Edge拡張のデバッグ URL欄に edge://extensions でEnter 左下の「開発者モード」をON dist/edge フォルダをドラッグ&ドロップ 対象ページを再読み込みして確認 2. 審査の準備 アイコン画像などの用意 各ブラウザで用途などを比較していきます。 アイコン画像 最低限 128x128 が必要です。 各ブラウザごとの違いの表です。 サイズ Chrome Firefox Opera Edge 全般 必須サイズありガイドライン なければデフォルト Chromeと同じ Chromeと同じ 16x16 拡張機能ページのファビコン Chromeと同じ Chromeと同じ 32x32 Windows向けにあると良い 48x48 拡張機能ページ 理想のサイズ Chromeと同じ Chromeと同じ 96x96 表示される実際のサイズ 公式の例で使用 128x128 必須。Chromeウェブストア Chromeと同じ Chromeと同じ 画像が用意できたら app/images フォルダに入れ、 app/manifest.json も用意した画像の分書き換えてください。 以下は 48x48 128x128 を用意したパターンです。 - "16": "images/icon-16.png", - "128": "images/icon-128.png" + "48": "images/icon-48.png", + "128": "images/icon-128.png" 今回はFireAlpacaというペイントソフトで適当にアイコンを作りました。 プロモーション画像 440x280 が必要となります。 スクリーンショットだと弾かれる可能性がありますが、用意が面倒なのでスクショで一旦申請してみても良いと思います。 サイズ Chrome Firefox Opera Edge 全般 画像ありは優先表示 設定不可 未設定だとフィーチャーされない 440x280 必須。Small x x 必須 920x680 Large x x 1400x560 Marquee x x あると良い 300x188 x 設定可能 スクリーンショット画像 1つは必要です。 1280x800 があると良いと思います。 サイズ Chrome Firefox Opera Edge 全般 最低1つ あると良い(?) ないとリジェクトの可能性あり あると良い 640x400 設定可 設定可 1280x800 望ましい 設定可 詳細用の文章の用意 messages.json のdescriptionとは別に、下の赤枠に表示するような詳細な文章が表示できます。 こちらは効率化のためなので任意ですが、コードで管理しておいたほうが多言語化の際に楽になります。 自分の場合は、 app/_locals/<言語コード>/detailed-description.txt に入れています。 プライバシーポリシー アナリティクスなど、個人情報を収集する処理を入れている場合は必要となります。 今回の記事は特にデータ収集などはしていないですが、以下のようなプラポリを設定しています。 https://github.com/yamadashy/qiita-rainbow-header/wiki/Privacy-Policy-JA レビュアー用の説明 レビュアーが拡張機能を試す際に必要な情報を事前にまとめておきます。Firefox, Edgeの申請時に使います。 確認用のURL 確認の手順 SNS用の拡張など、アカウントが必要となる場合はデバッグ用アカウントの情報 この拡張だと以下のようになります。 確認手順 1. 拡張機能をインストール 2. qiita.com を開き、適当な記事に飛びます 3. 記事内のヘッダーの色が変わっていることが確認できます。 Slackの拡張を作るとしたらこんな感じになると思います。 確認用の情報 - Slackワークスペース: 〇〇〇.slack.com - アカウント: 〇〇〇@gmail.com - パスワード: 〇〇〇 確認手順 1. 拡張機能をインストール 2. slackのワークスペースにログイン 3. 〇〇〇が確認できます パッケージ化 審査に提出するためのパッケージを作ります。 以下のコマンドで各ブラウザ向けのパッケージが packages フォルダに出力されます $ npm run build <ブラウザ名> # 以下のどれか $ npm run build chrome $ npm run build firefox $ npm run build opera $ npm run build edge レビュー提出用ソースコード Firefoxのレビューでは必要になります。 git管理している場合は git archive で簡単にzipが作れます。 $ git archive HEAD -o source.zip package.json の scripts に追加しておくと便利です { "scripts": { "archive": "git archive HEAD -o source.zip" } } 3. 審査・公開 いよいよ申請です。 各ベンダーへの審査について、迷いそうな点を重点的に説明していきます。 ブラウザ共通 以下の情報があると良いです。 Webサイトなどは、GitHubのリポジトリをREADMEを置くだけでも良いので作ると便利です。 拡張の情報 Webサイト ... GitHubのURLで大丈夫です サポートページ ... GitHubのissueページ プライバシーポリシー ... GitHubのWikiにページを作って設定 ストアの情報 説明 ... 事前に説明をまとめておきます。 または detailed-description.txt を使います Chrome拡張の申請 公式のガイドライン 1. 開発者アカウント登録 Googleアカウントを作り、デベロッパーとして登録します。 最初だけ500円の支払いが必要です。 https://chrome.google.com/webstore/devconsole/register 2. パッケージのアップロード 以下のページを開き「新しいアイテム」からアップロードできます。 https://chrome.google.com/webstore/devconsole/ 3. 内容を設定 「ストア掲載情報」を設定 説明 ... 事前にまとめた内容か detailed-description.txt を使います カテゴリ選択 画像設定 ... 用意しておいたアイコンとスクショなどを設定 ホームページURL ... GitHubのページなど サポートURL ... GitHubのissueページなど 「プライバシーへの取り組み」を設定 拡張機能の用途 権限が必要な理由 ... なにかしらの権限を要求している場合は、権限ごとにその理由を記載 データ使用 ... 特にデータを使用していない場合は、下のほうにある3つのチェックボックスを選択しておけば大丈夫です 最近のChromeは審査が厳しくなっているので、使用する権限は必要最低限にしたほうが良いです。 4. プライバシーポリシーの設定 ダッシュボードのトップページの「アカウント」から設定します。 プライバシーポリシーを設定しないとリジェクトされる可能性があります。 今回は以下のように記載しています。 ユーザーのデータを収集するか、収集する場合どう扱うかなどを明記していれば大丈夫なようです。 https://github.com/yamadashy/qiita-rainbow-header/wiki/Privacy-Policy-JA 5. 審査待ち https://chrome.google.com/webstore/devconsole 1,2日で審査結果が出ます。 体感的にリジェクトされるときは3,4日経ってからリジェクトされます。 Firefoxアドオンの申請 1. 開発者アカウント登録 開発者アカウント登録します。 https://addons.mozilla.org/ja/developers/ 2. パッケージのアップロード 「新しいアドオンを登録」ページを開き、パッケージをアップロードします。 https://addons.mozilla.org/ja/developers/addon/submit/distribution また、今回のようにwebpackなどを使用している場合は、ソースコードの提出が必要なので、レビュー提出用ソースコード の項を参考にzipを作り提出します。 3. 内容を設定 以下を埋めていきます 概要 ... 特に変更は必要ないです。 説明 ... 用意した内容か detailed-description.txt を使います カテゴリ選択 メールアドレス サポートサイト ... GitHubのissueページなど ライセンス 審査担当者へのメモ ... 拡張機能の説明や動作確認の手順などを書きます 一旦ここで登録が完了し審査が始まりますが、まだアイコンなどを登録していないので編集します。 4. アイコンなどの設定 アドオン管理ページから対象のアドオンのページを開きます。 https://addons.mozilla.org/ja/developers/addons 画像の「編集」ボタンから、アイコンとスクショを設定できます。 5. 審査待ち https://addons.mozilla.org/ja/developers/addons 2,3日で審査結果が出ます。 以前はかなり早かったのですが、コロナによる影響で遅くなっているようです。 Operaアドオンの申請 公式のガイドライン 1. Operaアカウントを作成 以下からOperaのアカウントを作成します。 https://auth.opera.com/account/signup 2. パッケージをアップロード Opera拡張ダッシュボードを開き「Upload new addon」からアップロードします。 https://addons.opera.com/developer/ 3. 内容を設定 「General」の設定 カテゴリ 「I want my extension to be available for auto-publishing.」にチェック。自動で公開するかの設定です。 「Versions」の設定 気づきづらいのですが、Versionsタブ内に表示されている各バージョンをクリックして、パッケージ説明などの編集ができます。 以下の項目を設定します。 1つ設定するごとにチェックマークで保存するのを忘れないようにしてください。 General ウェブサイトURL サポートURL ,,, GitHubのissueページなど ソースコードURL ... GitHubのページで大丈夫です(ソースが公開されている場合) 審査担当者用の情報 ソースコードURL ビルド方法 ... 審査担当者用にコードのビルド方法を記載 ライセンス プライバシーポリシー ... URLだけで良いです。 Media スクショ 1280x800 アイコン 64x64 Translations 用意した内容か detailed-description.txt を使います 記入が終わったらGeneralの「Submit Changes」で保存します。 同時に審査も始まります。 「Promotional Image」の設定 300x188 の画像を設定します 4. 審査待ち 「Conversation」タブで確認できます。 自分の場合は3分で審査が通りました。 よっぽど内容がおかしくない場合は一瞬で通りそうです。 Edge拡張の申請 公式のガイドライン 1. アカウント作成 Microsoftのアカウントを作成し、開発者として登録してください。 https://partner.microsoft.com/dashboard/microsoftedge 2. パッケージのアップロード 以下のページの「拡張機能を作成」からパッケージをアップロードします。 https://partner.microsoft.com/ja-jp/dashboard/microsoftedge/overview 3. 内容を設定 以下のようなページになるので、左側の「プロパティ」などを選択して設定していきます。 「プロパティの設定」を設定 カテゴリ プライバシーポリシー Webサイト サポート連絡先(サポートサイト) ... GitHubのissueページなど 「Store登録情報」を設定 画像のドラッグ&ドロップなどもできず、また多言語対応している場合は1つ1つ設定しないといけないので結構面倒です。 拡張機能の説明 ... message.json と detailed-description.txt をあわせた内容を設定します。 4. 審査用の説明を記載 最後に審査担当者が確認するための説明を記載して申請します。 5. 審査待ち https://partner.microsoft.com/ja-jp/dashboard/microsoftedge/overview 審査結果が出るまで4,5日程度かかります。 7営業日以内には審査結果が出ると公式には書かれています。 公開 無事すべて公開されました。 Chrome: Qiita Rainbow Header - Chrome ウェブストア Firefox: Qiita Rainbow Header – ? Firefox (ja) 向け拡張機能を入手 Opera: Qiita Rainbow Header extension - Opera add-ons Edge: Qiita Rainbow Header – Microsoft Edge Addons 審査は体感だとOperaが一番早くEdgeが一番遅いです。 またChromeが一番厳しいです。 おわりに 要点だけを書きましたが、それでもやること多いですね。 各ベンダーで協力して共通の拡張ストアページ作ってくれないですかね...。 最近のニュースで 大手のベンダー達がブラウザ拡張を改善するワーキンググループを作った みたいな話があるので、改善されていくことを願います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む