20190226のHTMLに関する記事は5件です。

Re:ゼロから始めるポートフォリオ開発【Vue.js】

概要

Web初心者がVue.jsでポートフォリオを作ってみた
2019/2/26 レスポンシブ対応してないので注意

準備

前回書いた記事を参照

作成物

ポートフォリオサイト
github

まずコンポーネントを作ろう

とりあえず全ページ共通で使用するものをコンポーネントとして用意すればいいっしょ?
という軽い気持ちで作成。

ヘッダー

Header.vue
<template>
  <div>
    <header class="header">
      <router-link to="/">Top</router-link>
      <router-link to="/about">About</router-link>
      <router-link to="/career">Career</router-link>
      <router-link to="/portfolio">Portfolio</router-link>
      <router-link to="/contact">Contact</router-link>
    </header>
  </div>
</template>

ページタイトル(画面毎に文言が変わるためpropsを設定)

Pagetitle.vue
<template>
  <div class="pagetitle">
    <p> {{pagetitle}} </p>
  </div>
</template>

<script>
export default{
  name:'pagetitle',
  props:{
    pagetitle:{
      type:String,
      default:''
    }
  }
}
</script>

ヘッダーは画面によって文言やデザインが変わる必要は無いのでApp.vueに記載

App.vue
<template>
  <div id="app">
    <Header></Header>
    <router-view/>
  </div>
</template>

<script>
import Header from '@/components/Header'

export default{
  name:'header',
  components:{
    Header
  }
}
</script>

ページの作成

HTMLとCSSでページを作成する。
作成したページタイトルコンポーネントに引数を入れて呼び出す。

トップ画面

Top.vue
<template>
  <div class="top">
    <Pagetitle pagetitle='Top'></Pagetitle>
    <p id="text">Doragon's Portfolio</p>
  </div>
</template>

<script>
import Pagetitle from '@/components/Pagetitle.vue'

export default{
  name:'top',
  components:{
    Pagetitle
  }
}
</script>

Router

作成したページをRouterに設定。

router.js
import Vue from 'vue'
import Router from 'vue-router'
import Top from './pages/Top.vue'
import About from './pages/About.vue'
import Career from './pages/Career.vue'
import Contact from './pages/Contact.vue'
import Portfolio from './pages/Portfolio.vue'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name: 'top',
      component: Top
    },
    {
      path: '/about',
      name: 'about',
      component: About
    },
    {
      path: '/career',
      name: 'career',
      component: Career
    },
    {
      path: '/portfolio',
      name: 'portfolio',
      component: Portfolio
    },
    {
      path: '/contact',
      name: 'contact',
      component: Contact
    }
  ]
})

main.jsにrouterの宣言を忘れずに。

main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

作成したポートフォリオを公開する

今回はgithub pagesにデプロイする。
ソースコードを公開する代わりに無料でwebページを公開してくれる優しいやつ。

vue.config.jsをディレクトリ直下に作成
このままbuildすると/dist直下にビルドファイルが作成されてしまう。
github pagesでは/docs直下のものが公開対象となるため設定を変更する。

vue.config.js
module.exports = {
  baseUrl: process.env.NODE_ENV === 'production'
    ? '/portfolio/'
    : '/',
  outputDir: 'docs',
};

コマンドラインでビルドを行う

$npm run build

github
/docs直下にビルドされたファイルが作成された事を確認した後、masterにpush。
githubのsettingを以下のようにする。
スクリーンショット 2019-02-26 23.28.15.png
反映まで少し時間がかかるので注意
せっかちな人は以下の記事を参考にしてください。
https://qiita.com/shge/items/ac20f45c9e8e0b4f33cc

一言

レスポンシブ対応がまだ出来ていないので後々やります・・・。
SASSも使いたかったけど、全く使っていない・・・。
HTMLとCSSとJSはまだまだ初心者レベルなので修行が必要だと痛感した。
Node.jsも勉強してwebに強くなりたい。

参考文献

https://qiita.com/plus_kyoto/items/a01578b782f17f573510
https://qiita.com/shge/items/ac20f45c9e8e0b4f33cc

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

複数のフォームデータを、javascriptを使って1つのボタンで送信する方法

複数フォームの内容をワンクリックで送信する方法を書きます。

結論
submitボタンのフォームにhiddenでフォームの数だけinputを作り、
ボタンを押した時にjavascriptを呼び出して、
入力されているフォームの内容を、json形式にして、
hiddenのinputのvalueにセットする。

そうすることで、複数フォームの内容をワンクリックで送信することができる。

例えば、

<select id="select">
  <option value="0" >0</option>
  <option value="1" >1</option>
  <option value="2" >2</option>
  <option value="3" >3</option>
  <option value="4" >4</option>
</select>

<textarea id="textarea"></textarea>

<form method="POST" action="/worktimes/edit/" id="post_form">
    <input type="hidden" name="form1" value="" />
    <input type="hidden" name="form2" value="" />
    <button type="submit" >送信</button>
</form>

という2つのフォームがあったら、
呼び出したjsファイル内で下記の処理を書く

// 送信ボタンが押された時に呼び出されるjsファイル内の関数
$('#post_form').submit(function () {
        var selectJsonData = get_select_data_by_json(); // selectの内容をjsonで取得
        $("input[name='form1']").val(selectJsonData); // form1のvalueにselectの内容をセット
        var textareaJsonData = get_textarea_data_by_json(); // textareaの内容をjsonで取得
        $("input[name='form2']").val(textareaJsonData); // form2のvalueにtextareaの内容をセット
        return true;
});

function get_select_data_by_json() {
    var select = $( '#select' + i ).val(); // idを元にselectのvalueの内容を取得
    return JSON.stringify(select); // JSON形式でリターン
}

function get_textarea_data_by_json() {
    var element = document.getElementById( 'textarea' ); // idを元にselectの内容を取得 
    var textarea = element.value; // selectのvlueを取得
    return JSON.stringify(textarea); // JSON形式でリターン
}

保存ボタンクリック→js関数実行→フォームのvalueに値セット→アクション実行
という処理の流れなので、
コントローラ側に値が渡される。

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

プログラミング初心者の2月

この記事について

プログラミングとは何なのか1も知らなかった人間が新しい道を歩き始めた経緯を簡単に記録します。
(初の投稿で自分記録用として書いているので間違えているところや勘違いしているところも多いと思います。)

プログラミングを始めたきっかけ

私は大学の時工学部所属で勉強はしていましたが、プログラミングや機械、電子、物理などの工学系ではなくどちらかというとデザインが重視される建築設計をやっていました。その後、建築の模型製作の仕事を過ぎ物の表現、具現に興味を持つことになりました。建築模型の場合、最終の提出用ぐらいが実際多くの人に見られみんながものを短く感じさせることが可能かと思います。世界を動かすとか世界的に使われたいとかの大きいことではなくても使ってくれる人々から「お、こういう機能も使えるんだ。」とか「前より使いやすくなったな。」を言われることやりたいと思うところプログラミングを勉強させていただくいい機会が来て本格的にこちらの道に関しての勉強を始めました。

勉強の進み

この文章を書くのはちょうど私が勉強始めて1か月が経っている時点です。
パソコンを使ってないわけではありませんが、目に見えるように具現化されているのを主に使ったため、そのプログラムがどういう原理で動いているのかについてはあまり考えてみたことがありませんでした。

少しでも手をかけてみたことを整理してみると....

-Python 3.7
-HTML(css,bootstrap)
-MySQL
-Bottle

どれも基本の中での基本的なところですが、それぞれを組み合わせての使い方について勉強させていただきました。

最初は本やネットの講座、先生の説明でしたが、やっぱり基本的なことを勉強した後時間をかけて自分でtitleをつけて書いて動かしての添削が効果的だったと思います。先生と一緒に書いたり本のどおりやっているときに「わー!できた。面白い!」と言っても翌日、勉強したことの応用の課題が与えられたら「ああ、私なんかアホやな、、、」と落ち込むことがかなりあったからです。もちろん自分ができそうな機能ばかりいれてしまってそれ以上の勉強進行にはよくないのでないかと悩みましたが、まず自分ができることもなかったらこれ以上のことを聞いても実際使えないと思ったので新しいものを勉強させてもらいながら時間を取って自分なりのものを作っています。少しずづ新しいものをいれながら(もちろん、見た目的にも機能的にも人に見せれるものではありませんが...)楽しんでいます。

3月やってみたいこと

  1. 上記のプログラム以外にも最近JavaScriptを始めようとしています。
    書き方としてPythonと違いどちらにも慣れないのが一番心配ではありますが(フランス語とドイツ語を同時に勉強したときの感覚です。)、やっぱり単独で使うよりシナージ効果が出るのではないかと楽しみです。

  2. 今まで勉強してきたものを全部混ぜて自分なりで書いてみているものが2つあります。こちらをもうちょっと実用的に使えることができるようになったら知り合いに使わせてもらいたいです。

  3. 可能な状況が来るかどうかは知りませんが、私がこちらの勉強を始める前に模型製作作業の中でレーザカッターをつかったり3D模型を作ったりAutoCADで設計図を書いたりすることが多かったです。これを使ってラズベリーパイのケースだったりこういう回路などのケースも作ってみたいと思います!

ETC.

始めったばかりものの記事を読んでいただきありがとうございます。
新しい道へと挑戦するところですが、楽しみながら難しいところも乗り越えていきたいと思います。

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

S3でWebSite Hostingした時にIPアドレスで制限をかける

S3にstaticなHTMLをおいて、それを特定のIPからのみ見えるようにする方法。

いつも迷ってしまうので備忘録で書いておく。

Access Control List は変更する必要なし。( List objects の everyone も - のまま)
バケットの中のファイルもPrivateな設定のままでOK。

{
    "Version": "2012-10-17",
    "Id": "Policy1551163444222",
    "Statement": [
        {
            "Sid": "Stmt1551163442413",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::[Bucket Name]/*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": "[IP]"
                }
            }
        },
        {
            "Sid": "Stmt1551164678580",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::[Bucket Name]/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "[IP]"
                }
            }
        }
    ]
}

いつも思ったように動かなくてもがくのは Resource 部分の最後に /* をつけ忘れて、ファイル見れないじゃん…ってなる。

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

僕の考えた最強のService Workerキャッシュ戦略で爆速サービスを作った

はじめに

聖地の写真を共有するサービス「Holy Place Photo」にService Workerによるキャッシュを実装しました。

そのためネットワークがオフラインでも下記のようにサクサク動きます!!!!

また、LighthouseのPWAのスコアは100を取れました。

スクリーンショット 2019-02-25 22.10.58.png

この記事ではService Workerを利用したキャッシュの設計、Laravel + Vue.jsのWebアプリケーションでの実装を解説していきたいと思います。

注意書き

  • 僕の考えた最強のService Workerのキャッシュの設計のため唯一の正解ではありません
  • キャッシュの設計はサービスによって違うと思うのでこの記事は一つの参考にしてください

Service Worker

まずService Workerについて簡単に解説します。

※今回はスコープについてはあまり説明しません

Service Workerとは

Service WorkerはWebページのバックグラウンドで動くスクリプトです。

オフライン対応・バックグラウンド同期・プッシュ通知などの機能を持ちます。

Progressive Web Appとセットでよく聞くと思います。

ここで注意してほしいのはService WorkerはリクエストにaddEventListenerを貼ることができるだけで、キャッシュする機能はService Workerには含まれていないという点です(キャッシュについてはCache Apiの項目で詳しく説明します)

詳しくはこちら。

Service Worker の紹介 | Web Fundamentals | Google Developers

Service Workerのライフサイクル

Service Workerはウェブページとは異なるライフサイクルで動作します。

スクリーンショット 2019-02-25 22.40.49.png

Service Workerの登録(Install)

Serivce Workerを登録するためにはnavigator.serviceWorker.registerを利用します。

app.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw.js').then(() => {
      console.log('登録成功');
    },() => {
      console.log('登録失敗');
    });
  });
}
sw.js
self.addEventListener('install', (event) => {
  console.log('インストール');
});

登録が成功するとinstallが実行されます。

このinstallは一度だけ発生します。

installは静的なアセットをキャシュするのによく使われます。

Service Workerによる制御(Activate)

installが成功、またはすでにinstallが成功しているページを表示するとactivateが実行されます。

installが成功 = 初回のアクセス時
installが成功しているページを表示する = 2回目以降のアクセス時

activateは古いキャッシュを削除したりするのによく使われます。

activateが完了するとService Worker はそのスコープ内のすべてのページを制御します。

た だ し !

Service Workerを登録時点では(初回のアクセス時)制御されず、次に読み込まれた際に制御されるようになります。

初回にService Workerの制御が始まらない理由は「Service Worker 登録」に記載されています。

新しい Service Worker スレッドを開始してリソースのダウンロードとキャッシュをバックグラウンドで実行すると、ユーザーがはじめてサイトにアクセスしたときに迅速なインタラクティブ エクスペリエンスを提供するという目標に逆行することになります。

初回のアクセスの際はブラウザはバックグラウンドでスクリプトを実行している場合ではないぞ、と書かれています(個人的解釈)

初回アクセス時にService Workerの制御を開始させる

実はactivate時にclients.claim()を実行すると、強制的にService Workerの制御を開始できます。

sw.js
self.addEventListener('activate', (event) => {
  event.waitUntil(self.clients.claim());
});

Service Workerの更新

Service Workerが更新されていると判断される条件は、1ByteでもService Workerのスクリプトに変化があった際です。

更新を検知すると新しいService Workerは待機状態になります。

待機状態から新しいService Workerの制御を開始するためには、すべてのタブを閉じるか、現在開いているページから別のページに移動する必要があります(ページの更新・リロードするだけでは制御は開始されません)

そのため、新しいService Workerの制御を開始させるためには1step必要になります。

Service Workerの更新時に即座に制御を開始させる

install時にskipWaiting()を実行すると、新しいService Workerの制御を即座に開始させられます。

sw.js
self.addEventListener('install', (event) => {
   event.waitUntil(self.skipWaiting());
});

Service Workerによるオフライン対応

なぜ、Service Workerを導入するとオフライン対応が可能になるのでしょうか?

それはService Workerはネットワークへのアクセスを捕まえることができるためです。

Service Workerが制御している状態で、ネットワークへのアクセス(ページ遷移、リソースの取得など)が発生すると、fetchイベントが発火します。

sw.js
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then((response) => {
      if (response) {
        return response;
      }
      return fetch(event.request);
    })
  );
});

Service Workerはこのfetchイベントに対してaddEventListenerを貼ることができます。

上記例ではキャッシュにあったらキャッシュから返して、なかったらfetchを実行してネットワークへアクセスし取得します。

このようにfetch内でキャッシュから返すことができるため、あらかじめ全てのリソース(HTML,CSS,JS,IMGなど)をキャッシュしていれば、オフライン状態でもWebページが見れるようになります。

Cache API

次にCache APIについて解説します。

2度目になりますが、Cache APIはService Workerの機能ではありません。

キャッシュは削除されない限り有効期限切れにはなりません。

キャッシュはcaches.open(CACHE_NAME)を実行しキャッシュオブジェクトを取得して利用します。

詳しくはこちら。

https://developer.mozilla.org/ja/docs/Web/API/Cache

キャッシュの登録

キャッシュへの登録はadd,addAll,putがあります。

const CACHE_NAME = 'v1';

caches.open(CACHE_NAME).then((cache) => {
  return cache.add('/hoge.png');
});

caches.open(CACHE_NAME).then((cache) => {
  return cache.addAll([
    '/fuga.png',
    '/piyo.png'
  ]);
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.match(event.request).then((response) => {
        return response || fetch(event.request).then((response) => {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});

addはURLを受け取り、ネットワークへアクセスして取得後、レスポンスオブジェクトをキャッシュに追加します。

addAllは配列を受け取り、ネットワークへアクセスして取得後、レスポンスオブジェクトをキャッシュに追加します。

putは取得済みのレスポンスオブジェクトをキャッシュに格納します。

addfetch + putのイメージです。

上記例ではService Workerのfetch内で、キャッシュから取得できなかった場合、fetchして、
レスポンスオブジェクトをputを利用しキャッシュに追加しています。

キャッシュの削除

削除はdeleteを利用します。

caches.open(cacheName).then((cache) => {
  cache.delete('hoge.png');
});

キャッシュの利用

追加のところで出ていますがmatchを利用します。

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.match(event.request).then((response) => {
        return response || fetch(event.request).then((response) => {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});

matchdeleteにはオプションが利用できます。

  • ignoreSearch: trueを指定するとクエリストリングを無視するようになります
  • ignoreMethod: trueを指定するとGET,HEAD以外のMETHODが利用できます

僕の考えた最強のService Workerキャッシュ戦略

ということで僕の考えた最強のService Workerキャッシュ戦略を解説していきたいと思います。

今回のキャッシュ戦略のポイントは下記になります。

  • 次のページに遷移する際に必要なリソースは全てキャッシュから返す
    • HTMLの有効期限は1日にする
    • アセット(CSS,JS)は無期限
    • ただしサードパーティのリソースは除く
  • 初回のユーザーもService Workerの制御下に置く

1つずつ解説していきたいと思います。

次のページに遷移する際に必要なリソースは全てキャッシュから返す

次のページで必要なリソースは主に動的なHTMLと、静的なアセット(CSS,JS)とサードパーティのリソースに分類されます。

動的なHTMLは有効期限を1日にして、静的なアセット(CSS,JS)は無期限キャッシュするようにします。

より頻繁に更新されるようなサイトでは、HTMLの有効期限はより短くていいかもしれません。

動的なHTMLのキャッシュ

document.querySelectorAll('.js-sw-fetch').forEach((e) => {
  const url = e.getAttribute('href')
  caches.open(cacheName).then((cache) => {
    cache.match(url).then((response) => {
      if (!response) {
        cache.add(url);
        localStorage.setItem(url, Date.now());
      } else {
        const time = localStorage.getItem(url);
        if (!time || Date.now() - time > 60 * 60 * 24 * 1000) {
          cache.delete(url).then(() => {
            cache.add(url);
            localStorage.setItem(url, Date.now());
          });
        }
      }
    });
  });
});

画面内にあるリンクをDOMから引っこ抜いて、Cache APIを使ってキャッシュに登録します。

キャッシュにない場合はaddを利用して保存し、ローカルストレージに遷移するURLをkeyにしてタイムスタンプを保存します。

キャッシュにある場合は、有効期限が切れていたらdeleteを利用してキャッシュを削除して、addを利用しキャッシュに保存します。

静的なアセット(CSS,JS)

今回FWにLaravelを採用しており、フロントのビルドにLaravel Mixを利用しています。

アセットのバージョン管理にはLaravel Mixのmix.version()を利用しています。

そのためビルドされるファイルにはid=12345といったクエリパラメーターが付与されます。

これはmix-manifest.jsonで管理されており、bladeで利用するmix()はこのmix-manifest.jsonを見ています。

{
    "/js/app.js": "/js/app.js?id=27081b345ad90ec7716a",
    "/css/app.css": "/css/app.css?id=ebe23bcc66ba35ecfe6c"
}

画面表示時に必要なアセットをコントローラーからviewに埋め込み、JSで引っこ抜いてキャッシュに登録させます。

<?php

namespace App\Http\Controllers;

class Controller extends Controller
{

    /**
     *
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function index()
    {
        $manifests = json_decode(file_get_contents(public_path('/mix-manifest.json')), true);
        // swもビルドされているため、除外
        $files = array_filter($manifests, function($key) {
            return $key !== '/sw.js';
        }, ARRAY_FILTER_USE_KEY);
        $swCacheList = json_encode(array_values($files));
        return view('index', compact('swCacheList');
    }
}
<div id="sw" data-sw-cache-list="{{$swCacheList}}"></div>
JSON.parse(document.getElementById('wrapper').dataset.swCacheList).forEach((url) => {
  caches.open(cacheName).then((cache) => {
    cache.match(url).then((response) => {
      if (!response) {
        cache.delete(url.split('?')[0], {ignoreSearch: true}).then(() => {
          cache.add(url);
        });
      }
    });
  });
});

cache.delete(url.split('?')[0], {ignoreSearch: true})により、URLパラメーターが違う同じファイルのキャッシュを削除することで、無期限にキャッシュしているファイルをキャッシュから削除しています。

管理しきれる間は上記方法でいいと思いますが、管理しきれなくなってきた場合は、HTMLをキャッシュする際に、HTMLをパースしてJSとCSSのURLを取得してキャッシュするという動的な方法が良いかもしれません。

初回のユーザーもService Workerの制御下に置く

上記の解説の通りService Workerは通常初回アクセスのユーザーは制御下におきません。

今回は初回のユーザーも制御下に置くために、activate時にclients.claim()を実行します。

また、Service Workerの更新時も初回から更新したService Workerの制御下に置くために、install時にskipWaiting()を実行します。

Service Workerの制御の開始を検知する

install後の制御の開始や、更新後の制御の開始はcontrollerchangeイベントが発火します。

そのため、Service Workerの制御中に何か処理をしたい場合は下記のように書くことで実現できます。

  const controllerChange = new Promise((resolve) => {
    navigator.serviceWorker.addEventListener('controllerchange', resolve);
  });
  navigator.serviceWorker.register('/sw.js').then(() => {
    return navigator.serviceWorker.ready;
  }).then(() => {
    if (navigator.serviceWorker.controller) {
      // 更新時
      return navigator.serviceWorker.controller;
    }
    // 初回
    return controllerChange;
  }).then(() => {
    // Service Workerの制御下
  });

おわりに

サクサクなのは本当に気持ちよく、やってやった感が高かったです。

作った後に、quicklinkという、Intersection ObserverrequestIdleCallbacknavigator.connection.effectiveTypeprefetch使って、リンクを高速に先読みするライブラリを知りました・・・。

最強のService Workerキャッシュ戦略は最強(改)にできそうです・ω・

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