1. VUE完整系統簡介

今天開始系統學習vue前端框架. 我是有前端基礎的, 剛工做那會, 哪裏分那麼清楚啊, 先後端我都得作, 因此, css, js, jquery, bootstrap都會點, 還系統學過ext, 哈哈,是否是都不知道是啥, 沒事, 都過期了. 如今開始, 學習最流行的Vue, 後端不會頁面, 說不過去呀.....php

言歸正傳, Ready, Go!css

目錄

1. 認識Vuejs

2. Vuejs的安裝方式

3. Vuejs的初體驗-三個案例

4. MVVM模型

5. Vue對象的生命週期

6. Vue源碼


一. 認識Vuejs

  1. 爲何學習Vuejs

  • 這幾年Vue.js成爲前端框架中最火的一個。愈來愈多的網站前端開始採用Vue.js開發。前端開發必備技能.
    • Vuejs是開源世界華人的驕傲,其做者是我國尤雨溪。學習門檻低,成本低,可跨設備和多平臺開發Vue.js.
    • 前端換工做, 面試必問的框架.

  2. 簡單認識一下Vuejs

  • 官網地址: https://cn.vuejs.org/
    • 是一套用於構建用戶界面的漸進式框架, 什麼是漸進式框架呢?

        漸進式框架是說, vue能夠做爲應用的一部分嵌入.html

        好比:以前項目使用的是jquery開發的, 項目體量比較大, 如今知道vue使用上,效果上都更方便, 想要替換爲vue, 可問題是以前的頁面特別多,若是所有替換,工做量太大,那麼不要緊, vue容許你部分嵌入, 也就是說原來的頁面依然使用jquery, 然後開發的頁面使用Vuejs. vue能夠做爲一部分嵌入到項目中. 後面再逐漸替換.前端

  • 若是是使用vue開發新項目, 那麼可使用vue的全家桶. 包括核心庫和和生態系統. 好比: Core+Vue Router + Vuex.

  3. Vuejs的核心功能

  • 解耦視圖和數據
    • 可複用的組件
    • 前端路由技術
    • 狀態管理
    • 虛擬DOM

二. Vuejs安裝方式

vuejs的安裝有三種方式, vue

  1. CDN引入

  • CDN引入有兩個版本: 開發環境和生產環境. 也就是說, 不用本地安裝vue, 而是引入CDN中vue的包
<!-- 開發環境 -->
<script src= "https://cdn.jsdelivr.net/npm/vue/dist/vue.js></script>
<!-- 生產環境 -->
<script src= "https://cdn.jsdelivr.net/npm/vue/vue.js></script>
生產環境建議帶上版本號, 避免因版本問題產生異常
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>

使用這個版本的優勢是: 速度快. 缺點也很明顯: 那就是每次打開都要去cdn上下載, 浪費時間. 學習不建議使用這種方式java

  2. 下載和引入

    • 這裏也有兩個版本, 開發環境和生產環境, 在CDN上下載很慢, 那麼咱們能夠將vue.js下載到本地, 引入到項目中
      開發環境
      https://vuejs.org/js/vue.js

生產環境
https://vuejs.org/js/vue.min.jsnode

開發時可使用開發包, 能夠看到源碼. 生產環境的包更穩定, 體積也會更小

##   3\. NPM安裝管理

   *   在用 Vue 構建大型應用時推薦使用 NPM 安裝
    *   vuejs能夠和webpack和CLI等模塊配合使用
    *   後續學習都是用這種方式操做的.

# 三. Vuejs初體驗

##   1\. Hello Vuejs

    咱們學習程序, 經典代碼helloworld. 這裏說一下開發工具, 開發工具建議使用vscode, 由於裏面有不少插件, 可是其餘也不是不能夠哈

    咱們在感覺vue的時候, 爲了簡單, 方便, 咱們使用第二種方式, 下載vue.js, 並引入到項目中. 接下來開始操做.

   *   ### 第一步: 先搭建一個簡單的項目. 個人項目名稱就叫vue-study. 在裏面建立一個文件夾js, 項目結構以下:

    ![image](https://upload-images.jianshu.io/upload_images/25734023-7bd9a920afcbafef.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

   *   ###  第二步: 而後下載vue.js, 將其放入到js文件夾中

    *   ###  第三步: 寫一個html頁面, 並引入vue.js.

<html>
<head>
<title>第一個vue程序</title>
<script src="../js/vue.js"></script>
</head>
<body>python

</body>

</html> jquery

咱們看到, 第一步引入了vue.js. 其實這裏有個簡單的辦法, 只須要把項目中js拖進來, 就能夠了.引入了vue.js, 那麼要如何使用呢? vue.js咱們能夠理解爲對象. 使用使用new Vue()的方式.

<html>webpack

<head>
    <title>第一個vue程序</title>
    <script src="../js/vue.js"></script>
</head>

<body>
    <div id="app">{{message}}</div>
    <script>
        const app = new Vue({
            el: "#app",
            data: {
                message: "hello, 怒放的太陽!"
            }
        });
    </script>
</body>

</html>

如上, 咱們看到了new Vue(), 至關於把這個對象構建了一份. 而後賦值給了一個常量const app. 這裏須要說一下, 之前,咱們都是直接使用var, 既能夠設置變量也能夠設置常量, 但在vue中, 咱們的變量和常量都有本身的聲明方式

 > 聲明方式:  常量使用const, 變量使用let.

建立vue對象的時候, 傳入了一個option, option中有兩個元素

> el:全稱element, 表示指向的元素.其值使用的是jquery表達式. 該屬性決定了這個vue對象掛載到那個元素上, 能夠看出, 咱們這裏是掛載到了id="app"的元素上
> data: 這個屬性用來存儲數據, 這些數據能夠試試手動寫的, 也能夠是動態從服務端取的

data: 這個屬性用來存儲數據, 這些數據能夠試試手動寫的, 也能夠是動態從服務端取的</pre>

data定義數據. 這裏須要重點說一下了. vue採用的是VMMV的設計模式, 也就是數據和試圖分離. 這裏的data指的就是數據. 而id="app"的div是視圖. 當頁面解析的時候, 解析到script腳本時發現, 咱們已經將div交給vue容器了, 那麼, 這時候, vue就會去尋找目標元素是否有待填補的變量. 這裏咱們看到<div id="app">{{message}}</div>裏面定義了一個變量message, 而這個變量在vue容器中進行了聲明, 所以能夠進行對應的自動填充. 

         
這裏若是暫時不理解, 也不要緊, 先混個眼熟, 後面還有詳細講解

*   ### 第四步: 分析瀏覽器執行代碼的流程

1 <html>
2
3 <head>
4 <title>第一個vue程序</title>
5 <script src="../js/vue.js"></script>
6 </head>
7
8 <body>
9 <div id="app">{{message}}</div>
10 <script>
11 const app = new Vue({
12 el: "#app",
13 data: {
14 message: "hello, 怒放的太陽!"
15 }
16 });
17 </script>
18 </body>
19 </html>

頁面渲染, 首先加載1-10行, 顯示出對應的html. 執行到第11行的時候, 建立了vue實例, 而且對照html進行解析和修改.

##   2\. Vue列表展現

    
下面來看一個稍微複雜一點的例子---列表展現

    
先來看看效果

![image](https://upload-images.jianshu.io/upload_images/25734023-19a60c9524075cdf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/340)

    
下面思考, 若是咱們使用jquery會如何實現呢? 須要些一個for循環, 而後在裏面定義n個li, 而後拼裝數據. 很複雜.  然而, 使用vue徹底不須要在js代碼中拼裝html元素的數據, 下面來看看怎麼作

   *   ### 第一步: 新建一個html頁面, 命名爲02-list.html, 而後引入vue.js

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表頁面</title>
<script src="../js/vue.js"></script>
</head>
<body>

</body>
</html>

*   ###  第二步構建vue對象
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        **<script src="../js/vue.js"></script>**
    </head>
    <body>
        **<div id="app">
            <h1>{{title}}</h1>
            <ul>
                <li v-for = "item in languages">{{item}}</li>
            </ul>
        </div>**
        <script>
            **const app = new Vue({
                el: "#app",
                data:{
                    title: "常見的後端編程語言有哪些?",
                    languages: ["python", "go", "java", "net", "php", "c++"]
                }
            });** </script>
    </body>
    </html>
這裏指定了當前構建的vue對象掛載在id="app"的元素上. 並填充值title和languages. 和上一個案例不一樣, 這裏有一個數組元素languages. 那麼數組元素應該如何取值呢?

<ul>
<li v-for = "item in languages">{{item}}</li>
</ul>

注意紅色粗體部分. 使用了一個vue的指令v-for, 這是表示for循環, 這個第一次見到, 先熟悉一下. 後面還會具體講. 咱們之前使用jquery會怎麼寫呢?

<ul>
<li >python</li>
<li >go</li>
<li >java</li>
<li >php</li>
<li >.net</li>
<li >...</li>
</ul>

之前咱們要這麼寫一大堆, 若是是動態從服務端取數據, 那麼還要拼li代碼, 很容易出錯, 還很費勁. 但使用了vue指令, 咱們發現一句話就搞定了, 這裏是否是能夠傲嬌一下. 怪不得vue能這麼流行.

##   3\. 案例:計數器
     
計數器是一個小的綜合案例, 經過這個案例來再次感覺一下vue的強大. 咱們先來看一下效果

    ![image](https://upload-images.jianshu.io/upload_images/25734023-cf0799e3021647b8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/240)
    
分析: 這裏有一個變量, 兩個按鈕. 點擊+, 數字加1, 點擊-, 數字減1\. 下面咱們就來實現這個功能

   *   第一步: 建立一個html文件03-計數器.html 

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app"> 當前數字: {{counter}} <br>
<button v-on:click="counter++"> + </button>
<button v-on:click="counter--"> - </button>

</div>
<script>
const app = new Vue({
el: "#app",
data:{
counter: 0 }
});
</script>
</body>
</html>

引入vue.js, 並建立一個Vue對象. 這些以前都說過, 就很少說了. 接下來看看

<button v-on:click="counter++"> + </button>

這是什麼意思呢? 這是vue的寫法. v-on是vue的指令, 這裏先有個印象, 後面會詳細講解. v-on表示要執行一個事件, :click就是具體的事件, 這裏是點擊事件, 點擊後執行什麼邏輯呢? 執行counter ++. 是否是很神奇? 也許尚未感受, 那麼咱們來看看, 若是是jQuery, 要怎麼作吧?

> 1. 給+按鈕添加一個點擊事件 
> 2. 獲取counter計數器對象的值 
> 3. 對counter進行++
> 4. 再講counter計算後的結果賦值給計數器對象.       

如今感覺到了吧, jquery是命令式編程, 一行命令執行一個語句. 這裏要執行好幾句話, 而vue一句話就搞定了.    
*   第二步: 這裏click事件中就有一句話, counter++, 那麼要是有好多邏輯怎麼辦呢? 那就須要提出來單獨處理了.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app"> 當前數字: {{counter}} <br>
<button v-on:click="add"> + </button>
<button v-on:click="sub"> - </button>
</div>
<script>
const app = new Vue({
el: "#app",
data:{
counter: 0 },
methods: {
add: function() {
console.info(**
"add方法被執行")
this.counter ++;
},
sub: function () {
console.info("sub方法被執行")
this.counter --**;
}
}

}); </script>
</body>
</html>

在vue裏面,要想增長一個事件, 那就放在methods屬性裏就能夠了. 這裏有一點須要注意. 在方法裏要對data中的變量執行counter ++, 直接這麼寫是不行的, 須要加上this.counter++. this表示的是new出來的Vue對象. 有朋友可能就要說了, this在方法裏面, 不該該是表示當前方法麼?vue作了一層代理, 因此, 這裏的this指的是new Vue()對象.  

# 四. Vuejs的MVVM

##   1\. 什麼是MVVM
MVVM是Model-View-ViewModel的簡寫。它本質上就是MVC 的改進版。MVVM 就是將其中的View 的狀態和行爲抽象化,讓咱們將視圖 UI 和業務邏輯分開。固然這些事 ViewModel 已經幫咱們作了,它能夠取出 Model 的數據同時幫忙處理 View 中因爲須要展現內容而涉及的業務邏輯。View綁定到ViewModel,而後執行一些命令在向它請求一個動做。而反過來,ViewModel跟Model通信,告訴它更新來響應UI。這樣便使得爲應用構建UI很是的容易。

    
MVVM有助於將圖形用戶界面的開發與業務邏輯或後端邏輯(*數據模型*)的開發分離開來,這是經過置標語言或GUI代碼實現的。MVVM的*視圖模型*是一個值轉換器,這意味着視圖模型負責從模型中暴露(轉換)數據對象,以便輕鬆管理和呈現對象。在這方面,視圖模型比視圖作得更多,而且處理大部分視圖的顯示邏輯。 視圖模型能夠實現中介者模式,組織對視圖所支持的用例集的後端邏輯的訪問。

##   2\. MVVM的優勢

MVVM模式和MVC模式同樣,主要目的是分離[視圖](https://baike.baidu.com/item/%E8%A7%86%E5%9B%BE)(View)和模型(Model),有幾大優勢

   *   低耦合。視圖(View)能夠獨立於Model變化和修改,一個ViewModel能夠綁定到不一樣的"View"上,當View變化的時候Model能夠不變,當Model變化的時候View也能夠不變。
    *   可重用性。你能夠把一些視圖邏輯放在一個ViewModel裏面,讓不少view重用這段視圖邏輯。
    *   獨立開發。開發人員能夠專一於業務邏輯和數據的開發(ViewModel),設計人員能夠專一於頁面設計,使用Expression Blend能夠很容易設計界面並生成xaml代碼。
    *   可測試。界面素來是比較難於測試的,測試能夠針對ViewModel來寫。 

##   3. MVVM模式的組成部分

   *   #### 模型
模型是指表明真實狀態內容的領域模型(面向對象),或指表明內容的數據訪問層(以數據爲中心)。

   *   #### 視圖
就像在MVC模式中同樣,視圖是用戶在屏幕上看到的結構、佈局和外觀(UI)。

  *   #### 視圖模型 
視圖模型*是暴露公共屬性和命令的視圖的抽象。MVVM沒有MVC模式的控制器,也沒有MVP模式的presenter,有的是一個*綁定器*。在視圖模型中,綁定器在視圖和數據綁定器之間進行通訊。

  *   ####  綁定器
聲明性數據和命令綁定隱含在MVVM模式中。綁定器使開發人員免於被迫編寫樣板邏輯來同步視圖模型和視圖。在微軟的堆以外實現時,聲明性數據綁定技術的出現是實現該模式的一個關鍵因素

##   4\. Vue中的VMMV 
下圖不只歸納了MVVM模式(Model-View-ViewModel),還描述了在Vue.js中ViewModel是如何和View以及Model進行交互的。    
![image](https://upload-images.jianshu.io/upload_images/25734023-dfcc86e17fa5cd4d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
ViewModel是Vue.js的核心,它是一個Vue實例。Vue實例是做用於某一個HTML元素上的,這個元素能夠是HTML的body元素,也能夠是指定了id的某個元素。

當建立了ViewModel後,雙向綁定是如何達成的呢?

    
首先,咱們將上圖中的DOM Listeners和Data Bindings看做兩個工具,它們是實現雙向綁定的關鍵。 
    
從View側看,ViewModel中的DOM Listeners工具會幫咱們監測頁面上DOM元素的變化,若是有變化,則更改Model中的數據; 
    
從Model側看,當咱們更新Model中的數據時,Data Bindings工具會幫咱們更新頁面中的DOM元素。

    
拿第一個案例來講

<html>

<head>
    <title>第一個vue程序</title>
    **<script src="../js/vue.js"></script>**
</head>

<body>
    <div id="app">{{message}}</div>
    <script>
        **const app = new Vue({
            el: "#app",
            data: {
                message: "hello, 怒放的太陽!" }
        });** </script>
</body>

</html>

在這裏, 定義了一個View, 定義了model, 建立了一個Vue實例(view-model), 它用於鏈接view和model

在建立Vue實例時,須要傳入一個選項對象,選項對象能夠包含數據、掛載元素、方法、模生命週期鉤子等等。

在這個示例中,選項對象的el屬性指向View,el: ‘#app’表示該Vue實例將掛載到`<div id="app">...</div>`這個元素;data屬性指向Model,data: { message: "hello, 怒放的太陽" 表示咱們的Model是一個對象。

Vue.js有多種數據綁定的語法,最基礎的形式是文本插值,使用一對大括號語法,在運行時{{ message }}會被數據對象的message屬性替換,因此頁面上會輸出」**hello, 怒放的太陽!**」。

# 五. Vue實例的生命週期

每一個 Vue 實例在被建立時都要通過一系列的初始化過程——例如,須要設置數據監聽、編譯模板、將實例掛載到 DOM 並在數據變化時更新 DOM 等。同時在這個過程當中也會運行一些叫作生命週期鉤子的函數,這給了用戶在不一樣階段添加本身的代碼的機會。

好比 [`created`]鉤子能夠用來在一個實例被建立以後執行代碼:

new Vue({
data: {
a: 1 },
created: function () { // this 指向 vm 實例
console.log('a is: ' + this.a)
}
}) // => "a is: 1"

也有一些其它的鉤子,在實例生命週期的不一樣階段被調用,如 [`mounted`]、[`updated`] 和 [`destroyed`]。生命週期鉤子的 `this` 上下文指向調用它的 Vue 實例。

 > **注意:**
>
>不要在選項 property 或回調上使用箭頭函數,好比 
>  created: () => console.log(this.a) 或 vm.$watch('a', newValue => >this.myMethod())。
>由於箭頭函數並無 this,this 會做爲變量一直向上級詞法做用域查找,直至找>到爲止,常常致使 
>Uncaught TypeError: Cannot read property of undefined 或 
>Uncaught TypeError: this.myMethod is not a function 之類的錯誤。

## 1\. 生命週期圖示

下圖展現了實例的生命週期。你不須要立馬弄明白全部的東西,不過隨着你的不斷學習和使用,它的參考價值會愈來愈高。 

 ![image](https://upload-images.jianshu.io/upload_images/25734023-e2c967b24c5fb46c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

##  2\. Vue生命週期函數

如上圖, 經常使用的生命週期函數有: beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, beforeDestory, destoryed, 這些鉤子函數都是回調函數, 在vue生命週期執行太重,方便用戶操做控制的入口

#  六. Vue源碼

咱們知道了vue的生命週期了, 接下來看看vue的源碼, 對vue的生命週期加深理解

源碼下載地址: https://github.com/vuejs/vue

咱們選擇一個release版本. 下載代碼到本地

![image](https://upload-images.jianshu.io/upload_images/25734023-4141561e4dac5d70.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

 下載好之後, 打開項目, 咱們來看看項目結構.

![image](https://upload-images.jianshu.io/upload_images/25734023-11b1fb25e30e0a07.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/640)

剛開始, 咱們不熟悉, 那麼先猜想一下, 哪一個是主要文件, 經驗告訴咱們, src裏面的纔是主目錄, 在src中和核心目錄是core. 

![image](https://upload-images.jianshu.io/upload_images/25734023-8c0f93a7d7f0f346.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/240)

 咱們看到了index.js, 一般一個網站的入口是index.html, 而對應的js腳本就是index.js. 打開index.js

import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'

initGlobalAPI(Vue)

Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})

Object.defineProperty(Vue.prototype, '$***Context', {
get () {
/ istanbul ignore next /
return this.$vnode && this.$vnode.***Context
}
})

// expose FunctionalRenderContext for *** runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})

Vue.version = 'VERSION'

export default Vue

這裏面有兩句很是重要的話, 第一句

export default Vue

這句話表示export導出Vue, 咱們new的就是這裏導出的Vue. 咱們看到index.js中沒有主邏輯, 那主邏輯在哪裏呢? 在第二句話裏面: 

import Vue from './instance/index'

導入了./instance/index中的文件. 咱們來看看這個文件

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the new keyword')
}
this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

首先, 咱們看到定義了一個Vue對象, 在對象裏面執行了不少操做, 初始化, 事件監聽, 生命週期處理, 渲染等等. 這就是vue的整個流程. 咱們進入到initMixin(Vue)初始化方法裏面看一下

/ @flow /

import config from '../config'
import { initProxy } from './proxy'
import { initState } from './state'
import { initRender } from './render'
import { initEvents } from './events'
import { mark, measure } from '../util/perf'
import { initLifecycle, callHook } from './lifecycle'
import { initProvide, initInjections } from './inject'
import { extend, mergeOptions, formatComponentName } from '../util/index'

let uid = 0

export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++

let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
  startTag = `vue-perf-start:${vm._uid}`
  endTag = `vue-perf-end:${vm._uid}`
  mark(startTag)
}

// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
  // optimize internal component instantiation
  // since dynamic options merging is pretty slow, and none of the
  // internal component options needs special treatment.
  initInternalComponent(vm, options)
} else {
  vm.$options = mergeOptions(
    resolveConstructorOptions(vm.constructor),
    options || {},
    vm
  )
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
  initProxy(vm)
} else {
  vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')

......

初始化的時候又作了一系列的操做. 注意在方法建立以前有一個鉤子函數callHook(vm, 'beforeCreate'),  方法建立以後, 有一個callHook(vm, 'created')函數, 這裏能夠和上面的生命週期圖對比研究, 就能更加熟悉Vue的聲明週期了
相關文章
相關標籤/搜索