11月16日 Web Application 製作入門
富山IT勉強会#4のハンズオン資料をまとめたものです。
非エンジニア、およびWebアプリ未経験者を対象に簡単なWebアプリ(SPA)を作成するハンズオンを行います。
このハンズオンのゴール(目的)
- 必要最低限の知識で Webアプリを作れるようになる(概念、環境構築 ~ Local環境での動作確認)
- 今後、更にレベルアップしていく際の切り口を共有する
おことわり
一日(4h ほど)で行う予定であるため、最低限知っていれば取りあえずアプリが作れる程度に厳選しております。
そのため、かなり内容は端折っておりますのでご了承ください。
また、私自身プログラミング経験はWindowsアプリケーションがほとんどで、
Web関係は直近一年弱程度ですので情報にはかなり偏りがあります。
また、誤情報等、お気付きの場合にはご指摘頂けると幸いです。
やること
- JavaScript入門(環境構築〜変数、制御構文、クラス、関数を最小限、簡単に。npmコマンドにもふれる)
- Node.js、Vue を使用したWebアプリケーションを作成(Vue基本構文, Vue/Cliコマンド)
- expressを使用したWebAPIの作成とWebアプリとの通信(express 最小構成でのWebAPIサーバー構築)
- 時間があった時のオプション
- PythonでもWebAPIを作成してみる(Flask 最小構成)
- -> AI 機能を組み込んだWebAPIへ応用可?
- Electronを使用したデスクトップアプリ化(electron-vueを使用)
- -> Web技術を使用したデスクトップアプリ開発へ繋げたい場合
- CSSフレームワークを使用してみる(vuetify)
やらないこと
- formタグを使ったGet、Post等の通信
- yarn
- 本番環境へのデプロイ(ローカル環境でのみの作業とします)
- セキュリティ関連
- DataBase操作
1. Web(Server - Client 型アプリケーション) の仕組み
本当にザックリですが図を作ってみました。
![image.png]()
Webサイト、Webアプリなどは、このClient-Server型のアプリケーションになります。
Client Side はChrome等のブラウザを通してサーバーへリクエストを行います。
サーバーPCの中では、この時Apache等のサーバーアプリやデータベースが起動していて、
リクエストに応じた処理を行い、結果をHtmlファイルやJson等で返信します(レスポンス)。
今回は、この図でいうバックエンドのサーバーアプリ部分をexpressで作ります。
また、レスポンスとして返すHtmlファイルを、Vueを使って作成します。
2. 環境構築
Node.jsのインストール
nodejs.org :https://nodejs.org/ja/からLTS版をダウンロード。
ダウンロードされたインストーラーを実行してインストールを行ってください。
![Node_js.png]()
インストール完了後、以下のコマンドをshellへ入力して、バージョンが返ってきたらOK。
visual studio code (vscode) のインストール
vscodeを用いてハンズオンを行いますが、お気に入りのエディタがある場合はそちらを使っていただいてもかまいません。
https://code.visualstudio.com/downloadから自分のOSに合わせたインストーラーをダウンロードし、インストールを行ってください。
3. JavaScript(というかNode.js)入門
まずはhello world
hello.js
constmsg='hello world';console.log(msg);
node ./hello.js
=> hello world
変数の型(es6)
今回使用するもののみピックアップ。
Number
数値型。C#やPython等では整数型(Int)、浮動小数点型(Float)等あったがjsはこれだけ
String
文字列型
constmessage='hello world';// '' で囲むvarname="huga";// "" もOK
Boolean
true
or false
constflg=true;console.log(flg);// => trueconsole.log(!flg);// => falseif(flg){console.log('flg is true');// => flg is true}
Array(配列)
配列。どちらかというとリストに近いような...
// 宣言方法constarr=[];// arr => [](空配列)letvalues=newArray();// values => [](空配列)constarr2=[1,2,3];// arr2 => [1,2,3]constvalues2=newArray(5);// values => [ <5 empty items> ] (5要素の配列)console.log(values2[1]);// => undefined// 要素の追加arr.push(1);arr.push(2);console.log(arr);// => [1,2]// 要素の参照console.log(arr[0]);// => 1console.log(arr[1]);// => 2// 要素の取り出しconsole.log(arr.pop());// => 2console.log(arr);// => [1]
Function
関数
// 通常?の宣言functiontwice(num){returnnum*2;}// ラムダ式constjoinStr=(str1,str2)=>{returnstr1+str2;};console.log(twice(2));// 4console.log(joinStr("hello ","world"));// hello world
Object
オブジェクト、連想配列。classとかもes5ではオブジェクトになる?? keyに対して任意のデータを割り当てる
// 宣言constobj={};constobj2=newObject();// 要素の追加obj["id"]=1;obj.name="huga";obj.getName=function(){returnthis.name;};console.log(obj);// => { id: 1, name: 'huga', getName: [Function] }// 要素の参照console.log(obj["id"]);// => 1console.log(obj.name);// => hugaconsole.log(obj.getName());// => huga
null, undefined
null は他の言語と大体同じ?参照先が未割当の状態。
undefined はそもそも項目すらない状態?(よくわかってません)
今回はここを理解していなくてもできるようにやっていきます。
const, let, var
var
変数を宣言し、ある値に初期化することもできる。グローバル変数として定義される。
let
ブロックスコープのローカル変数を宣言し、ある値に初期化することもできる。
ローカル変数(スコープ内で有効な変数)で、再代入が可能。
const
読み取り専用の名前付き定数を宣言する。
ローカル変数(スコープ内で有効な変数)で、再代入が不可能。
制御構文
if
分岐処理
letflg=false;if(flg){// 実行されない}else{// 実行される}if(flg!=true){// 実行される}
for
繰り返し処理
constnumbers=[1,2,3,4,5];for(leti=0;i<numbers.length;i++){constelement=numbers[i];console.log(element);// => 1// => 2// => 3// => 4// => 5}
他にもswich
, while
等あります。
参考リンク
- 文法とデータ型(MDN)
- ES2015(ES6) 入門
4. HTML
適当なフォルダをvscodeで開いて、適当な名前.html
というファイルを作成します。
ファイル内で html
と入力すると以下のように補完が表示されると思いますので、html5
を選択します。
![]()
以下のようにテンプレートが挿入されます。
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge"><title>Document</title></head><body></body></html>
body
タグの中にHTMLを記述していきます。
Header
見出しです。h1 ~ h6まであり、数字が小さい程基本的には表示が大きくなります。
bodyタグの中に記述して確認してみます。
<body><h1>h1 ヘッダーです</h1><h2>h2 ヘッダーです</h2><h3>h3 ヘッダーです</h3><h4>h4 ヘッダーです</h4><h5>h5 ヘッダーです</h5><h6>h6 ヘッダーです</h6></body>
〇結果
h1 ヘッダーです
h2 ヘッダーです
h3 ヘッダーです
h4 ヘッダーです
h5 ヘッダーです
h6 ヘッダーです
リスト
リストを表示します。番号付き(<ol></ol>
)と番号無し(<ul></ul>
)があります。
<body><!-- 番号付きリスト --><ol><li>リスト1</li><li>リスト2</li><li>リスト3</li><li>リスト4</li></ol><!-- 番号無しリスト --><ul><li>リスト1</li><li>リスト2</li><li>リスト3</li><li>リスト4</li></ul></body>
〇 結果
表
表を表示します。
<body><tableborder="1"><tr><th>項目1</th><td>A</td></tr><tr><th>項目2</th><td>B</td></tr><tr><th>項目3</th><td>C</td></tr></table><hr><tableborder="1"><thead><tr><th>キー</th><th>値</th></tr></thead><tbody><tr><td>項目1</td><td>A</td></tr><tr><td>項目2</td><td>B</td></tr><tr><td>項目3</td><td>C</td></tr></tbody></table></body>
〇 結果
入力要素
テキストボックスやボタン、スライダー等。
通常は<form></form>
タグの中で使用してGETやPOSTメソッドで送信に使いますが、
Vue等のフレームワークでは変数の操作に用いたりします。
<body><div><h3>テキストボックス </h3><inputtype="text"></div><div><h3>テキストボックス(パスワード) </h3><inputtype="password"></div><div><h3>ボタン </h3><inputtype="button"value="ボタンです"></div><div><h3>チェックボックス </h3>チェックしてください<inputtype="checkbox"></div><div><h3>ラジオボックス </h3><inputtype="radio"name="my-radio"value="radio-item1">ラジオ1
<inputtype="radio"name="my-radio"value="radio-item2">ラジオ2
<inputtype="radio"name="my-radio"value="radio-item3">ラジオ3
</div><div><h3>コンボボックス </h3><select><optionselected="0">選択アイテム1</option><option>選択アイテム2</option><option>選択アイテム3</option><option>選択アイテム4</option></select></div><div><h3>スライダー</h3><inputtype="range"></div><div><h3>カラーピッカー </h3><inputtype="color"></div><div><h3> FILE API </h3><inputtype="file"id="file-handler"></div></body>
〇結果 (Chrome)
![image.png]()
4. Vue入門
準備
Webアプリケーション フレームワークのVueを使用して作成していきます。
〇 参考リンク:
- Vue.js(本家)
- 基礎から学ぶ Vue.js(猫本サイト)
まずはHTMLファイルとスタンドアロン版のVue.js(CDN)を使用してVueの基本構文を確認します。
フォルダ内にvue-starter.html
を作成し、以下のように入力します。
vue-starter.html
<!DOCTYPE html><htmllang="ja"><head><metacharset="utf-8"><title>Vue Starter</title></head><body><divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --></div><!-- vue.js を読み込んでからVueオブジェクトを記載していく --><script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script><script>// Vue オブジェクトvarapp=newVue({el:'#app',data(){return{// object 形式でプロパティを記述していく};},// 算出プロパティ(キャッシュされる)computed:{},// 監視プロパティwatch:{},// 関数(キャッシュされない、都度評価される)methods:{},// ライフサイクルフック ---------------------------------------------------// https://jp.vuejs.org/v2/api/#beforeUpdatebeforeCreate(){},created(){},mounted(){},beforeUpdate(){},updated(){},activated(){},deactivated(){},beforeDestroy(){},destroyed(){}// ライフサイクルフック ---------------------------------------------------});</script></body></html>
プロパティ名 | 説明 |
---|
el | VueでレンダリングするHTML DOMのidを記述する。 |
data | レンダリングで参照されるプロパティ。画面描画のもとになる |
computed | dataを元に算出されるプロパティ。いわゆるgetter |
watch | 登録されているdataの変化を監視し、変更があった場合の処理を記述する |
methods | 関数。ボタンのクリックイベント等、特定のイベントにフックして実行する処理を記述する |
ライフサイクルフック
Vueが描画を行う際の特定のタイミングで呼ばれるイベント。
詳しくは以下参照。
宣言的レンダリング(プロパティの参照)
描画DOMエリアとVueオブジェクトを以下のように修正します。
<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><p>{{message}}</p></div>
// Vue オブジェクトvarapp=newVue({el:'#app',data(){return{// object 形式でプロパティを記述していくmessage:'Hello Vue!'};}});
〇結果
![image.png]()
繰り返しレンダリング(v-for)
配列やオブジェクトの要素を繰り返しレンダリングします。
描画DOMエリアとVueオブジェクトを以下のように修正します。
<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><p>Array のレンダリング</p><ul><liv-for="(item, index) in itemList">{{index}}: {{item}}</li></ul><p>Object のレンダリング</p><tableborder="1"><thead><th>key</th><th>value</th></thead><tbody><trv-for="(item, key) in obj"><td>{{key}}</td><td>{{item}}</td></tr></tbody></table></div>
data(){return{// object 形式でプロパティを記述していくitemList:['huga','hoge','poyo'],obj:{name:'Vue',id:1,lang:'JavaScript'}};},
〇結果
![image.png]()
イベント(v-on)
ボタンクリック等のイベントをフックして処理を実行します。
描画DOMエリアとVueオブジェクトを以下のように修正します。
<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><buttonv-on:click="call()">click me!!</button></div>
methods:{call(){alert('button clicked');}},
〇結果
ボタンをクリックするとアラートが表示されます。
![image.png]()
条件付きレンダリング(v-if, v-show)
v-if
、v-show
共に条件を満たす場合にのみレンダリングされます。
違いは、レンダリングされない場合、以下の点が異なる。
- v-if
: レンダリングされない場合、DOMが生成されない。
- v-show
: レンダリングされない場合もDOMが生成される。style属性がdisplay: none;
となることで表示されなくなる。
描画DOMエリアとVueオブジェクトを以下のように修正します。
<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><button@click="toggleFlg()">フラグ切り替え</button><pv-if="isShow == true">フラグがtrue なら描画されます</p></div>
data(){return{// object 形式でプロパティを記述していくisShow:true};},// 関数(キャッシュされない、都度評価される)methods:{toggleFlg(){this.isShow=!this.isShow;}}
○結果
ボタンを押すたびに下部のテキストの表示、非表示が切り替わります。
算出プロパティと監視プロパティ(computed,watch)
描画DOMエリアとVueオブジェクトを以下のように修正します。
<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><h2>computed</h2><p>num is: {{num}}</p><p>twice is: {{twice}}</p><button@click="increment()">numへ1加算</button><h2>watch</h2><p>{{watchStr}}</p></div>
data(){return{// object 形式でプロパティを記述していくnum:1,watchStr:""};},// 算出プロパティ(キャッシュされる)computed:{twice(){// 二乗の数を返すreturnMath.pow(this.num,2);}},// 関数(キャッシュされない、都度評価される)methods:{increment(){this.num++;}},// 監視プロパティwatch:{// 関数名は監視するプロパティ名num(newValue,oldValue){this.watchStr=`${oldValue} -> ${newValue}`;}},
○結果
![Vue_Starter.png]()
form要素との連携(v-bind、v-model)
input
タグのform要素と同期してdata
を操作できます。
html部
<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><h2>v-model(双方向同期)</h2><inputtype="text"v-model="message"><p>{{message}}</p><selectv-on:input="selectedItem = $event.target.value;"><optionselected="0"v-for="item in listItems">{{item}}</option></select><p>入力値:{{selectedItem}}</p><!-- v-bind:r="radius" は :r="radius" と省略してもOK --><h2>v-bind(片方向同期)</h2><inputtype="range"v-model="radius"max="100"min="0"><p>radius = {{radius}}</p><svgviewbox="0 0 300 300"width="300"height="300"><circlev-bind:r="radius"cx=150cy=150></circle></svg></div>
JavaScript部
data(){return{// object 形式でプロパティを記述していくmessage:'hello vue',selectedItem:null,listItems:["item1","item2","item3",],radius:10,};},
○結果
![Vue_Starter.png]()
Vue CLI を使ったWebアプリ(SPA)の作成
ここからfront-end、back-end に分けてWebアプリ(のようなもの)を作っていきます。
まずは、front-end側から作り始めます。
プロジェクトフォルダ作成
お好きなところにvue-spa-sample
という名前でフォルダを作ります。
vue/cliのインストール
vue-spa-sample
フォルダ内でコマンドラインを立ち上げます。
以下のコマンドでVue CLI をグローバルへインストールします。
なお、npm のコマンドは以下が纏まっていてみやすかったです。
npm 入門
インストールが終了したら以下のコマンドでバージョンが返ってくることを確認します。
front-end プロジェクトの作成
続けてコマンドラインから
bat
vue create front
でプロジェクトを作成します。
front
の部分はプロジェクト名になるので、
本来はお好きな名前をつけていただいてOKです。
? Please pick a preset:
default (babel, eslint) を選択します。
パッケージのダウンロードが始まります。
全て終了したら、表示されているように以下のコマンドを入力してdev serverを立ち上げます。
dev server が立ち上がったら、ブラウザからhttp://localhost:8080
へ接続します。
以下のような画面が表示されます。
![front_と_front_—_node_◂_npm_TERM_PROGRAM_Apple_Terminal_NVM_CD_FLAGS__TERM_xterm-256color_—_80×24.png]()
確認したら、vscodeでfrontフォルダを開きます。
これからVueのSFCファイルを見て行きますが、ハイライトや補完等の機能が使えるように、
エクステンションでvetur
を検索し、インストールしておきます。
単一ファイルコンポーネント(SFC)とコンポーネント
Vue CLI で作られたプロジェクトはSFCに対応しています。
SFCとは、コンポーネント(Vueの描画領域を部分的に記述し、部品化したもの)をtemplate
、script
、css
のそれぞれのタグに分けて記述したものです。
現在のプロジェクトで言うと、src/App.vue
が画面全体を描画しているコンポーネントで、
src/components/HelloWorld.vue
は部分的リンクの部分を描画している部品になります。
コードで見るとこんな感じです。
![全画面_2019_11_16_15_23.png]()
コンポーネントを作ってみる
src/components/
内にMainPage.vue
という名前でSFCを作ります。
以下のようにコードを記述します。
src/components/MainPage.vue
<template><div><h1>自作コンポーネントのページです</h1><h2>hello {{name}}</h2></div></template><scriptlang="ts">importVuefrom'vue';exportdefaultVue.extend({name:'main-page',data(){return{name:'vue',// 好きな名前を入れてください}}});</script><style></style>
ファイルを保存したら、src/App.vue
を以下のように修正します。
src/App.vue
<template><divid="app"><imgalt="Vue logo"src="./assets/logo.png"><!-- コメント化 --><!-- <HelloWorldmsg="Welcome to Your Vue.js App"/> -->
<!-- 追加 --><main-page/></div></template><scriptlang="ts">// import HelloWorld from './components/HelloWorld.vue' // <-コメント化importMainPagefrom"./components/MainPage.vue";// <-追加、.vueを忘れないでexportdefault{name:'app',components:{// HelloWorld // コメント化MainPage// 追加}}</script><style>#app{font-family:'Avenir',Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50;margin-top:60px;}</style>
ファイルを保存します。
再度先ほどブラウザで開いたhttp://localhost:8080
を見るとオートリロードがかかって
以下のようになっていると思います。(なっていなかったら手動でリロードしてください)
![front_と_App_vue_—_front.png]()
MyButton コンポーネントの追加
src/components/
内にMyButton.vue
という名前でオリジナルのボタンを作って見ます。
私はデザインはできないので、以下のサイト様からパク参考にさせて頂きました。
CSSで作る!押したくなるボタンデザイン100(Web用)
src/components/MyButton.vue
<template><ahref="#"class="btn-square"@click="change()">{{label}}</a></template><scriptlang="ts">importVuefrom'vue';exportdefaultVue.extend({name:'my-button',props:['label'],methods:{change(){this.$emit('label-update','changed');}}})</script><stylescoped>.btn-square{display:inline-block;padding:0.5em1em;text-decoration:none;background:#668ad8;/*ボタン色*/color:#FFF;border-bottom:solid4px#627295;border-radius:3px;}.btn-square:active{/*ボタンを押したとき*/-webkit-transform:translateY(4px);transform:translateY(4px);/*下に動く*/border-bottom:none;/*線を消す*/}</style>
style
タグ内に付いているscoped
は、SFCファイル独自のもので、
記述すると定義されているCSSをこのコンポーネントのみに適用することができます。
また、Vueオブジェクト内のprops
は、コンポーネントの呼び出し側からデータを受け取ることのできるプロパティです。また、methods内のthis.$emit
は、このコンポーネント呼び出すイベントを定義しています。
具体的には、ボタンが押下されると、label-update
イベントが起き、呼び出し元の親コンポーネントへ変更された値を通知します。
(Vueでは子コンポーネントがporpを勝手に書き換えることはNGとされており、このようにイベントとして親コンポーネントに通知を送り、親オブジェクトに変更をかけてもらうようにしています。)
MainPage.vueを以下のように変更し、結果を確認して見ます。
src/components/MainPage.vue
<template><div><h1>自作コンポーネントのページです</h1><h2>hello {{name}}</h2><!-- 追加 --><my-button:label="btnName"@label-update="updateName"/></div></template><scriptlang="ts">importVuefrom'vue';importMyButtonfrom"./MyButton.vue";// 追加exportdefaultVue.extend({name:'main-page',components:{MyButton},data(){return{name:'vue',// 好きな名前を入れてくださいbtnName:'Myボタン'// 追加}},// 追加methods:{updateName(val:string){this.btnName=val;}}});</script><style></style>
![front_と_MainPage_vue_—_front.png]()
5. WebAPIの作成
express を使ってWebAPIを作成します。
vue-spa-sample
フォルダ内にback
フォルダを作り、その中でコマンドラインを立ち上げます。
以下のコマンドを順に打ち込んで行きます。
bat
npm init -y
npm i -S express cors body-parser
インストールが終わったら、vscodeでback
フォルダを開きます。
back/src
フォルダ内にrouter
フォルダを作成し、その中にroot.js
ファイルを作成します。
src/router/root.js
constexpress=require("express");constrouter=express.Router();router.get('/',(req/* リクエスト */,res/* レスポンス */)=>{// json でレスポンスres.send({message:'res from express'});});module.exports=router;
back/src
フォルダ内にapp.js
ファイルを作成し、以下のコードを記述します。
src/app.js
constexpress=require("express");constbodyparser=require("body-parser");constcors=require("cors");constindexRouter=require("./router/root");constapp=express();// CORS 制限の解除app.use(cors());// 通信にJsonを使用するapp.use(bodyparser.json())// /にindexRouterをルーティングapp.use('/',indexRouter);module.exports=app;// appを公開
back/src
フォルダ内にindex.js
ファイルを作成し、以下のコードを記述します。
src/index.js
constapp=require("./app");constport=3000;app.listen(port,()=>{// port listen を始めた時に実行される処理console.log(`express listen port ${port}`);});
全て保存したら、コマンドラインで以下のコマンドを実行
サーバーが起動したら、ブラウザからhttp://localhost:3000
にアクセスする。
以下のようになったらOK。
![localhost_3000.png]()
6 WebアプリとWebAPI で通信
これまで作ってきたfront-end と back-end を連携して行きます。
front側のdev-serverをctrl + c
で終了し、以下のコマンドを入力する
axiosがインストールされたら、再度
でdev-server を起動して起きます。
axiosで通信する
src/components/MainPage.vue
を以下のように修正します。
src/components/MainPage.vue
<template><div><h1>自作コンポーネントのページです</h1><h2>hello {{name}}</h2><my-button:label="btnName"@label-update="updateName"/><!-- 追加 --><div><p>{{message}}</p><button@click="getExpress()">get express root</button></div></div></template><scriptlang="ts">importVuefrom'vue';importMyButtonfrom"./MyButton.vue";import*asaxiosfrom"axios";exportdefaultVue.extend({name:'main-page',components:{MyButton},data(){return{name:'vue',// 好きな名前を入れてくださいbtnName:'Myボタン',message:'no message'// 追加}},methods:{updateName(val:string){this.btnName=val;},// 追加getExpress(){axios.default.get('http://localhost:3000').then((responce)=>{this.message=responce.data.message;});}}});</script><style></style>
http://localhost:8080
にアクセスしてget express root
のボタンを推してみる。
no message
が res from express
に変わったら成功。localのexpressサーバーと通信できてます。
![hangouts_google_com_が画面を共有しています。_と_front2.png]()
今後やって行くこと