1. 程式人生 > >深入淺出學 Vue 開發

深入淺出學 Vue 開發

               

課程介紹

前端技術日新月異,每一種新的思想出現,都代表了一種技術的躍進、架構的變化,那麼對於目前的前端技術而言,MVVM 的思想已經可以代表當今前端領域的前沿思想理念,Angular、React、Vue 等基於 MVVM 思想的具體實現框架,也成為了人們爭相學習的一個熱點。而 Vue 作為其中唯一沒有大公司支援但卻能與它們並駕齊驅並且隱隱有超越同類的趨勢,不得不說這種增長讓人感到驚奇。

本系列課程內容將會帶領大家由淺入深的學習 Vue 的基礎知識,瞭解 Vue 的原始碼設計和實現原理,和大家一起看一下尤雨溪先生的程式設計思想、架構設計以及如何進行程式碼實現。本系列課程內容主要分為三大部分:

  • Vue 的基礎知識:在這一部分將學習 Vue 的基礎語法及其原始碼的實現。例如,Vue 的生命週期鉤子如何設計?當聲明瞭一個 directive 時,Vue 究竟執行了什麼?為什麼只有通過 vue.set 函式才能為響應式物件新增響應式屬性?如果我們自己要實現一個響應式的框架的話,應該如何下手、如何思考等。
  • Vue 的周邊生態:在這一部分將學習 Vue 的周邊生態圈,包括有哪些 UI 庫可以和 Vue 配合快速構建介面、如何使用 vue-router 構建前端路由、如何使用 Vuex 進行狀態管理、如何使用 Axios 進行網路請求、如何使用 Webpack、使用 vue-cli 構建出的專案裡的各種配置有什麼意義?
  • 專案實戰:在這一部分將會通過一個有意思的自動對話系統來進行專案實戰,爭取通過這個小專案把學到的知識點進行一個整合。

作者介紹

劉國棟,資深軟體開發工程師、慕課網認證講師、開源愛好者,目前就職於濟南某國企,並且成立了個人工作室,負責前端和移動端的工作。擁有 5 年前端、移動端開發經驗,在 JS、Android、iOS 領域有獨特的見解,統籌過的多個專案使用者數已過千萬。

課程內容

導讀:為什麼選擇 Vue
 

讀者朋友們好,很高興選擇這門課程來學習,作為撰寫者的我,也感到非常的榮幸。

程式人生

我們選擇了這個 IT 行業,自然希望能夠在這裡走得更遠。我相信沒有任何一個人會甘於平庸,大家都希望能夠在自己所處的行業以及所處的領域中有所建樹,希望可以實現自己的價值,以獲取社會的認可。而如果要實現這個目標的話,我們將要投入更多的時間和精力,才能博得更多的機會。在成長的過程中,不可避免的會踩一些坑、走一些彎路,那麼有沒有辦法可以避免少踩一些坑、少走一些彎路呢?萬幸的是,答案是有的。

站在巨人的肩膀上,我們可以看得更遠,約翰 · 雷西格釋出了 jQuery 的第一個版本,從此讓我們進入了 jQuery 時代;尤雨溪創造了 Vue,使我們正式邁入 Vue 的時代。這些領軍者,讓我們能夠看得更遠,讓我們的工作和生活變得更加簡單。我與這些領軍者相去甚遠,微不足道,但依然希望可以儘自己的一些能力來幫助到一些人,讓大家可以少走一些彎路、少踩一些坑。懷揣著這樣的一個夢想,我開設了這一門課程。

框架為什麼會存在

這個世界上任何事物的存在,必然是解決了世界上的一些問題。同樣的道理,框架為什麼會存在呢?框架的存在也必然是解決了某一個或某一類的問題,這裡引入一下 Vue 的作者尤雨溪說過的話:框架的存在是為了幫助我們應對複雜度。框架作為一個工具用來幫助我們應對一類複雜的問題,我想這就是框架的定義了。

當然框架本身一樣存在著複雜度,就好像我們使用扳手去擰螺絲時,扳手就是框架,它是一個工具,這個工具本身一樣有學習成本,只不過它的學習成本比較低,我們使用這個工具來去解決擰螺絲這樣一個如果沒有扳手會變得相當複雜的問題,這和使用前端框架去解決在工作中遇到的一個複雜專案的時候所經歷的過程是一樣的。

所以,很自然的就會在我們使用的框架(工具——扳手)和想要去解決的問題(複雜度——擰螺絲)之間進行抉擇,我們會更趨向於使用一個簡單的工具來去解決一個複雜的問題,這就是框架存在的意義了。

所以我們本能的希望框架變得越來越簡單,而它能夠解決的問題越來越複雜。萬幸的是:這個時代正在按照我們的希望來發展

主流框架

目前討論比較多的前端框架主要有三個:

 

Angular   React   Vue

通過它們在 GitHub 的 Star 歷史和 NPM 的下載趨勢圖來看一下。

Angular、React 和 Vue 的 GitHub Star 歷史:

這裡寫圖片描述

npm 的下載趨勢圖:

這裡寫圖片描述

可以看到在 GitHub 上面,Vue 的資料上升最快,並且在 2018 年 6 月 28 日已經超過了 React,成為了最受歡迎的前端框架。而在 npm 中,React 的資料遙遙領先,這得益於 React 強大的社群力量,我們無法否認,React 社群作為當前最活躍的前端社群,提供出了特別多優秀的想法和理念,它們為 React 提供了強大的生態支援,同時也讓我們在選擇周邊框架的時候需要進行更多的選擇。

當然這三個框架都非常的優秀,我們不去討論它們的優劣,我們的選擇都只是基於我們的取捨:我們希望工具足夠簡單,而它可以解決的問題卻要足夠複雜。這就夠了,不是嗎?

 

Angular:Angular 期望做的事情非常多,比如說它會包含著它自己的路由,這讓我們決定去使用 Angular 的時候,就必須要接受它的全部,這就使得學習成本變得更高,但同時選擇變得會更少,不過有時候選擇少了並非是一件壞事,它可以讓我們更加專注。

 

React 與 Vue:React 與 Vue 一樣都是專注只做介面,而其他的所有一切都會有各種配套的工具,比如說路由,或者狀態管理工具,所以說使用它們的話你可能需要做更多的選擇,而這種方式則使得它們本身(React、Vue)的學習曲線相對平緩。

從上面的分析可以看出,Angular 提供了一個複雜的工具,用於解決一整套複雜的問題。而 React 與 Vue 則專注於解決一個特定的問題,而把其他的問題交給了它們的生態圈來處理,這樣做的同時也會讓我們花更多的時間來選擇合適的周邊工具。

所以說這些框架各有優劣,並沒有絕對的誰好誰壞之分,我們選擇什麼樣的工具,取決於我們面對了什麼樣的問題。沒有人會喜歡用大炮去打蚊子,也沒有人會想到用蒼蠅拍來打大象。我們喜歡剛剛好,追求事半功倍,如果你也是這麼想的,那麼至少我們的底層觀念是一致的。

Vue 的特點

經過了那麼多的鋪墊,總算到了本次課程內容的重點:Vue,它到底有什麼魅力?為什麼值得我們花費時間去學習?先來看一下 Vue 到底擁有什麼樣的特點。

我們會從以下8個方面來對 Vue 進行分析:

  • MVVM 框架
  • 單頁面應用程式
  • 輕量化與易學習
  • 漸進式與相容性
  • 檢視元件化
  • 虛擬 DOM(Virtual DOM)
  • 社群支援
  • 未來的 Vue 走向

(1)MVVM 框架

所謂 MVVM 框架就是:View-ViewModel-Model,就像下面這樣:

這裡寫圖片描述

那麼這個 MVVM 框架,應該怎麼去理解呢?它的第一個 View,相當於頁面中的 DOM,最後一個 Model 相當於資料來源,就像下面這個樣子:

這裡寫圖片描述

其中,a 標籤就是 DOM,data 物件就是資料來源,這兩者之間永遠不會直接通訊,它們所有的聯絡都是通過 ViewModel,也就是監控者來進行的。監控者會去負責檢測資料的變化,然後把資料實時展示在頁面中。例如,把 text 的內容更改為 “Hello Vue” 的話,那麼 a 標籤中展示的內容,也會自動變為 “Hello Vue”。這樣就不需要手動的操作 DOM,所有對 DOM 操作都會通過監控者來完成。如果以前寫過複雜的 DOM 操作的話(如 **.parent().parent().parent()...),就會發現這種方式帶來的便利。

Vue 正是使用了這種 MVVM 的框架形式,並且通過宣告式渲染響應式資料繫結的方式來幫助我們完全避免了對 DOM 的操作。

(2)單頁面應用程式

單頁面應用程式(SPA),一般指為:一個頁面就是一個應用(或子應用)。隨著技術的發展,現在的前端網頁早已不只侷限於在瀏覽器上展示了,手機 App 上、微信公眾號上都有了越來越多的展示機會。

那麼如果把傳統的多頁面應用形式放入到我們的手機上面會是什麼樣子呢?當進行頁面跳轉開啟一個新的頁面的時候,它會變成這樣:

這裡寫圖片描述

 

等的花兒都謝了 有沒有?

而如果使用單頁面的形式來開發的話,就不會出現這樣一種情況。因為我們的整個應用就只有一個頁面,當我們的這一個單頁被載入進來之後,就不會在進行關於頁面的網路請求。Vue 配合生態圈中的 Vue-Router 就可以非常方便的開發複雜的單頁應用。

(3)輕量化與易學習

我們知道網頁中引入的 JS 體積越大,那麼載入所需要耗費的時間就越長,反之體積越小,則越節省時間。所以我們會更傾向於使用體積更小的 JS 檔案,這也是為什麼在生產版本會引入 .min 的 JS 的原因。下面是我從 Vue 官網的截圖:

這裡寫圖片描述

目前 Vue 的最新穩定版本為 2.5.16,從截圖中可以看到 Vue 的生產版本只有 30.90KB 的大小,幾乎不會對我們的網頁載入速度產生影響。同時因為 Vue 只專注於檢視層,單獨的 Vue 就像一個庫一樣,所以使我們的學習成本變得非常低。

(4)漸進式與相容性

漸進式框架就是:我只做分內的事情,並且不會對你要求太多。

 

Vue 的核心庫只關注檢視層,不僅易於上手,還便於與第三方庫或既有專案整合。

這是 Vue 官網上面的一句話,正如在上面所說的,Vue 只做介面,而把其他的一切都交給了它的周邊生態來做處理,這就要求 Vue 必須要對其他的框架擁有最大程度的相容性。

例如,一開始只想做一個靜態站,那麼可以只引入 Vue 來去構建介面,過了一段時間,你想在網站上加入訪問網路的功能,那麼你可以再引入 axios(Vue 官方推薦)或者其他的(哪怕是 jQuery)網路請求框架,而後來隨著你的網站越做越大,你想要把你的網站變成一個大型的 Web 應用的時候,可以引入一些其他你需要的 JS 檔案,如 Loadsh.js、Velocity.js 等。

(5)檢視元件化

所謂檢視元件化就是把我們的網頁拆分為一個個的元件,就像下面這樣:

這裡寫圖片描述

Vue 允許通過元件來去拼裝一個頁面,每個元件都是一個可複用的 Vue 例項,元件裡面可以包含自己的資料,檢視和程式碼邏輯。比如說:

這裡寫圖片描述

CSDN 的這個個人資料模組,大家都已經不陌生了吧,當我們的 Web 應用中有多個頁面都使用到這個個人資料模組的時候,就可以把它封裝成一個元件,這個元件擁有單獨的程式碼邏輯、CSS 樣式、資料等,在任何一個我們需要使用到它的地方,就可以通過

<component-name></component-name>Vue.component('component-name', {    ...});

這種方式來直接引入了。

(6)Virtual DOM

Virtual DOM 也就是虛擬 DOM,大家知道瀏覽器去處理 DOM 操作時,是存在效能問題的,這也是我們在使用 jQuery 或者原生 JavaScript 來去頻繁操作 DOM 進行資料渲染的時候,我們的頁面經常出現卡頓的原因。

而虛擬 DOM 則是預先通過 JavaScript 的各種運算,把最終需要生成的 DOM 計算出來,並且進行優化,在計算完成之後才會將計算出的 DOM 放到我們的 DOM 樹中。由於這種操作的方式並沒有進行真實的 DOM 操作,所以才會叫它虛擬 DOM。

我們在前面說過:

 

Vue 是通過宣告式渲染響應式資料繫結的方式來幫助我們完全避免了對 DOM 的操作。

Vue 之所以可以完全避免對 DOM 的操作,就是因為 Vue 採用了虛擬 DOM 的方式,不但避免了我們對 DOM 的複雜操作,並且大大的加快了我們應用的執行速度。

(7)來自社群的支援

雖然在全球中 Vue 的社群並沒有 React 社群那麼的繁華,但得益於 Vue 的本土化身份(Vue 的作者為國人尤雨溪),再加上 Vue 本身的強大,所以湧現出了特別多的國內社群,如 https://www.vue-js.com/、https://vuejs.com.cn/ 等。這種情況在其他的框架身上是沒有出現過的,這使得我們在學習或者使用 Vue 的時候,可以獲得更多的幫助。

(8)未來 Vue 的走向

Vue 是由國人尤雨溪在 Google 工作的時候,為了方便自己的工作而開發出來的一個庫,而在 Vue 被使用的過程中,突然發現越來越多的人喜歡上了它。所以尤雨溪就進入了一個邊工作、邊維護的狀態,在這種情況下 Vue 依然迅速的發展。

而現在尤雨溪已經正式辭去了 Google 的工作,開始專職維護 Vue,同時加入進來的還有幾十位優秀的開發者,他們勵志把 Vue 打造為最受歡迎的前端框架。事實證明 Vue 確實在往越來越好的方向發展了(從 Angular、React、Vue 的對比圖中可以看出 Vue 的勢頭)。所以我覺得完全不需要擔心未來 Vue 的發展,至少在沒有新的顛覆性創新出來之前,Vue 都會越做越好。

本課能夠幫你些什麼

說了那麼多,大家應該已經對 Vue 有了一個基礎的瞭解了,那麼我們看一下,這節課的主要內容。

本課將從基礎語法、生態系統、專案實戰這三個部分來幫助大家詳細的瞭解 Vue 的知識,看一下下面的腦圖。

enter image description here

這就是我們本課將要學習的所有內容。如果你是在手機上訪問的話,也可以直接掃描這個二維碼來檢視我們的專案。

這裡寫圖片描述

通過本次課程可以學習到:

  • Vue 的基礎語法
  • Vue 的實現原理
  • Vue 的周邊生態
  • Webpack 模組打包器
  • vue-cli 腳手架解析
  • 如何通過 vue-cli + Webpack 來開發我們的自動聊天系統

因為本次課程涉及到了 Vue 的一些實現原理,同時也會對使用 vue-cli + Webpack 構建出來的程式碼進行解析,所以在學習本課程之前需要具備 JavaScript 的基礎知識,如果對 Vue 已經有了一些瞭解或者已經使用 Vue 開發過一兩個專案的話,那麼我相信你會在本次課程中擁有更大的收穫!

第01課:初探 Vue

在正式開始學習 Vue 的知識之前,我們需要先對 Vue 的整體有一個大致的瞭解。為了達到這個目的,首先了解以下內容:

  • Vue 對移動端和瀏覽器版本的支援
  • 如何初始化一個 Vue 的專案
  • Vue 的程式碼結構

Vue 對移動端和瀏覽器版本的支援

因為 Vue 本身使用了 ECMAScript 5 特性,所以 Vue 支援所有相容 ECMAScript 5 的瀏覽器。我們根據下面的圖示來解釋一下,資料來自-caniuse.com

這裡寫圖片描述

 

紅色:幾乎不支援

 

黃色:大部分支援(有很小的可能會影響使用)

 

綠色:幾乎全部支援(不影響使用)

由上面的圖示可以看出,對 IE 來說,在 IE 8 及其以下的時候,是不支援 ECMAScript 5 的,也就是說,是無法使用 Vue 的,我們看一下對 IE 8 的描述。

這裡寫圖片描述

 

IE 8 幾乎沒有 ES 5 支援,僅支援對字串的 Object.defineProperty、Object.getOwnPropertyDescriptor、JSON 解析和屬性訪問。

所以說對使用了 ECMAScript 5 特性的 Vue 來說,是沒有辦法在 IE 8 及以下使用的。IE 8 作為天坑的分界線,其實是一直被大家所詬病的,那麼 IE 9 呢?從圖示中可以看出,IE 9 的背景為黃色,也就是說大部分支援,那麼不支援的部分是哪些呢?我們來看一下。

這裡寫圖片描述

可以看到,對 IE 9 來說,它不支援嚴格模式,這對我們使用 Vue 的影響就已經很小了,所以說可以在 IE 9 及以上可以安全的使用 Vue。

而對於其他的瀏覽器和移動端來說,對 ECMAScript 5 的支援還是非常不錯的,Chrome 和 Firefox 都對 ECMAScript 5 早早的提供了支援,而 iOS 6 和 Android 4.4 以上也都對 ECMAScript 5 的支援非常完善了,也就是說我們可以在這些瀏覽器上面放心地使用 Vue 了。

如果大家對這些資料比較感興趣,也可以點選這裡,自己來看一下,這裡就不再過度贅述了。

如何初始化一個 Vue 的專案

然後我們來看一下,應該如何去初始化一個 Vue 的專案。引入 Vue 的方式主要有兩種:

 
  • 直接通過 <script> 標籤引入;
  •  
  • 通過 npm 的方式引入。
  •  

分別看一下這兩種引入方式應該如何去做,然後再去把它們做一些對比。

先來看一下第一種,如何通過 <script> 標籤來引入 Vue。

通過 <script> 標籤引入 Vue

目前 Vue 最新的穩定版本為 2.5.16,可以直接把 Vue 編譯之後的程式碼下載到本地,然後在本地通過 <script> 標籤引入,也可以直接通過 cdn 的方式來引入 Vue。具體方法如下:

// 開發版本,包含完整的警告和除錯模式。// 下載地址:https://vuejs.org/js/vue.js    <script src="vue.js"></script>     // 生產版本,刪除了警告,30.90KB min+gzip。    // 下載地址:https://vuejs.org/js/vue.min.js    <script src="vue.min.js"></script>     // CDN引入方式,版本號為2.5.16。    // 你也可以直接通過 https://cdn.jsdelivr.net/npm/vue/ 這個地址來檢視Vue的原始碼    // 如果你想要引入生產版本的話,那麼只需要把最後的vue.js改為vue.min.js    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>

通過這種方式來引入 Vue,就可以在 .js 檔案或者 .vue 檔案(需要對 .vue 檔案進行解析,比如使用 vue-loader)中直接使用 Vue 的語法了。

通過 npm 的方式引入

第二種方式為通過 npm 來直接引入。對於 npm,大家如果有過前端開發經驗的話,那應該已經不陌生了。npm 作為在前端被廣泛使用的包管理工具,Vue 自然也是需要提供支援的,可以直接使用如下命令來安裝 Vue 的最新版本。

npm install vue

兩種方式的對比

Vue 的官網推薦我們在用 Vue 去構建大型專案時,使用 npm 的方式去安裝,但是說的卻並不是很詳細,先來看一下官網的說法:

 

在用 Vue 構建大型應用時推薦使用 npm 安裝。npm 能很好地和諸如 Webpack 或 Browserify 模組打包器配合使用,同時 Vue 也提供配套工具來開發單檔案元件。

翻譯一下官網的意思:之所以推薦使用 npm 的方式來安裝 Vue,是因為:

 
  • 使用 npm 的方式可以更好的配合模組打包器(Webpack 或者 Browserify);
  •  
  • 可以更方便的使用單檔案元件(.vue 檔案)。
  •  

上面兩點就是推薦使用 npm 方式的原因,關於模組打包器(Webpack)和單檔案元件(.vue 檔案),在後面的篇幅中會詳細講解,如果大家現在就想對 Webpack 或者單檔案元件進行詳細瞭解,也可以點選連結到它們的官網來詳細瞭解。我們在這裡只大概的說一下,目的是讓大家更清楚的瞭解 Vue 引入方式的區別。

一般在使用 Vue 來構建大型專案的時候,通常會通過npm + vue-cli + webpack的方式進行專案初始化。

 

我們需要安裝好 npm,可以點選這裡下載最新的 Node 版本。

 

然後使用 npm install -g vue-cli 指令下載最新的 vue-cli。

 

然後通過vue init <template-name> <project-name>這種方式初始化我們的專案,<template-name>是模組打包器,<project-name>是專案名稱;比如可以通過 vue init webpack my-project 構建一個名字叫做 my-project 的專案。

一路回車之後,就可以得到一個使用npm + vue-cli + webpack構建出的專案了,來看一下專案的結構:

├── README.md├── build├── config├── index.html├── node_modules├── package.json├── src├── static└── test

在 package.json 檔案下,可以看到一個dependencies:{"vue": "版本號"},這個 vue 就是通過 npm 來引入的 Vue,在 src 資料夾下面也已經生成了 .vue 的單檔案元件。

我們可以直接通過npm run dev的方式在本地執行這個專案,也可以通過 npm run build 對這個專案進行打包。

那麼是不是通過npm + vue-cli + webpack方式生成的專案就不能通過<script>標籤的形式引入 Vue 了呢?顯示不是的。

通過 npm 引入 vue,在專案執行打包的時候,會被打包到可執行檔案之中,在通過瀏覽器去訪問專案的時候,則會訪問專案所部署的伺服器中的 vue 檔案,而如果伺服器的頻寬並不高的話(特別是一些個人的伺服器,經常只有 1M 的頻寬),此時,我們就要儘量的避免伺服器中的流量流出。

在這種情況之下,要使用<script>標籤的形式在 CDN 中去引入 vue,不失為是一個好的選擇,比如可以在專案的 index.html 檔案中,通過<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script> 這種方式來引入 vue,從而儘量減少伺服器的壓力。

就像在導讀中說的:我們選擇什麼樣的工具,取決於我們面對了什麼樣的問題。沒有任何一種工具或者方式是在任何一種情況下都完全適用的。

Vue 的程式碼結構

在使用 Vue 開發專案時,我們的程式碼一般會寫到兩種檔案中,分別是:

 
  • 非單檔案元件(.js 檔案);
  •  
  • 單檔案元件(.vue 檔案)。
  •  

通過這兩種檔案來看一下 Vue 的程式碼結構是什麼樣子的。

非單檔案元件(.js 檔案)

非單檔案元件就是我們正常的 js 檔案,js 檔案是可以直接被瀏覽器解析的,可以在 js 檔案中,直接通過(可以直接新建一個 html 檔案,通過<script>標籤的形式引入 Vue,然後複製上面的程式碼,看一下執行的效果)以下方式直接生成一個 Vue 物件。其中,el 為 element 的簡寫,#app表示 ID 為 App 的 div 標籤,意思為ID 為 App 的 <div> 標籤中的所有 html 內容都將被 Vue 解析。而 data 物件則是在 Vue 中生成的資料集,當修改 message 的內容時,div 中展示的資料也會隨之改變。

<div id="app">  {{ message }}</div>var app = new Vue({  el: '#app',  data: {    message: 'Hello Vue!'  }})

大家還記得我們在導讀中說過的 MVVM 模型嗎?下面的程式碼是View:

<div id="app">  {{ message }}</div>

下面的程式碼是 Model。

data: {    message: 'Hello Vue!'  }

而 Vue 框架,即 ViewModel,Vue 連線了檢視和資料,當資料發生改變時,檢視中展示的資料也會發生改變,這種方式在 Vue 中被稱作宣告式渲染

而其他的一些宣告也都會被寫入到 Vue 物件之中,例如,如果要在 Vue 中去定義以下方法,那麼則需要使用到 methods 宣告,如下:

var app = new Vue({  el: '#app'data: {    message: 'Hello Vue!'  },  methods: {      methodsName: function (event) {          ...      }  }});

同樣,如果要監聽 Vue 的宣告週期或者計算屬性的話,也同樣以這種方式來宣告即可,後面在講解到這些這些內容的時候,會詳細解釋。

單檔案元件(.vue 檔案)

第二種方式,也就是通過單檔案元件的方式來使用 Vue,這也是官網所推崇的一種方式。大家可以想一下,如果專案足夠複雜,若通過上面的方式使用 Vue 的話,那 js 檔案得多大啊,同樣也非常不利於以後程式碼的維護,那在以前構建專案的時候遇到這種問題時,一般想到的處理方式是什麼?相信大家如果使用過 jQuery 來開發過完整專案的話,都應該能夠想到,那就是模組化,比如我們常用的:AMD、CMD 或者 ES6 Module 的模組化,那麼在 Vue 中的這種模組化方式,我們稱它為單檔案元件(.vue 檔案)

先來看一下單檔案元件(.vue 檔案)的檔案結構。

<template>     // html</template><script>    // js</script><style>    // css</style>

每一個 .vue 檔案都會被當成使一個“元件”,每個“元件”中都有其單獨的 html、css、js,然後再把這些“元件”拼裝成一個完整的專案(如果大家對這一塊不瞭解,可以看一下導讀中提到的元件化知識)。

使用這種單檔案元件(.vue 檔案)的優勢非常明顯,它使得專案元件化,因為每個模組的功能都被單獨分割,所以程式碼耦合性變得更低,也更利於團隊開發日後維護(這塊會在專案實戰中實地展示這種方式的優勢)。

但是 .vue 檔案瀏覽器是無法直接解析的,需要使用到一些解析工具把 .vue 檔案解析成瀏覽器能夠看懂的 html、css 和 js 檔案。幹這個事的工具一般為vue-loader(就好像我們去解析 .less 檔案的 Koala,不同的是vue-loader並不是一個單獨的應用),而我們去使用vue-loader也都會配合 Webpack 來使用,這也是為什麼構建正式大型專案的時候,一般都會使用npm + vue-cli + webpack方式的原因之一。

然後再看一下,在剛才通過npm + vue-cli + webpack來生成的my-project專案,通過這個專案來大體的認識一下單檔案元件(.vue 檔案)

分別開啟/src/App.vue/src/main.js這兩個檔案,為了防止過多的程式碼對大家造成困擾,我把這些程式碼進行了一些簡化。

App.vue:

<template>  <div id="app">    <img src="./assets/logo.png">    <p>{{message}}</p>  </div></template><script>export defaultdata: function () {    return {      message: 'Hello Vue'    }  }}</script><style>#appfont-family: 'Avenir', Helvetica, Arial, sans-serif;  -webkit-font-smoothing: antialiased;  -moz-osx-font-smoothing: grayscale;  text-align: center;  color: #2c3e50margin-top: 60px;}</style>

main.js:

import Vue from 'vue'import App from './App'new Vue({  el: '#app'components: { App },  template: '<App/>'})

大家可以把這兩部分的程式碼複製到專案中,然後執行npm run dev,通過瀏覽器開啟http://localhost:8080/#/,則可以看到一個 Vue 的圖示和“Hello Vue”的一串文字。

如果可以正常執行,那麼來看一下這些程式碼的具體含義。

main.js裡面它通過 ES6 的方式引入了 Vue 和 App.vue 兩個檔案,然後在初始化 Vue 的時候,通過 el 指定最外層的 div 為id為app的<div>,然後指定了 App.vue 作為 Vue 的一個元件 components,並且給他指定了模板 template 為<App/>

然後在 App.vue 中,它在<template>標籤中聲明瞭 html,在<script>標籤中,指定了資料 data 為{message : 'Hello Vue'},在<style>中聲明瞭樣式,這就生成了一個完整的元件,然後把這個元件通過 ES6 module 的方式注入到 main.js 的 components 中,這樣的一個迴圈的過程,就構成了模組化方針。

從第02課開始會正式進入 Vue 的學習,我們會依照如下腦圖的順序進行。

enter image description here

 

在基礎語法部分,會通過html+<script>的形式來引入 Vue,致力於通過最簡單直接的方式能夠讓大家對 Vue 的常用 API 有一個足夠的瞭解。

 

在周邊生態部分,會通過各種例項來幫助大家去學習 Vue 的周邊生態。

 

最後會通過npm + vue-cli + webpack的方式,以一個實戰專案來讓大家把本課程所學到的所有內容都整合起來。

第02課:基礎語法(上)

從本篇開始將學習 Vue 的基礎語法,在學習基礎語法的時候會使用<script>標籤的形式來引入 Vue,一直持續學習到vue-cli+webpack之後,才會通過npm + vue-cli + webpack的形式來帶大家進行專案實戰。

本篇的內容主要包括以下 3 點:

  • 宣告式渲染
  • 條件渲染
  • 列表渲染

宣告式渲染

宣告式渲染:允許採用簡潔的模板語法宣告式地將資料渲染進 DOM。

用第01課中使用的事例,我們建立一個 index.html 檔案,通過<script>標籤的形式引入 Vue,body 中的程式碼如下:

<div id="app">    {{ message }}  </div>  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>  <script>    var app = new Vue({      el: '#app',      data: {        message: 'Hello Vue!'      }    })  </script>

通過在 data 欄位中宣告的 message 欄位,然後通過{{}}雙大括號的形式在id 為 app 的 div中展示出來,這樣的一種方式,Vue 稱它為宣告式渲染,這種{{}}雙大括號的形式可以稱為 Mustache(這種方式最初是由 Mustache.js 提出的),並且當宣告 message 發生變化的時候,渲染出的 dom 內容也會同步發生改變,可以在開啟的 index.html 的控制檯中,通過app.message = '改變message內容' 命令,來觀察一下 DOM 內容的變化。

我們剛才說了,宣告式渲染為一種方式,也就是說所有符合通過簡潔的語法來宣告式的將資料渲染進 DOM 的方法,都屬於宣告式渲染的一種。比如把上面的程式碼略作修改:

<div id="app">    {{ message }}    <span v-bind:title="spanTitle">我是一個span</span>    <img v-bind:src="imgSrc" v-bind:alt="imgAlt">  </div>  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>  <script>    var app = new Vue({      el: '#app',      data: {        message: 'Hello Vue!',        spanTitle: '我是span的title',        imgAlt: '我是一個img',        imgSrc: 'https://avatar.csdn.net/6/F/4/3_u011068996.jpg'      }    })  </script>

我們新增了兩個標籤<span><img>,然後聲明瞭三個資料 spanTitle、imgAlt 和 imgSrc,通過 v-bind 指令來把資料渲染到 DOM 中,其中的v-bind為 Vue 中的指令。在 Vue 中,所有帶v-字首的,都表示它們為 Vue 提供的特殊特性。

我們重新整理頁面,可以看到宣告出來的資料都已經被渲染到 DOM 中了:

這裡寫圖片描述

這個時候如果細心的讀者,應該可以發現,當我們去重新整理頁面的時候,宣告的 {{ message }},會在 DOM 中一閃而過。

這裡寫圖片描述

這是因為在 Vue 尚未編譯到關聯例項的時候,我們的指令還無法被解析。如果有使用過 jQuery 或者原生 JS 開發專案經驗的話,那麼應該知道我們一般在使用 jQuery 或者原生 JS 時,一般會監聽$().ready(function)或者document.addEventListener("DOMContentLoaded", function);用於處理 DOM,而在 Vue 中使用的是虛擬 DOM 的方式,把這兩者的執行速度進行一個簡單的對比:

<div id="app" >    {{ new Date().getTime() }}  </div><script>    document.addEventListener('DOMContentLoaded', function(event) {        console.log('DOMContentLoaded:' + new Date().getTime());    });  </script>

通過{{}}語法直接去渲染編譯完成之後的時間戳,然後去監聽 JS 的 DOMContentLoaded 事件,並打印出當前的時間戳。

通過對比可以發現 Vue 的編譯速度是在 DOMContentLoaded 之前的,同時 Vue 提供了v-cloak指令,用來解決這種閃爍的問題。我們可以使用v-cloak配合 CSS 規則[v-cloak] { display: none }來隱藏未編譯的 Mustache({{Mustache}})標籤直到例項準備完畢。

<div v-cloak>  {{ message }}</div><style>    [v-cloak] {      display: none;    }</style>

條件渲染

Vue 中的條件渲染指令主要包括以下兩個:

 

v-if  v-show

通過程式碼來看一下:

 <div id="app">    <div v-if="isIf === 1">        isIf為1的時候展示:{{isIf}}    </div>    <div v-else-if="2">        isIf為2的時候展示:{{isIf}}    </div>    <div v-else>            isIf不為1或者2的時候展示:{{isIf}}        </div>    <div v-show="isShow">        當isShow為true的時候展示:{{isShow}}    </div>  </div>  <script>    var app = new Vue({      el: '#app',      data: {        isIf: 1,        isShow: false      }    });  </script>

我們可以直接在控制檯中通過app.isIf = 1/2/3;  app.isShow = true;的方式來控制展示的效果。

那麼v-ifv-show的區別在哪裡?通過渲染出的 element 來看一下。

這裡寫圖片描述

通過 DOM 結構可以看出,v-ifv-else直接控制 DOM 是否被渲染,而v-show則是通過控制 css 的方式來控制 DOM 是否展示。

v-if中,如果一開始條件為,則 Vue 什麼也不會做,當條件變為的時候,Vue 才會去渲染這一部分 DOM,當條件再次變為的時候,與這一塊相關的所有內容都會被從 DOM 樹中刪除掉。

v-show則要簡單的多,它僅僅是通過控制 css 中的 display 來控制 DOM 的顯示和隱藏。

列表渲染

看一下 Vue 中的列表渲染v-for指令,可以通過item in items這種特殊語法的方式指令根據物件(陣列或者是 object 物件)的選項列表來進行渲染,其中 items 為資料來源,item 為每一條資料的別名,通過下面的程式碼來看一下。

<div id="app">      <ul>          <li v-for="(item, index) in items" v-if="item.isShow" v-bind:key="index">            <p>{{index}}</p>            <p>{{item.name}}</p>            <p>¥{{item.price}}</p>          </li>      </ul>  </div>  <script>    var app = new Vue({      el: '#app',      data: {        items: [            {                name: 'android',                price: '12.00',                isShow: false            },            {                name: 'js',                price: '13.00',                isShow: true            }        ]      }    });  </script>

如程式碼所示,通過一個v-for指令來便利了 items 陣列,並且把它的每一條資料的別名定為 item,然後通過 isShow 欄位來控制<li>標籤的渲染,通過兩個<p>標籤來展示 name、price,其中的 index 為每一條資料的索引值,v-bind:key指令為每一條列表資料的唯一 id。當元素列表進行更新的時候,Vue 會根據繫結的v-bind:key=""key 的值來去決定 DOM 的複用策略。

我們在控制檯中可以通過app.items[0].isShow = true的方式,來控制第一條資料的展示;也可以通過app.items.push({name:'IOS', price: '14.00', isShow: true})的方式,來為 items 新增一條新的資料,新新增的資料會被立刻呈現到 DOM 列表中。

v-for 指令不但可以便利陣列,還可以便利 object 物件,看一下 object 物件是如何去便利的。

<div id="app">        <ul>            <li v-for="(value, key, index) in dataSource" v-bind:key="index">              <p>                    {{ index }}. {{ key }}: {{ value }}              </p>            </li>        </ul>    </div>    <script>      var app = new Vue({        el: '#app',        data: {          dataSource: {              name: 'Vue',              price: '12.99',              type: 'javaScript'          }        }      });    </script>

執行上面的程式碼可以看到,v-for指令把 dataSource 物件的屬性進行了迭代。每一條屬性提供了三個引數 value、key、index,執行上面的程式碼來看一下效果。

在通過 v-for 去便利陣列的時候,可以通過 push 方法修改陣列資料的方式來讓 DOM 渲染新的資料,那麼在 object 中,是否也可以用同樣的方式呢?我們來試一下,在控制檯中通過app.dataSource.newKey = 'newKey'的方式來為 dataSource 物件新增一個新的欄位。但是,新新增的欄位並沒有被展示出來,但是可以通過Vue.set(object, key, value)Vue.delete(object, Key)這種方法來為 dataSource 新增或刪除響應式屬性,比如可以在控制檯中通過:Vue.set(app.dataSource, 'newKey', 'newKey') 這種方式來新增響應式屬性。

這是由於 JavaScript 的限制,Vue 不能檢測到物件屬性的新增或刪除,下面列舉一下 Vue 可以檢測到的變化和無法檢測到的變化。

Vue 可以檢測的變化

現有資料欄位的變化:item[0].isShow = true;通過Vue.set(object, key, value)的方式新增的屬性通過Vue.delete(object, Key)的方式刪除的屬性push()pop()shift()unshift()splice()sort()reverse()filter()concat()slice()

Vue 無法檢測到的變化

利用索引去設定新的資料時:items[indexOfItem] = newItem;直接修改陣列的長度時:items.length = newLength;直接去新增或刪除物件的屬性:app.dataSource.newKey = 'newKey'