這幾天在練習用vue & firebase刻一個仿line即時同步聊天的功能,
直接初體驗vue.js + firebase + webpack三種願望一次滿足XD!
update: 20170923更新
[用 Vue.js + Firebase 製作即時聊天功能(2) - storage]
(https://guahsu.io/2017/09/vue-firebase-realtime-line-chat-2-storage/)
>DEMO<
>原始碼-GitHub<
這幾天想到就會再稍微更新,GitHub與下方文章可能會略有不同
環境設定步驟
- 首先安裝node.js,理論目前的版本都會有內建npm了。
下載位置:https://nodejs.org
可以透過node -v
及npm -v
來查證是否已安裝完成node與npm。 - 安裝vue-cli,透過指令
npm install --g vue-cli
安裝。
可以透過
vue -V
來查證是否已安裝完成(-V是大寫唷)。
Vue-cli & webpack
- 先起一個資料夾來存放專案,並進入該資料夾內,
- 接著建立專案,透過vue-cli可以透過指令直接建立一包專案,
這裡我們使用指令vue init webpack
來建立webpack的專案包。
安裝vue-cli後,可以在命令列下
vue list
列出可用的template
建立專案使用vue init <template>
這次用到的是webpack。 - 建立設定項目:[]<方框內的是我的設定選項
123456789
? Generate project in current directory? //建於當前資料夾[Yes]? Project name (folder_name) //專案名稱,注意需小寫 [自訂專案名稱]? Project description A Vue.js project //專案描述 [自訂描述]? Author //作者,預設抓當前環境git user [自訂]? Vue build standalone [Enter]? Install vue-router? //安裝vue-router [Yes]? Use ESLint to lint your code? [No]? Setup unit tests with Karma + Mocha? [No]? Setup e2e tests with Nightwatch? [No]
- 當環境建立好後,輸入指令
npm install
使相依套件都下載到當前專案中 - 主要目錄結構:
12345678910
|- build (webpack的設定檔)|- config (專案設定檔)|- dist (編譯後產出的位置)|- src (專案程式碼目錄) |- assets (其他 css, js, images) |- components (主要 vue 元件) |- router (vue 的路由器) |- App.vue (主要樣版檔) |- main.js (vue js 主檔案)|- index.js 靜態首頁(進入點)
Firebase
- 建立一個專案
- 將Firebase的連結資訊複製起來
- 進入Database
- 修改權限並發布
vue-router設定
- 到index.html把firebase剛才複製的那串載入至head中
- 到router/index.js修改程式:
vue-router是vue的路由器,備註內部使用方式如下:
12345678910111213141516171819
import Vue from 'vue'import Router from 'vue-router' import ChatRoom from '@/components/ChatRoom' Vue.use(Router)export default new Router({ routes: [ { //路徑用於網址列 path: '/', //name用於設定連結,例如樣板頁中可用下面方式來寫連結,就不用寫<a>掛path了 //<router-link :to="{ name: 'ChatRoom' }>ChatRoom Page</router-link> name: 'ChatRoom', //到這個ChatRoom(/)時,使用ChatRoom元件 component: ChatRoom } ]})
而routes內是陣列包覆物件,所以要再新增一個就只要透過逗號(,)的物件新增方式即可,
而router的結果都會被呈現在<router-view></router-view>
中(參考main.js)。
在我的程式碼中,Hello已被替換為ChatRoom(預設範例為Hello)
其實這個練習中目前並沒有實際用到router的功能,因為僅載入一頁XD。
詳細設定可參閱官方文件vue-router 2官方文件
流程
- 輸入使用者名稱後才能發文
- 然後自己的發文是綠底,其他人是灰色(跟line一樣)
- 就這樣XD
- (傳圖功能請參考第二篇->用 Vue.js + Firebase 製作即時聊天功能(2) - storage)
主程式撰寫ChatRoom
- 到components/ChatRoom.vue(預設是Hello.vue我改名了)
- HTML與JS都有用到vue的寫法,我將撰寫的程式已備註如下:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
<template> <div class="container"> <!-- 區塊:name area --> <div class="name"> <h3>Name:{{ userName }}</h3> <!-- 註解:使用@click來偵測click,觸發時執行method中的setName() --> <div class="reset" @click="setName()">Reset Name</div> </div> <!-- 區塊:chat room --> <div class="chatRoom"> <!-- 區塊:head --> <div class="roomHead"> <div class="roomHead__topButtons"> <div class="roomHead__button close"></div> <div class="roomHead__button minimize"></div> <div class="roomHead__button zoom"></div> </div> <img src="https://lorempixel.com/50/50/" class="roomHead__img" draggable="false"> <div class="roomHead__title">Test Room</div> </div> <!-- 區塊:body --> <div id="js-roomBody" class="roomBody"> <!-- 註解:使用template來當迴圈容器,或是判斷用的容器,當條件達成時產出template內容 --> <template v-for="item in messages"> <!-- other people --> <template v-if="item.userName != userName"> <div class="messageBox"> <img src="https://lorempixel.com/40/40/" class="messageBox__img" draggable="false"> <div class="messageBox__content"> <!-- 註解:Vue使用雙花括號{{}}來顯示script中data:的資料 --> <div class="messageBox__name">{{item.userName}}</div> <div class="messageBox__text">{{item.message}}</div> </div> <div class="messageBox__time">{{item.timeStamp}}</div> </大专栏 用 Vue.js + Firebase 製作即時聊天功能ss="name">div> </template> <!-- 區塊:self --> <template v-if="item.userName == userName"> <div class="messageBox messageBox--self"> <div class="messageBox__time messageBox__time--self">{{item.timeStamp}}</div> <div class="messageBox__content messageBox__content--self"> <div class="messageBox__text messageBox__text--self">{{item.message}}</div> </div> </div> </template> </template> </div> <!-- 區塊:bottom --> <!-- 註解:使用:class來寫class是否顯示的判斷式{ class: 判斷式 } --> <div class="roomBottom" :class="{ disable: !userName }"> <div class="roomBottom__tools"></div> <div class="roomBottom__input"> <!-- 若要再帶入原生js的event(e)到function中,必須使用$event當參數傳入 --> <textarea id="js-message" class="roomBottom__input__textarea" :class="{ disable: !userName }" @keydown.enter="sendMessage($event)"></textarea> </div> </div> </div> <!-- 區塊:modal --> <div id="js-modal" class="modal"> <div class="modal__container"> <header class="modal__header"> <h2 class="view-title">輸入名稱</h2> </header> <div class="modal__body"> <!-- 註解:使用@keydown.enter來偵測keydown enter,觸發時執行method中的saveName() --> <input type="text" id="js-userName" class="userName" maxlength="6" @keydown.enter.="saveName()"> <div class="button" @click="saveName()">設定</div> </div> <footer class="modal__footer"></footer> </div> </div> </div></template> <script>// msgRef = firebase中的資料表/messages/,若沒有的會自動建立const msgRef = firebase.database().ref('/messages/');export default { // 指定此頁使用的name name: 'ChatRoom', // 資料位置,於html中可用{{}}渲染出來 data() { return { userName: '', messages: [] } }, // 這個頁面的functions methods: { /** 彈出設定視窗 */ setName() { document.querySelector('#js-modal').style.display = 'block'; }, /** 儲存設定名稱 */ saveName() { // vue的mtthod中this是指export中這整塊的資料 const vm = this; const userName = document.querySelector('#js-userName').value; if (userName.trim() == '') { return; } // 這裡的vm.userName(this.userName)就是data()裡面的userName vm.userName = userName; document.querySelector('#js-modal').style.display = 'none'; }, /** 取得時間 */ getTime() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); const format = (hours >= 12) ? "下午" : "上午"; return `${format} ${hours}:${minutes}`; }, /** 傳送訊息 */ sendMessage(e) { const vm = this; let userName = document.querySelector('#js-userName'); let message = document.querySelector('#js-message'); // 如果是按住shift則不傳送訊息(多行輸入) if (e.shiftKey) { return false; } // 如果輸入是空則不傳送訊息 if(message.value.length <=1 && message.value.trim() == '') { // 避免enter產生的空白換行 e.preventDefault(); return false; } // 對firebase的db做push,db只能接受json物件格式,若要用陣列要先轉字串來存 msgRef.push({ userName: userName.value, message: message.value, // 取得時間,這裡的vm.getTime()就是method中的getTime timeStamp: vm.getTime() }) // 清空輸入欄位並避免enter產生的空白換行 message.value = ''; e.preventDefault(); } }, // mounted是vue的生命週期之一,代表模板已編譯完成,已經取值準備渲染HTML畫面了 mounted() { const vm = this; msgRef.on('value', function(snapshot) { const val = snapshot.val(); vm.messages = val; }) }, // update是vue的生命週期之一,接再munted後方代表HTML元件渲染完成後 updated() { // 當畫面渲染完成,把聊天視窗滾到最底部(讀取最新消息) const roomBody = document.querySelector('#js-roomBody'); roomBody.scrollTop = roomBody.scrollHeight; }}</script> <style scoped>/* CSS太多,不占版面放於github供參考 */</style>
執行與編譯
- 使用指令
npm run dev
來讓這專案在本機掛server起來(預設8080port),
之後每次調整檔案內容,網頁就會自動刷新,非常方便開發及測試:D! - 編譯使用
npm run build
會將src中所撰寫的資訊都壓縮至dist資料夾內。
稍微備註,編譯後的index在載入js/css時的路徑有多一個
.
,
會導致放靜態主機時讀取錯誤(因為其實是在同一層),
所以可以到config/index.js中將設定調整為assetsPublicPat = ''
來解決
心得
目前還有卡著JS30尚未練習及寫完心得,
但看著各路大神分享的資源,就一直很想寫看看XD
vue-cli & firebase & webpack都是第一次使用,
這個練習後,對這三神器終於有很基礎的理解了。
vue.js
我最初會想學習vue是因為有中文文件(遮臉),
以及方便載入(可以直接掛載一個vue.js在html中來使用,像jQuery一樣),
目前這專案我學習到的是HTML中的template及v-if/v-for,
以及on(@),bind(:)的用法,很方便可以組織動態的前端邏輯,
而不用在js中組大量的字串模板。
在js控制中,我覺得生命周期的設定很棒,
可以很方便且”清楚”的在預想的狀況中設定應該出現的效果,
例如整個渲染完成前可以掛一個加載的效果等等..
但這小練習我都把邏輯整在同一個vue中,
還未學到/使用在vue中正確拆分邏輯的做法。
Firebase
之前有稍微聽過被google合併,但就僅此於而已此從未使用及了解過XD
這次練習中使用到的database覺得很新奇阿,是一個雲端即時同步的noSQL,
設定非常簡單方便,也是第一次親身寫出/感受到websoket的效果感(超酷)。
其他相關的功能也很多,之後有機會一定要在多研究一下(越來越多待讀項目..)
Webpack
一直有聽到,看過,但從未使用過,
但其實這次使用後的算是知道如何使用,但不了解內容,
對於設定檔目前沒有細讀,並不是很熟悉各相關設定檔,
反而覺得最特別的是熱加載及編譯後的程式碼壓縮混淆!
但日後練習都用webpack起,遇到問題找解答,應該會越來越熟吧XD
感謝
六角學院放出的Vue教學系列,從幼幼班入門到vue-cli & firebase介紹,
讓我能從零學習相關知識,從而建立完這個練習:)
六角學院在本週日也要釋放Bootstrap的課程了!
但不知何時才有空可以把全系列看過並實作完啊QQ….
原文地址:https://www.cnblogs.com/liuzhongrong/p/12257922.html