後端開發者的Vue學習之路(一)

首發日期:2019-01-19html


前言:


有時候,一個後端開發者「不得不」本身去搭建前端界面。若是有的選,固然選一個比較好學的前端「框架」咯(框架不少時候封裝了普通的html元素,使得能更加方便地使用)。
若是你作的項目的界面是一個偏後臺管理的並且要求並不高的界面的時候,你能夠考慮easy UI這個較爲知名的前端框架。
若是你想要界面變得好看一些(easy UI的界面太簡單了,缺少較強的定製性),那麼你能夠考慮vue這個框架。vue這個框架自己並無多少好看的樣式,但學習了這個框架就能夠學會怎麼使用它的第三方組件庫了(這些第三庫看起來都還行)。前端


iview組件庫示例


element組件庫示例


【Vue本身有官方教程,爲何寫這個呢?主要是感受本身的前端知識不太穩固,看教程的時候有點迷糊。推己及人,因此有了這個博文】vue



Vue的介紹


  • 官網:https://cn.vuejs.org/
  • [官方的話]:Vue (讀音 /vjuː/,相似於 view) 是一套用於構建用戶界面的漸進式框架。與其它大型框架不一樣的是,Vue 被設計爲能夠自底向上逐層應用。Vue 的核心庫只關注視圖層,不只易於上手,還便於與第三方庫或既有項目整合。另外一方面,當與現代化的工具鏈以及各類支持類庫結合使用時,Vue 也徹底可以爲複雜的單頁應用提供驅動。
  • 小菜鳥前端:Vue是一個簡單容易上手前端框架,在網上有不少基於vue的web UI框架(element等),因爲有大量相似於easyUI這些快速搭建頁面的框架,因此咱們可使用vue來快速搭建頁面。若是咱們做爲一個後端開發者想掌握一個前端框架,vue是一個好選擇,由於它足夠的易學。


例以下面的代碼能夠快速構建一個表格:java

<template>
    <el-table :data="tableData" style="width: 100%">
      <el-table-column  prop="date"  label="日期"  width="180"></el-table-column>
      <el-table-column  prop="name"  label="姓名"  width="180"></el-table-column>
      <el-table-column  prop="address" label="地址"> </el-table-column>
    </el-table>
  </template>

  <script>
    export default {
      data() {
        return {
          tableData: [{
            date: '2016-05-02',
            name: '王小虎',
            address: '上海市普陀區金沙江路 1518 弄'
          }, {
            date: '2016-05-04',
            name: '王小虎',
            address: '上海市普陀區金沙江路 1517 弄'
          }, {
            date: '2016-05-01',
            name: '王小虎',
            address: '上海市普陀區金沙江路 1519 弄'
          }, {
            date: '2016-05-03',
            name: '王小虎',
            address: '上海市普陀區金沙江路 1516 弄'
          }]
        }
      }
    }
  </script>


兼容性:

Vue不支持IE8 及如下版本,由於 Vue 使用了 IE8 沒法模擬的 ECMAScript 5 特性。但它支持全部兼容 ECMAScript 5 的瀏覽器。【因此若是項目兼容性要求較高,那麼不適合使用Vuejquery


學習Vue須要的前置知識:

  • html+ js 【基礎的內容,前端的基礎,不瞭解不行。但只要你能看得懂基本的html和js代碼便可,不瞭解的能夠查。】
  • es6 【 ECMAScript 6(ES6)是JavaScript語言的下一代標準。但不須要了解太多,Vue裏面有些語法是ES6的,但咱們能夠看着例子瞭解有哪些常見的語法便可】
  • webpack 【不須要了解太多,是一個項目構建工具,會涉及一些Vue項目的運行知識。】
  • npm 【vue入門不須要了解太多,可能只須要了解npm的幾個命令】


MVVM模型

  • MVVM是Model-View-ViewModel的簡寫。
  • MVVM(Model-View-ViewModel)的基礎是MVP模式,【MVP相似於MVC,後端開發者應該對MVC比較熟悉了】

    【常見的MVP前端開發有幾個步驟:1.建立頁面元素,2.在script中給元素綁定事件,3.在script中處理事件(這個事件可能會影響頁面的元素的顯示或返回數據)】
    【上面的幾個步驟能夠說是內容(頁面元素)行爲(js操做)分離了】
    【爲何要有MVVM呢?主要是MVP模式有太多的dom操做了(大量的DOM 操做使頁面渲染性能下降,加載速度變慢)。MVVM解決了這個問題,而且提供了一些其餘的好處。】

【若是你瞭解ORM模型,你應該很能體會到MVVM模型的好處,有了VM層幫咱們管理了數據,咱們就只須要處理好Model層了,這就好像ORM中定義了數據映射關係,而後咱們只須要操做實體類。】webpack


補充:

  • Vue的核心特色是簡單易用,因此它相對適合非專業前端的學習。它須要一些前置的前端知識,並且它本身的知識也是須要時間來學習的,若是與easy UI相比,easy UI的學習速度會更快,但vue更能構建比較優美的頁面。



安裝/導入


【要想使用Vue,必須先導入/安裝,就像使用jquery必須導入jquery.js同樣;而vue可使用webpack來構建,因此也可使用webpack安裝Vue,但若是你是初學者,先掌握靜態導入的方法吧】ios


導入Vue

Vue的本質也是javascript,因此它也能夠經過導入js文件來使用功能(js能夠用cdn的,也能夠手動導入本地的vue.js)。
這種也是最簡單的使用vue的方式,因此咱們能夠這種方式來做爲入門學習,但正式使用時都會使用webpack來構建vue項目。es6

在html文件中使用以下代碼:【這裏先講導入,後面講使用】web

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script>


安裝

  • 安裝vue:npm install vue
  • 全局安裝vue-cli: npm install --global vue-cli
  • 建立一個基於 webpack 模板的新項目:vue init webpack 項目名


兩種方式的區別:

  • 對於普通的靜態js導入,在面對多個組件共同開發的時候,會比較不方便。【若是你是後端開發者,你應該知道把多個類寫在一個文件中是多麼地不方便,這也是同樣的】
  • js:
    • 簡單,不須要安裝,直接導入js便可。
  • npm
    • 在用 Vue 構建大型應用時推薦使用 NPM 安裝
    • NPM 能很好地和諸如 webpack 或 Browserify 模塊打包器配合使用。
    • 同時 Vue 也提供配套工具來開發單文件組件。



在入門部分將使用js導入方式的例子,在涉及構建多個組件的時候(頁面須要多個組件時,若是把多個組件定義在一個文件中會顯得贅餘。這比如把多個類放到同一個文件中定義。)將使用npm安裝方式演示。


HelloWorld示例


<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>

    <body>
        <!--2.這是被vue實例管理的區域-->
        <div id="app">
            {{ message }}
        </div>
    </body>
    <!--1.導入js-->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        //3. 建立實例:new Vue()裏面有一個{}類型的參數,屬性el是一個元素的id名,表明被vue實例管理的區域;
       var app = new Vue({
                  el: '#app',
                  data: {
                    message: 'Hello Vue!'
                  }
                })
    </script>
</html>


代碼分析:

  1. 導入了js:<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  2. 定義一個元素,給它id起名app。[名字是能夠隨意的,但在下面的new Vue建立實例時,el參數必須是這個id名]
  3. 新建一個vue實例,屬性el表明把對應元素區域交給vue管理(el: '#app'表明把id爲app的區域交給vue管理)。那麼對應元素區域裏面就可使用vue的語法了
  4. data屬性:data裏面能夠定義一系列變量,可用於這個實例掌控的區域。
  5. {{ message }} : vue語法的內容,表明獲取變量名爲message的數據,並顯示出來。


代碼效果:


導入了js以後,vue把管理的區域裏面的vue語法都解析了,因此 {{ message }}就取出了vue實例中名叫message的數據。




如今咱們瞭解了一些vue的渲染頁面的知識,下面咱們來了解更多的渲染技巧。


實例中能夠定義的內容

每一個 Vue 應用都是經過使用 Vue 函數建立一個新的 Vue 實例開始的。
實例中的定義的內容就是咱們可使用在Vue應用中的內容。
下面講實例中能夠定義哪些內容。

  • 數據
  • 方法
  • 生命週期鉤子函數
  • 其餘(有些內容比較重要,留到後面講)


定義數據

  • 定義了數據,那麼就能夠在Vue管理的區域中使用Vue的獲取數據的語法來獲取數據。
  • 這種操做就像是咱們把數據裝到實例中,實例再把數據傳給視圖組件。
  • 任何你想顯示到頁面的數據你均可以考慮定義在實例中。
var app = new Vue({
                  el: '#app',
                  data: {
                    message: 'Hello Vue!'
                  }
                })
        //<div id="app">
        //    {{ message }}
        //</div>


定義方法methods

  • 在js中,方法是很重要的,Vue實例中能夠定義方法。【爲何要有方法這個就不解釋了吧?】
<body>
        <div id="app">
            <!-- 觸發事件,建議使用Vue的語法來綁定事件(@事件類型,表明綁定什麼事件) -->
            <!-- 實例內定義的函數,使用Vue的語法來綁定;實例外定義的,可使用原生的方式綁定事件 -->
            <button @click="myFunction">按鈕</button>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
       var app = new Vue({
                  el: '#app',
                  data: {
                    message: 'Hello Vue!'
                  },
                  methods: {
                    myFunction: function () {
                        alert('haha')
                    }
                  }
                })
    </script>


生命週期與生命週期鉤子函數

要結合圖來看(懂英文是多麼地好)

  • beforeCreate:在建立了vue示例時,在進行了一部分基礎的初始化以後就會自動執行beforeCreate函數
  • created:建立完示例後,會自動執行created函數
  • beforeMounted:渲染完模板後(能夠說是vue知道區域內是一個怎麼的html環境,但還沒把數據顯示到這個環境中的時候),會自動執行這個函數
  • mouted:模板與數據結合以後自動執行這個函數。【此時頁面已經渲染完畢】
  • beforeUpdated:數據發生變化後,新數據被渲染前會自動執行這個函數。
  • updated:新數據被渲染成功會自動執行這個函數。
  • beforeDestory:當實例被destory時會自動執行這個函數。
  • destory:當實例被徹底銷燬是自動執行這個函數。

【若是你對生命週期鉤子感興趣,能夠自查,這裏很少講,後面以後根據開發需求來說一些】


測試代碼:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>

    <body>
        <div id="app">
            <button @click='updateValue'>點擊觸發beforeUpdate,updated</button>
            <button onclick='deleteApp()'>點擊觸發beforeDestory,destoryed</button>
            <p ref='a'>{{ msg }}</p>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var app = new Vue({
                  el: '#app',
                  data: {
                    msg: 'hello'
                  },
                  beforeCreate: function(){
                    console.log('beforeCreate')
                  },
                  created: function(){
                    console.log('created')
                  },
                  beforeMount: function(){
                    console.log(this.$refs.a)
                    // 上面的你會看到undefined,由於數據還沒綁定上
                    console.log('beforeMount')
                  },
                  mounted: function(){
                    console.log(this.$refs.a)
                    // 上面的你會看到p,由於數據綁定上了
                    console.log('mounted')
                  },
                  beforeUpdate: function(){
                    console.log('beforeUpdate')
                  },
                  updated: function(){
                    console.log('updated')
                  },
                  beforeDestroy: function(){
                    console.log('beforeDestory')
                  },
                  destroyed: function(){
                    console.log('destoryed')
                  },
                  methods: {
                    updateValue: function () {
                        this.msg = 'haha'
                    }
                  }
                })
        function deleteApp() {
            app.$destroy()
        }
    </script>
</html>


補充:

  • 實例中其實還能夠定義別的內容(如計算屬性,監聽器等)。但主要內容是上面這些,暫時瞭解這些就夠了。
  • 建立了實例,就能夠在控制檯中使用實例名來獲取數據了,好比上面定義了實例app,那麼app.message就能夠獲取定義在data中名爲message的數據,app.myFunction()就能夠調用定義在methods中名爲myFunction的函數。【因此後面的例子中的一些數據更改都是使用控制檯來更改實例中的數據

渲染


插入文本

在上面已經演示了使用Mustache表達式(能夠俗稱插值表達式){{ }}來獲取實例中的數據了,其實還可使用其餘方式來輸出數據


v-text

v-text能夠向元素中輸出文本內容

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


v-html

v-html能夠向元素中輸出html內容:

<div id="app">
            <span v-html="message"></span>
            <!-- message: 'Hello Vue!'(帶標題樣式的) -->
        </div>
        <!--
                var app = new Vue({
                  el: '#app',
                  data: {
                    message: '<h1>Hello Vue!</h1>'
                  }
                })
        -->


Mustache 語法不能做用在 HTML 特性上,也就是說在html元素中沒有 id="{{ id }}" 這樣的操做 ,這種時候須要使用vue語法:v-bind:id="id"
{{ }}裏面可使用javascript表達式,例如:{{ message.split('').reverse().join('') }}


用v-bind綁定屬性:

v-bind用於給元素的屬性綁定值

  • 綁定標題:v-bind:title
<div id="app">
              <span v-bind:title="message">懸浮幾秒,查看標題</span>
        </div>
        <!--
                var app = new Vue({
                  el: '#app',
                  data: {
                    message: 'Hello Vue!'
                  }
                })
        -->
  • 給a元素綁定href : v-bind:href='xxx'
  • 給元素綁定id : v-bind:id='xxx'
    【理論上,可使用v-bind給元素的任意屬性綁定值】

v-bind的簡寫

<!-- 完整語法 -->
<a v-bind:href="url">...</a>

<!-- 縮寫 -->
<a :href="url">...</a>


把對象的全部屬性綁定到元素:

v-bind的參數能夠是一個對象,是一個對象時,會把對象的全部屬性都綁定到元素上。具體以下圖。


條件渲染

  • v-if能夠判斷是否渲染一個元素,若是不渲染,這個元素將不會產生(不是隱藏!能夠理解爲這個元素被delete了。)
<div id="app">
             <p v-if="seen">如今你看到我了</p>
             <!-- seen: true 時渲染,seen: false時不渲染 -->
        </div>
        <!--
        var app = new Vue({
                  el: '#app',
                  data: {
                    seen: false
                  }
                })
        -->
  • 除了v-if,還有v-else和v-else-if,v-else不能夠再帶條件判斷,v-else-if能夠帶條件判斷。
<div id="app">
            <h1 v-if="gender === '男'">男</h1>
            <h1 v-else-if="gender === '女'">女</h1>
            <h1 v-else>unknown</h1>
        </div>
        <!--
       var app = new Vue({
                    el: '#app',
                    data: {
                        gender: '女'
                    },
                })
         -->

使用v-else 的元素必須緊跟在帶 v-if 或者 v-else-if 的元素的後面,不然它將不會被識別。

  • v-show 的元素始終會被渲染並保留在 DOM 中。v-show 只是簡單地切換元素的 CSS 屬性 display。
<div id="app">
            <h1 v-show="ok">Hello!</h1>
        </div>
        <!--
       var app = new Vue({
                    el: '#app',
                    data: {
                        ok: true
                    },
                })
         -->


循環

  • v-for能夠循環渲染出若干個元素。
  • v-for 指令須要使用 item in items 形式的特殊語法,items 是源數據數組,而 item 是數組元素迭代的別名。
<div id="app">
            <ol>
                <!-- 每一次迭代了出數據給todo,都會有一個li元素,並會在li元素中輸出todo的text -->
                <li v-for="todo in todos">
                  {{ todo.text }}
                </li>
            </ol>
        </div>
        <!--
       var app = new Vue({
                  el: '#app',
                  data: {
                    todos: [
                      { text: '學習 JavaScript' },
                      { text: '學習 Vue' },
                      { text: '搞事情' }
                    ]
                  }
                })
        -->

代碼效果:

在 v-for 塊中,咱們擁有對父做用域屬性的徹底訪問權限。[這是來自官網的話,我以爲有點多餘,感受子元素獲取父元素的數據是很正常的操做。]

v-for 還支持一個可選的第二個參數爲當前項的索引,它會從0開始。用來標識當前迭代的是第幾個元素。

能夠用 of 替代 in 做爲分隔符,由於它是最接近 JavaScript 迭代器的語法:
<div v-for="item of items"></div>

也能夠用 v-for 迭代一個對象的全部屬性,第一個參數是value,第二個可選參數是key,第三個可選參數爲索引

第二個參數爲key

<div v-for="(value, key) in object">
  {{ key }}: {{ value }}
</div>

第三個參數爲索引:

<div v-for="(value, key, index) in object">
  {{ index }}. {{ key }}: {{ value }}
</div>

使用三個參數時的代碼效果:

在遍歷對象時,是按 Object.keys() 的結果遍歷,可是不能保證它的結果在不一樣的 JavaScript 引擎下是一致的。
v-for 也能夠取一個整數做爲源數據(v-for="i in 10")。在這種狀況下,它將重複屢次模板。
當 v-if 與 v-for 一塊兒使用時,v-for 具備比 v-if 更高的優先級。【這個問題官網提了一下,這裏我也提一下,注意使用】



計算屬性

  • 當某個數據須要另一個數據計算獲得的時候,你可能會定義兩個數據,其中一個數據初始值由另外一個數據計算得出。但上面的值只能初始化一次,若是但願數據A變的時候,數據B也變,那麼你可能須要去監聽數據的變化,從而每次數據變化時再作操做。
  • 計算屬性簡化了這樣的狀況,它的返回值是一個計算獲得的數據,當內部的計算元素髮生變化的時候,返回值也會變化。
  • 計算屬性使用computed來定義。


<div id="app">
            <p>原消息: "{{ message }}"</p>
            <p>逆反後: "{{ reversedMessage }}"</p>
        </div>
        <!--
        var app = new Vue({
                    el: '#app',
                    data: {
                    message: 'Hello'
                  },
                  computed: {
                    // 計算屬性的 getter
                    reversedMessage: function () {
                      // `this` 指向 vm 實例
                      return this.message.split('').reverse().join('')
                    }
                  }
                })
         -->


與函數的區別

下面的代碼也能夠達到上面的效果
(也是即時的,有人不懂爲何這個函數會重複調用,而非頁面初始化時調用一次;由於當頁面中數據更新的時候,涉及頁面數據的函數也會從新執行。)

<div id="app">
            <p>原消息: "{{ message }}"</p>
            <p>逆反後: "{{ reversedMessage() }}"</p>
            <p>{{ Date.now() }}"</p>
            <!-- 後面的now是用來測試數據刷新時,函數會從新執行 -->
        </div>
        <!--
        var app = new Vue({
                    el: '#app',
                    data: {
                    message: 'Hello'
                  },
                  methods: {
                    reversedMessage: function () {
                      // `this` 指向 vm 實例
                      return this.message.split('').reverse().join('')
                    }
                  }
                })
         -->

函數也能夠達到一樣的效果,可是函數沒有緩存效果,而計算屬性有緩存。沒有緩存時,函數每一次都要從新計算來獲得結果,若是這是一個比較消耗資源的計算的話,那麼會減慢頁面的速度;而計算屬性有緩存,只要內部的計算元素沒有發生變化,那麼會使用緩存來做爲計算結果。


與偵聽屬性的區別

上面的計算屬性達到效果須要留意計算元素的變化,你可能會想到一些相似的監聽數據變化的方法,而vue中也是有這樣的東西的。
下面的代碼監聽了firstName和lastName,當這兩個數據變化的時候會從新給fullName賦值。

<div id="app">
            <div>{{ fullName }}</div>
        </div>
        <!--
                var app = new Vue({
                    el: '#app',
                    data: {
                        firstName: 'Foo',
                        lastName: 'Bar',
                        fullName: 'Foo Bar'
                    },
                    watch: {
                        firstName: function (val) {
                          this.fullName = val + ' ' + this.lastName
                        },
                        lastName: function (val) {
                          this.fullName = this.firstName + ' ' + val
                        }
                    }
                })
         -->

使用計算屬性達到上面效果(顯然省了很多代碼):

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})


計算屬性的setter

  • 上面的fullName例子中,當firstName和lastName更改的時候,fullName會更改。
  • 但有沒有可能要求fullName更改的時候,firstName和lastName會對應更改呢?
  • 上面的獲取變化後的fullName是getter,其實還能夠設置setter
<div id="app">
            <div>{{ fullName }}</div>
        </div>
        <!--
       var app = new Vue({
                    el: '#app',
                    data: {
                        firstName: 'Foo',
                        lastName: 'Bar'
                    },
                    computed: {
                      fullName: {
                        // getter
                        get: function () {
                          return this.firstName + ' ' + this.lastName
                        },
                        // setter
                        set: function (newValue) {
                          var names = newValue.split(' ')
                          this.firstName = names[0]
                          this.lastName = names[names.length - 1]
                        }
                      }
                  }
                })
         -->



偵聽器


雖然計算屬性已經提供了不少的好處,但有些時候計算屬性也不能知足咱們的要求。好比咱們但願監聽某個屬性的變化來獲得另外一個屬性的結果,可是不但願它立刻獲得結果,那麼這時候計算屬性就達不到需求了。而監聽器裏面能夠寫一些其餘代碼(好比一些延遲去獲得結果的代碼)。
當須要在數據變化時執行異步或開銷較大的操做時,watch是最有用的。
爲何有時候不但願計算新結果那麼快呢?這就比如有人在百度搜索中輸入一個個字,若是每一個字都要進行檢索的話,那麼就對百度來講開銷就很大了,若是稍微等等,確認用戶輸入完畢再去計算,那麼就節省了不少資源了,下面的來自官網的例子正是一種這樣的考慮了資源開銷的例子。


<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>

    <body>
        <div id="watch-example">
            <p>
              Ask a yes/no question:
              <input v-model="question">
            </p>
            <p>{{ answer }}</p>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
    <script>
    var watchExampleVM = new Vue({
      el: '#watch-example',
      data: {
        question: '',
        answer: 'I cannot give you an answer until you ask a question!'
      },
      watch: {
        // 若是 `question` 發生改變,這個函數就會運行
        question: function (newQuestion, oldQuestion) {
          this.answer = 'Waiting for you to stop typing...'
          this.debouncedGetAnswer()
        }
      },
      created: function () {
        // `_.debounce` 是一個經過 Lodash 限制操做頻率的函數。
        // 在這個例子中,咱們但願限制訪問 yesno.wtf/api 的頻率
        // AJAX 請求直到用戶輸入完畢纔會發出。想要了解更多關於
        // `_.debounce` 函數 (及其近親 `_.throttle`) 的知識,
        // 請參考:https://lodash.com/docs#debounce
        this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
      },
      methods: {
        getAnswer: function () {
          if (this.question.indexOf('?') === -1) {
            this.answer = 'Questions usually contain a question mark. ;-)'
            return
          }
          this.answer = 'Thinking...'
          var vm = this
          axios.get('https://yesno.wtf/api')
            .then(function (response) {
              vm.answer = _.capitalize(response.data.answer)
            })
            .catch(function (error) {
              vm.answer = 'Error! Could not reach the API. ' + error
            })
        }
      }
    })
    </script>
</html>
相關文章
相關標籤/搜索