昨天大概粗糙的了解了一下Vue的概況之后,並沒有從框架、語法的細節來進一步學習。那今天通過一個簡單的實例來繼續完善一下Vue這方面的空白,用一些看得見的效果摸的着的代碼在不斷完成小目標的過程中慢慢消化一下相關的知識點。當然用一個簡單的實例來完全掌握Vue也不太現實,只是通過這樣一個例子來樹立學習的信心,了解Vue基礎知識點。
Vue-cli-todolist
,是一個用vue-cli3工具初始化項目,然后在此基礎上完成todolist的案例,相關的技術棧:Vue-cli3, Vue,VueRouter,Vuex
。Element-ui,IView, Vux
等UI組件為輔助。
先看一下我們完成的效果:
1. 安裝Vue-cli
分享前的約定: 以下大部分命令步驟基於Mac系統,Windows系統差異點也會提到。
1.1 先在Github建立倉庫
為了保持代碼能夠長期有效,也方便面試時展示,建議將代碼以Git的方式存放,比如:http://github.com
,http://gitee.com
,https://bitbucket.org/
等等。選擇任意一款新建倉庫:vue-cli-todolist
,git clone
倉庫到本地(Windows
系統建議非桌面的盤符路徑存儲,比如D:/vue-todolist),然后cd ..
,在iTerm
新建一個 tab 准備進行安裝Vue-cli
,留存當前的tab等安裝完成之后運行npm run serve
。
1.2 Vue-cli的作用
- 本地生成文件和配置,減少繁瑣的配置,以最短的時間最小的文件結構讓項目先跑起來,讓更多的精力關注開發本身。vue-cli3中vue.config.js不是必須,新建之后自動覆蓋默認設置。
- 那如果不使用vue-cli那怎么辦?就是手動在本地創始化package.json,然后其它相應文件手動創建手寫配置,效率低,出錯可能大。
1.3 Vue-cli的全局安裝
如果有舊版vuecli先得卸載,然后才能安裝新版。
npm uninstall -g vue-cli
vue -V
輸出:vue不是內外部命令時說明卸載成功,同樣,新版安裝成功通過這個命令來驗證。
- 安裝前提必須安裝nodejs,安裝完之后通過
node -v
來確定是否安裝成功,如果輸出類似v10.15.3
字樣表示安裝成功。//同時也安裝了npm
,輸入以下命令來確認npm -v
,如果輸入6.4.1
字樣表示安裝成功。 call:node - 通過
npm install -g @vue/cli
命令安裝最新版,其中-g
的意思是安裝到全局,就像一個全局變量一樣,以后在任何一個目錄都可以運行創建命令vue create
。
PS: 如果是使用Mac的同學此處可能需要增加sudo,完整的命令就是這樣:sudo npm install --global vue-cli
,否則會報這樣的錯誤Error: EACCES: permission denied, access '/usr/local/lib/node_modules'
,如果是Windows同學注意在右鍵用管理員方式打開cmd。 vue create vue-cli-todolist
通過此命令在原來git clone項目的Tab中來創建一個項目。
這時會有三個選項選擇,
- Overwrite
- Merge
- Cancel
建議選擇Merge,如果是Overwrite會刪除.git隱藏文件,導致不能進行git commit,安裝完成后通過vue -V
來驗證是否安裝成功,如果輸出3.7.0
這樣的字樣,就說明安裝成功了。
$ cd vue-test
$ npm run serve
根據命令行中提示的端口號,就可以預覽到一個基礎的項目頁面。
4. 然后刪除根目錄下.gitignore文件中的dist目錄,說明以后dist目錄也需要提交上傳,git提交代碼,推送到遠程,在github倉庫刷新頁面看到代碼結構,說明代碼遠程推送成功。
npm i vue-router -S
通過此命令來安裝路由組件,很多朋友剛開始不太會區分-S,-D的區別,這個-S表示上線依賴,是--save
縮寫,表示上線之后還需要存在的代碼塊,比如Vue.js,對應package.json中的dependencies: {}
,-D表示開發依賴,是--save-dev
縮寫,表示只是在開發階段使用,上線之后只使用他的結果文件就可以了,比如sass-loader, babel,對應package.json中的devDependencies: {}
。npm i sass-loader -D
通過此命令來安裝我們后面用到的scss,也可以通過一個命令用空格分隔安裝多個組件,比如:npm i sass-loader vue-loader -D
。
ps: call:npm
2. 路由的配置
增加路由的目的還是為了讓我們更方便的進行編程,比如:有些頁面需要用戶登錄之后才能訪問?比如有些頁面進入之后后退就不讓他進入這個頁面?等等這樣的場景都可以通過路由來實現。
- 在
src
目錄下新建router.js
,引入相關的對象,然后use
,最后export
。
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: () => import('@/views/home'),
}
],
})
- 這樣router路由我們就建好了,但是他還沒有被使用,所以還需要在main.js中引入之后被使用,這樣配置的路由才會起作用,根據路由訪問時才會跳轉到期望的畫面。
import router from './router'
new Vue({
router,
render: h => h(App),
}).$mount('#app')
- 然后刷新頁面,多了一個
/#/
,最后顯示為http://localhost:8080/#/
,且顯示畫面沒有報錯,說明我們的路由配置文件添加成功。
3. Sass-loader
加入scss的目的是讓我們更快更方便的方式編寫css
,了解一下。
需要在項目中使用scss
,只需要在.vue組件中的style中添加lang="scss",就可以使用了:
<style lang="scss">
</style>
//node-sass包比較難安裝,需要多次重新安裝。
-
但是添加之后命令工具報錯
Can't resolve 'sass-loader'
,這時候就需要我們通過命令npm i sass-loader -D
來安裝依賴。他主要的功能就是負責把scss編譯為css。 -
安裝完成之后,還是會報
Cannot find module 'node-sass'
,我們繼續npm i node-sass -D
安裝依賴。
安裝完成之后刷新頁面還是報錯,這時候需要重新啟動服務了。具體步驟為先:ctrl+c
停止服務,然后輸入命令npm run serve
啟動,再刷新頁面,畫面重現。 -
因為這次的重點是Vue-cli,所以相關Html, CSS直接復用,剛好todo相關css已經有npm包,我們直接安裝相關css模塊,
npm i todomvc-common todomvc-app-css -S
,然后在main.js中引入:
import 'todomvc-common/base.css'
import 'todomvc-app-css/index.css'
那到這一步我們前期的准備工作已經完成了,下面就是根據頁面特點進行組件的拆分了。
4. 組件
拆分組件的難點在於,組織結構上趨於分散,但數據處理上趨於集中,集中的數據便於管理或驅動頁面視圖。
組件是Vue開發當中最小的組成單元,以.vue擴展名結尾,他包括:Html結構, JS, CSS,數據流,監聽處理子組件觸發事件,引用子組件(ref)。
如何拆分是一個開發習慣,並沒有一個准確的原則,細拆可以拆分一個按鈕為一個組件單元,但拆的越細壞處是會造成頁面維護的困難,好處是頁面組件的復用性更好。如果拆的少,組件結構關系比較簡單,但是頁面組件復用性不高,最終要達到期望效果還是要平衡和取舍。
根據當前頁面的特點,大體拆分為以下四個部分,分別為:Header, List, Footer, Copyright,新建對應的*.vue文件並且放置在vue-cli-todolist/src/components
目錄下,然后相應的組件在vue-cli-todolist/src/views/home.vue
中引入,然后在增加:
components:{ Header, List, Footer }
需要注意的是Copyright
在整個操作容器外部,所以放在App.vue
中。
4.1 home.vue
由於Vue單數據流的限制規則,此組件頁面是操作整個Todolist數據源的總頁面,所有對數據的操作方法都匯總在此組件來實現,包括子組件對數據源的操作。父級 prop 值的更新會向下傳遞,但不能反向傳遞,也不能在子組件直接修改 prop。這樣做的目的是為了防止從子組件意外改變父級組件的狀態,從而導致數據流向難以理解。其實對Todolist的操作,就是常說的是增刪改查,只是少了一個查找而已,頁面上的常規操作就是對Todolist數據源對象的的增加刪除修改,那對應的代碼其實就是數據的push, splice, forEach
等方法。需要注意的是這些操作數據的結果都是基於瀏覽器的,刷新之后並不存在,解決辦法后端接口存儲和本地存儲,本案例中通過localStorage存儲在本地。
然后我們找一行有特征引入組件的方式來分析一下,比如:
<main-view :todos="filteredTodos" @del-todo="delTodo" @all-done="allDone"></main-view>
- main-view表示組件名稱,如果是全局組件那名稱是唯一的。
- :todos="filteredTodos",冒號表示變量是動態的,如果不是冒號默認是String,todos表示子組件接受數據的變量名稱,filteredTodos表示當前頁面的變量名稱。子組件中通過props屬性來接受父組件傳來的數據,通過$emit的方法觸發事件然后在父組件修改數據,整個的數據即能靈活的傳下去,也能有效的回轉回來。
- @del-todo="delTodo",@表示事件處理,del-todo為子組件$emit方法名,delTodo為當前組件事件處理方法名。組件數據通過
:todos="filteredTodos"
傳下去,然后通過@del-todo="delTodo" @all-done="allDone"
進行捕獲處理。
4.2 Header.vue
home.vue
中輸入<header>....</header>
,刷新頁面之后發現並沒有達到期望的效果,只有<header>....</header>
的代碼片段。這時候就發現組件名稱不能與HTML的原有標簽名稱重復,否則當普通的html標簽解析。修改Header.vue為HeaderView.vue之后刷新頁面,得到期望結果頁面。另外的一個辦法就是大寫,比如:<Header>....</Header>
。
4.3 Main.vue
此頁面雖然Html元素不多,但需要處理的東西還是挺多,包括點擊進行中處理,雙擊進入編輯狀態,.enter.blur事件退出編輯狀態,基本每個Html元素上都需要js代碼的處理,這里邊大概有這幾點分享:
4.3.1 template
- v-model
擴展為:
<input v-model="field" />
<input :value="field" @input="field = $event.target.value" />
每當輸入框內容發生變化時,就會觸發 oninput ,把最新的value傳遞給 field。
-
:class
- 對象語法 { active: isActive, 'text-danger': hasError },通過Boolean值控制是否有值
- 數組語法 [activeClass, errorClass],通過變量合並顯示
文檔:https://cn.vuejs.org/v2/guide/class-and-style.html
-
@dblclick,@click
綁定事件的縮寫形式,完整的為:v-on:click="handle"
文檔:https://cn.vuejs.org/v2/guide/events.html -
{{ item.title }}
基礎變量顯示,原始html需要v-html="rawHtml"指令。
https://cn.vuejs.org/v2/guide/syntax.html -
@keyup.enter.prevent
通過事件修飾符連寫,達到處理冒泡,阻止自定義事件等等的目的
https://cn.vuejs.org/v2/guide/events.html -
computed -> set || get
一般計算屬性默認都是getter,全選的處理需要主動的修改數據源。
https://cn.vuejs.org/v2/guide/computed.html
4.3.2 script
- props接受數據,data初始化變量
- computed中allDone通過進行中的數量是否為零來有效的在初始化時得到全選的狀態。
- util方法就是對跨組件代碼塊復用的封裝export然后import之后使用。
4.3.3 style
- vh
css3新單位,相對於視口的高度的百分比,視口被均分為100單位的vh,1vh = 視口高度的1%。
h1 {
font-size: 8vh;
}
如果視口的高度是200px,那么上述代碼中h1元素的字號將為16px,即(8x200)/100=16
4.4 Footer.vue
點擊狀態每次切換需要切換畫面,畫面的變化依賴於路由,所以需要記錄路由名稱 this.current
,在home.vue中通過watch來實現:
'$route' (to) {
this.current = to.name
},
4.5 Copyright.vue
版權頁面布局采用Flex形式布局。擴展認識:Flex垂直水平居中對齊
5. main.js
如果說App.vue是項目的組件入口文件的話那么main.js就是項目的代碼入口,所有相關js的初始化以及npm包的引用都在此文件處理。ES6規定,import必須放在首行,有的組件需要Vue.use,有的則不需要。
Vue全家桶相關的Router, Vuex都需要在此文件綁定。
6. package.json
- dependencies 上線依賴,npm i -S moduleName
- devDependencies 開發依賴 npm i -D moduleName
同時習慣於vue-cli2 npm run dev方式的同學,也可以在package.json中增加:
"scripts": {
"dev": "npm run serve",
"serve": "vue-cli-service serve"
}
可以用npm run dev繼續運行當前項目。
7. package-lock.json
記錄當前狀態下實際安裝的各個npm package的具體來源和版本號,package.json文件只能鎖定大版本,也就是版本號的第一位,並不能鎖定后面的小版本你每次npm install都是拉取的該大版本下的最新的版本,為了穩定性考慮幾乎是不敢隨意升級依賴包的,這將導致多出來很多工作量,所以package-lock.json安裝一個依賴的時候就鎖定在你安裝的這個版本。
8. npm run build
在根目錄增加vue.config.js,css,js默認訪問根目錄,而我們發布之后的目錄是http://www.host.com/dist/,多了一層dist,所以需要配置publicPath,否則控制台報找不到資源的錯誤:
module.exports = {
publicPath: './',
productionSourceMap: false
}
至此為止,今天階段性的學習就到此告一段落了。學習到此處有疑問的同學,加QQ群:364912432
,Vue交流微信群需要加入QQ群之后邀請。
源碼在這,vue-cli-todolist
如果還想進一步學習,那可以看我們這些分支:
帶m的移動端,由此可以發現,這個項目不但是可以學習入手的項目,也是可以做為相關其它UI組件Demo場地,再加個路由就可以自由的玩耍了:
dev-base
: 是基礎版,沒有Vuex的版本dev-vuex
: 是Vuex版本dev-element-ui
: 是element-ui版本dev-m-vux
: 是移動端vux版本,帶m的移動端dev-base
: 是基礎版gh-paes
: 在線預覽頁面分支