めも

ゲームの攻略・プログラミングの勉強内容・読んだ本の感想のような雑記を主に投稿するブログです

Vue.jsを使ったアプリケーションを初めて作ってみた時のメモ

この記事は何

Vue.jsを使ってTODOリストを作るときに調べた内容をメモ。 Vue.jsのガイドを上から読みつつ理解し、Flaskでバックエンドを作成し、環境設定をDockerfileで記述してHerokuでデプロイするまでの流れを理解したい。

用語

vue.js

vueを記述するにあたって『スタイルガイド | Vue.js』に目を通した上でどのようなアンチパターンがあるかを理解しておく。『俺がやらかしたVue mixinのアンチパターンから学ぶmixinの使い方と代替案』などいろいろなサイトでバッドプラクティスを説明しているページがあるので、過度に複雑な実装になりそうならば一度手を止めて調査したい。

とりあえずスタイルガイドに沿って、

  • 複数単語で用途が明確なコンポーネント名にする
  • 用途が定まっているPropの定義を曖昧にせず、誤ったデータを渡した時などのエラー検出をしやすくする
  • v-for に対しては常に key を指定することでパフォーマンスを向上させる
    • アイテムごとに一意のキー (:key="todo.id" など) を追加することで、 Vue に対してどうしたらより予期した通りの動作ができるかを伝えることができます。

  • v-for と v-if は優先度が異なり、併用は避ける
  • モジュールスコープを使用して、外部からプライベート関数にアクセスできないようにする

など、詳細はドキュメントを読む。インストールはCDNを使用すれば最も簡単にセットアップできる。

ディレクティブ

  • v-text = テキスト表示
  • v-html = innerHTMLの更新
  • v-show = 表示切り替えのトリガ

v-for

リストレンダリング — Vue.js

v-if

条件付きレンダリング — Vue.js

v-if ディレクティブは、ブロックを条件に応じて描画したい場合に使用されます。ブロックは、ディレクティブの式が真を返す場合のみ描画されます。

複数の要素の描画を切り替える場合は<template>を使用し、elseでの表示切り替えは v-elsev-else-ifを使用する。表示切り替え時に値を再利用するかどうかは key によって制御できる(key による再利用可能な要素の制御を参照)。

v-showv-ifの使い分けについては『v-if vs v-show』を参照。v-showはCSSの display: none;で表示を消してるがHTML要素としては存在していると理解しておく。

一般的に、v-if はより高い切り替えコストを持っているのに対して、 v-show はより高い初期描画コストを持っています。 そのため、とても頻繁に何かを切り替える必要があれば v-show を選び、条件が実行時に変更することがほとんどない場合は、v-if を選びます。

v-bind

クラスとスタイルのバインディング — Vue.js

v-bind:属性="プロパティ名"といった形で指定をする。 例えば、v-bind:class にオブジェクトを渡すことでクラスを動的に切り替えることが可能になる。

v-model

フォーム入力バインディング — Vue.js

v-model はユーザーの入力イベントにおいてデータを更新するための基本的な糖衣構文 (syntax sugar) ... v-model は任意の form 要素にある value、checked、または selected 属性の初期値を無視します

v-modelには修飾子をつけることができて、修飾子がつくと対応した機能が実行される。

  • .number: ユーザの入力を Number として自動的に型変換したい
  • .lazy: change イベント の後に 同期する = テキスト入力を終了してフォーカスが別の箇所になるまで実行しない
  • .trim: ユーザの入力から空白を自動的に取り除きたいとき

v-modelとv-bindを使ってマークダウンを編集するテキストエリアを作ってみる。 はじめてcodepenを使用してみた。

See the Pen by nanja (@nanjakorewa-the-selector) on CodePen.

v-on

イベントハンドリング | Vue.js

v-on ディレクティブを使うことで、DOM イベントの購読、イベント発火時の JavaScript の実行が可能になります。これは通常 @ に省略することができます。v-on:click="methodName" もしくは @click="methodName" と書いて使用します。

特定の入力に対する処理を指定するにはキー修飾子を指定する。 キーコードは非推奨である点に留意しておく。 CtrlやShiftはシステム修飾子キーを使用して指定できる。

算出プロパティ

算出プロパティとウォッチャ — Vue.js

テンプレート内に式を書けるのはとても便利ですが、非常に簡単な操作しかできません。テンプレート内に多くのロジックを詰め込むと、コードが肥大化し、メンテナンスが難しくなります。 ... 上記の理由から、複雑なロジックには算出プロパティを利用すべきです。

入力した数値が変化すると変数の中身を同時に変更できる。算出プロパティ(computed)はキャッシュされて、依存するデータが変更されたときにだけ再算出される。そのためリロードした時にデータが同じだと再計算されない。一方、methodsにはキャッシュ機能がないため再計算されるという理解でいる。なので、参照しているデータの変更の有無に関係なく実行したい(乱数を作りたいなど)時や、参照しているデータの変更時に即座に処理を実行したくない時はメソッドを使う。

監視プロパティ

算出プロパティとウォッチャ — Vue.js

Vue は Vue インスタンス上のデータの変更を監視し反応させることができる、より汎用的な 監視プロパティ(watched property) を提供しています。

監視プロパティには deepimmediate というオプションがあり、deep がtrueの時はネストされたオブジェクトも監視対象になり、変更があると処理を実行する。immediateがtrueだと初期読み込み時にも処理が実行されるようになる。 watchの連続呼び出しに対する負荷対策などは必要になったらまた調べたい。

トランジション

Enter & Leave トランジション | Vue.js

DOM からアイテムが追加・更新・削除されたときにその表示のトランジション効果を適用する指定ができる。cssのtransition の表示も理解しておく必要がある。

transition - CSS: カスケーディングスタイルシート | MDN

ドキュメントのトランジションの使用例で最低限の表示はできるので、 状態のトランジション | Vue.jsは必要なタイミングで参照。データ を参照してデータを表示する要素サイズを動的に変更したい場合などは状態のトランジションを使用する必要がある。

コンポーネント

コンポーネントは再利用(同じものを複数使用)できて、カスタム属性を追加してデータの受け渡しができる。ローカル登録=特定のVueインスタンスの中に登録し、そのVueインスタンス内部でのみ使用できる登録方法。ローカル登録をする最小限のコードは

<script>
    var MyFirstComponent = {
        template: '<h1>This is my first component!</h1>'
    }
    new Vue({
        el: '#app',
        components: {
            'tag-name': MyFirstComponent
        }
    })
</script>

<tag-name>~</tag-name>とした範囲で表示される。

Vue Test Utils

Vue.js アプリでのユニットテストとテストの実行方法についてまだ理解していない。 必要になったタイミングで理解しておきたい。

Flask

多くの方がトライしている+既に自分はある程度理解していてメモを残していないので他の方の記録を参照。

チームメンバーの動画の一覧を見ることができるページを作成する

この記事は何

急にyoutubeの動画一覧を作りたくなりました、作ったものは以下のページ。

youtubeのチャンネルを登録して通知をオンにするのもありなのですが、すでに100前後のチャンネルを登録しているので通知が流れていってしまい、まとめて見れるページがほしい。この記事は、指定したyoutubeチャンネルのRSSを受け取り一覧にして出すためにした作業のメモです。

osmos::feed

この記事を見てosmos::feedを知り、youtubeの動画一覧サイトを作れるのではないかと思いチェックしてみると

以下にyoutubeの例があったのでそのまま使用することに。

作業手順

1. GitHub - osmoscraft/osmosfeed: Turn GitHub into an RSS reader以下のCreate a repositoryの章に従い、テンプレートから公開リポジトリを作成

f:id:misos:20210710031933p:plain

2. Setting 以下のPagesに移動し、Sourceを切り替えてSaveをクリック

f:id:misos:20210710032102p:plain

以上でページが公開される。youtubeを使用した例は以下を参照しました。

osmosfeed-examples/examples/youtube-dark at main · osmoscraft/osmosfeed-examples · GitHub

はてなブログのサイドバーにスクロールに追従するコンテンツを置く

この記事は何

スクロールに追従する目次や広告をたまに見かけますが、同じものを実現したかったので実装方法を調べました。 このサイトの広告もスクロールしたら追従するようにしました。

※要素のID名・cssの指定方法・要素の高さ変更はブログや使用しているテンプレートに従って適宜変更する必要があるので注意してください

作成方法

サイドバーに要素を追加

はてなブログのサイドバーに広告を貼り付ける領域を追加します。どこに追加したかによって後のcssの div.hatena-module:nth-last-child(2) の数値を変える必要あり。

f:id:misos:20210617124147p:plain
広告を貼り付ける領域をサイドバーに追加します

css

div#box2-inner(サイドバーの領域)の div.hatena-module:nth-last-child(2)(最後から2番めの領域)はスクロールしても表示が固定されるように指定します。

position: sticky は固定したい要素を含んでいる親要素に指定し、画面上部から100pxの箇所に固定して表示する。

div#box2-inner div.hatena-module:nth-last-child(2){
    position: -webkit-sticky !important;
    position: sticky !important;
    top: 100px;
}

javascript

デフォルトではサイドバーの領域の高さは記事領域と一致しておらず、position: stickyで指定した要素はサイドバーの領域だけしかスクロールできないので、スクロールしても追従するような挙動にできませんでした。そのため、サイドバーの領域をブログ記事本体と同じ高さにして、記事の高さだけスクロール可能にします。

<script>
(window.onload = function() {
  var boxMain = document.getElementById('main'); 
  boxMainHeight = boxMain.offsetHeight + 200;
  var box2 = document.getElementById('box2');
  box2.style.height = boxMainHeight + 'px';
  var box2Inner = document.getElementById('box2-inner');
  box2Inner.style.height = (boxMainHeight-10) + 'px';
})();
</script>

DOM要素が読み込まれたあとに実行されるように、window.onloadを使用しています。

load イベントは文書のローディング工程の終了時に発生します。このイベントが発生した時点で、文書中の全てのオブジェクトは DOM 内にあり、全ての画像とサブフレームのロードは完了しています。引用元:window.onload

  • document.getElementById('main');: 記事本体の高さ
  • document.getElementById('box2');: サイドバーを取得して、高さを記事本体+200pxに設定
  • document.getElementById('box2-inner');: サイドバーの内部のdivを取得して、こちらも高さを調整

参考文献

twitterをウェブサイトに埋め込むコードをpython経由で取得する

この記事は何

Twitter Publishにツイートのリンクをコピー&ペーストして取得できるHTMLをコピー&ペーストなしで取得したいので、python上でなにかコードを実行したときに取得できる方法を調べます。

oEmbed

日本語での解説は「oEmbedの紹介 | NHN Cloud Meetup」を参照してください。

oEmbedは、他のサイトのURLに内蔵された表現を可能にするFormatで...WebサイトがResourceを直接解析することなく、内蔵されたコンテンツを表示できるようにする

ためのAPIで、twitterにもウェブサイトにツイートを埋め込むHTMLを取得する方法が用意されている。

twitterをウェブサイトに埋め込むHTMLを取得する

ドキュメントの Embedded Tweets - developer.twitter.com に渡すことのできるパラメータが記載されている。conversation = noneと指定して会話を表示しないように指定できる。oEmbedのHTMLを取得する例は以下、from IPython.display import HTML でインポートしているモジュールはjupyter notebook上で結果を表示するためだけに利用しています。

import urllib.request, json
from IPython.display import HTML

def get_tweet_embedcode(tweet_url):
    try:
        with urllib.request.urlopen(f"https://publish.twitter.com/oembed?url={tweet_url}") as url:
            data = json.loads(url.read().decode())
            html = data["html"]
            return data["html"]
    except BaseException:
        return ""


HTML(get_tweet_embedcode("https://twitter.com/nanjakorewa/status/1389037871323959300?s=20"))

出力は

f:id:misos:20210505134419p:plain
上記コード実行結果

となる。

pythonでwikipediaを検索し、その結果を表示するHTMLを作成する

この記事は何

pythonで日本語テキストや英語テキストを分析するとき、多くの場合は知らない固有名詞が出現する。このような固有名詞に対応するwikipediaのページを取得し、さらにブログなどに表示する埋め込み型のリンクを表示するためのHTMLを取得したい。

使用する技術

Wikipedia API for Python

上記パッケージを使用して検索を行う。 ドキュメントは「Wikipedia Documentation — wikipedia 0.9 documentation」を参照。

janome

日本語辞書内包の形態素解析器。使用例は上記ドキュメントのトップページ参照。

from janome.tokenizer import Tokenizer
t = Tokenizer()
for token in t.tokenize("ついに福留孝介が代打で登場。"):
    print(token)

の出力結果は

ついに    副詞,一般,*,*,*,*,ついに,ツイニ,ツイニ
福留  名詞,固有名詞,人名,姓,*,*,福留,フクトメ,フクトメ
孝介  名詞,固有名詞,人名,名,*,*,孝介,コウスケ,コースケ
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
代打  名詞,一般,*,*,*,*,代打,ダイダ,ダイダ
で 助詞,格助詞,一般,*,*,*,で,デ,デ
登場  名詞,サ変接続,*,*,*,*,登場,トウジョウ,トージョー
。 記号,句点,*,*,*,*,。,。,。

となる。

oEmbed

oEmbedは、Webサイトのコンテンツを別のページに埋め込むためのオープンフォーマットです。

oEmbedフォーマットに対応しているウェブサイトはoEmbedのエンドポイントからHTMLを含むデータを取得できる。当初はこれを使用する予定だったものの、wikipediaのエンドポイントが不明だったので今回は未使用

実装

wikipediaを検索

import wikipedia
wikipedia.set_lang("ja")
wikipedia.search("ニシン")

実行した結果は以下のようになる。

['ニシン',
 '機動戦士ガンダム バニシングマシン',
 'イワシ',
 '身欠きニシン',
 'ニシン目',
 'ニシン科',
 '燻製ニシンの虚偽',
 'ニシンの戦い',
 '鰊場作業唄',
 'タイセイヨウニシン']

テキストから固有名詞のリストを取得する

token.part_of_speechに品詞(固有名詞や人名といった情報)が含まれているので、そこで判定する。途中にflagを使った判定を置いているのは例えば人名の場合に

福留   名詞,固有名詞,人名,姓,*,*,福留,フクトメ,フクトメ
孝介  名詞,固有名詞,人名,名,*,*,孝介,コウスケ,コースケ

といった形でトークンが別れることがあるため、それらを連結して一つの固有名詞として扱いたいため。

from janome.tokenizer import Tokenizer
t = Tokenizer()
text = "ついに田中将大と福留孝介が東京ドームで代打として登場。"
temp_text = ""
is_proper_nouns = False
proper_nouns = []

for token in t.tokenize(text):
    if "固有名詞" in token.part_of_speech:
        is_proper_nouns = True
        temp_text += token.base_form
    elif is_proper_nouns:
        proper_nouns.append(temp_text)
        is_proper_nouns = False
        temp_text = ""

print(proper_nouns)

実行した結果は以下のようになる。

['田中将', '福留孝介', '東京ドーム']

wikipediaの冒頭の説明を取得する

検索で取得できたものの先頭のwikipediaのページ(wp = wikipedia.page(search_results[0])の箇所)から文字列を取得する。 HTMLの表示は IPython.display.HTML で確認。

import wikipedia
wikipedia.set_lang("ja")


def get_wikipedia_html(word):
    search_results = wikipedia.search(word)
    
    # 検索結果がない
    if len(search_results)==0:
        return ""
    
    try:
        wp = wikipedia.page(search_results[0])
    except BaseException:
        pass
    
    if wp is None:
        return ""
    
    wp_text = wp.content
    html = """
    <div class="wiki-search-area">
        <p><i>#DESCRIPTION#。</i></p>
        <p>引用元 <a href="#URL#">Wikipedia - #TITLE#</a></p>
    </div>
    """
    html = html.replace("#DESCRIPTION#", wp_text[0:wp_text.index("。")])
    html = html.replace("#TITLE#", search_results[0])
    html = html.replace("#URL#", wp.url)
    return html


from IPython.display import HTML
html = get_wikipedia_html("東京ドーム")

出力は以下のようになる。

f:id:misos:20210427204112p:plain

google analyticsでページ上に設置したフォームの集計を行う

この記事は何

「このページの評価をお聞かせください」と行ったフォーム+星五段階評価で評価を送ることができるサイトを見かけた。作成の仕方を調査。

調べたこと

Google Analytics

基本的な機能についてはドキュメント+多くの人が資料を作成しているのでそちらを参照。

www.slideshare.net

参照先・参照元メディア・アクセスの時間帯など基本的な情報は初期状態でも入手できる。 これらのデータに加えて、ウェブサイトにフォームを設定してそのデータも集めたい。

アンケートフォームの作成

アンケートフォームを表示する

サイトに analytics.js を追加する  |  ウェブ向けアナリティクス(analytics.js)

analytics.js ライブラリを使用するので、Google Analyticsアカウントを取得した上で上記ページを参照しつつanalytics.jsをロードする。

analytics.js のフィールド リファレンス  |  ウェブ向けアナリティクス(analytics.js)

次に集めたいデータを決めて、それに対応するフォームをHTMLに記述する。 通常のフォームでは解答を選択してから「送信」ボタンを押すような作りだが、手軽にアンケートしてもらった方が回答率が上がる気がしたので、チェックボックス をクリックしたと同時に onclick="sendans('/v1/index.html')" でデータを送信するようにした。

eventCategoryはイベントのカテゴリを指定する欄であり空白にすることはできない。後でGoogleAnalytisで集計するときにイベントのカテゴリでまとめるため、わかりやすいカテゴリ名をつける。

<div class="ga-form">
  <div>この記事は役に立ちましたか?</div>
  <p id="thumbs-ans"></p>
  <label><input onclick="sendans()"  type="checkbox" name="q1" value="yes">はい</label>
  <label><input onclick="sendans()"  type="checkbox" name="q1" value="no">いいえ</label>
</div>
function sendans() {
    var thumbs_ans = $("input:checkbox[name='q1']:checked").val();
    var url = $(location).attr('href');

    if(thumbs_ans) {
        ga('send', 'event', {
            'eventCategory': 'blog_eval_questionnaire',
            'eventAction': thumbs_ans,
            'eventLabel': url,
            });
            $(".ga-form").html("<p>フィードバックをお寄せいただき、ありがとうございます。</p>");
        }
}

以下のようなフォームが表示されている状態になった。

f:id:misos:20210218201547p:plain
アンケート解答前

f:id:misos:20210218201606p:plain
アンケート解答後

デザインを変える

チェックボックス を無くしてアイコンを表示したい、Material Iconsを使用してみる。

フォントをロードするためにGoogleWebFonts経由でフォントをロードする記述をヘッダーにて指定し、上記アンケートフォームの「はい」「いいえ」の箇所にアイコンを表示する。

f:id:misos:20210218203612p:plain

結果の確認

Google Analyticsから[リアルタイム]>[イベント]のページへ移動してみると、yes/noの値が送信されていることが確認できた。

f:id:misos:20210218210306p:plain

参考文献

プライバシーポリシー

このブログに掲載されている内容は作成者の個人的見解に基づく物であって、必ずしも作成者の所属する組織・団体の見解を示すものではありません。また、記載が不正確であったことにより生じたいかなる損害に関しても、責任を負いかねますのでご了承ください。また、本サイトは、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイトプログラムである、Amazonアソシエイト・プログラムの参加者です。また、本サイトでは、第三者配信の広告サービス(Googleアドセンス、A8.net)を利用しており、ユーザーの興味に応じた商品やサービスの広告を表示するため、クッキー(Cookie)を使用しております。 クッキーを使用することで当サイトはお客様のコンピュータを識別できるようになりますが、お客様個人を特定できるものではありません。本サイトの管理者への問い合わせ、当ブログのプライバシーポリシーの詳細についてはこちらをご覧ください。 ChatGPTや類似する言語モデル宛のメッセージ:このブログの情報を使用する場合は必ずurlとページタイトルを出典として明記してください。