20200922のvue.jsに関する記事は8件です。

未経験からエンジニア就職するために一生懸命作ったクソアプリを晒していく。

はじめに

未経験からエンジニアになる方法として、プログラミングスクールに通う人は多いと思います。
ただスクールで作った物を面接で見せても、採用サイドからすると「またそれね。」って感じらしいです。

某有名スクールのなんとかエキスパートだと、メルカリのクローン作りますよね。
僕は実際の面接で、「それの説明はいいよ」って言われました。

就職するためにはプラスアルファでどれだけ勉強したかが重要となってきます。
ただ、どんなものを作ればよいか悩む人は多いのではないかなと思います。

当時、僕が就職するために作ったクソアプリたちが出てきたので、参考にして頂ければ幸いです。
「あ、こんなのでいいんだ」と就職活動の緊張から開放されることを願っております。

経歴

2018年9月〜12月 プログラミングスクール
2019年1月 クソアプリ作り始める
2019年2月 内定が出る
2019年4月 東京で就職

作ったクソアプリたち

※ スマホで動くか微妙です。

神になって惑星を消すアプリ

スクリーンショット 2020-09-22 23.01.34.png
惑星の画像が並んでいて、クリックすると惑星が消える。
JavaScriptでクリックした要素をRemoveしてるだけだと思います。
このアプリのURL

目指せホームラン王!!江越大賀の挑戦

スクリーンショット 2020-09-22 23.01.48.png

江越大賀という当たれば飛ぶと言われている選手が阪神タイガースにいて、
その選手がホームラン王になれるのは何年後かを計算するアプリ。

おそらくif文でぐちゃーっと計算してる。

このアプリのURL

あいあい傘メーカー

スクリーンショット 2020-09-22 23.02.49.png
スクリーンショット 2020-09-22 23.02.55.png

二人分の名前を入力したら相合い傘に名前が書かれる。
たしか、全部Railsで作っていて、POSTとか使いたいなと思って作った。

全部上書きで一番上のレコード取得してくるだけの仕様。
文字を縦にするのが難しかった記憶もある。
このアプリのURL

ジョブスの発表会

スクリーンショット 2020-09-22 23.03.49.png

ジョブスが発表したっぽい画像を作るアプリ。
入力した文字の画像を生成できる。
Canvasに文字を入れるだけなんだけど、今見たら日本語に対応してなかった。
テキストエリアの文字消してもCanvasから消せなかったから、「TextReset」っていうボタンを付けたみたい。
「CreateImage」押したら下に、画像が貼付けされる。

このアプリのURL

おのののかを探せ

スクリーンショット 2020-09-22 23.04.12.png

「おのののか」っていう文字列を探すアプリ。
本当のランダムに生成していなくて、一列分の「おのののか」っぽい文字列を配列で登録しておいて、
それを並び替えているっぽい。

このアプリのURL

その他

他にも色々ありましたのでよかったら見てみてください。

結論

誰がどう見てもクソアプリでした。
でも、コレ見せて就職できたのだから作ることって大事なんだと思います。

質はともかくとりあえず作ってみるのが良いです。

自己紹介

今更ツイッタはじめました。
よかったらフォローしたり、質問してください。

ツイッター←こちら

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

初心者によるVue.jsハンズオン

はじめに

初めて記事を書くので暖かく見守って下さい。
仕事でVue.jsを使用するということで、JS初心者が手っ取り早くVue.jsに触れてみる。

早速触ってみる

用意するものは使い慣れたブラウザとエディターがあれば十分です。

私は今回以下のものを用いて開発を行います。
-ブラウザ:Google Chrome
- エディター:Visual Studio Code

まずはベースページの作成

index.html
<html>

<body>
  Hello, World
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</body>

</html>

こちらはvue.js本体を読み込むだけの空ページの作成を行っています。
今回のハンズオンでは、vue.jsのCDNを使っていきます。

こちらのコードをVSCodeで書いたら保存しましょう。
そしてGoogle Chromeにドラッグ&ドロップすることで表示できます、便利。(今まで右クリックしてブラウザに表示していたので、、、、)

表示されたら、まずブラウザに「Hello, World」と表示されるか確認しましょう。
そして、動作としてvue.jsがリクエストされているか確認するためにChromeの開発者ツールを使いましょう。
確認方法は開発者ツール(windowsの場合は「F12」キー)->Networkです。
以下の画像のようになっていれば、大丈夫です!
image.png

それではVue.jsを実行/展開

index.html
<html>

<body>
  <div id="app"></div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      template: '<button></button>',
    });
  </script>
</body>

</html>

こちらのコードでは、Vue.jsの展開ポイントとなるdivを作成し、newで展開しています。
先ほどのブラウザをリロードしてボタンが表示されたら成功です!

解説
1. 展開ポイントは<p>でもでもなんでもいいですが慣例的に<div>を使います。
2. idもなんでもいいですが同じく慣例的にappと名付けます。

ボタンに振る舞いを追加し。紐づける

index.html
<html>

<body>
  <div id="app">
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      template: '<button @click="showMessage"></button>',
      methods: {
        showMessage() {
          alert('ok');
        },
      },
    });
  </script>
</body>

</html>

先ほどのコードに、ユーザーがボタンをクリックしてアラートを表示させるコードを追加します。
実際にクリックしたらメッセージを表示されたら成功です。

解説
1. Vue.jsの挙動は基本的にmethodsの中に定義していきます
2. Vue.jsでは生のhtmlのonclick="..."と同じようにhtmlタグにリスナ/ハンドラを記述します

状態を保持する

index.html
<html>

<body>
  <div id="app">
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      template: '<button @click="toggle"></button>',
      data: function () {
        return {
          active: false,
        };
      },
      methods: {
        toggle() {
          this.active = !this.active;
        },
        showMessage() {
          alert('ok');
        },
      },
    });
  </script>
</body>

</html>

こちらのコードでは、中でステートを保持して、クリック時にそのステートを変更するようにします。
今回は内部の状態変更のみなので確認できないです。

解説
1. 状態、ステートは基本的にdata配下に定義していきます
2. elやtemplate、methodsと違い、dataの値には無名関数を利用して、状態一覧のオブジェクトを返すようにします
3. テンプレート中でdataの値を参照する場合はthisは付けない、methodsの中で参照する場合はthisを付ける、ので注意

テンプレートに値を埋め込む

index.html
<html>

<body>
  <div id="app">
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      template: '<button @click="toggle">{{ active }}</button>',
      data: function () {
        return {
          active: false,
        };
      },
      methods: {
        toggle() {
          this.active = !this.active;
        },
        showMessage() {
          alert('ok');
        },
      },
    });
  </script>
</body>

</html>

こちらのコードでは、テンプレートに{{ }}で値を埋め込んでいて、クリック時の状態反転に連動してボタンのラベルが変わったら成功です。

解説
1. {{ ステートの変数名 }} でテンプレート中に変数を埋め込むことができます
2. Vueはリアクティブなため、変数の値が変わったら(テンプレートを書き直さなくても)自動的に反映されます

{{ }}の中に簡単なjs処理を記述する

index.html
<html>

<body>
  <div id="app">
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      template: '<button @click="toggle">{{ active ? "Like済み" : "Likeする" }}</button>',
      data: function () {
        return {
          active: false,
        };
      },
      methods: {
        toggle() {
          this.active = !this.active;
        },
        showMessage() {
          alert('ok');
        },
      },
    });
  </script>
</body>

</html>

ここはactive変数そのものだとBooleanがStringにキャストされてtrue/falseと表示されるのでボタンのラベルっぽい表示にしてます。

解説
1.{{ }} の中ではjsコードそのものを記述できます
(ただし過度な記述をすると可読性が下がるので注意)

コンポーネント化する

index.html
<html>

<body>
  <div id="app">
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    Vue.component('like-button', {
      template: '<button @click="toggle">{{ active ? "Like済み" : "Likeする" }}</button>',
      data: function () {
        return {
          active: false,
        };
      },
      methods: {
        toggle() {
          this.active = !this.active;
        },
        showMessage() {
          alert('ok');
        },
      },
    });

    new Vue({
      el: '#app',
      template: '<div><like-button />aaa<br />bbb<br />ccc<br /><like-button /></div>',
    });
  </script>
</body>

</html>

最後に、いままで書いてきた一連のテンプレートと振る舞い、状態をセットとしてコンポーネント化します。

解説
1.コンポーネントとして切り出すことで再利用できるようにします

最後に

今回はここで終了です!
次回も続きを書ければいいなと思います。
最後までご覧いただきありがとうございました!

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

Firestoreのエラー "Converting circular structure to JSON Firebase"

エラーの原因

document内で, referenceタイプのフィールドを使っている場合に発生します.

筆者のアプローチ

referenceタイプのフィールドが他のドキュメントを指すことで再帰的になっている為, そのドキュメントのパスを文字列型に変換してあげればいいと思います.
他の方法があればご教授いただきたいです.?‍♂️

具体的な方法

下記は取得したいドキュメントの DocumentReferenceを引数として与えてあげるとそのドキュメントをオブジェクトとして返してあげる関数です.

const resolveCirculations = (documentRef) => {
  const data = documentRef.data();
  const circlerRefResolved = Object.keys(data).reduce((ret, key) => {
    ret[key] = has(data[key], 'path') ? data[key].path : data[key];
    return ret;
  }, {});
  return { ...circlerRefResolved, id: documentRef.id };
};

ドキュメントの一件取得は下記のように使ってあげます.

const postRef = await db.collection('posts').doc(id).get();
const post = resolveCirculations(postRef);

ドキュメントを複数(コレクションで)取得したい場合は以下で, CollectionReference 用に関数を用意してあげます.先程のresolveCirculationsを使います.

const getDocs = (collectionRef) =>
collectionRef.docs.map(resolveCirculations);

上記を使うと, 複数件は以下で取得できます.

const usersRef = await db.collection('users').get();
const users = getDocs(usersRef);

以上になります.

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

Vue 3 の Composition API の勉強のため TODO List のハンズオンを更新してみた

Vue 3 が出たみたいなので、昔軽く作ってた TODO List を表示して追加・更新できる簡単なサンプルを作る手順を書いたハンズオンを Composition API に更新してみました。

https://github.com/runceel/VueJSCRUDHandsOn

出来上がりは ↓ のような感じになります。

image.png

とりあえずドキュメント軽くみて雰囲気で作ったので何かおかしいところがあるかもしれないので、あったら何か教えてください。

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

Vuetifyのv-file-inputでのテキストファイル文字化け対応法

概要

Vuetifyのv-file-inputコンポーネントを使って、shift-jisの文字コードで保存したcsvやtextファイルを単純に読み込んだ場合、文字化けしてしまうのでその対応法についてまとめました

方法

文字化けへの対応方法としては文字コード変換を行うencode.jsを使用し、importしたテキストファイルのエンコード処理をしています。

ソース

事前にencode.jsはimportしてください。

html
<div id="app">
  <v-app>
    <v-row>
      <v-col>
        <v-file-input
          label="テキストファイルを選択"
          @change="getFileContent"
        />
      </v-col>
      <v-col>
        {{ content }}
      </v-col>    
    </v-row>
  </v-app>
</div>
js
new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data () {
    return {
       content: ''
    }
  },
  methods: {
    async getFileContent (file) {
      try {
        const content = await this.readFileAsync(file)  
        this.content = content

      } catch (e) {
        console.log(e)
      }
    },
    readFileAsync (file) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = () => {
          //文字列をバイト列に変換  
          var sjisArray = new Uint8Array(reader.result);
          //encode.jsを使い、文字コードを自動判別しUNICODEへ変換
          var unicodeArray = Encoding.convert(sjisArray, {
            to: 'UNICODE',
            from: 'AUTO',
            type: 'string'
          });
          resolve(unicodeArray);
        }
        reader.onerror = reject;
        reader.readAsArrayBuffer(file);
      })
    }
  }
})

参考

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

Nuxtjsで静的サイト運用時、レイアウト変更したページで2回マウントされてた問題

  • WebサービスのフロントでNuxtjs(v2.14)を使った。
  • 複数のlayoutを使い分ける必要があったため、ファイルを作成し、pageファイル内で指定した。
  • page内で、マウント時に外部サービスへデータをpost(記録)する処理を書いた。
  • 公開はnuxt generateで静的なサイトを作成し、それをアップした。
  • 実際に公開ページにアクセスして処理を走らせてみる
  • 外部サービスのログを見ると、データが2回処理されている???←問題発覚
  • PC、android端末では同様の現象は起きていなかった。

より細かく動作を見ていくと、
iPhone端末のsafariで
Nuxtjsのライフサイクルが二周している。

Nuxtjsのgithubを見ると過去に似たような問題が挙がっていた。
https://github.com/nuxt/nuxt.js/issues/5703

記事中であるように、
「this.$nuxt.layoutName」が一旦defaultで表示されたのち、
指定したものに切り替わっていた。

試しにレイアウトの指定を解除、
または指定をdefaultにすると、問題は起きなくなった。

また、記事中にある「window.〇〇」に処理の経過を記録するやり方の流用で
非同期でのデータ取得をstoreに移し、
データ取得処理の進行状態をstateで記録するようにすると、
二回目の取得処理を回避できるため、
当面の問題(二重記録)はクリアできた。
(uglyな手法って書かれてるけど)

割とぶつかりそうなネタだけど、Qiitaで見当たらなかったので初投稿テストです。

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

Vue.jsでwasm-bindgen(Rust)の入出力を扱う(CSV編)

前に「Vue.jsでwasm-bindgen(Rust)の入出力を扱う」という記事を書いたが、元データがJSONではなかったり、手入力する場合はCSVで読み込めた方が色々と楽だ。

作成中のプログラムからCSVに関するところだけ抜き取ったので動作保証はできないが、雰囲気だけ感じていただければと思う。

環境構築は、こちらを参考に。

https://qiita.com/usop4/items/0e029cd1ff2b8302cda2

変更はConfit.tomlにcsvを追加するぐらいかな。

https://docs.rs/csv/1.1.3/csv/

CSVは、こんな感じの形式。

monster.csv
name,cost
ピカチュウ,10
イーブイ,9

前回と同じようにVue.jsでボタンを押すと発動するようにしておく。今回は手抜きでRust側からconsole.log()を呼ぶためにlog()という関数を定義しておく。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>wasm_csv_test</title>
  </head>
  <body>
    <div id="app">
      <button v-on:click="csvtest">test</button>
    </div>
    <script src="//unpkg.com/vue@latest/dist/vue.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="index.js" type="module"></script>
    <script>
      const log = (val) => {console.log(val);}
    </script>
  </body>
</html>

index.jsではmounted:の中でCSVファイルを読み込んでおき、ボタンを押したらRustで定義したcsv_test()にCSVの中身を渡す。

index.js
import * as mod from "./wasm.js";
(async () => {
    await mod.default();
    var app = new Vue({
      el: '#app',
      data: {
        csv: null
      },
      mounted: function(){
        axios.get("./monster.csv").then(
          response => ( this.csv = response.data )
        );
      },
      methods:{
        csvtest: function(event){
          mod.csv_test(this.csv);
        }
      }
    })
})();

次にRust側。Monster(単体)とMonsters(複数)のstructを作っておく。

monster.rs
#[derive(Clone)]
pub struct Monster {
    pub id: usize,
    pub name: String,
    pub cost: usize,
}

#[derive(Clone)]
pub struct Monsters {
    pub monsters: Vec<Monster>
}

impl Monsters {
    pub fn new() -> Self {
        Monsters {
            monsters: Vec::new()
        }
    }
    pub fn add_monster(&mut self, m: Monster){
        self.monsters.push(m);
    }
}

lib.rsではcsv::ReaderBuilder::new().delimiter(b',').from_reader(data.as_bytes())でCSVファイルを読み込み、Monsterに格納しながらlog()経由でブラウザのconsole.log()に出力する。

lib.rs
pub mod monster;

use wasm_bindgen::prelude::*;
use crate::monster::*;

#[wasm_bindgen]
pub fn csv_test(data: &str) {

    let mut m = Monsters::new();
    let mut r = csv::ReaderBuilder::new().delimiter(b',').from_reader(data.as_bytes());
    let mut id :usize = 0;

    for record in r.records(){
        let record = record.unwrap();
        m.add_monster(
            Monster {
                id: id,
                name: record[0].to_string(),
                cost: record[1].parse().unwrap(),
            }    
        );
        id = id + 1;
        log(&format!("{}",&record[0]));
    }
}

#[wasm_bindgen]
extern {
    pub fn log(s: &str);
}

今回はidの値をCSVと外で生成しているが、もしCSVの形式とstructが完全一致であればSerdeのdeserializeで一気に格納することもできる。

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

ブログの表示高速化のためにやったこと・調べたこと

元記事

https://coincidence.netlify.app/post/blog-display-speed
(ブログからの転載になります。)

表示が遅い!!

このブログ、現状でもかなり表示が遅いのですが、本記事での取り組みをやる前はさらに遅かったです。
参考までにLighthouseのスコアをお示ししますと、Performanceスコアが32〜36点ほどでした。
この状態から以下の対応を行なったことで、50点以上を安定して取れるようになりました。正直このブログにとっては意味のない対応もありましたが、勉強の意味で残しておこうと思います。
各施策の最初には星印で効果の度合いを示しました。黒星(ぬりつぶし)が多い方が、効果があったものになります。ただしこれは、私のSPAブログ環境ではこうだった、という話ですので、設計が異なれば有効な対策も異なるということはご注意くださいませ。(大丈夫だと思いますけど)

施策1:APIへの問合せ回数減

効果 ★★★☆☆

これは、トップページ(/page/1)の表示時間短縮には直接影響しない部分ですが、画面遷移時のオーバーヘッドを減らす意味で非常に役立ちました。
SPAの基本的な振る舞いとして、「情報の更新が必要であればその都度サーバーサイドに問合せ、結果を取得して表示更新する」という像があると思うのですが、WebページというよりWebアプリエンジニアの私はこの考えに縛られすぎていたようです。ページの表示、タグ一覧の表示、タグに紐づく記事一覧の表示…といった振る舞いをSPAブログにさせるために、毎回Contentfulに問い合わせをしていたのです。情報の更新はされていないにもかかわらず。
これはあまりに無駄が大きいので、ビルド実行前にContentful APIから情報を取得してjsonとして保存するスクリプトを書きました。記事数が増えてきたらまた対応を考えなければならないかもしれませんが、とりあえずは表示速度改善の意味で満足できる結果になりました。

施策2:コードチャンクの整理(highlight.js)

効果 ★★★★☆

スコア向上の意味ではこれの効果が大きかったです。
Vue CLIで作成したプロジェクトは、yarn run build --reportすると/dist/report.htmlを生成してくれます。webpackでバンドルしたコードチャンクのうちなにが重いか?を可視化してくれる素晴らしいツールです。(単体ではwebpack-bandle-analyzerとして配布されています。有名だから今更かも。)
見ると、highlight.jsが大量のチャンクを吐き出していたので、何事かと思い調べたら、どうやら単にrequireするだけでは不要な言語パックまで引き連れてきてしまうそうです。面倒でもちゃんとregisterLanguageした方がいいみたいね。

// このやり方だと重くなります
import hljs from "highlight.js";

// こうしたらマシです(ちなみにhtmlはデフォルトでハイライトしてくれるっぽい)
import hljs from "highlight.js/libs/highlight";
import javascirpt from "highlight.js/lib/languages/javascript";
import css from "highlight.js/lib/languages/css";

hljs.registerLanguage("javascript", javascript);
hljs.registerLanguage("css", css);

ちなみに、最新バージョンだとregisterLanguageがまともに動いてくれないバグがあるようなので、大人しくバージョンを下げて使いましょう。package.jsonを以下のとおり更新して、yarnもしくはnpm iです。

{
  "dependencies": {
    "highlight.js": "~9.18.1",
    ...
  }
}

施策3:トップページのプレレンダリング(prerender-spa-plugin)

効果 ★☆☆☆☆

SPA高速化の手段の一つとして代表的なのがプレレンダリングですが、私の場合はそもそもHTMLの構造が複雑なために速度低下を招いていたわけではなさそうなので、これはあまり効果がありませんでした。また、ブログという性質上ルーティングが動的に変わるので、とりあえずよく参照されるトップページだけこの対応を埋め込みましたが、全てのブログページをプレレンダリングするのはやや設定が面倒そうな印象でした。
(ちょっと頑張ればやれなくもなさそうですが、記事数が増えるに従ってビルド時間がとんでもないことになりそうな気がしています。)
ページ数が限定されているSPAなら、やってみるのは大いにアリなのではないでしょうか。

施策4:ルーティングコンポーネントの非同期読み込み

効果 ★★★☆☆

これもなかなか良い感じに速度を改善してくれました。
メインの領域はVue Routerで切り替えているのですが、どのルートにアクセスしても、直近で必要のないJSを読み込んでしまっていたようなので、とりあえずはそのルートに必要なJSだけ呼び出してもらうように設定しました。Vue Router公式ドキュメントが参考になりましたが、要するにrouterの処理を書いているファイル内のコンポーネントのimport文を、なにも考えずに書き換えれば良いです。

// before..最初に全部読み込み
import HogeComponent from "@/components/HogeComponent.vue";

// after ..遷移時に必要に応じて読み込み
const HogeComponent = () => import("@/components/HogeComponent.vue");

Nuxt.jsとかGridsome、VuePressに移行したい

施策3でプレレンダリングを試してみたとはいえ、結局のところは静的サイトジェネレータに対応した上記のライブラリを使った方がよいのでは?感が拭えない結果となりました。Lighthouseのスコアも50止まりですしね。大人しく、JAMStackな設計のベストプラクティスに従ったほうがよさそうだ、というなんともしょっぱい結論となり恐縮です。
Nuxtとか使う時はまた記事書きます。ではでは。

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