20200707のJavaScriptに関する記事は30件です。

java scriptの基礎(配列、条件分岐) ~学習メモ~

はじめに

プログラミングの学習を進めて行く中で、JavaScriptの理解が浅いと痛感認め、基礎に戻りました。

今回は、その内容を学習メモとして投稿します。

動型付け言語

JavaScriptは”動型付け言語”です。”動型付け言語”とは、自動的に言語が型を判別してくれる言語の事です。

以下、例))

const test = 123; //int型と必要がない
const test_2 = 'テスト'; //str型と必要がない

console.log(test);
console.log(test_2);

静型付け言語(余談)

動型付け言語の逆が"静型付け言語"です。具体的には、自動的に言語が型を判別してくれないため、型を書く必要がある言語です。

例:java,goなど、、、
int strなどと型を書く必要のある言語

動型付け言語の型の確認の仕方

console.log();内にtypeof と書く

onst test = 123;
const test_2 = 'テスト';

console.log(test);
console.log(test_2);

console.log(typeof test);
console.log(typeof test_2);

==========================
=> 123
=> テスト
=> number //型の確認
=> string //型の確認

配列と連想配列

配列はとは、値を管理するボックスの様なイメージです。

書き方と参考例

const array = [1,2,3];
const array_2 = [
  ["りんご", "バナナ", "イチゴ"],//0番
  ["ぶどう", "", "スイカ"]    //1番
];

console.log(array);
console.log(array[1]);

console.log(array_2);
console.log(array_2[1][2]);

==============
array          => [1, 2, 3]
array[1]       => 2
array_2        => ["りんご", "バナナ", "イチゴ"]
               => "ぶどう", "", "スイカ"]
array_2[1][2]  => スイカ

連想配列

オブジェクトの書き方

//基本文
const 変数名 = {
  //キー: バリュー
};

参考例

//オブジェクト
const member = {
  //キー: バリュー
  'name': 'ヤマト',
  'hobby': '音楽'
};

console.log(member);
console.log(member.hobby);


==============

member      => {name: "ヤマト", hobby: "音楽"}
member.hobby => 音楽

演算子

//四則: +,-,*,/,%
//足し算、引き算、掛け算、割り算、割り算あまり

//代入: +=
//左に右を代入(加える)

//比較: >,>=,===,!==
//右左の比較

//理論:  &&,||,!
//左右両方を満たすか、または片方のみを満たすか

条件分岐

・if
・for-of(配列の抽出)
・for,switch,while,配列.foreach

javascriptでのif分の基本的な書き方

if(){
  条件が真なら実行
 }eles{
条件が偽なら実行
 }

参考例

const num = 10;

if(num === 10){
  console.log('番号は' + num + '番です');
}else{
  console.log('番号が違います!')
}
===============================
=> 番号は10番です

補足
比較演算し、`==`と`====`の違い

比較演算子 意味
==の場合 左右が一致
===の場合 左右が一致、また型も一致

終わりに

自分学習メモとして、投稿しました。
参考になれば、幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

100枚の画像が固められたzipをブラウザ上で解凍→表示

成果物

https://ecstatic-boyd-e586c5.netlify.app/

画像が表示されるまでちょっと時間かかる。

リポジトリ

https://github.com/yuzuru2/zlibjs

解凍するzip

https://ecstatic-boyd-e586c5.netlify.app/files.zip

↓これが100枚固められている
download.jpg

コマンド

$ git clone https://github.com/yuzuru2/zlibjs.git zl
$ cd zl
$ npm i
$ npm start

package.json

{
  "scripts": {
    "start": "parcel src/index.html --out-dir public",
    "build": "parcel build ./src/index.html --public-url ./ -d public --no-source-maps"
  },
  "dependencies": {
    "babel-polyfill": "^6.26.0",
    "file-type": "^14.6.2",
    "parcel-bundler": "^1.12.4",
    "zlibjs": "^0.3.1"
  }
}

src/index.js

import 'babel-polyfill';
const Unzip = require('zlibjs/bin/unzip.min').Zlib.Unzip;
const FileType = require('file-type');

const main = async () => {
  // zipファイルを取得
  const _res = await fetch('/files.zip');

  // 解凍
  const _unzip = new Unzip(Buffer.from(await _res.arrayBuffer()));

  // ファイル名一覧
  const _filenames = _unzip.getFilenames();

  for (let filename of _filenames) {
    // ファイル(バイナリ)
    const _buffer = Buffer.from(_unzip.decompress(filename));

    // ファイルのMIME
    const _type = await FileType.fromBuffer(_buffer);

    if (_type === undefined) {
      continue;
    }

    // dom作成
    const _dom = document.createElement('img');
    _dom.src = `data:image/${_type.ext};base64,${_buffer.toString('base64')}`;
    _dom.width = 100;

    document.getElementById('app').appendChild(_dom);
    document.getElementById('app').appendChild(document.createElement('br'));
  }
};

main();

src/index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>zlibjs</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="index.js"></script>
  </body>
</html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ちょっとまって、そのforループめちゃくちゃ遅くない???

色々あって、この令和の時代にjQuery2.xで書かれたコードを読んでいました。
対象のHTMLから、要素の数だけループして特定の値を抜き出す、というごくごくシンプルなjQueryです。
シンプルなのですが、毎回DOMツリーを全探索していて、要素数が多いと遅くなりそうだったので、実際に検証した結果をメモとして残しておきます。

結論

結論から先に書くと、やっぱり遅かったので、高速化大事だなぁという話です。
まとめると以下です。

  • ループのlengthは毎回計算しない
  • 無駄にDOMツリーを全探索してはいけない
  • 取得したオブジェクトはキャッシュして使いまわす
  • jQueryを使わなくていいなら極力使わない

対象のHTML

<div>
    <div class="hoge">
        <a href="#a1">リンク1</a> <!-- ★1 hrefの値を取得したい -->
    </div>
    <div class="fuga">
        なにかしらテキスト1 <!-- ★2 fugaのテキストをhrefの値とペアで取得したい -->
    </div>
</div>
<div>
    <div class="hoge">
        <a href="#b2">リンク2</a>
    </div>
    <div class="fuga">
        なにかしらテキスト2
    </div>
</div>
...
<!-- 以降、データの数だけhogeとfugaを含むdivが続く -->

対象のjQuery

for(var i = 0; i < $(".hoge").length; i++) {
    var hoge_a = $(".hoge a")[i].href;
    var fuga_text = $(".fuga")[i].innerHTML;
}

ループが回るたびに、3回DOMツリー全探索とjQueryオブジェクトへの変換が行われるため、要素数が多いページだと遅くなりそうです。

比較対象用のコード

比較対象がないと遅いかどうかわからないので、以下の比較用のコードを書きました。なるべく元のコードと似た形で目的を達成しようとしています。

jQueryを使ったまま改良したコード

const target = $(".hoge"); // 取得したオブジェクトはキャッシュする
const len = target.length; // lengthは計算しておく
for (let i = 0; i < len; i++) { // eachは遅いので使わない
    let hoge_a = $(target[i]).children().prop("href"); // 全探索せずに、検索対象を絞り込む
    let fuga_text = $(target[i]).next().text();
}

jQueryを一切使わないコード

もうこれjQuery使うメリット1つもねえだろと思ったので素のJavaScriptで書きました。

const target = document.getElementsByClassName("hoge");
const len = target.length;
for(let i = 0; i < len; i++) {
    let hoge_a = target[i].firstElementChild.href;
    let fuga_text = target[i].nextElementSibling.textContent;
}

検証結果

divを1000個と100個を生成して、それぞれのコードで比較しました。自分のマシンで10回実行した平均値です。

対象 要素数1000の実行時間(ms) 要素数100の実行時間(ms)
元のコード 561 10
改良版 jQuery使用 10 2
改良版 jQuery無し 1 0

順位は大体の予想通りになりました。
高速化テクニックに関しては実際の効果の程がわかっていなかったのと、意識しないと自分でもやらかしてしまうので、試してみてよかったです。

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配列の要素を取り出す forEach()

 初心者対象 想定

今日は~ forEach()です。
たしかPHPにもありましたよねフォーイーチ氏。。

うろ覚えだったので、今日もメモがてら書いていくっうううう!!!

さて。今日はお三方にしておきます。

const array = ['菅田将暉','米津玄師',`坂上忍`];

この配列の要素をそれぞれ取り出していきたいと思いますっ!

 メソッド forEach()

const array = ['菅田将暉','米津玄師',`坂上忍`];
array.forEach(function(element,i){
 console.log(element)
});

// expected output: "菅田将暉"
// expected output: "米津玄師"
// expected output: "坂上忍"

こうですね!!!

取り出すことができました。

アロー関数で表すと、

array.forEach(element => console.log(element));

こうです!!

ではどんな構文なのか、ドキュメントをみていきましょ!!

arr.forEach(callback(currentValue [, index [, array]])[, thisArg])

詳しくは MDN にて。

ちょっとまて、アロー関数ってなんなん??? それとコールバック!? 高階関数って!?

安心してください、僕もうっすらとしか説明できません。

ということで次回それらを順々にまとめていきます!

JS先生いらっしゃりましたらお力添えをよろしくお願いいたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jsの配列の操作関数 ふえてて 怯えた

jsの配列操作は、ほとんど、毎回googleにたよってるというか、それしかない。

最近、思ったのだが、どんだけ 操作関数が増えてるんだかw

お、これも
お? ここも

とか 思うけど

最後みたとき

え?
なにげ、スッキリ満足感たっぷりなのだが、きがついてみれば

[...Array(4).keys()]

おいwこれ

[0,1,2,3]

でいいだろw 的なとこがあるw

そんだけ贅沢フルセットな関数群であった。w

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

足踏みしながらyoutubeの動画を指定した秒ずつコマ送り表示していくPGM

足踏みしながらyoutubeの動画を指定した秒ずつコマ送り表示していくPGM


カメラを起動し、画像内の差分が一定時間以上発生したときに前進と判定し、指定したyoutubeの動画を指定秒(デフォルト1.0秒)ずつコマ送りしていくPGMを作成しました。

PGMのgithubURL:https://github.com/NanjoMiyako/walkWithYoutube

サンプルページURL:https://nanjomiyako.github.io/walkWithYoutube/



使い方
1.再生動画IDの欄にコマ送りしたいyoutubeの動画IDを入力
2.コマ送り時の秒数を入力(デフォルト1.0秒)
3.「再生動画のセット」ボタンをクリック
4.「ビデオ開始」ボタンをクリック
5.「歩きながらyoutube再生」ボタンを2回クリック(なぜか1回だけだと動かない...orz)
6.ここでそのままだと再生されてしまうのでyoutubeのプレーヤー制御コントロールから一時停止
の状態にする(自動的に一時停止させる方法がわからなかった...orz)
7.この状態からカメラ起動しながら動いていればコマ送り、動かなければ一時停止の状態になります。



サンプル画像

sampleImage1.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSONPでクロスドメイン制約を超える

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptでクラスとインスタンスを作成(100 days of code)

はじめに

100 days of code3日目。
今日はJavaScriptでクラスとインスタンスについて復習しました。

クラスとインスタンス

前回オブジェクトとクラスとインスタンスの概念について勉強しました。
そこで今回はJavaScriptでクラスとインスタンスを用いると、どのような形で実装できるのかを確認していきます。

index.js
// Slimeクラスの作成
class Slime {
  // コンストラクタ
  constructor(color, level){
    this.color = color;
    this.level = level;
  }
}

// Slimeインスタンスを生成
const slimeA = new Slime('blue', 5);
const slimeB = new Slime('red', 50);

// 出力結果をconsole.logで確認
console.log('slimeA : ', slimeA.color); // blue
console.log('slimeA : ', slimeA.level); // 5

console.log('slimeB : ', slimeB.color); // red
console.log('slimeB : ', slimeB.level); // 50

感想

今日はクラスとインスタンスの基礎的な部分について勉強しましたが、フレームワークで応用できるようにできればと思います。
明日も頑張ります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

dom-to-imageを試す

HTML の Element を画像に変換するライブラリ dom-to-image を試しました。Chrome では良好ですが、Firefox では文字のサイズが変わってはみ出すようです。

See the Pen Test: dom-to-image by 七誌 (@7shi) on CodePen.

↑ 左が撮影元の Element、右がそれを画像化したものです。内容はデザインでよく使われる lorem ipsum と呼ばれるダミーテキストで、意味はありません。

シリーズの記事です。同じ目的のライブラリを比較します。

  1. dom-to-imageを試す ← 今回の記事
  2. html2canvasを試す

dom-to-image

リポジトリ: tsayen/dom-to-image: Generates an image from a DOM node using HTML5 canvas

画像に変換する方法は README に説明があります。DeepL で翻訳したものに手を加えて引用します。

このライブラリは SVG の <foreignObject> タグの中に任意の HTML コンテンツを入れることができる機能を使っています。そして、その DOM ノードをレンダリングするためには、以下の手順を踏む必要があります。

  1. 元の DOM ノードを再帰的にクローン

  2. ノードと各サブノードのスタイルを計算して、対応するクローンにコピー

    • 疑似要素の再作成を忘れないでください。それらはどのような方法でもクローンされません。
  3. ウェブフォントを埋め込む

    • ウェブフォントを表す可能性のある @font-face 宣言をすべて探す
    • ファイルの URL を解析して、対応するファイルをダウンロード
    • base64 エンコードして、コンテンツを data: で表される URL としてインライン化
    • 処理されたすべての CSS ルールを連結して1つの <style> 要素にまとめ、それをクローンにアタッチ
  4. 画像を埋め込む

    • <img> 要素に画像 URL を埋め込む
    • CSS の background プロパティで使用される画像を、フォントと同様の方法でインライン化
  5. クローンされたノードを XML にシリアライズ

  6. XML を <foreignObject> タグにラップして SVG に入れて、データ URL を作成

  7. オプションで、PNG コンテンツや生のピクセルデータを Uint8Array として取得するには、SVG をソースとして Image 要素を作成して、オフスクリーンで Canvas を作成してレンダリングし、Canvas からコンテンツを読み込む。

  8. 完成!

以下の記事ではライブラリを用いずにこの方法を説明しています。

利用方法

自前でどこかに置かなくても、jsDelivr 経由で参照できます。

<script src="https://cdn.jsdelivr.net/npm/dom-to-image@2.6.0/dist/dom-to-image.min.js"></script>

README のサンプルコードを元に async/await で書き換えます。

(async () => {
  try {
    let img = new Image();
    img.src = await domtoimage.toPng(src);
    dst.appendChild(img);
  } catch (e) {
    console.error("oops, something went wrong!", error);
  }
})();

動作結果

この記事の冒頭に貼ったサンプルのスクリーンショットを見ます。

Chrome では下に少し余白ができますが、文字のサイズはそのままで画像化されます。
image.png

Firefox では文字のサイズが変わってしまうため、画像からはみ出してしまいます。
image.png
撮影対象がブラウザに依存するのでなければ、Chrome で利用するのが無難なようです。

Firefox

手動になってしまいますが、Firefox では対象の Element の右クリックで画像化できます。

  1. 右クリック → 要素を調査
  2. 開発ツールが表示される
  3. 選択されているノードを右クリック → ノードのスクリーンショットを撮影
  4. ダウンロードフォルダにスクリーンショットが生成

※ dom-to-image は自動化にメリットがあるとは思いますが…

なお、この方法は SVG の画像化にも利用できます。

参考

dom-to-image は以下の記事で知りました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js ✕ CSS Modules のコンポーネントのつくり方

さいきん流行りのコンポーネント化をすすめるにあたり、
弊社(ビザスク)では Vue.js ✕ CSS Modules を採用しています。

これをマークアップするのがちょっと難しかったので、
初心者向けに マークアップのコツコンポーネントの使い方 をまとめてみました。

※ 担当外なので、script内のことや、vue.js自体の話はしません
※ なぜCSS Modulesを使っているかは以下を参照。
https://qiita.com/mascii/items/3202b9e18fd4a7366ac1

基本説明

以下がコンポーネントファイルの基本の構成です。

ComponentName.vue
<template>
  <div>
    <!-- ここにコンポーネントの中身のHTMLを書く -->
  </div>
</template>

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
  name: 'ComponentName', // ここにコンポーネントの名前を記載
});
</script>

<style lang="scss" module>
/* ここにscssを書く */
/* "lang=scss"を取り除くとcssで書ける */
</style>

上段の <template> で囲まれた部分にHTMLを書き、
中段の <script> で囲まれた部分にTypeScriptを書き、
下段の <style> で囲まれた部分にscssを書くことで、
コンポーネントが作ることができます。

<template> の書き方

<template>の直下は ひとかたまりにする必要があります

こういうのはできない
<template>
  <h1>タイトル</h1>
  <p>テキスト</p>
</template>
こうする
<template>
  <div>
    <h1>タイトル</h1>
    <p>テキスト</p>
  </div>
</template>

CSS Modulesのクラスの指定方法

HTMLでの通常の書き方とは異なります。
複数のクラスを使いたい時などが特にややこしいので注意が必要です。

1.基本の書き方
<!-- :class と $style がミソ -->
<div :class="$style.class_name"></div>
2.複数のクラスを付けたい時
<!-- Arrayに入れる -->
<div :class="[$style.class1, $style.class2]"></div>
3.条件によってクラスをつけたりつけなかったりしたい時
<!-- Objectにする / 左側は [$style.class_name] のように [] で包む -->
<div :class="{[$style.class_name]: isHoge === true}"></div>
4.?2と3を組み合わせたいとき
<!-- Arrayの中にObjectを入れる -->
<div :class="[{[$style.hoge1]: isFuga}, $style.hoge2]"></div>
コンポーネントにも付けられる
<ComponentName :class="$style.class_name"></ComponentName>

:class ではなく class を使うと通常のglobalなcssも使えます。
当然ながらglobalなcssを書き換えられた際に影響を受けます。
コンポーネント化している意味が薄れるので、よっぽどのことがなければ使わない方がいいと思います。

ちなみに <template><slot /> (後述)には クラスが付けられません
<div> で囲ったり直下に <div> を置いたりして回避してください。

CSS Modules のscssの書き方

<style lang="scss" module>
.container {
  background: #fff;
  font-size: 16px;
}
</style>

先程クラスを :class="$style.class_name" と指定しましたが、
その内の .class_name をクラス名とし、
<style> 内で普通のscss(css)を書くとstyleが反映されます。

CSS Modulesには「外部の影響を受けない」「外部に影響しない」という特徴があるため、
同コンポーネント内で名前が被らない限りは 汎用的なクラス名を使うことができます

例: .container .heading .form .text など

他のコンポーネントの使い方

<script lang="ts">
import Vue from 'vue';
import ComponentName from '@/components/path/ComponentFileName.vue';

export default Vue.extend({
  name: 'Name',
  components: {
    ComponentName,
  }
});
</script>

<script> 内で
使いたいコンポーネントを import して(2行目)
その名前をcomponents として指定する(7行目)と使えるようになります。

コンポーネントは <template> 内のお好みの箇所で、以下のように使用します。

<template>
  <div>
     <ComponentName />
  </div>
</template>

コンポーネントの中身を可変にする その1

同じコンポーネントでも中の文字は変えたい、
外側は同じstyleだけど中身は変えたい、といったときに使います。

コンポーネント側
<template>
  <div :class="$style.container">
    <slot />
  </div>
</template>
コンポーネントを使う側
<div>
  <ComponentName>
    <!-- ここに書いたものが <slot /> のところに入る -->
  </ComponentName>
</div>

このように書くと <slot /> 部分が可変になり、
コンポーネントの要素で囲った部分が <slot />に代入されます。

コンポーネントの中身を可変にする その2

slotに名前をつけることで、複数の箇所を可変にできます。
コンポーネントを使うときは <template #name> でどのslotに代入するか指定します。

なお、用意したslotは使わなくても大丈夫です。
その場合 <slot /> 部分に何も代入されなくなります。

コンポーネント側
<template>
  <div :class="$style.container">
    <h1 :class="$style.title">
      <slot name="title" />
    </h1>
    <div :class="$style.contents">
      <slot name="contents" />
    </div>
  </div>
</template>
コンポーネントを使う側
<div>
  <ComponentName>
    <template #title>タイトル</template>
    <template #contents>中身</template>
  </ComponentName>
</div>

コンポーネントの中身を可変にする その3

用意したslotを使わなかった場合、styleだけ当たった空の要素が発生することがあります。
そういうときは「slotがあるときだけこの要素を表示」みたいなロジックを添えるとよいです。

<template>
  <div :class="$style.container">
    <h1 :class="$style.title" v-if="$slots.title">
      <slot name="title" />
    </h1>
  </div>
</template>

上記の例では v-if="$slots.title" を付け足しているので、
<template #title>がないときは <h1> 自体が表示されなくなります。

コンポーネントの中身を可変にする その4

propsを使う方法もあります。
その場合は <script>内に追加の記述が必要です。

コンポーネント側
<template>
  <div :class="$style.container">
    {{ label }} <!-- ここが可変 -->
  </div>
</template>

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
  name: 'ComponentName',
  props: { // これを追記
    label: {
      type: String,
      required: true,
    },
  },
});
</script>
コンポーネントを使う側
<div>
  <ComponentName label="ここに書いたものが代入される" />
</div>

上記の例では文字列を代入させています。
コンポーネント側の記述量が増えますが、使用する側の記述量は減っています。
短い文字列だけを渡したい時などはこの方法が便利です。

コンポーネントの中身を可変にする その5

その4に記載した propsboolean などを渡すと、
コンポーネント内の表示を切り替えたりすることもできます。

以下の例では「ラベル付きのフォーム」をコンポーネント化しています。
コンポーネント側で作成した required 属性を利用することで、任意項目と必須項目の表示が切り換わるようになっています。

コンポーネント側
<template>
  <div> 
    <label>{{ label }}</label>

    <!-- 以下が切り替わる -->
    <span v-if="required" >(必須)</span>
    <span v-else>(任意)</span>

    <slot />
  </div>
</template>

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
  name: 'FormComponent',
  props: {
    label: { 
      type: String,
      required: true,
    },
    required: { // これを追記
      type: Boolean,
      required: true,
    },
  },
});
</script>
コンポーネントを使う側
<div>
  <FormComponent label="パスワード" :required="true">
    <input type="password" />
  </FormComponent>
</div>

※ 文字列以外を渡す際は 属性に : を付ける必要がある ことに注意してください。

おわりに

コンポーネント化が進むと同じコードを何度も記述しなくて済むようになりますが、
コンポーネント化すること自体の難易度が高く「コピペしたほうが楽だから…」と後回しにしがちです。

このぐらいまで覚えると簡単なコンポーネントであれば一人で作れるようになります。
弊社の様にマークアップをデザイナーが担当している会社もあるかと思いますが、
コンポーネントを作成・編集する際、エンジニアに依頼しなくてもデザイナー自身が行えると開発効率が上がると思います。

マークアップを担当するデザイナーやVue.js ✕ CSS Modulesの環境で開発したいエンジニアさんなど、お役に立てれば幸いです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】タブの切り替え表示を実装する【JQuery】

はじめに

webサイトでよく見るタブの切り替えを、JavaScriptを使って実装する方法を解説します。
あまり難しいことはないので順番に見ていきましょう!

先に今回のサンプルの完成イメージを載せておきます。
3d9f2899143344eb9d1a27b1c14f7420.gif

htmlファイル

タブとそれぞれのタブに対応した中身を用意します。
一つ目のタブ(tab)にactiveクラスをつけ、タブの中身(content)の一つ目にshowクラスをつけておきます。

htmlファイル
<div class="tab-area">
  <div class="tab active">
    タブ1
  </div>
  <div class="tab">
    タブ2
  </div>
  <div class="tab">
    タブ3
  </div>
  <div class="tab">
    タブ4
  </div>
  <div class="tab">
    タブ5
  </div>
</div>
<div class="content-area">
  <div class="content show">
    タブ1の中身です
  </div>
  <div class="content">
    タブ2の中身です
  </div>
  <div class="content">
    タブ3の中身です
  </div>
  <div class="content">
    タブ4の中身です
  </div>
  <div class="content">
    タブ5の中身です
  </div>
</div>

cssファイル

scssで書いてます。
タブのactiveとそれ以外、中身のshowとそれ以外がどのように表示されたら良いか書けばOKです。

scssファイル
.tab-area {
  display: flex;
  justify-content: space-around;
  background-color: #222e3e;
  cursor: pointer;
  .tab {
    width: 300px;
    height: 30px;
    line-height: 30px;
    text-align: center;
    color: white;
    border-right: 1px solid #50637b;
    border-left: 1px solid #222e3e;
  }
  .tab.active {
    background-color: #ccc;
    color: #222e3e;
    border: none;
  }
}
.content-area {
  font-size: 30px;
  text-align: center;
  .content {
    display: none;
  }
  .content.show {
    margin-top: 50px;
    display: block;
  }
}

jsファイル

JQueryを使って書いてます。

jsファイル
$(function() {
  let tabs = $(".tab"); // tabのクラスを全て取得し、変数tabsに配列で定義
  $(".tab").on("click", function() { // tabをクリックしたらイベント発火
    $(".active").removeClass("active"); // activeクラスを消す
    $(this).addClass("active"); // クリックした箇所にactiveクラスを追加
    const index = tabs.index(this); // クリックした箇所がタブの何番目か判定し、定数indexとして定義
    $(".content").removeClass("show").eq(index).addClass("show"); // showクラスを消して、contentクラスのindex番目にshowクラスを追加
  })
})

最後に

クリックした箇所のクラス名をJavaScriptを使って変更していくだけなので、難しいことは特になかったかと思います。

タブを使った表示ができるとモダンな感じになるので積極的に使っていきましょう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SendGridでいい感じにメールの配信停止グループを管理する

個人で開発しているサービスにメールの配信基盤を整えようと思って色々調べていたので、その結果わかったことを備忘録としてまとめておきます。

メールの配信停止設定で最低限やっておきたいこと

メールの配信設定を行うにあたって、以下の項目が守られていることが個人的に望ましかったです。

  1. 簡単にメールを配信停止できること
  2. 自分のサービスとメール配信サービスでメールの配信停止設定を共有できること

1. 簡単にメールを配信停止できること

まず、送られるメールは簡単に配信停止できるようにすべきです。配信停止のベストプラクティスと、SendGrid の便利な機能にも以下のように書かれています。

  1. 簡単に配信停止できるようにする

配信停止の手順が簡単であればあるほど、受信者と送信者、双方にとってメリットがあります。配信停止のボタンやリンクをどこに置くか決める時、隠すように配置してはいけません。また、リンクがきちんと機能するか複数回チェックしましょう。リンクをフッタに配置するケースをよく見かけますが、どこに配置したとしても、見つけやすくわかりやすくする必要があります。

サービス運営側としては宣伝のためメールを送りたいと思うので、できればメールの配信を続けたいかと思いますが、そのあまり配信停止の設定が不便だとユーザーからの信用を損ないますし、UX も悪いです。

2. 自分のサービスとメール配信サービスでメールの配信停止設定を共有できること

SendGrid にて、メールの配信停止を行う手順は メールの種類ごとに配信停止を管理する - ドキュメント | SendGrid にもあるように、わりと簡単に行えそうでした。

しかし、実際にメールの配信設定を管理する際は、自分で作っているサービス側でもメールの配信設定ができる必要があります。

例えば、SendGrid 側でメールの配信を停止した場合、自分で作っているサービス側のメール配信設定もオフになっているべきです。この実装をどのようにすればよいか、あまりメール配信に慣れていない自分は調べるのに苦労しました。

メールの配信停止設定を管理する方法

いろいろ試した結果、SendGrid の Suppressions の API を利用すると、やりたいことが実現できそうでした。

SendGrid ではメールの配信停止グループを設定できるのですが、こちらの API を使うことで特定のメールアドレスと配信停止グループに対して

  • メールの配信設定の取得
  • メールの配信停止
  • メールの配信停止の取り消し

ができます。

あとは、自分のサービス上でこの API を呼び出してあげれば良いです。自分は Node.js を使っていたので、sendgrid/sendgrid-nodejsでのコードを参考までに載せておきます。

メールの配信設定の取得
import client from "@sendgrid/client";
import RequestOptions from "@sendgrid/helpers/classes/request";

client.setApiKey("SG.xxxxx.yyyyy");
(async () => {
  const request = {} as RequestOptions;
  const email = "example@gmail.com";
  request.method = "GET";
  request.url = `/v3/asm/suppressions/${email}`;
  const [response] = await client.request(request);
  console.log(response.body);
})();
メールの配信停止
import client from "@sendgrid/client";
import RequestOptions from "@sendgrid/helpers/classes/request";

client.setApiKey("SG.xxxxx.yyyyy");
(async () => {
  const request = {} as RequestOptions;
  const email = "example@gmail.com";
  const groupId = 12345;
  const data = {
    recipient_emails: [email]
  };
  request.body = data;
  request.method = "POST";
  request.url = `/v3/asm/groups/${groupId}/suppressions`;
  const [response] = await client.request(request);
  console.log(response.body);
})();
メールの配信停止の取り消し
import client from "@sendgrid/client";
import RequestOptions from "@sendgrid/helpers/classes/request";

client.setApiKey("SG.xxxxx.yyyyy");
(async () => {
  const request = {} as RequestOptions;
  const email = "example@gmail.com";
  const groupId = 12345;
  request.method = "DELETE";
  request.url = `/v3/asm/groups/${groupId}/suppressions/${email}`;
  const [response] = await client.request(request);
  console.log(response.body);
})();

ちなみに、余談ですがメールの送信も以下のような感じでできます。

import sendgrid from "@sendgrid/mail";

sendgrid.setApiKey("SG.xxxxx.yyyyy");

const msg = {
  to: "example@gmail.com",
  from: "no-reply@example.com",
  subject: "Sending with Twilio SendGrid is Fun",
  html: `
    <html>
        <body>
            ここが本文です<br />
            <a href="<%asm_preferences_raw_url%>">配信停止を管理する</a><br />
        </body>
    </html>`,
  asm: { groupId: 12345 }
};
(async () => {
  try {
    await sendgrid.send(msg);
  } catch (error) {
    console.error(error);

    if (error.response) {
      console.error(error.response.body);
    }
  }
})();

これにより、自分のサービスの設定画面からメールの受信・停止設定を行うことができますし、送信したメールの「メール配信停止」リンクからメールの配信を停止した際も、自分のサービス側で設定を同期できます。

おわりに

今回は SendGrid を利用しましたが、似たような API があるサービスであれば今回実現したかったことは実装できるかと思います。

ただ自分はあまりこの手のサービスを知らないので、もしおすすめのサービスをご存じの方がいらっしゃれば、ぜひコメント欄にて教えていただけますと嬉しいです!?


というわけで、この記事で書いた内容をもとにメールの配信機能を実装してみました!

メール通知機能を追加しました!|AnyMake|note

もしよろしければ、AnyMake もチェックしてみてください?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptテスト投稿

見出し

テストテスト

見出し2

テストテスト

見出し3

テストテスト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

webpackのモードについて

webpackのモードについて解説します。
最後に、実際に本番環境で使用するプラグインについても紹介します。

1.設定ファイルに記述

webpack.config.js.modeを記述します。

webpack.config.js
module.exports = {
  mode: 'development',  //これを追加するとモードを指定できる(デフォルトはproductionになっている)

  entry: './src/main.ts',

  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'), 
    publicPath: '/dist/'
  },
  devtool: 'inline-source-map',
  module: {
    rules: [{
      test: /\.ts$/,
      use: 'ts-loader',
      exclude: /node_modules/
    }]
  },
  resolve: {
    extensions: ['.ts','.js']
  }
}

2.buildを実行

以下のコマンドを実行し、dist/bundle.jsを作成

npm run build

デフォルトの設定でwebpackでbuildすると、1行のコードになりますが、modeをdevelopmentに指定すると見やすいコードになります。

bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/        }
/******/    };
/******/
/******/    // define __esModule on exports
/******/    __webpack_require__.r = function(exports) {
/******/        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/        }
/******/        Object.defineProperty(exports, '__esModule', { value: true });
/******/    };
/******/
/******/    // create a fake namespace object
/******/    // mode & 1: value is a module id, require it
/******/    // mode & 2: merge all properties of value into the ns
/******/    // mode & 4: return value when already ns object
/******/    // mode & 8|1: behave like require
/******/    __webpack_require__.t = function(value, mode) {
/******/        if(mode & 1) value = __webpack_require__(value);
/******/        if(mode & 8) return value;
/******/        if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/        var ns = Object.create(null);
/******/        __webpack_require__.r(ns);
/******/        Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/        if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/        return ns;
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __webpack_require__.n = function(module) {
/******/        var getter = module && module.__esModule ?
/******/            function getDefault() { return module['default']; } :
/******/            function getModuleExports() { return module; };
/******/        __webpack_require__.d(getter, 'a', getter);
/******/        return getter;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "/dist/";
/******/
/******/
/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = "./src/main.ts");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./src/food.ts":
/*!*********************!*\
  !*** ./src/food.ts ***!
  \*********************/
/*! exports provided: Food */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

ーー以下省略ーー

しかし、これだと毎回書き換えなければならないので面倒ですよね。

例えば、devサーバーで使用する時はdevelopmentにして、buildする時は、productionにする。

といった方法があります。

それでは実際にやり方について解説します。

developmentとproductionの使い分け

developmentとproductionを使い分ける方法について解説していきます。

1.webpack.dev.jsとwebpack.prod.jsを作成

以下のように2つのファイルを作成します
(webpack.prod.jsを新規作成し、webpack.config.jsをwebpack.dev.jsに改名)
スクリーンショット 2020-07-07 18.11.11.png

2."build"と"start"のそれぞれのコマンド実行時に使用するconfigファイルを指定

package.jsonに以下を記述し使用するconfigファイルを指定する

package.json
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.prod.js",
    "start": "webpack-dev-server --config webpack.dev.js"
  },

こうすることで開発環境ではwebpack.dev.jsが読み込まれて、本番環境ではwebpack.prod.jsが読み込まれるようになる。

3.webpack.dev.jsとwebpack.prod.jsの中身を記述

それぞれの中身を記述します。

webpack.prod.js
const path = require('path'); //requireimportと同じ扱い

module.exports = {
  mode: 'production',

  entry: './src/main.ts', //一番最初に読み込ませるjsファイル

  output: {  //生成したファイルをどこに格納するかを指定
    filename: 'bundle.js', //生成されるファイル名
    path: path.resolve(__dirname, 'dist'),  //生成されるファイルの格納ディレクトリ
  },
  devtool: 'none',
  module: {
    rules: [{
      test: /\.ts$/, //どういうファイルに対して
      use: 'ts-loader',//何をする
      exclude: /node_modules/ //このファイルは例外だよ
    }]
  },
  resolve: {
    extensions: ['.ts','.js'] //左側から拡張子があるかを調べてくれる
  }
}
webpack.dev.js
const path = require('path'); //requireimportと同じ扱い

module.exports = {
  mode: 'development',

  entry: './src/main.ts', //一番最初に読み込ませるjsファイル

  output: {  //生成したファイルをどこに格納するかを指定
    filename: 'bundle.js', //生成されるファイル名
    path: path.resolve(__dirname, 'dist'),  //生成されるファイルの格納ディレクトリ
    publicPath: '/dist/' //これを記述するとwebpackがどこにbundle.jsを返すかを指定できる
  },
  devtool: 'inline-source-map',
  module: {
    rules: [{
      test: /\.ts$/, //どういうファイルに対して
      use: 'ts-loader',//何をする
      exclude: /node_modules/ //このファイルは例外だよ
    }]
  },
  resolve: {
    extensions: ['.ts','.js'] //左側から拡張子があるかを調べてくれる
  }
}

これで問題なくそれぞれ実行できるようになりました。

3.応用編(本番環境でよく使用するプラグイン)

ここからは、本番環境で実際によく使われるプラグインについて解説します。

現状、新たにjsファイルを作成した後に

npm run build

のコマンドを実行したら、もちろんbundle.jsは作成されますが、新しく作成したファイルはそのままになります。
スクリーンショット 2020-07-07 18.36.40.png

そこで今回紹介するプラグインを使うと、build実行時に毎回ファイルを削除後に新規作成してくれるのでフォルダ内がごちゃごちゃしません。

そのプラグインをproductionで使用することはよくあるのでやっていきましょう。

1.プラグインをインストール

今回使用するプラグインは、clean-webpackです。

以下のコマンドでインストールします。

npm install --save-dev clean-webpack-plugin

2.プラグインを使用する

webpack.prod.jsに以下を記述します。

webpack.prod.js
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); //追加


module.exports = {
  mode: 'production',

  entry: './src/main.ts',

  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  devtool: 'none',
  module: {
    rules: [{
      test: /\.ts$/,
      use: 'ts-loader',
      exclude: /node_modules/ 
    }]
  },
  resolve: {
    extensions: ['.ts','.js']
  }
  plugins : [ //追加
    new CleanWebpackPlugin()
  ]
}

これでプラグインを使えるようになりました。

以下のコマンドで確認してみましょう。

npm run build

スクリーンショット 2020-07-07 18.52.13.png

整理されましたね!!!

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【javascript】 クリックイベント click  文字色変更 文字変更 innerHTML / classList / toggle

【ゴール】

その① 文字色変化

画面収録 2020-07-07 18.26.59.mov.gif

その② 文字大きさ変化

画面収録 2020-07-07 18.29.16.mov.gif

【メリット】

■ javascript 理解度向上

【必要なもの】

■ HTML記述(ページ内容表示、文や、文字の表示)
■ CSS記述(ページの装飾、色変更等)
■ javascript記述(CSSでは実現できない動き、)

【コード】

その①文字色を変更

HTML

*id を付与。

index.html
<button id="button">文字色が変わるよ</button>
<h2 id="text">文字色が変わるよ</h2>

CSS

*クリックイベント後の処理

style.css
.font-color {
      color: pink;
    }

javascript

*button id がクリックされたらスコープ内を処理
toggleメソッドはクラスが存在していれば削除、なければ付与。超絶便利メソッド

script.js
const change = document.getElementById('button');

    change.addEventListener('click', () => {
     document.getElementById('text').classList.toggle('font-color');
    });

その② 文字を変更

HTML

*その①と同じ

index.html
<button id="button">文字が変わるよ</button>
<h2 id="text">文字色が変わるよ</h2>

CSS

今回は不要

javascript

*button id がクリックされたらスコープ内を処理
innerHtmlメソッドで文字を変更

script.js
const change = document.getElementById('button');

change.addEventListener('click', () => {
  document.getElementById('text').innerHTML = '<h2>変わったね</h2>';
});

【留意点】

■アロー関数を積極的に使う。推奨の為
■javascript取得するIDを間違えないようにする
■メソッド、基本的な物を覚える。

【合わせて読みたい】

■ 【Javascript】 メソッド まとめ 基礎基本コード メモ
https://qiita.com/tanaka-yu3/items/2438798d159fa402b1d5

■ 【アロー関数】 javascript () => {}  意味 一言で、 入門 
https://qiita.com/tanaka-yu3/items/e16d51280f647aa19a58

■ドットインストールカリキュラム javascript
https://dotinstall.com/lessons/basic_javascript_v4/50603

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt.js】Nuxt文法編:$emit

? この記事はWP専用です
https://wp.me/pc9NHC-od

$emitとは

通常は子→親には何もできません。
$emitを使えば子自身のイベントや
値を親に渡せます?
値に関してはvuexで管理しても⭕️
ただシンプルな物なら
わざわざvuex使う必要もないし…
って時とかに便利です✨

ちなにみ親→子ならpropsが⭕️

書き方

@clickとセットで使用します?
書き方は2種類

メソッド記法
@click="methods名"
this.$emit('イベント名', 第二引数)

インライン記法
@click="$emit('イベント名', 第二引数)"

? 続きはWPでご覧ください?
https://wp.me/pc9NHC-od

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

typescriptから直接bundle.jsを作成する方法

本記事ではtypescriptから直接bundle.jsを作成する方法について書いていきます。

<今までのbundle.jsの作成方法>
1.tsファイルをコンパイルしてjsファイルを作成
2.jsファイルをbundleしbundle.jsを作成

この流れが面倒なので直接やってしまおう。

1.ts-loaderをインストールする

npm install --save-dev ts-loader typescript

2.webpackの設定ファイルに記述

webpack.config.jsにどのファイルにts-loaderを実行するかを記述する。

webpack.config.js
const path = require('path');

module.exports = {
  entry: './src/main.ts',  //最初に読み込ませるファイルもtsファイルに変更

  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  devtool: 'inline-source-map',

  module: {
    rules: [{
      test: /\.ts$/,  //どういうファイルに対して
      use: 'ts-loader',  //何をするか
      exclude: /node_modules/   //このファイルは例外
    }]
  }
}

3.buildしてみる

1.以下のコマンド実行。
npm run build

あれ、エラーだけど、、、、
ERROR in ./src/main.ts
Module not found: Error: Can't resolve './foods.js' in '/Users/*****/*****/******/src'
@ ./src/main.ts 1:0-35 2:0-5

ここでのエラーで原因は、main.tsの中でjsファイルをインポートしようとしているため。

main.ts
import { Foods } from "./foods.js";

Foods.getInstance();

そしたらfoods.tsに書き換えればいいんじゃない?
スクリーンショット 2020-07-07 15.23.58.png

それでもエラーが出てしまいます。

しかし、Webpackを使用する場合は拡張子は.tsで正解なんです!

でもエラーが出ているままって気持ち悪いですよね。。。

こうすることで解決できます!

1.importもとの拡張子を消しちゃいます。
スクリーンショット 2020-07-07 15.28.53.png
※このままだとbuild時にエラーになります

2.webpackの設定ファイルに以下を記述します。

webpack.config.js
const path = require('path');

module.exports = {
  entry: './src/main.ts',

  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  devtool: 'inline-source-map',

  module: {
    rules: [{
      test: /\.ts$/,
      use: 'ts-loader',
      exclude: /node_modules/
    }]
  }
--------ここから--------------------
  resolve: {
    extensions: ['.ts','.js']
  }
}

これを書くことで拡張子がなくてもOK。

この記述でインポートしようとした時に拡張子がなかったら左から調べてくれるようになります。

Webpackって本当に便利ですね。。。

3.再度buildしてみる

以下のコマンド実行。
npm run build

今回は無事にエラーなくbundle.jsが作成されましたね!!!

以上になります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript(TypeScript)の論理判定のベストプラクティスを考える

結論からいうと

  • Truthy/Falsyは絶対利用しない(必ずif (変数 === undefined)のように書く)
  • if (変数 === undefined || 変数 === null)といちいち書くのはやばいので、nullは利用しない
  • 外部APIとの接続でnullが必要な場合は、必ずラッパーに閉じ込める。ラッパーの外にnullを持ち出さない。
  • Lintで設定しよう

Truthy/Falsyを使いたくない

こうやって書きたい時、よくありますね。

const user = getUser();
if (user){
    // userが存在する場合の処理
}

ぼくも上のように書いてたんですが、JavaScript/TypeScriptのTruthy/Falsyの仕様上バグりやすすぎるのでやめることにしました。

JavaScript(TypeScriptも)のTruth/Falsyが罠

https://typescript-jp.gitbook.io/deep-dive/recap/truthy

上のケース、userがobjectなら(普通ならそうのはず)問題ないけど、stringnumberだったらまずい。

TypeScriptでuser: Userと書いたところでそれは保証できない。type User = stringかもしれないし。

それに、「stringnumber以外ならTruthy/Falsyを利用してもよい」という運用は複雑でよくなさそう。

Truth/Falsyは、常に利用しない

if (user) -> if (user !== undefined)
if (!user) -> if (user === undefined)

と書き換えるのが結局ベストプラクティスとおもう。

nullだったらどうする?

nullundefinedが混在してるコードでは、こう書かなきゃいけない。

if (user) -> if (user !== undefined && user !== null)
if (!user) -> if (user === undefined || user === null)

これは
- 見た目きたねえ
- 書くのだるい
- いつかミスる

ので絶対やめたほうがよさそう。

if (user) -> if (user != undefined)
if (!user) -> if (user == undefined)

とすればnullもみてくれるが、==は一律で禁止したいので却下。(仮にここだけ==を利用する運用にした場合、ミスって===と書くかもだし、そもそも誰かが(未来の自分含め)「あ、ここ==使ってんじゃん。だめだめ、===に修正、っと」としうる。ミスに見えうる書き方はだめってダグラス・クロックフォードもいってた。)

null、やめよう。

そもそもnullを使うメリットってあるのか?

  • undefinedだと「まだ定義してない」と「明示的にない」の区別がつかないじゃないか!
    • -> それを区別してどうしたいのか?仮にしたいなら、そんな曖昧な方法じゃなくてもっと明確に用意すべきじゃないか?

このへんの議論はまあいろいろあるので、興味あればこちらなど。
https://media-massage.net/blog/javascript-typescript%E3%81%A7null%E3%82%92%E4%BD%BF%E3%81%86%E3%81%B9%E3%81%8D%E3%81%AA%E3%81%AE%E3%81%8B/

null使わない派
https://typescript-jp.gitbook.io/deep-dive/recap/null-undefined

ということで、undefined統一した。もし問題がでたらまた考える。

ESLintで設定する

こういうのはLintで機械的にみてほしい。

ESLint - no-null <- 言及してるイシュー
ESLint - strict-boolean-expressions

TSlintなら
TSLint - strict-boolean-expressions
no-nullは探してない

課題感

とはいえ毎回if (arg !== undefined)はごちゃつくから簡単にかけるリテラルがほしいなあ。

あと、外部APIのためのラッパーではどうしてもnullを使う必要があるけど、(試してないけど)no-nullでそこはいいかんじに除外できるんかな?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js で プルダウンメニューの作り方 (基礎編)

プルダウンとは?

検索バーとしてよく使用されるプルダウンメニューバー。
Webサイトなどにおけるメニューの表示方法の一種で、クリックなどの操作によって複数のメニュー項目を表示させるタイプの表示方法のことである。

完成コード

HTML

<select v-model="selectedFruits">
  <option disabled value="">果物一覧</option>
  <option v-for="fruit in optionFruits" 
    v-bind:value="fruit.name" 
    v-bind:key="fruit.id">
  {{ Fruit.name }}
  </option>
</select>

Vue.js

export default {
  data() {
    return {
      selectedFruits: '', 
      optionFruits: [ 
          { id: 1, name: 'りんご' }, 
          { id: 2, name: 'みかん' }, 
          { id: 3, name: 'ぶどう' } 
      ], 
    }
  }
}

手順① dataを関数に

export default {
  data() {
    return {
  }
}

コンポーネントのdataオプションは関数でなければいけないため、return 以下に初期設定したいdataプロパティの内容を記述します。

手順② dataの値を設定

selectedFruits: '', 
optionFruits: [ 
  { id: 1, name: 'りんご' }, 
  { id: 2, name: 'みかん' }, 
  { id: 3, name: 'ぶどう' } 
], 

selectedFruitsプロパティにはからの文字列("")で指定します。
そしてプルダウン指定値を配列 [] で用意し、その中に複数の{}オブジェクトを用意します。

手順③ HTMLで表示する

<select v-model="selectedFruits">
  <option disabled value="">担当</option>
  <option v-for="fruit in optionFruits" 
    v-bind:value="fruit.name" 
    v-bind:key="fruit.id">
  {{ Fruit.name }}
  </option>
</select>

selectタグにデータバインディングを作成するためのv-modelを指定します。
そしてその値の中に先ほど用意した、selectedFruitsプロパティにはからの文字列("")をあてます。

<select v-model="selectedFruits"></select>

次にoptionタグはfor文で値を回し、それを一つ一つ選択できるように設定します。
その際、先ほど配列 []で用意したoptionFruitsをあてます。

  <option v-for="fruit in optionFruits" 
    v-bind:value="fruit.name" 
    v-bind:key="fruit.id">
  {{ Fruit.name }}
  </option>

key, valueは実際にフォームとして送信したいケースに合わせて値を変更してください。

実際使用するケースとしては、
バックエンド側と連携するプルダウンを作成するために、axiosを使用し、
apiを叩いてselectする値まで取得するケースが多いです。
そちらの記事はまた後ほど追加で書きます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【アプリ編】textlintではじめる自動文章校正

textlintとは

textlint(テキストリント)は、設定した校正ルールに基づいたミスの指摘・修正を自動化するツールです。

本来はコマンドプロンプト等の『黒い画面』からインストールして使うのですが、作者の方(@azu_reさん)がより使いやすいデスクトップアプリ版をリリースしてくださっています。
こちらで十分な方にとっては圧倒的におすすめです。

アプリのダウンロード

https://github.com/textlint/textlint-app/releases/tag/v1.4.1

↑こちらのページからインストーラをダウンロード、実行します。

アプリを開くとこのような画面が出ます。▼
スクリーンショット 2020-07-07 11.47.51.png

基本的にはメモ帳やテキストエディットのような「エディタアプリ」と同じだということがわかと思います。

しかしこのアプリを使うためには、以下の2点を用意する必要があります。

・ワーキングディレクトリ(作業用フォルダ)
・校正ルールプリセット

順番に説明します。

ワーキングディレクトリの設定

”ディレクトリ”というのは、つまりフォルダのことです。
筆者も最初わからなかったんですが、プログラミングの工程でコマンド操作をする際には、フォルダのことをディレクトリと呼ぶみたいです。

textlintを使う上では、どこのディレクトリで校正作業するのか?という設定が必要になります。

その設定をするのは、左カラムの『Settings』画面。▼
スクリーンショット 2020-07-07 11.48.02.png

上の方にあるWorking directoryの部分に、作業するフォルダのパスを入力します。
何も設定をしないとデフォルトでtextlint-appのフォルダが使われますが、実際に使う際にはデスクトップ等の見やすい場所に置いておく方が何かとスムーズかと思います。お好みでフォルダをご用意ください。
スクリーンショット 2020-07-07 12.00.18.png
↑今回はこのフォルダを使います。

作業用のフォルダを決めたら『.textlintrc』というファイルを用意します。
『.』から始まるこのファイルは”隠しファイル”といって、フォルダを開いても表示されません。

ファイルを作る時はエディタを使いましょう。
サクラエディタやAtomで空のファイルを作成し、名前をつけてフォルダへ保存します。
スクリーンショット 2020-07-07 12.24.42.png
(Macの場合「command」 + 「shift」 + 「.」でこのように隠しファイルを表示できます)

ファイルを用意できたら、同じフォルダ内に『rule』というフォルダを作ります。
スクリーンショット 2020-07-07 12.34.49.png

ここまでできたら、次はルールの設定をしていきます。

校正ルールの設定

現状では、textlintは何の校正ルールも持っていません。
実際に校正を行うには、ルールを自分で設定するか、誰かの作ったルールプリセットをインストールする必要があります。

今回やりたいのは『表記揺れを統一させること』(『是非』は『ぜひ』の表記に統一したいなど)なので、textlint-rule-prhというプリセットを使ってみます。

prhを使うためには、間違い表現と正しい表現をまとめたymlファイルが必要です。
エディタで『jisho』フォルダを開いて、『rule』フォルダ内にファイルを作成しましょう。
スクリーンショット 2020-07-07 12.44.52.png
今回は『jisho.yml』というファイル名にしました。
(画像で使っているVSCodeでは、ymlファイルを開くと紫の!マークがつきます。特にエラーではないのでご心配なく)

ymlファイルにルールを記述する

https://github.com/textlint-rule/textlint-rule-prh
▲こちらのページに詳しい記述方法が書いてありますが、基本的にはこんな感じです。

jisho.yml
version: 1

rules:
  - expected: 正しい表現です。
    pattern: 間違った表現です。

  - expected: 正しい書き方。
    patterns:
      - こんなパターン
      - どんなパターン
      - あんなパターン

このように『patternに該当する表記があったらexpectedが正しいですよ』という指示を書きます。
「是非」→「ぜひ」のような統一の場合はこの書き方。

もし書き間違いなど、正解パターンに対して間違いパターンをいくつも指定したいときは、下にあるようにpatternsと書いたあと間違いパターンを書き連ねていきます。

.textlintrcの中身を書く

最初に作った隠しファイルは、使うルールを指定するための設定ファイルです。
その中にymlファイルのパスを引いて、「これを使います」と指示しましょう。

{
  "rules": {
    "prh": {
      "rulePaths": ["rule/jisho.yml"]
    }
  }
}

(コピペでOKです)

アプリ側での設定

ここまで、作業用フォルダとその中身を用意しました。

jisho
 └ rule - jisho.yml
 └ .textlintrc

このようなファイル構成になっているかと思います。
このjishoフォルダを、textlintアプリで開いてみます。
スクリーンショット 2020-07-07 13.13.58.png
Working directoryの入力欄に、jishoフォルダのパスを書きます。
Windowsだと、フォルダを開けば上の方にパスが表示されるので、それをコピーして貼り付ければOKです。
スクリーンショット 2020-07-07 13.16.35.png
パスを書いてLoadを押すと、中に入っている.textlintrcの中身が下に出てきます。この状態で下の方にあるinstallsaveを押しましょう。

すると、アプリに内蔵されているnpmによってプリセットがインストールされ、アプリでprhが使えるようになります。

試しにエディタに戻って、間違い表現を入力してみましょう。
(最初に書いてあるガイド文は消して大丈夫です)
スクリーンショット 2020-07-07 13.28.51.png
右のカラムに『正規表現はこっちですよ?』という指摘が出ました。
間違いの数だけこの指摘が表示されます。

Fix all errorsと書かれた白いところを押せば、一括で正しい方に修正されます。
スクリーンショット 2020-07-07 13.29.02.png
何個あってもワンクリックです。とても便利!
ただ逆にいうと一括での修正しかできないので、「ここだけは例外的に直さない」という使い方はできません。
日本語的に変な箇所がないかどうか注意しましょう。

(アプリ版ではなくCLIで導入してVSCode上で使う場合なら、そういう融通も利かせられます。環境構築が難しいですがよろしければそちらも挑戦してみてください。)

その他のルール

prhは表記揺れを直すだけですが、他のルールを使えば

・一文で「、」は4つまで
・「かもしれない」などの弱い表現は禁止する

などの校正ルールを追加することもできます!

ルールの自作・カスタマイズができるのもtextlintの魅力ですが、そちらはプログラミング初学者にはけっこうハードルが高いようなので、筆者はまだチャレンジしていません。
おもしろそうなのでJavaScriptに慣れてきたらいずれ…

他にもいろんな方のルールがGitHub上で公開されているので、是非探してみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.jsフレームワークを使いこなす

この記事では、Nuxtフレームワークを取り上げ、Alibaba Cloud ECSサーバ上に設定する方法を説明します。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

序章

Nuxt.jsはVue.jsアプリケーションを作成するためのユニバーサルフレームワークです。ユニバーサルなフレームワークを持つ目的は、サーバーサイドでレンダリングされたアプリとしても、静的に生成されたサイトとしても、単一ページのアプリケーションとしても使用できるように柔軟性を持たせることにあります。

Nuxt.jsの主な焦点は、クライアント/サーバーの配信を抽象化しながら、開発のUIレンダリングの側面にあります。この記事では、Nuxt フレームワークを見て、どのように設定するか、また、Vue を搭載したアプリケーションを構築する際にどのように構造化されているかを見ていきます。

なぜNuxtを使うのか?

Nuxt.js には見逃せない機能がバンドルされており、これらの機能はすべて箱から出してすぐに利用できるので、ウェブアプリケーションを構築する際に活用することができます。これらの機能には以下のようなものがあります。

  • 自動コード分割
  • Vueパワード
  • 静的ファイルのレンダリング
  • ホットモジュールの交換
  • プリプロセッサ:Sass、Stylus、Less
  • サーバーサイドレンダリング
  • HTTP/2 サポート

Nuxt アプリを足場にする

Nuxt.jsは本当に簡単に始められます。Nuxtチームは、creat-nuxt-appと呼ばれるNuxtアプリを数秒で作成できるようにするための足場ツールを作成しました。npxyarnを使って以下のコマンドでNuxtプロジェクトを作成することができます。

npx create-nuxt-app <project-name> or 
yarn create nuxt-app <project-name>

image.png

create-nuxt-appコマンドを使用するときに利用できるオプションは多様で、統合されたサーバーサイドフレームワーク、UIフレームワーク、テストフレームワーク、PWAやlintingのような他の必要なツールを持つかどうかを選択することができ、それはあなたのために自動的に生成され、設定されます。しかし、このチュートリアルでは最低限のことしかしないので、いくつかの機能を選択しました。

プロジェクトを実行する

依存関係がインストールされたら、プロジェクトを実行できるコマンドは以下のコマンドです。

yarn run dev

他にも、アプリをビルドしたり、プロジェクト全体をリントしたり、generateスクリプトを使って静的サイトを生成したりするコマンドがあります。

image.png

アプリを起動するコマンドを実行した後、[http://localhost:3000](http://localhost:3000/)に移動すると、以下のような画面でアプリが起動しているのが確認できるはずです。

image.png

フォルダ構造

nuxt アプリのフォルダ構造は、以下のファイルとフォルダディレクトリで構成されています。

image.png

Assets:このフォルダに含まれるファイルは、Nuxtアプリで使用されるイメージ、フォント、スタイルシートなどのアセットで構成されています。

Components: このフォルダに含まれるファイルは、Nuxt アプリで使用されるイメージ、フォント、スタイルシートなどのアセットです。コンポーネントディレクトリは、アプリケーションを構築する際に使用される再利用可能なVue.jsコンポーネントで構成されています。

layouts: layoutsディレクトリには、アプリケーションのlayoutsが含まれています。layoutsは、ページの外観を変更するために使用されます(例えば、ホームページを含めるなど)。layoutsディレクトリには、アプリケーションが使用できる様々なレイアウトが定義されています。ここは、ヘッダーやフッターなど、アプリ全体で使用される共通のグローバル コンポーネントを追加するのに最適な場所です。このファイルには、新しいページごとに何度も何度も再定義したくないコンポーネントを含める必要があります。

middlewaremiddleware ディレクトリには、アプリケーションのmiddlewareが含まれます。middlewareを使用すると、ページまたはページのグループをレンダリングする前に実行できるカスタム関数を定義できます。

pages: pagesディレクトリには、アプリケーションのビューとpagesグループが含まれます。pages ディレクトリには、アプリケーションのビューとルートが含まれます。フレームワークは、このディレクトリ内のすべての.vue ファイルを読み込み、アプリケーション ルーターを作成します。例えば、index.vue が / ルートになり、about.vue/aboutルートになります。

plugins:plugins ディレクトリには、ルート Vue.js アプリケーションをインスタンス化する前に実行したい Javascript pluginsが含まれています。コンポーネントをグローバルに登録したり、関数や定数を注入したりする場所です。

static:static ディレクトリは assets と似ていますが、サーバー ルートに直接マッピングされており (/static/robots.txt は http://localhost:3000/robots.txt 以下からアクセスできます)、変更されない可能性の高いファイル (ファビコンなど) が含まれています。

store:storeディレクトリには、Vuex Storeのファイルが格納されています。Vuex StoreにはNuxt.jsが付属していますが、デフォルトでは無効になっています。このディレクトリにindex.jsファイルを作成すると、storeが有効になります。

nuxt.config.js: このファイルには、Nuxt.jsのカスタム設定が含まれています。余分な設定がないとファイル名を変更できないので注意してください。

package.json:このファイルには、すべての依存関係とスクリプトが格納されており、ファイル名を変更することはできません。

ルーティング

Nuxt.jsでルーティングが動作する方法は、pageディレクトリ内の.vueファイルのファイルツリーに基づいて、自動的にvue-router設定を生成することです。その構造を見て、その構造に基づいてルート設定を自動的に生成します。Nuxt と vue-router は、その下ですべての作業を行います。

そのような構造の例は以下の通りで、ファイルツリーは以下の構造と全く同じようになります。

pages/
--|index.vue
--|product.vue
 --|index.vue
 --|one.vue 

のようなルートを生成します。

router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'product',
      path: '/product',
      component: 'pages/product/index.vue'
    },
    {
      name: 'product-one',
      path: '/product/one',
      component: 'pages/product/one.vue'
    }
  ]
}

ダイナミックルート

ダイナミックルートを扱う場合、どのように動作するのか気になるかもしれません。パラメータでダイナミックルートを定義するには、.vue ファイルを定義するか、アンダースコアで始まるディレクトリを定義する必要があります。

pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue

と入力すると、以下のようなルートが生成されます。

router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'users-id',
      path: '/users/:id?',
      component: 'pages/users/_id.vue'
    },
    {
      name: 'slug',
      path: '/:slug',
      component: 'pages/_slug/index.vue'
    },
    {
      name: 'slug-comments',
      path: '/:slug/comments',
      component: 'pages/_slug/comments.vue'
    }
  ]
}

ネストされたルート

Nuxt.js を使用すると、vue-router の子ルートを使用してネストされたルートを作成することができます。

ネストされたルートの親コンポーネントを定義するには、子ビューを含むディレクトリと同じ名前の Vue ファイルを作成する必要があります。

pages/
--| products/
-----| _id.vue
-----| index.vue
--| products.vue

このようになります。

router: {
  routes: [
    {
      path: '/products',
      component: 'pages/products.vue',
      children: [
        {
          path: '',
          component: 'pages/products/index.vue',
          name: 'products'
        },
        {
          path: ':id',
          component: 'pages/products/_id.vue',
          name: 'products-id'
        }
      ]
    }
  ]
}

ページ間を移動する際には、vue-router が使用する router-link ではなく、nuxt-link コンポーネントを使用することをお勧めします。

Nuxt.js アプリのデプロイ

Nuxt.jsには、開発用と本番用の両方の便利なコマンドが付属しています。

ここでは、Nuxt アプリの実行に使用されるすべてのスクリプトを見て、Nuxt アプリをデプロイするためのスクリプトを掘り下げていきます。

コマンド 説明
dev localhost:3000で開発サーバーをホットリロードして起動します。
build webpackでアプリケーションを構築し、JSとCSSをミニマイズします(本番用)。
start 本番モードでサーバを起動します(nuxt buildを実行した後)。
generate アプリケーションをビルドし、すべてのルートをHTMLファイルとして生成します(静的ホスティングに使用されます)。

上記のコマンドは、ターミナル上で yarn または npm を使って実行できるスクリプトです。本番環境では、アプリケーションをデプロイする必要があります。Nuxt.jsでは、アプリケーションのデプロイ方法を3つのモードから選ぶことができます。サーバーサイドレンダリング、静的生成、シングルページアプリケーションです。

デプロイ方法の選択に応じて、次のような方法で本番環境向けの Nuxt アプリケーションを構築することができます。

サーバーレンダリング

アプリケーションを実行するには、以下のコマンドを実行する必要があります。

$ yarn build or npm run build

静的に生成された
アプリケーションを静的ファイルに生成するには、以下のコマンドを実行する必要があります。

$ yarn generate or npm run generate

シングルページアプリケーション

Nuxt の SPA は、2 つのモードを使って生成することができます。
- mode: 'spa'nuxt.config.js ファイルに追加する

export default { 
  mode: 'spa'
}
  • すべてのスクリプトコマンドに -spa フラグを追加
"scripts": {
    "dev": "nuxt --spa",
    "build": "nuxt build --spa",
    "start": "nuxt start --spa",
    "generate": "nuxt generate --spa",
  },

上記で説明した以下の手順で、Nuxtアプリを3つの異なるモードでデプロイすることができます。

Nuxtアプリをアリババクラウドにデプロイする

今回は、世界中のどこにでも大量のデータを保存できるクラウドストレージサービスであるAlibaba Object Storage Serviceを使用して、nuxtアプリケーションをデプロイします。

開始するには、すでにアリババクラウドのアカウントを持っている必要がありますが、作成していない場合は、こちらに行って作成してください。

Alibaba Cloud OSSで静的ウェブサイトをホストするためには、まず以下の手順を完了する必要があります。

  • ファイル、画像、スクリプトなどのウェブサイトのファイルを保存するバケットを作成します。
  • 次に、ファイルをバケットにアップロードします。
  • バケットを静的ウェブサイトホスティングモードに設定します。

これらの手順を行った後、サイトをAlibaba Cloudにデプロイし、同様のリンク http://nuxt-app.oss-ap-southeast-1.aliyuncs.com/ を使用してアクセスすることができます。

Alibaba OSSでバケットを作成する

バケットを作成するには、画像に表示されている「バケット作成」ボタンをクリックしてください。モーダルが表示されるので、バケット名リージョンを入力してください。ストレージクラスアクセス制御リスト(ACL)を下の画像のデフォルトに設定してください。

image.png

image.png

バケットにファイルをアップロードする

次のステップは、先ほど作成したバケットにファイルをアップロードすることです。静的サイトとしてデプロイしているので、アップロードできる静的ファイルを作成するために、nuxt generate コマンドを実行する必要があります。コマンドを実行すると、プロジェクトディレクトリにdistフォルダが作成されます。そして、ファイルをアップロードするためにAlibabaに向かい、ファイルをクリックすると、アップロードをクリックできるようになります。先に進み、modalをアップロードするためにdistフォルダ全体をドラッグすると、数秒でファイルがアップロードされます。

image.png

バケットを静的サイトモードに設定する

そして最後のステップとして、バケットを静的なウェブサイトのホスティングモードに設定する必要があります。ダッシュボードで、基本設定をクリックして静的ページに移動し、index.htmlをデフォルトのホームページとして追加します。

image.png

次のステップを経て、我々はAlibaba OSSによって生成されたカスタムドメイン上でサイトを実行していることになります http://nuxt-app.oss-ap-southeast-1.aliyuncs.com/

image.png

結論

アプリケーションとしてのNuxt.jsは、静的サイトの生成、サーバーレンダリング、Vue.jsを使用したシングルページアプリケーションの生成のいずれかに使用することができ、それが普遍的なものであることを示しています。この記事では、プロジェクトの設定からNuxt.jsの機能の理解、ディレクトリ構造と異なるバージョンのNuxtアプリをデプロイする方法まで、Nuxt.jsを使い始める方法を見てきました。これで、Nuxt.jsを使ってWebアプリケーションを構築することができるようになりました。

関連コンテンツ
Vue.JSフレームワークの紹介に関する別のAlibaba Clouderの記事をチェックしてみましょう。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

QiitaAPIを利用して、Facebookみたいにユーザーの「知り合いかも」リストを作成する機能を作った【記事内で試せます】

はじめに

筆者は仕事中、Slackの雑談チャンネルや技術共有チャンネルを見ているとたまにQiitaアカウントが流れてきます。

〇〇さんの記事が伸びてるね。(記事かアカウントページへのリンク)

ROM専だけどQiita垢こちらです。(アカウントページへのリンク)

リンクをクリックして記事やユーザーページだけでなくフォローフォロワーアカウントも見ることが多いですが、あることに気付きました。
フォローフォロワーアカウントに弊社の社員が居る確率がやけに高いことです。
そして、以下のことを考えました。

現実世界での知り合いや同僚がフォローフォロワーに居るQiitaユーザーは多いのではないか?
フォローフォロワーアカウントを探索することで、特定ユーザーの知り合いかもリストが作れるのではないか?

という訳でQiitaAPIを叩いて以下のように知り合いかもしれないユーザーのリストを作成する機能をhtmlとjsで作りました。
リストの例

「知り合いかも」リスト生成ページ

実際に作成したものがこちらです。
JSを使用し、CodePenでページを作ったので埋め込むことでQiita内でもリスト生成が可能です。
IE以外のブラウザであれば問題なく動作すると思います。
貴方のIDや気になるアイツのIDを入力して知り合いかもしれないユーザーのリストをまじまじと眺めましょう。

See the Pen Qiita知り合い探索 by T.D (@td12734) on CodePen.

大きい画面で作成したい人はこちら

ヘルプ、備考など

  • 深度を設定し、深度n+1のユーザーは深度nのユーザーのフォローフォロワーと定義します。
  • 特定ユーザーのフォローアカウントIDまたはフォロワーアカウントIDを調べることを探索と定義します。
  • アカウントIDのフィールドに入力したIDを持つアカウントの知り合い候補のリストを生成します。
    • このユーザーの深度を1とします。
  • 探索深度のフィールドに入力した深度まで知り合い候補を探索します。
    • 例えばフィールドの値が2のとき、深度1のユーザーのみ探索し、深度1のユーザーのフォローフォロワーは探索しません。
  • 何度もエラーが生じる場合はこのページの下に書いてある方法でアクセストークンを発行し、アクセストークン自分で用意にして発行したものを貼り付けてください。

「知り合いかも」リストのアカウントの選定方法

「知り合いかも」リストを生成する上で、Facebookの「知り合いかも」リストの選定方法を参考にしました。
Facebookのヘルプを読むと、以下の基準で選定しています。

  • 共通の友達がいる人。おすすめに選ばれる最も一般的な理由です。
  • 同じFacebookグループに参加している人、または同じ写真にタグ付けされている人。
  • 同じネットワーク(学校、大学、職場など)に所属する人。
  • アップロードした連絡先に登録されている人。

最初の共通の友達がいる人ですが、Qiitaで置き換えると共通のフォローフォロワーがいるユーザーになると考えられます。
Facebookが最も一般的と言い張る選定方法なのでQiitaでも知り合いっぽい人の選定方法としてかなり期待できそうです。

2番目と3番目のグループやネットワークをQiitaで置き換えるとQiita Teamでしょうか?
Qiita Teamについてはよく知りませんし、有料なのもあってFacebookのグループ機能ほど一般ユーザーに浸透していないと考えられるので今回は選定方法から外します。

最後の連絡先はQiitaで置き換えるとプロフィールで設定できる所属している組織・企業になります。
全ユーザーの所属している組織や企業を調べる場合、APIを大量に叩くことになってすぐ規制されそうなのでこちらも選定方法から外しました。

よって、今回は共通のフォローフォロワーがいるユーザー、より具体的に言うと対象のユーザーのフォローフォロワーと、対象のユーザーのフォローフォロワーたちのフォローフォロワーに共通して存在するユーザーをQiitaAPIを叩いて探索し、ヒットしたユーザーたちを「知り合いかも」リストに含めることにしました。

「知り合いかも」リスト生成の流れ

大まかな流れです。
そんなに難しいことは行っていません。

  1. アカウントIDなど必要な情報を入力し、知り合い探索開始ボタンを押す。
  2. 入力したIDを持つアカウントが存在するか確認する。無ければここで終了。
  3. 入力した深度のユーザー全員を探索し終えるまで以下を繰り返す。
    1. 現在の深度のユーザー全員のフォローフォロワーをfetch apiで探索し、リストに入れる。
    2. リストの表示を更新する。

詳細な実装を見たい人はCodePenにあるコードを見てください。

使用したQiitaAPI

GET /api/v2/users

ユーザーの存在確認に使用しました。

GET /api/v2/users/:user_id/followees
GET /api/v2/users/:user_id/followers

上のAPIでユーザーのフォローアカウント一覧、下のAPIでユーザーのフォロワーアカウント一覧を取得しました。
どちらも1回の呼び出しで最大100件のアカウントしか取得できませんが、ユーザーの全取得処理の実装が面倒だったので100件までの取得で妥協しました。

QiitaAPIの呼び出し制限について

今回の「知り合いかも」リスト生成における最大の難点がQiitaAPIの呼び出し制限の存在です。
初期状態では毎時60回しかAPIを叩けません。
これでは深度3までの探索が関の山で、連続してリストは作れません。

しかしアクセストークンを発行すれば毎時1000回までAPIを叩けるようになるので今回は発行しました。

QiitaAPIのアクセストークン発行の流れ

  1. 右上のメニューの設定を開きます。
    設定

  2. 左からアプリケーションを選択し、新しくトークンを発行するを選択します。
    新しくトークンを発行する

  3. スコープread_qiitaにチェックを入れてアクセストークンを発行します。
    スコープの設定

  4. アクセストークンが表示されるので保存します。アクセストークンの文字列は再表示できないので気を付けましょう。
    pic4.png

おわりに

ここからポエムになります。

使ってみた感想

弊社の社員のQiitaアカウントは何件か知っていますが試しに知り合い探索したところ、弊社の社員っぽいアカウントが出るわ出るわの大漁でした。
我ながら恐ろしいもの作っちゃった感はあります。
また、10件以上ヒットしたユーザーは知り合いよりではなく有名ユーザーの方が多かったです。
ヒット数が2~5程度のユーザーの方が知り合いっぽい人を見つけられるかもしれません。

作ってみた感想

あんまり使い慣れていないfetchやPromiseを使用しましたが、非同期処理ってやっぱり何かと便利ですね。
エラー処理をはじめとする全体的なコードやhtmlのデザインはかなり雑ですが動いたからヨシ!(現場猫並感)

今後実装したい機能など

Facebookで言う同じグループやネットワークに所属する人からの知り合い探索ですが、QiitaではQiita Teamではなく特定のタグのフォロワーの中から探索するのが良い気がしてきました。
なぜならタグのフォローは一般ユーザーでも無料でできて敷居が低いですし、マイナーなタグのフォロワーは必然的に現実世界での知り合いまたは知り合いになると意気投合しそうな人たちになるかなって思ったからです。
余力があればタグのフォロワーから知り合い探索をする機能も追加してみたいなーっと考えています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者用】GASを利用してSlackにメッセージを送るbotを作成する

何かしらのメッセージをSlackにbotとして投稿する際に、準備することや書き方をメモがてら記載します

準備

まだ、投稿したいSlackにbotなどを追加したことがない場合は、以下の手順を踏みます。
 →すでにWebhook URLを発行済みの方はこちら

Slackにbotを追加するには【Webhook URL】というものが必要になります。
そのURLの発行・取得の方法をここで記載します。

こちらにアクセスし上部の「管理」をクリック
②表示された画面の左メニューの「カスタムインテグレーション」をクリック
③その後、上部の検索窓で「incoming Webhook」と入力
④候補一番上に三角っぽいアイコンの「Incoming Webhook」というものが出てくるので、クリック
⑤表示された画面で「Slackに追加」をクリック(以下の画像のような画面です)
Slackの画面
⑥その後、どのチャンネルに追加するかを設定します
 この際テストであれば、一旦自分宛に届く様に設定してもよいと思います
 また、投稿するチャンネルは後から変更できるので、安心して設定しましょう!
⑦チャンネルを設定したら、「Incoming Webhookの追加」をクリック
⑧次の画面で【Webhook URL】が赤字で表示されます 以下のようなURLです。
3015665dc1367af26b82f96030a89067.png

ここまでで、WebhookURLの取得は完了です。
では、これを使って実際に投稿をためしてみましょう。

投稿してみる

既にWebhookを設定している方はこちらから、【Webhook URL】を取得してください。

実際にGASを利用利用してSlackに投稿できるようにしてみましょう!
まずこちらの画面にアクセスします。
普段はスプレッドシートやフォームに紐づいたGASを利用することが多いのですが、今回はとりあえず投稿を試すだけなので、こちらから新規作成をしてみましょう。
ということで、左上に表示されている「新しいプロジェクト」をクリック!

すると、エディタ画面が開きますので、以下を入力しましょう。

function sendToSlack() {
  //このurlの部分には、先ほど取得したURLを入力してください。
  var url = "https://hooks.slack.com/services/XXXXXXXX/XXXXXXXX/XXXXXXXXXXX";
  var data = {
    //ここで投稿したいチャンネルを設定します
    //自分宛に送る場合は「#」以降に、普段自分宛メンションされる時の名前を入力
    "channel" : "#program",

    //ここでbotの名前を決めてあげます
    "username" : "botの名前ですん",

    //ここで投稿する内容を設定します
    "text" : "こんにちは世界",

    //アイコンを、そのチャンネルにある絵文字の中から選択します
    "icon_emoji" : ":earth_asia:",

    "link_names":1};

  //ここから下は魔法の言葉です。
  var payload = JSON.stringify(data);
  var options = {
    "method" : "POST",  
    "contentType" : "application/json",
    "payload" : payload,
  };
  var response = UrlFetchApp.fetch(url, options);
}

これを全てコピペして、【チャンネルだけ変更して】上部の「▶︎」をクリックしてみましょう。
おそらく、承認画面がポップアップで表示されますので、許可してあげると
こんにちはせかい

こんな感じで、投稿が手元に届いたんじゃないでしょうか?
上記コード内でも記載しておりますが、チャンネルやBot名、内容は如何様にも変えることができます。
便利ですね!

最後に

今回は投稿する方法を説明するだけのページでした。
今後は、別のfunctionでメッセージを生成して、トリガーで投稿するタイミングを決めて...
など様々な内容を書いていきます。

※非エンジニアの記事なので、変な部分があれば逆にお教えいただけると嬉しいです...

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JekyllフレームワークとAlibaba Cloud OSSでJAM Stackを使用して個人ブログを構築する

このチュートリアルでは、JekyllフレームワークでJAMスタックを使用して、静的資産だけを使って自分の個人ブログを構築する方法を学びます。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

インストール

まず最初に、インストールです。ここでは、簡単のために、一つのアーキテクチャとOS(Windows)へのインストールに焦点を当てています。そこで、まずはJekyllフレームワークの開発環境を設定するための前提条件を提示し、そのフレームワークを使い始めるためのツールをインストールしていきます。

必要条件

Jekyll フレームワークは ruby のパッケージなので、どのプラットフォームや OS (Windows, Linux, Mac OS) でも利用できますが、今回は 64 ビットの Windows 10 が動作するマシンで Jekyll を利用した場合のプロセスを紹介します。

適切なJekyll開発環境を作るためには、以下のような前提条件があります。

  • Ruby: Jekyll とそのプラグインを実行できるようにする。
  • Gem: ruby のパッケージを管理する。
  • bundler: ruby のパッケージを作成する。
  • ウェブ開発のための統合開発環境(IDE):私の場合、このチュートリアルではVisual Studio Codeを使用します。

ツールのインストール

それでは、上記で説明したツールをインストールしていきます。

  • Ruby のインストール (バージョン 2.4.0) Windows プラットフォームに Ruby をインストールする最も簡単な方法は、Ruby installerを使うことです。

1、Ruby installerをダウンロードしてインストールしてください: Ruby、Ruby 言語ドキュメント、ruby パッケージマネージャ gem がインストールされます。
2、インストール後、以下のコマンドを実行して、ネイティブな拡張子を持つ gem をインストールします。

 > ridk install
  • Jekyll と Bundler のインストール
> gem install jekyll bundler

ツールがインストールされたので、Jekyllプロジェクトの構造を続けて、それを始めることができます。

Jekyllとは

Jekyllの仕組み

Jekyll gemを使うと、ターミナルでjekyllの実行ファイルを利用できるようになります。

このコマンドはいくつかの方法で使用できます。

  • jekyll new: デフォルトのgemベースのテーマで新しいJekyllサイトを作成します。
  • jekyll new —blank: 空白のJekyllサイトを新規に作成します。
  • jekyll build or jekyll b: ワンオフでサイトを./_siteにビルドします(デフォルトでは)。
  • jekyll serve またはjekyll s.:ソースファイルが変更されるたびにサイトをビルドし、ローカルに提供します。
  • jekyll doctor:非推奨や設定の問題を出力します。
  • jekyll new-theme: 新しい Jekyll テーマの足場を作成します。
  • jekyll clean: 生成されたサイトとメタデータを削除します。生成されたサイトとメタデータファイルを削除します。
  • jekyll help:jekyll help build のように、オプションで指定したサブコマンドのヘルプを表示します。

Jekyll のテンプレ

Jekyllの構文を使って、以下のような異なるオブジェクトを考えることができます。

  • オブジェクト

オブジェクトは Liquid にコンテンツを出力する場所を指示します。二重中括弧で表示されます。{{}}です。例えば

{{ page.title }}

ページにpage.titleという変数を出力します。

  • タグ

タグは、テンプレートのロジックと制御フローを作成します。タグは中括弧とパーセント記号で表されます。{%%}です。例えば、以下のようになります。

{% if page.show_sidebar %}
  <div class="sidebar">
    sidebar content
  </div>
{% endif %}

page.show_sidebarがtrueの場合、サイドバーを出力します。Jekyll で利用可能なタグについてはこちらをご覧ください。

- フィルタ

フィルタは液体オブジェクトの出力を変更します。それらは出力の中で使用され、|で区切られています。

{{ "hi" | capitalize }}

Hiを出力し、Jekyllで利用できるフィルターについてはこちらでご紹介しています。

- フロントマター

フロントマターはファイルの先頭にある2つのトリプルダッシュの行の間にあるYAMLのスニペットです。フロントマターは、例えばページの変数を設定するために使われます。

---
my_number: 5
---

フロントマターの変数は、Liquidではpage変数の下にあります。例えば、上の変数を出力するには、次のようにします。

{{ page.my_number }}

- レイアウト

JekyllはHTMLと同様にMarkdownをサポートしています。Markdownは、生のHTMLよりも冗長ではないので、シンプルなコンテンツ構造(段落、見出し、画像だけ)のページに最適です。次のページで試してみましょう。

- Includes
Includesタグを使用すると、_includes フォルダに保存されている別のファイルのコンテンツをインクルードすることができます。Includesは、サイト内で繰り返されるソースコードのための単一のソースを持っていたり、可読性を向上させたりするのに便利です。

ナビゲーションのソースコードは複雑になることがあるので、時にはそれをインクルードに移すのもいいでしょう。

ここでは、includeタグの使用法の基本的な例を示します。

以下の内容で _includes/navigation.html にナビゲーション用のファイルを作成します。

<nav>
  <a href="/">Home</a>
  <a href="/about.html">About</a>
</nav>

includeタグを使って、ナビゲーションを_layouts/default.htmlに追加してみてください。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{{ page.title }}</title>
  </head>
  <body>
    {% include navigation.html %}
    {{ content }}
  </body>
</html>

ブラウザで http://localhost:4000 を開いて、ページを切り替えてみてください。

- データファイル

Jekyllは_dataディレクトリにあるYAML、JSON、CSVファイルからのデータの読み込みをサポートしています。データファイルは、ソースコードからコンテンツを分離してサイトのメンテナンスを容易にするための素晴らしい方法です。

ここではデータファイルの使用例を紹介します。

YAMLはRubyのエコシステムでは一般的なフォーマットです。YAMLはRubyのエコシステムでよく使われているフォーマットです。

ナビゲーション用のデータファイルを _data/navigation.yml に以下のように作成します。

- name: Home
  link: /
- name: About
  link: /about.html

Jekyll はこのデータファイルを site.data.navigation.html で利用できるようにしています。各リンクを _includes/navigation.html で出力する代わりに、データファイルを反復して出力することができるようになりました。

<nav>
  {% for item in site.data.navigation %}
    <a href="{{ item.link }}" {% if page.url == item.link %}style="color: red;"{% endif %}>
      {{ item.name }}
    </a>
  {% endfor %}
</nav>

出力は全く同じになります。違うのは、新しいナビゲーション項目を追加したり、HTML構造を変更したりするのが簡単になったことですね。

CSS、JS、画像のないサイトは何の意味があるのでしょうか?Jekyllでのアセットの扱い方を見てみましょう。

Jekyllのスタイリング

- CSSでアセットを追加してスタイリングする
CSS、JS、画像、その他のアセットの使用は、Jekyll を使えば簡単です。それらをサイトフォルダに配置すれば、構築されたサイトにコピーされます。

Jekyll サイトでは、アセットを整理しておくためにこの構造を使用することがよくあります。

.
├── assets
|   ├── css
|   ├── images
|   └── js
...

この段階では、メインのcssファイルを用意するだけです。./assets/css/main.cssに以下の内容のcssファイルを作成します。

.current {
  color: green;
}

レイアウトのスタイルシートを参照する必要があります。

layouts/default.htmlを開き、スタイルシートを<head>に追加してください。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{{ page.title }}</title>
    <link rel="stylesheet" href="/assets/css/main.css">
  </head>
  <body>
    {% include navigation.html %}
    {{ content }}
  </body>
</html>

http://localhost:4000 を読み込んで、ナビゲーションのアクティブなリンクが緑色になっていることを確認してください。

次はジキルの最も人気のある機能の一つであるブログについて見ていきましょう。

コンテンツ制作

- 投稿
ブログの記事は_posts というフォルダにあります。投稿のファイル名は、公開日、タイトル、拡張子の順になっています。

最初の投稿を _posts/2018-08-20-bananas.md に以下の内容で作成します。

---
layout: post
author: bineli
---
Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime, doloremque a laudantium nihil ad voluptatum optio, illum minus debitis odit fugit aut recusandae cum, commodi voluptate accusamus laboriosam earum adipisci.

Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime, doloremque a laudantium nihil ad voluptatum optio, illum minus debitis odit fugit aut recusandae cum, commodi voluptate accusamus laboriosam earum adipisci.

Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime, doloremque a laudantium nihil ad voluptatum optio, illum minus debitis odit fugit aut recusandae cum, commodi voluptate accusamus laboriosam earum adipisci.

これは前に作成したabout.mdのようなものですが、creatorと異なるレイアウトを持っています。authorはカスタム変数で、必須ではありません。

- Collections
それぞれの著者が自分のページを持つように、著者の肉付けを見てみましょう。

これを行うには、Collectionsを使用します。Collectionsは投稿に似ていますが、Collectionsを日付でグループ化する必要がありません。

- 設定方法
Collectionsを設定するには、それをJekyllに伝える必要があります。Jekyllの設定は、_config.ymlというファイルで行われます(デフォルトでは)。

以下のようにルートに_config.ymlを作成します。

collections:
  authors:

-Gemfile
あなたのサイトのために Gemfile を持つことは良いことです。これにより、Jekyllやその他のジェムのバージョンが異なる環境でも一貫性を保つことができます。

以下のようにルートにGemfileを作成します。

source 'https://rubygems.org'

gem 'jekyll'

その後、ターミナルで bundle install を実行してください。これにより、ジェムがインストールされ、Gemfile.lockが作成され、将来のバンドルインストール用に現在のジェムのバージョンをロックします。gemのバージョンを更新したい場合は、bundle updateを実行してください。

Gemfileを使う場合は、bundle execの前にjekyll serveのようなコマンドを実行します。つまり、完全なコマンドは以下のようになります。

> bundle exec jekyll serve

これにより、Ruby環境ではGemfileに設定されたジェムのみが使用されるように制限されます。

ブログを書く

プロジェクトの発表

このチュートリアルのプロジェクトは、Jekyllを使用してウェブ上で自分の才能を披露するために使用できる個人ブログを構築することです。

プロジェクトの設定

プロジェクトを設定するには、以下のコマンドをCLIで実行する必要があります。

> jekyll new blog

このコマンドを実行すると、blogという名前の新しいJekyllプロジェクトが作成されます。blogプロジェクトは上記のような構造のフォルダになります。

_posts
.gitignore
404.html
about.md
Gemfile
Gemfile.lock
index.md
_config.yml

コマンドの実行が終了したら、以下のように実行して、簡易サイトが編集できるようになったことを確認します。

> cd blog
> bundle exec jekyll serve

コマンドラインで以下のように表示されているので、devサーバーが動作していることを確認する必要があります。

Server address: http://127.0.0.1:4000/
Server running... press ctrl-c to stop.

今すぐブラウザを開き、アドレスを指定してください: http://127.0.0.1:4000/ Webサーバーが効果的に動作しているかどうかを確認することができます。

ブログを公開する

CDNを選択する

ブログを書き終わったら、CDNサーバーを使ってオンラインでデプロイします。今回のケースでは、Alibaba Cloudが提供するObject Static StorageというCDNを使用します。

ウェブサイトの展開

このステップでは、JAMスタック組織が推奨するAlibaba Cloud Static File Storage CDN上に静的ウェブサイトをデプロイする方法を紹介します。

1、https://www.alibabacloud.com にログインする必要があります。
2、次に、オブジェクトストレージサービスをクリックします。
3、次に、静的ウェブサイトのファイルを保存するために使用する新しいバケットを作成します。
4、バケットの作成が完了すると、作成したバケットのインターフェースにリダイレクトされます。
5、そして、バケット内にサイトのファイルをアップロードすることができます。
OSS バケットの作成とその中にファイルをデプロイする方法の詳細については、以下のチュートリアルを参照してください: https://www.alibabacloud.com/getting-started/projects/host-a-static-website-on-oss

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WebpackのMultiple Targetsを使ってIE11互換とそうでないjsを一度でビルドする

やりたいこと

IE11向けのページと、そうでないページのビルドをどっちも一度のwebpackコマンドでビルドする。よくありそうなケースとしてはLPサイトはIE11向け、そうでないページはパフォーマンスを考えて少しでも容量が少なくなるようIE11互換を切りたい、二つのビルド設定が必要だがビルドは1コマンドで行いたいようなケース。またはUA判定してIEはIE11互換のjsを読み込んで、そうでないブラウザはIE11互換を切ったjsを読み込ませる、など。

動作確認済の実装

https://github.com/yas-tyoukan/webpack-multi-build-sample

Webpack Multiple Targets

https://webpack.js.org/concepts/targets/#multiple-targets

webpack.config.jsで返すwebpackの設定オブジェクトは、配列で返すことができ、それを使って複数の設定を記述できる。

caller

babel-loaderのoptionsに、babelのconfigに渡せるcallerを設定できる

https://babeljs.io/docs/en/options#caller

babelの設定箇所(babel.config.js)で、callerを使って呼び出し元の情報を取得できる

https://webpack.js.org/loaders/babel-loader/#customize-config-based-on-webpack-target

具体的な実装

webpack.config.babel.js

import path from 'path';

export default (env, args) => {
  const isProduction = args.mode === 'production';
  const devtool = !isProduction && 'inline-source-map';
  const rules = [
    {
      test: /\.js?$/,
      use: ['babel-loader'],
    },
    {
      test: /\.css$/,
      use: ['style-loader', 'css-loader'],
    },
  ];

  const plugins = [];

  return [{
    devtool,
    entry: './entry.js',
    output: {
      path: path.join(__dirname, './'),
      filename: 'bundle.js',
    },
    module: { rules },
    plugins,
  }, {
    devtool,
    entry: './entry.js',
    output: {
      path: path.join(__dirname, './'),
      filename: 'bundle_for_ie.js',
    },
    module: {
      rules: [{
        test: /\.js?$/,
        use: [{
          loader: 'babel-loader',
          options: {
            caller: { target: 'ie11' },
          },
        }],
      }, ...rules.slice(1)],
    },
    plugins,
  }]
};

babel.config.js

const isIncludeIE11 = (caller) => caller && caller.target === 'ie11';

module.exports = (api) => {
  const includeIE11 = api.caller(isIncludeIE11);
  const targets = {
    chrome: '79',
    firefox: '72',
    safari: '13',
  };
  if (includeIE11) {
    targets.ie = '11';
  }
  const presets = [
    [
      '@babel/preset-env',
      {
        targets,
        useBuiltIns: 'entry',
        corejs: 3,
        debug: true,
      },
    ],
  ];

  const plugins = [];

  return {
    presets,
    plugins,
  };
};

結果(一部抜粋)

見やすくするためにdevelopment buildにしている

bundle.js

const buttonEl = document.getElementById('sample-button');
const inputEl = document.querySelector('[name=key]');
buttonEl.addEventListener('click', () => {
  alert(Object(_maskKey__WEBPACK_IMPORTED_MODULE_0__["default"])(inputEl.value));
});

bundle_for_ie.js

var buttonEl = document.getElementById('sample-button');
var inputEl = document.querySelector('[name=key]');
buttonEl.addEventListener('click', function () {
  alert(Object(_maskKey__WEBPACK_IMPORTED_MODULE_0__["default"])(inputEl.value));
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでHello World!を表示させる。

まずはhead部分

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

続いてbody

<body>
    <div id="app">
        {{ message }}
    </div>
    <script>
        new Vue ({
            el: "#app",
            data: {
                message: "Hello World!"
            }
        });
    </script>
</body>
</html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsとjQueryを混在させる

HTML側

CDNでvue.jsとjqueryを読み込みます

<div id="app">
  <button id="okBtn">ok</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script src="https://player.vimeo.com/api/player.js"></script>

javascript側

mountedにjqueryで処理する内容を書きます。

const vueApp = new Vue({
  el: "#app",
  data() {
    return {
      message:"hello world"
  };
  },
  mounted() {
    $('#okBtn').on('click',function(){
      alert(vueApp.message);
    })
  }
})

See the Pen abdYzXO by koji miyazaki (@donkey-maru) on CodePen.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SPA(シングルページアプリケーション)って何者

はじめに

以前React.jsで開発していた際に、SPAについて調べる機会がありましたので、記載していきます。

SPAとは

「単一ページでアプリケーションを構成すること」
なんだかよくわかりませんね。。。。

まずは従来のアプリケーションとSPAを比較してみましょう。

◇従来のアプリケーション
image.png
 1.ユーザ操作
 2.サーバーにリクエスト
 3.サーバーでWEBページを生成
 4.クライアント側へWEBページを送信
 5.サーバーから受け取ったWEBページを描画

特徴
ユーザがリクエストする度にWEBページ全てが読み込まれることです。

◇SPA
image.png
 1.初期ページ読み込み(初期描画)
 2.ユーザ操作
 3.サーバーにリクエスト
 4.サーバーからクライアント側へ差分データを送信
 5.差分のみを更新

特徴
最初のリクエストのみWEBページ全体を読み込む
その後は差分データ(JSONなど)を受け取りJavaScriptで必要な場所だけを更新する
※従来のものと違い、毎回ページ全体の更新をする必要がなくなる

要するにSPAでは、ページ全体をロードするのは初回のみで
それ以降は、JavaScriptで差分のみを更新するということ。

これが「単一ページでアプリケーションを構成すること」とどうつながるのかというと、
一つのHTMLに対してJavaScriptを用いて更新しているというのがポイントです。

※おまけ
 SPAを作成できるフレームワークとして「React」「Vue」などがあります。
 これらは「仮想DOM」を用いて差分更新を実現しています。よければ調べてみてください。

SPAのメリット/デメリット

◇メリット
・画面全体のロードが最初の描画のみなので、通信負荷や通信容量が削減されて画面表示の高速化につながる
・画面を表示したまま、コンテンツの変更が可能になる
 (更新対象のみを変更するので「待ち」ストレスが無くなる)
・サーバー側でWEBページを生成する必要がなくなり、クライアント側とサーバー側が疎結合になる

◇デメリット
・初期ページを読み込むのに時間がかかる
  JavaScriptの記述量が増えるため、初期描画に時間がかかる
・開発の複雑化
  表示側の多くをJavaScriptで制御する必要があるため  

以前感じていた疑問

SPAとして作ったアプリケーションでもボタン押下等のアクション後にURLが変更され、
画面遷移しているのではと思ったことはないでしょうか。

あれは動的にURLをJavaScriptで変更し、画面の一部を更新しているだけで
擬似的な画面遷移を実現しているのです。
(ユーザ目線では画面遷移しているように見える。)

例として簡単にあげます
Vue.jsでは「vue-router」を使用して、動的にURLを変更しています。
(一部抜粋)

app.vue
<template>
  <div>
    <p>テスト</p>
    <router-view/>
  </div>
</template>

「router-view」と記載されている箇所のみがURLによって
動的に「component」(画面のパーツのようなもの)を切り替えています。

なので実際に画面遷移をしているのではなく、画面の一部がURLによって更新されているということです。
(ちょっと長くなってしまいそうなのでこの辺にさせていただきます。)

まとめ

SPAはユーザへのストレスを減らし、UXをより良いものにしてくれると思います。
そのためにもちゃんと理解をして実装をする必要があります。

ここまでずっとSPAを推しましたが、なんとなくSPAを選んだではダメです。
一つの手段にすぎないため、自分の知識を深め最適かどうかを判断することも重要です。
(自分にも言い聞かせてます。。。。。。。。)

参考文献

参考にさせていただきました。
【qiita】
https://qiita.com/takanorip/items/82f0c70ebc81e9246c7a

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SPA(シングルページアプリケーション)っていったいなんだ

はじめに

以前React.jsで開発していた際に、SPAについて調べる機会がありましたので、記載していきます。

SPAとは

「単一ページでアプリケーションを構成すること」
なんだかよくわかりませんね。。。。

まずは従来のアプリケーションとSPAを比較してみましょう。

◇従来のアプリケーション
image.png
 1.ユーザ操作
 2.サーバーにリクエスト
 3.サーバーでWEBページを生成
 4.クライアント側へWEBページを送信
 5.サーバーから受け取ったWEBページを描画

特徴
ユーザがリクエストする度にWEBページ全てが読み込まれることです。

◇SPA
image.png
 1.初期ページ読み込み(初期描画)
 2.ユーザ操作
 3.サーバーにリクエスト
 4.サーバーからクライアント側へ差分データを送信
 5.差分のみを更新

特徴
最初のリクエストのみWEBページ全体を読み込む
その後は差分データ(JSONなど)を受け取りJavaScriptで必要な場所だけを更新する
※従来のものと違い、毎回ページ全体の更新をする必要がなくなる

要するにSPAでは、ページ全体をロードするのは初回のみで
それ以降は、JavaScriptで差分のみを更新するということ。

これが「単一ページでアプリケーションを構成すること」とどうつながるのかというと、
一つのHTMLに対してJavaScriptを用いて更新しているというのがポイントです。

※おまけ
 SPAを作成できるフレームワークとして「React」「Vue」などがあります。
 これらは「仮想DOM」を用いて差分更新を実現しています。よければ調べてみてください。

SPAのメリット/デメリット

◇メリット
・画面全体のロードが最初の描画のみなので、通信負荷や通信容量が削減されて画面表示の高速化につながる
・画面を表示したまま、コンテンツの変更が可能になる
 (更新対象のみを変更するので「待ち」ストレスが無くなる)
・サーバー側でWEBページを生成する必要がなくなり、クライアント側とサーバー側が疎結合になる

◇デメリット
・初期ページを読み込むのに時間がかかる
  JavaScriptの記述量が増えるため、初期描画に時間がかかる
・開発の複雑化
  表示側の多くをJavaScriptで制御する必要があるため  

以前感じていた疑問

SPAとして作ったアプリケーションでもボタン押下等のアクション後にURLが変更され、
画面遷移しているのではと思ったことはないでしょうか。

あれは動的にURLをJavaScriptで変更し、画面の一部を更新しているだけで
擬似的な画面遷移を実現しているのです。
(ユーザ目線では画面遷移しているように見える。)

例として簡単にあげます
Vue.jsでは「vue-router」を使用して、動的にURLを変更しています。
(一部抜粋)

app.vue
<template>
  <div>
    <p>テスト</p>
    <router-view/>
  </div>
</template>

「router-view」と記載されている箇所のみがURLによって
動的に「component」(画面のパーツのようなもの)を切り替えています。

なので実際に画面遷移をしているのではなく、画面の一部がURLによって更新されているということです。
(ちょっと長くなってしまいそうなのでこの辺にさせていただきます。)

まとめ

SPAはユーザへのストレスを減らし、UXをより良いものにしてくれると思います。
そのためにもちゃんと理解をして実装をする必要があります。

ここまでずっとSPAを推しましたが、なんとなくSPAを選んだではダメです。
一つの手段にすぎないため、自分の知識を深め最適かどうかを判断することも重要です。
(自分にも言い聞かせてます。。。。。。。。)

参考文献

参考にさせていただきました。
【qiita】
https://qiita.com/takanorip/items/82f0c70ebc81e9246c7a

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PromiseとAwaitの違い、Promiseで勘違いしたこと

「PromiseとAwaitの違い」というタイトルがもう、わかってない感がすごくてヤバイっすね
Promiseが基礎機能で、Awaitはpromiseを使って待つ仕組み的なもの。

Promise の .then() は待たないという話

Promise の .then() は一気に実行される。最初のとこすら待ってくれない。一気に流したいなら promise 作って then するだけで非同期になる。 .then() の中でAPI取得とかしてたら、(実行順は揃うけど結果の)前後はまったく保証されない。

e.g.

4秒待つ、3秒待つ、2秒待つ・・・と時間がかかるかと思いきや、最期の1秒待つやつから順に帰ってくる。

log = (str) => console.log(str);

new Promise(function(resolve, reject) {
  setTimeout(() => log(1), 4000);
  resolve();
}).then((result) => {
  setTimeout(() => log(2), 3000);
}).then((result) => {
  setTimeout(() => log(3), 2000);
}).then((result) => {
  setTimeout(() => log(4), 1000);
});

result

4
3
2
1

待つために Await がある

先の例を順番通り実行したい場合は、awaitで同期モードにする。

worker = (i) => {
    return new Promise((resolve) => {
        setTimeout(function(){resolve(i)}, i);  
    });
}

async function start() {
    let res = await worker(4000);
    log(res);
    res = await worker(3000);
    log(res);
    res = await worker(2000);
    log(res);
    res = await worker(1000);
    log(res);
}

start();

result

4000
3000
2000
1000

await に then() をぶら下げても、一気に実行される

最初の4000にだけawaitをつける
→ 4000はすぐ実行される
→ その他は4000の直後にだららっと実行される(非同期モード)

worker = (i) => {
    const promise = new Promise((resolve) => {
        setTimeout(function(){
            console.log(i);
            resolve(i);
        }, i);  
    });
    return promise;
}

async function start() {
    let res = await worker(4000)
                .then(worker(3000))
                .then(worker(2000))
                .then(worker(1000));
}

start();

result

000
2000
3000
4000

意味不明すぎるので、狙ってたとしてもこんなことはしてはいけない。(これがやりたい場合は素直に Promise.all() しましょう)

then() の中でawait すれば、待つ

then() の中でも async を書かないといけないので少しトリッキー。

worker = (i) => {
    const promise = new Promise((resolve) => {
        setTimeout(function(){
            console.log(i);
            resolve(i);
        }, i);  
    });
    return promise;
}

async function start() {
    let res = await worker(4000)
                .then(async () => await worker(3000))
                .then(async () => await worker(2000))
                .then(async () => await worker(1000))
}

start();

result

4000
3000
2000
1000

結論

awaitはawaitだけで使う。then()とは組み合わせない (チェーンにしない)

// good 
await worker(1);
await worker(2);

// wrong
await worker(1)
      .then(async () => await worker(2))

以上、基礎連でした。

アロー関数ちなみに

アロー関数のことをラムダ式と呼ぶらしい。

これは

log = (str) => console.log(str);

これと同じ

function log (str) {
    console.log(str);
}

1行だけなら {} が省略できる

引数が1つなら () すら省略できる

log = str => console.log(str);

log = str が並んでる時点でかなりヤバイ見た目になる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む