這幾天在練習用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。 建立設定項目:[]<方框內的是我的設定選項
1
2
3
4
5
6
7
8
9? 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
使相依套件都下載到當前專案中- 主要目錄結構:
1
2
3
4
5
6
7
8
9
10|- 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的路由器,備註內部使用方式如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import Vue from 'vue'
import Router from 'vue-router'
//載入元件ChatRoom
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的寫法,我將撰寫的程式已備註如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160<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>
</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….