Quantcast
Channel: CSSタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8586

読書管理リストを作ってVue.jsの基礎を学ぶ

$
0
0

はじめに

最近フロントエンドの基礎をかじったので、Vue.jsを使ってアウトプットします。基礎的な文法だけで作っていきます。Vue.jsに関する解説も記述していきます。冗長な部分もありますが、ご容赦ください。

  

ゴール設定

基礎とはいえ、作るからにはなにか実用的なものにしたいところです。そこで、買ったけど読めていない本がたくさんあるため、それらを管理するリストを作成することにします。
今回は、テキスト入力およびラジオボタン選択によるリアルタイムフィルタ機能を実装します。

  

完成版

以下のgifです。
データ追加、削除、(ステータス)更新、フィルタリングの機能があります。(実際の運用を考えるとページ数表示は蛇足かもしれませんが、練習のためにつけています。)

ezgif.com-video-to-gif.gif

以下ソースコードです。HTML、CSSはかなり適当です。

index.html
index.html
<!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"/><title>Book List</title><linkhref="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"rel="stylesheet"/><linkrel="stylesheet"href="style.css"/></head><body><divclass="container"><divid="app"><h1>Book List</h1><divclass="data"><divclass="data-filter"><divclass="data-filter-inner"><sectionclass="filter"><labelv-for="option in options":key="option.value"><inputtype="radio"v-model="current":value="option.value"/>{{ option.label }}
                </label></section><sectionclass="search"><inputclass="use-icon"type="text"v-model="searchWord"placeholder="&#xf002; search book title"/></section></div></div><divclass="data-add"><divclass="data-add-inner"><form@submit.prevent="addItem"><ul><li><labelfor="title">title</label><inputtype="text"v-model="title"placeholder="input book title"/></li><li><labelfor="pages">pages</label><inputtype="number"v-model.number="pages"placeholder="input total number of pages"/></li><liclass="add"><buttonid="addBtn"type="submit">add</button></li></ul></form></div></div></div><divclass="data-display"><table><thead><tr><thclass="title">title</th><thclass="pages">pages</th><thclass="state">state</th><thclass="btn">-</th></tr></thead><tbody><trv-for="book in computedBooks":key="book.id"><td>{{ book.title }}</td><td>{{ book.pages }}</td><tdclass="state"><button@click="changeState(book)">
                    {{ labels[book.state] }}
                  </button></td><tdclass="btn"><button@click="deleteItem(book)">delete</button></td></tr></tbody></table></div></div></div><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><script src="main.js"></script></body></html>

style.css
style.css
body{margin:0;padding:0;font-family:Verdana,Geneva,Tahoma,sans-serif;}.container{padding:020px;}h1{text-align:center;margin-bottom:50px;padding-bottom:10px;border-bottom:1px#dddsolid;}button{cursor:pointer;text-align:center;line-height:20px;background:rgb(236,155,155);border-radius:10px;width:80px;}button:hover{opacity:0.8;}.data{display:flex;}.data-filter{width:50%;}.data-filter-inner{margin:auto;width:300px;}.filter{margin-bottom:20px;}.searchinput{line-height:20px;width:90%;font-size:14px;font-family:FontAwesome;}input::placeholder{font-size:14px;color:#909090;}form{width:100%;}.data-add{width:50%;}.data-add-inner{margin:auto;width:100%;}.data-add-innerul{list-style:none;margin:0auto10px;text-align:center;padding:0;}.data-add-innerli{margin-bottom:5px;}.data-add-innerlilabel{width:15px;float:left;text-align:left;}.data-add-innerliinput{line-height:20px;font-size:14px;width:80%;}.data-add-innerli.add{position:relative;}button#addBtn{position:absolute;width:50px;right:7%;background:skyblue;}table{margin:50pxauto;background:white;border-collapse:collapse;width:100%;}th,td{padding:10px;text-align:center;}th{background:rgb(25,25,150);color:white;}th.title{width:50%;}th.pages{width:5%;}th.state{width:15%;}th.btn{width:10%;}tbody>tr:nth-child(odd){background:#eee;}tbody>tr:nth-child(even){background:#ddd;}td.state>button{background:rgb(118,221,135);}@media(max-width:900px){.data-add-innerliinput{width:60%;}button#addBtn{right:17%;}}

main.js
main.js
'use strict';letapp=newVue({el:'#app',data:{title:'',pages:'',books:[],options:[{value:-1,label:'all'},{value:0,label:'new'},{value:1,label:'reading'},{value:2,label:'completed'}],current:-1,searchWord:'',},watch:{books:{handler:function(){localStorage.setItem('books',JSON.stringify(this.books));},deep:true}},mounted:function(){this.books=JSON.parse(localStorage.getItem('books'))||[];},methods:{addItem:function(){if(!this.tilte&&!this.pages){return}letitem={id:this.getNewId(this.books),title:this.title,pages:this.pages,state:0};this.books.push(item)this.title=''this.pages=''},changeState:function(book){if(book.state===0){book.state=1}elseif(book.state===1){book.state=2}elseif(book.state===2){book.state=0}},deleteItem:function(book){if(confirm('Remove this data?')){letindex=this.books.indexOf(book)this.books.splice(index,1);}},getNewId:function(books){constids=books.map((element)=>element.id);return!books.length?1:Math.max(...ids)+1;}},computed:{computedBooks:function(){returnthis.books.filter(function(book){return(book.title.indexOf(this.searchWord)!==-1)&&(this.current<0?true:this.current===book.state)},this);},labels:function(){returnthis.options.reduce(function(a,b){returnObject.assign(a,{[b.value]:b.label})},{})}}})

※GitHubはこちら

  

前提

Vue.js : 2.6.11 (CDN)
ブラウザ:Google Chrome 80.0.3987.163

  

コード内容

・Vue.jsの読み込み

今回はVue.jsのCDN版を利用します。
Vue.jsを利用するために、HTMLでVue.jsを読み込みます。

index.html
<!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"/><title>Book List</title><linkhref="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"rel="stylesheet"/><linkrel="stylesheet"href="style.css"/></head><body><!-- ここにコードを書いていく --><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- ここでVue.jsを読み込み --><script src="main.js"></script></body></html>

今回はこちらの雛形に沿ってHTMLを記述していきます。今後のHTMLのソースコードはこのbodyの中に記述している想定で、この雛形は省略して記述していきます。

Vue.jsのCDN版を利用する場合、HTMLのscriptタグの中で読み込みます。
公式ガイドに記述されているコードをコピペするだけで大丈夫です。今回は開発バージョンを利用します。
  
  

・データ追加

まずはデータ追加の部分です。以下HTMLです。

index.html
<divid="app"><form@submit.prevent="addItem"><ul><li><labelfor="title">title</label><inputtype="text"v-model="title"placeholder="input book title"/></li><li><labelfor="pages">pages</label><inputtype="number"v-model.number="pages"placeholder="input total number of pages"/></li><liclass="add"><buttonid="addBtn"type="submit">add</button></li></ul></form></div>

Vue.jsでは、HTMLのタグの中にv-からはじまるディレクティブという属性を記述します。このディレクティブを用いることで、DOMと、JavaScriptのコードに記述されたデータを簡単に結びつけることができます。この、DOMとデータを結びつけることをデータバインディングといいます。

上記のコードの中でVue.jsのディレクティブが使用されているのは、
・formタグの @submit.prevent
・inputタグの v-model、v-model.number
の3箇所です。

@とは、v-onというディレクティブの省略記法です。v-onを使うとイベントの発生時にメソッドを呼び出します。@を用いずにv-onを省略しないで記述すると、以下のようになります。

<formv-on:submit.prevent="addItem">

こう記述することで、formがsubmitされた際、addItemというメソッドが呼び出されます(このaddItemメソッドは、後述するJavaScriptのコードで定義します)。
またsubmitは、action属性で動作として画面遷移する想定ですが、preventがついていると画面遷移しません。つまり、formがsubmitされた際、画面遷移せずにaddItemというメソッドが呼び出されます。

formタグの中でtype属性にsubmitを持つのはbuttonなので、
buttonが押下される → formがsubmitされる → 画面遷移せずにaddItemが呼ばれる
という処理になります。

  
また、formをJavaScriptで定義したデータと結びつける(バインドする、と言います)には、v-modelというディレクティブを用います。

<form><inputtype="text"v-model="title"><inputtype="number"v-model.number="pages"></form>

上記のようにv-modelディレクティブを用いて記述することで、そのformがJavaScriptで定義されるtitleおよびpagesというデータとバインドされます(このtitle、およびpagesは、JavaScriptのコードで定義します)。数値を用いたい場合は、type属性をnumberにし、v-modelに.numberという修飾子をつけます。

v-modelを用いてデータバインドした状態で、ブラウザから値を入力すると、JavaScriptで定義しているデータの値がブラウザで入力した値で更新されます。

スクリーンショット 2020-04-30 18.51.00.png
こちらはテキストボックスのイメージ図です。今回だと、ブラウザに表示されたtitlepagesのテキストボックスに入力した値が、JavaScriptで定義されるtitlepagesというデータの値となります。

  
  
では、JavaScriptのコードを見てみます。

main.js
letapp=newVue({el:'#app',data:{title:'',pages:'',books:[],},methods:{addItem:function(){if(!this.tilte&&!this.pages){return}letitem={title:this.title,pages:this.pages,state:0};this.books.push(item)this.title=''this.pages=''},}})

  
まず、Vueアプリケーションは、Vue関数でVueインスタンスを作成することによって起動されます。このように1行目でインスタンスを作成し、それ以降にオプションを記述していきます。

letapp=newVue({// 以下省略

  

elはマウントする要素を記載します。以下のように記述すると、id=appとなるタグをマウントします。今回id=appとしているのは、HTMLに記述している一番外側のdivタグです。

el:'#app',

  

dataには、アプリケーションで使うデータを定義します。先ほどHTMLで出てきたtitlepagesを空文字で定義しておきます。先ほども記述した通り、ブラウザに表示されるテキストボックスに値を入力することで、この空文字がその値に更新されます。
また次のmethodsで使うbooksという配列も定義します。

data:{title:'',pages:'',books:[],},

  

methodsでは、アプリケーションで使うメソッドを定義します。ここでは先ほどHTMLで呼び出したaddItemというメソッドを定義します。thisをつけると先ほどのdataプロパティで定義したデータを参照できます。

methods:{addItem:function(){if(!this.tilte&&!this.pages){// titleまたはpagesが空欄の際、メソッドを終了return}letitem={title:this.title,// thisをつけることでdataを参照pages:this.pages,state:0};this.books.push(item)this.title=''// ブラウザ上のテキストボックスを空文字に戻すthis.pages=''},}

addItemでは、itemというオブジェクトをbooks配列にpushしています。
titlepagesは、先ほどHTMLの中でv-modelディレクティブによりformとバインドされています。
つまり、addItemでは、画面上のformで入力した値(titlepages)が、itemオブジェクトの値になり、books配列にpushされるという処理がなされます。もしtitlepagesに値が入っていなかった場合、returnでメソッドを終了させています。また、ステータスとしてitemオブジェクトにはstateというプロパティも持たせます。

またaddItemの最後に、

this.title=''this.pages=''

という処理を入れています。これにより、データのtitleと、pagesが空文字に戻ります。このtitlepagesはv-bindによってformとバインドされているので、すごいことに、この処理を行うことでブラウザ上のテキストボックスも空文字に戻ります。このように、v-modelでバインドした場合、ブラウザ上からもスクリプト上からもデータを更新することができます。これを双方向データバインディングといい、Vue.jsの大きな強みの一つです。この双方向データバインディングについては、また後ほど記述します。

  

ここまでのデータ追加の処理を振り返ると、

  ブラウザ上のテキストボックスに入力した値が、JavaScriptのデータであるtitle, pagesの値になる
→ ブラウザ上のaddボタンを押下すると、JavaScriptで定義したaddItemメソッドが呼ばれる
→ データで定義されたbooks配列にitemオブジェクト(プロパティとしてtitle, pages, stateを持つ)がpushされる
→ データのtitle, pagesが空文字になり、テキストボックスの中身も空文字になる

となります。
  
  

・データ参照、削除

次にデータを参照するtableです。tableの中に、ステータスを更新するボタン、データを削除するボタンを実装します。

以下HTMLです。

index.html
<divid="app"><!-- 省略 --><table><thead><tr><thclass="title">title</th><thclass="pages">pages</th><thclass="state">state</th><thclass="btn">-</th></tr></thead><tbody><trv-for="book in books"><td>{{ book.title }}</td><td>{{ book.pages }}</td><tdclass="state"><button@click="changeState(book)">
            {{ labels[book.state] }}
          </button></td><tdclass="btn"><button@click="deleteItem(book)">delete</button></td></tr></tbody></table></div>

  

ここでは、繰り返し処理のv-forというディレクティブが登場しています。booksから一つずつ要素を取り出し、bookという変数に代入して処理を行なっています。forの処理自体はJavaScriptと共通しているのでイメージしやすいです。

<trv-for="book in books">

  

また、v-onの省略記法である@も登場しています。以下ではbuttonのclick時にdeleteItemというメソッドが呼ばれます。上で登場したbookを引数として与えています。

<button@click="deleteItem(book)">delete</button>

  

なお、Vue.jsで扱うデータは、HTML側で以下のように{{ }}で囲むことで参照できます。

<td>{{ book.title }}</td>

この記法を「Mustache(マスタッシュ)」と呼びます。
tdタグでは、先ほどbooks配列に追加したtitlepagesstateを参照し、ブラウザ上に表示しています。
  

以下では、ステータスを表示しています。ここで登場するlabelsは後ほどJavaScriptで定義します。

{{ labels[book.state] }}

  

データ追加から参照までを要約すると、

formにデータを入力 → addボタン押下 → books配列にデータがpush → tableに表示

という処理となっています。以下にCSS記述後のイメージを載せておきます。
  
  

※以下formイメージ図(再掲)

スクリーンショット 2020-04-30 18.51.00.png
  
  

※以下tableイメージ図

スクリーンショット 2020-04-30 14.12.08.png
  
  

では、上記HTMLにて記述した、stateボタンを押すと呼ばれるメソッドであるchangeStateと、deleteボタンを押すと呼ばれるメソッドであるdeleteItemを、以下のJavaScriptで定義していきます。

main.js
letapp=newVue({el:'#app',data:{title:'',pages:'',books:[],options:[{value:-1,label:'all'},{value:0,label:'new'},{value:1,label:'reading'},{value:2,label:'completed'}],},methods:{addItem:function(){// 省略changeState:function(book){if(book.state===0){book.state=1}elseif(book.state===1){book.state=2}elseif(book.state===2){book.state=0}},deleteItem:function(book){if(confirm('Remove this data?')){letindex=this.books.indexOf(book)this.books.splice(index,1);}},},computed:{labels:function(){returnthis.options.reduce(function(a,b){returnObject.assign(a,{[b.value]:b.label})},{})}}})

  

メソッドから見ていきます。changeStateでは、stateが持つ数値を変更する処理が行われます。この数値に対応するステータスは、データのoptionsで定義しています(このstateの数値をoptionslabelに変換する処理は、後ほど記述するcomputedの中で行なっています)。

changeState:function(book){if(book.state===0){book.state=1}elseif(book.state===1){book.state=2}elseif(book.state===2){book.state=0}},

  

deleteItemでは、booksから該当のindexのデータ (book)を削除します。

deleteItem:function(book){if(confirm('Remove this data?')){// 削除前に確認 trueの場合のみ削除letindex=this.books.indexOf(book)this.books.splice(index,1);}},

  

次に、computedについてです。computedは、関数によって算出されたデータのことで、dataと似たように扱うことができます。computed内のデータを、算出プロパティと言います。

computed:{labels:function(){returnthis.options.reduce(function(a,b){returnObject.assign(a,{[b.value]:b.label})},{})}}

ここでは、labelsという算出プロパティが定義されています。これは先ほど、HTMLで登場した、ステータスを表示するボタンで呼びだされていたlabelsのことです。
ちなみに、labelsの中ではreduceを使っています。説明はJavaScriptの話になるので省略しますが、処理としてはHTML側で

{{ labels[book.state] }}

として呼び出すことで、stateの数値を、データのoptionsで定義しているlabelに変換して表示します。
  
  

・key属性

もう一度tableのtbodyのHTMLをみてみます。

index.html
<tbody><trv-for="book in books"><!-- key属性が必要 -->>
    <td>{{ book.title }}</td><td>{{ book.pages }}</td><tdclass="state"><button@click="changeState(book)">
        {{ labels[book.state] }}
      </button></td><tdclass="btn"><button@click="deleteItem(book)">delete</button></td></tr></tbody>

  
実は、v-forディレクティブを用いた時は、一意な値であるkey属性を持たせる必要がある、とスタイルガイドに明言されています。
なので以下のようにkeyにidを持たせておきます。このidは後ほどJavaScriptで定義します。

<trv-for="book in books":key="book.id">

ここで登場する:keyはv-bindディレクティブの省略記法を用いています。省略しないで記述すると

<trv-for="book in books"v-bind:key="book.id">

となります。v-bindについては後述しますが、HTMLの属性とJavaScriptのデータをバインドするときに用います。今回は、trタグのkey属性とbook.idというデータをバインドしています。
  
  
  
では、JavaScript側でどうすれば良いかみていきます。

main.js
letapp=newVue({// 省略methods:{addItem:function(){if(!this.tilte&&!this.pages){return}letitem={id:this.getNewId(this.books),//追加title:this.title,pages:this.pages,state:0};this.books.push(item)this.title=''this.pages=''},getNewId:function(books){//追加constids=books.map((element)=>element.id);returnbooks.length==0?1:Math.max(...ids)+1;}},// 省略

  
まずは、itemidプロパティを持たせます。idは一意な値にしたいため、「books配列の要素が持つidの最大値に1を加算した値」とします。これでidが重複することはないはずです。
このidを取得するため、getNewIdというメソッドを定義します(こちらの記事を参照させていただいています。三項演算子を用いて、booksに要素が一つもない場合に0を返す処理を入れています)。定義したgetNewIdを、idプロパティを定義する際にthisを用いて呼び出します。引数にはbooks配列を渡します。これで、idプロパティを追加することができました。
  

上記でidが正しく追加されているかどうかは、 tableにidのカラムを追加して出力してみるとわかります。(実際のコードでは、idは不要なので消しています。)

html.index.html
<table><thead><tr><thclass="id">id</th><!-- 追加 --><thclass="title">title</th><thclass="pages">pages</th><thclass="state">state</th><thclass="btn">-</th></tr></thead><tbody><trv-for="book in computedBooks":key="book.id"><td>{{ book.id }}</td><!-- 追加 --><td>{{ book.title }}</td><td>{{ book.pages }}</td><tdclass="state"><button@click="changeState(book)">
          {{ labels[book.state] }}
        </button></td><tdclass="btn"><button@click="deleteItem(book)">delete</button></td></tr></tbody></table>

  
  
  

・フィルタリング

いよいよ、テキスト入力およびラジオボタン選択によるリアルタイムのフィルタ機能をつけていきます。
以下HTMLです。まずはテキスト入力によるフィルタリングです。

index.html
<!-- テキスト入力によるフィルタリング --><inputtype="text"v-model="searchWord"placeholder="search book title"/>

inputはテキストボックス(検索ボックス)です。ここに入力された値をsearchWordというデータとバインドして検索できるようにします。searchWordは後ほどJavaScriptで定義します。
  
  
CSSで装飾すると、以下のようなイメージです。
スクリーンショット 2020-05-01 16.00.25.png

  

続いて、ラジオボタンによるフィルタリングです。

html.index.html
<!-- ラジオボタンによるフィルタリング --><labelv-for="option in options":key="option.value"><inputtype="radio"v-model="current":value="option.value"/>{{ option.label }}
</label>

こちらのinputはラジオボタンです。先に{{ option.label }}について解説します。labelタグでv-forが用いられており、{{ option.label }}ではoptions配列の要素が持つlabelプロパティを取り出して表示しています。keyには一意な値であるoption.valueを与えています。念のため、以下にoptionsを再掲します。

options:[{value:-1,label:'all'},{value:0,label:'new'},{value:1,label:'reading'},{value:2,label:'completed'}],

ラジオボタンのイメージは以下のようになります。

スクリーンショット 2020-04-30 15.25.33.png
  
  

inputタグの中の:valueは、先ほども紹介した通りv-bindディレクティブの省略記法を用いています。v-bindとは、タグの中の属性に対してバインドを行いたいときに使います。この場合、inputタグのvalue属性とoption.valueをバインドしています。

<inputtype="radio"v-model="current"value="{{ option.value }}"/><!-- これはエラーになる -->

上記のように、属性に対してMustache記法を用いるとエラーになります。なので、

<inputtype="radio"v-model="current"v-bind:value="option.value"/>

とする必要があります。コードでは、v-bindの部分を省略して以下のような記述としています。

<inputtype="radio"v-model="current":value="option.value"/>

  

v-modelとv-bindが両方出てきてややこしいですが、双方向データバインディングを行えるのはv-modelのほうです。データ追加の章でも解説しましたが、双方向データバインディングとは、スクリプト⇄ブラウザの両方向から値の更新ができる仕組みです。この双方向データバインディングを簡単に実装できるのがVue.jsの強みです。v-bindはv-modelとは違い、スクリプト→ブラウザの単方向のデータバインディングとなります。
  

では、今回のコードの解説をします。今回は、v-bindにより、inputタグのvalue属性とoption.valueがバインドされています。これにより、inputタグ(ラジオボタン)のvalue属性は、-1〜2の数値になります。
スクリーンショット 2020-05-01 15.19.23.png

そして、そのvalueが、v-modelでバインドされているcurrentというデータの値になります。つまり、選択したラジオボタンによって、 currentの値は-1〜2のどれかに変化します。この「ブラウザで選択したラジオボタンのvalueが、スクリプトで定義されているcurrentの値になること」がブラウザ→スクリプトのバインドです。
  
  
また、ラジオボタンを何も選択していない場合、初期値としてスクリプト側でcurrentを定義した際の値が入っています。つまり、JavaScriptのdataで

current:'-1',

と定義しておくと、初期値は-1となり、ラジオボタンのallが選択された状態になっています。これがスクリプト→ブラウザのバインドです。このように、v-modelを用いることで双方向のデータバインドが可能になります。
  
  
ちなみに、ラジオボタンにv-modelとv-bindを用いるという使い方は公式で紹介されています。
  
  

上記で解説したv-modelとv-bindの違いを簡単にまとめておきます。

特徴v-modelv-bind
バインド対象form, input等属性
バインド方向双方向(スクリプト⇄ブラウザ)単方向(スクリプト→ブラウザ)

  

それでは、以下JavaScriptです。ここでは、テキスト入力やラジオボタン選択によるフィルタ処理を実装します。

main.js
letapp=newVue({el:'#app',data:{// 省略  current:-1,searchWord:'',},// 省略computed:{computedBooks:function(){returnthis.books.filter(function(book){return(book.title.indexOf(this.searchWord)!==-1)&&(this.current<0?true:this.current===book.state)},this);},// 省略}})

まずはデータで先ほど出てきたcurrentsearchWordを定義しておきます。currentの初期値は-1とします。
  
computedでは、computedBooksを定義します。ここでは、books配列の中から、titleの中にsearchWord(=テキストボックスに入力された値)を持ち、かつstateの値がcurrent(=ラジオボタンで選択された値)と等しいデータのみにフィルタリングするという処理を行なっています。
  

先ほどのHTML上のtableで記述したBooksを、このcomputedBooksに置き換えます。

<tbody><trv-for="book in computedBooks"key="book.id"><td>{{ book.title }}</td><td>{{ book.pages }}</td><tdclass="state"><button@click="changeState(book)">
        {{ labels[book.state] }}
      </button></td><tdclass="btn"><button@click="deleteItem(book)">delete</button></td></tr></tbody>

これでフィルタ機能が実装されました。  
  
  
  

・ローカルストレージへの保存

現在のままだと、画面の更新を行うとデータがすべて消えてしまいます。そこで最後に、ローカルストレージにデータを保存する処理、そして保存したデータを呼び出す処理を行います。
これらの処理は、JavaScript内で行います。

letapp=newVue({el:'#app',// 省略watch:{books:{handler:function(){localStorage.setItem('books',JSON.stringify(this.books));},deep:true}},mounted:function(){this.books=JSON.parse(localStorage.getItem('books'))||[];},// 省略

watchウォッチャと呼ばれ、特定のデータや算出プロパティの状態を監視して、変化があったときに実行させたい処理を記述します。
ここでは、booksに変化があったとき、JSON形式でローカルストレージに保存する処理を行なっています。deepプロパティをtrueにすると、ネストされたオブジェクトも監視してくれます。
  

mountedでは、ローカルストレージからデータの呼び出しを行なっています。このmountedはライフサイクルフックと呼ばれているメソッドの一つです。ライフサイクルフックは、Vueのインスタンスが生成された後に呼ばれます。呼ばれるタイミングはそのメソッドによって異なり、例えばmountedは、インスタンスがマウントされた後に呼ばれます。今回は、インスタンスのマウントがされた後に、ローカルストレージからデータの呼び出しがなされています。

(ライフサイクルフックはJavaScriptのフレームワークに触れていないと理解するのが難しいです。最初はよく使うmountedとcreatedの区別からできるようになると良いかもしれません。公式のガイドはこちらです。)

これらの処理によって、画面遷移が行われても、データが消えずに残り続けます。
  
  

あとはCSS用に、記述を加えてあげると完成です。
  
  

おわりに

書いた後に気付きましたが、ソート機能も付ければよかったなあと思います。
Vue.jsはちょっと動画見た程度なので、まだまだ勉強が必要ですが、記事としてアウトプットすることで理解が深まった気がします。もう少し使えるようになったら、今後はPythonのフレームワークであるdjangoと組み合わせてなにか作っていきたいと考えています。
  
  

参考

Vue.js 公式ガイド
基礎から学ぶVue.js チュートリアル
追加と削除が繰り返される配列要素のオブジェクトに一意のid番号を振る

  

◆以下gifでテストデータとして使わせていただいた書籍
自宅ではじめるDocker入門―人気のコンテナ型「仮想化ソフト」を使ってみる!
基礎から学ぶVue.js
体系的に学ぶ安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
独学プログラマー Python言語の基本から仕事のやり方まで
自走プログラマー ~Pythonの先輩が教えるプロジェクト開発のベストプラクティス120
  


Viewing all articles
Browse latest Browse all 8586

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>