Vue.js學習

Vue.js 是用於構建交互式的 Web 界面的庫。
Vue.js 提供了 MVVM 數據綁定和一個可組合的組件系統,具備簡單、靈活的 API。javascript

其實和Jquery同樣,VueJs就是一個Js庫,可是是面向前端的庫,具體來說叫作MVVM(Model-View-ViewModel)庫.
也就是說,有部分功能和Jquery是差很少的,Vuejs能作的,Jquery也能作。這下咱們就放心了,Jquery多簡單啊,使人髮指的是Vuejs在實現相同功能的時候更簡單(否則用你幹嗎啊);css

理解Vuejs最關鍵的一句話叫作「數據驅動視圖」,好比用Jquery來作一個列表,這個列表的數據是從Laravel來的,那麼咱們要遍歷這個數據,而後把列表的html元素加到dom裏面去, 要刪除一個列表項的時候,先要在找到列表項在dom的位置,而後去除這個節點。Vuejs不用,數據在的時候,列表就在,數據減一,列表項就自動實時相應減一。也就是說,你只要操做數據就夠了,不用管dom。這基本就是Vuejs的中心思想。html

1、基礎學習

1.MVVM模式

下圖不只歸納了MVVM模式(Model-View-ViewModel),還描述了在Vue.js中ViewModel是如何和View以及Model進行交互的。前端

clipboard.png

ViewModel是Vue.js的核心,它是一個Vue實例。Vue實例是做用於某一個HTML元素上的,這個元素能夠是HTML的body元素,也能夠是指定了id的某個元素。vue

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

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

1.2 Vue基本語法

先來看一個簡單的示例,Hello,World!webpack

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue學習</title>
<script src="http://static.runoob.com/assets/vue/1.0.11/vue.min.js"></script>
</head>
<body>
<div id="app">
  {{ message }}
</div>
<!-- JavaScript 代碼須要放在尾部(指定的HTML元素以後) -->
<script>
new Vue({
    el:'#app',
    data: {
        message:'Hello World!'
    }
});
</script>
</body>
</html>

示例詳解:ios

1.2.1 data屬性和方法

每一個Vue實例都會代理其data對象中的全部屬性:laravel

var data = { a: 1 }
var vm = new Vue({
    data: data
})

vm.a === data.a // -> true

// setting the property also affects original data
vm.a = 2
data.a // -> 2

// ... and vice-versa
data.a = 3
vm.a // -> 3

須要注意的是隻有代理屬性是反應式的,若是在實例建立以後添加一個新的屬性到實例上,將不會觸發任何視圖更新。關於這一點咱們將在後續反應系統中討論。

除了數據屬性以外,Vue實例還提供了許多有用的實例屬性和方法,這些屬性和方法都以$開頭以便和代理數據屬性進行區分。例如:

var data = { a: 1 }
var vm = new Vue({
    el: '#example',
    data: data
})

vm.$data === data // -> true
vm.$el === document.getElementById('example') // -> true

// $watch is an instance method
vm.$watch('a', function (newVal, oldVal) {
    // this callback will be called when `vm.a` changes
})

1.2.2 實例生命週期

每一個Vue實例在建立時都會經歷一系列實例化步驟,例如,須要設置數據觀察、編譯模板、以及建立必要的數據綁定。在這個過程當中,還會調用生命週期鉤子,從而方便咱們執行自定義邏輯,例如,created鉤子會在實例建立後調用:

var vm = new Vue({
    data: {
        a: 1
    },
    created: function () {
        // `this` points to the vm instance
        console.log('a is: ' + this.a)
    }
})
// -> "a is: 1"

還有一些鉤子會在實例生命週期的不一樣階段調用,例如compiled、readydestroyed,全部被調用的生命週期鉤子經過this指向調用它的Vue實例,一些用戶可能會疑惑在Vue.js的世界中有沒有「控制器」的概念,答案是沒有。組件的自定義邏輯會被分割到這些生命週期鉤子中。

1.2.3 props 用法

props將數據從父做用域傳到子組件。在 Vue.js 中,父子組件的關係能夠總結爲 props down, events up 。父組件經過 props 向下傳遞數據給子組件,子組件經過 events 給父組件發送消息。

<div id="app-7">
  <ol>
    <!-- 如今咱們爲每一個todo-item提供待辦項對象    -->
    <!-- 待辦項對象是變量,即其內容能夠是動態的 -->
    <todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
  </ol>
</div>


Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})
var app7 = new Vue({
  el: '#app-7',
  data: {
    groceryList: [
      { text: '蔬菜' },
      { text: '奶酪' },
      { text: '隨便其餘什麼人吃的東西' }
    ]
  }
})

clipboard.png
這只是一個假設的例子,可是咱們已經設法將應用分割成了兩個更小的單元,子單元經過 props 接口實現了與父單元很好的解耦。咱們如今能夠進一步爲咱們的 todo-item 組件實現更復雜的模板和邏輯的改進,而不會影響到父單元。

組件實例的做用域是孤立的。這意味着不能(也不該該)在子組件的模板內直接引用父組件的數據。要讓子組件使用父組件的數據,咱們須要經過子組件的props選項。

子組件要顯式地用props 選項聲明它期待得到的數據:

Vue.component('child', {
  // 聲明 props
  props: ['message'],
  // 就像 data 同樣,prop 能夠用在模板內
  // 一樣也能夠在 vm 實例中像 「this.message」 這樣使用
  template: '<span>{{ message }}</span>'
})

而後咱們能夠這樣向它傳入一個普通字符串:

<child message="hello!"></child>

結果:

hello!

1.2.4 data屬性對象和函數返回對象的區別

咱們先來看一下這個比較經典的問題,當初在學Vue的時候也犯過這樣的迷惑,不知道什麼時候傳遞data對象,什麼時候傳遞data函數 。Vue.js的data是要一個對象仍是一個function?

Vue 實例的數據對象。Vue.js 會遞歸地將它所有屬性轉爲 getter/setter,從而讓它能響應數據變化。這個對象必須是普通對象:原生對象,getter/setter 及原型屬性會被忽略。不推薦觀察複雜對象。

在實例建立以後,能夠用 vm.$data 訪問原始數據對象。Vue 實例也代理了數據對象全部的屬性。

在定義組件時,同必定義將建立多個實例,此時 data 必須是一個函數,返回原始數據對象。若是 data 仍然是一個普通對象,則全部的實例將指向同一個對象!換成函數後,每當建立一個實例時,會調用這個函數,返回一個新的原始數據對象的副本。

簡單說, 在實例中data是對象, 在組件中data就得是函數返回對象。

組件中的data寫法示例:

<div id="example-2">
  <simple-counter></simple-counter>
</div>

var data = { counter: 0 }
Vue.component('simple-counter', {
  template: '<button v-on:click="counter += 1">{{ counter }}</button>',
  // 技術上 data 的確是一個函數了,所以 Vue 不會警告,
  // 可是咱們返回給每一個組件的實例的卻引用了同一個data對象
  data: function () {
    return data
  }
})
new Vue({
  el: '#example-2'
})

因爲這三個組件共享了同一個 data , 所以增長一個 counter 會影響全部組件!這不對。咱們能夠經過爲每一個組件返回全新的 data 對象來解決這個問題:

data: function () {
  return {
    counter: 0
  }
}

更多詳情請參考:官網組件數據data傳遞說明(Component)

1.2.5 組件寫法需注意的幾個問題

一個組件下只能有一個並列的 div,能夠這麼寫,因此複製官網示例的時候只要複製 div 裏面的內容就好。

clipboard.png

可是不能這樣寫:

clipboard.png

第二。數據要寫在 return 裏面而不是像文檔那樣子寫

clipboard.png

錯誤的寫法:

clipboard.png

組件使用 :
firstComponent.vue

<template>
  <div id="firstcomponent">
    <h1>I am a title.</h1>
    <a> written by {{ author }} </a>
  </div>
</template>

<script type="text/javascript">
export default {
  data () {
    return {
      author: "微信公衆號 jinkey-love"
    }
  }
}
</script>

<style>
</style>

其餘Vue問題,能夠參考該博文:Vue2.0 新手徹底填坑攻略——從環境搭建到發佈

2.Vue.js的經常使用指令請參考官方文檔

Vue官方文檔

1.v-model,v-for,v-on

在瞭解了vue的基本用法(數據綁定、指令、縮寫、條件渲染等)後,來看一個完整示例,動態添加/刪除元素:

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title></title>
        <link rel="stylesheet" href="styles/demo.css" />
    </head>

    <body>
        <div id="app">

            <fieldset>
                <legend>
                    Create New Person
                </legend>
                <div class="form-group">
                    <label>Name:</label>
                    <input type="text" v-model="newPerson.name"/>
                </div>
                <div class="form-group">
                    <label>Age:</label>
                    <input type="text" v-model="newPerson.age"/>
                </div>
                <div class="form-group">
                    <label>Sex:</label>
                    <select v-model="newPerson.sex">
                    <option value="Male">Male</option>
                    <option value="Female">Female</option>
                </select>
                </div>
                <div class="form-group">
                    <label></label>
                    <button @click="createPerson">Create</button>
                </div>
        </fieldset>
        <table>
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Age</th>
                    <th>Sex</th>
                    <th>Delete</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="person in people">
                    <td>{{ person.name }}</td>
                    <td>{{ person.age }}</td>
                    <td>{{ person.sex }}</td>
                    <td :class="'text-center'"><button @click="deletePerson($index)">Delete</button></td>
                    <td><button @click="greet">Greet</button></td>
                </tr>
            </tbody>
        </table>
        </div>
    </body>
    <script src="js/vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                newPerson: {
                    name: '',
                    age: 0,
                    sex: 'Male'
                },
                people: [{
                    name: 'Jack',
                    age: 30,
                    sex: 'Male'
                }, {
                    name: 'Bill',
                    age: 26,
                    sex: 'Male'
                }, {
                    name: 'Tracy',
                    age: 22,
                    sex: 'Female'
                }, {
                    name: 'Chris',
                    age: 36,
                    sex: 'Male'
                }]
            },
            methods:{
                createPerson: function(){
                    this.people.push(this.newPerson);
                    
                    // 添加完newPerson對象後,重置newPerson對象
                    this.newPerson = {name: '', age: 0, sex: 'Male'}
                },
                deletePerson: function(index){
                    // 刪一個數組元素
                    this.people.splice(index,1);
                    alert(index);
                },

                greet:function(){
                    alert(this.newPerson.sex);
                }
            }
        })
    </script>

</html>

clipboard.png

Demo
Github示例源碼

實戰示例:

添加、刪除表單數據

<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>Vue js</title>
<link rel="stylesheet" type="text/css" href="../assets/css/bootstrap.min.css">
</head>
<body>
  <header class="navbar navbar-fixed-top navbar-inverse">
    <div class="container">
    <h1>Vue開發</h1>
    </div>
  </header>

<div class="container" id="app" style="margin:100px;">
  <div class="row">
    <div class="col-md-offset-2 col-md-8">
      <div class="panel panel-default">
        <div class="panel-heading">
          welcome to Vue.js
        </div>
        <div class="panel-body">
          <h1>{{ message }}</h1>
          <!--
          <input type="text" class="form-control" v-model="message" />
          -->

          <ul class="list-group">
            <li class="list-group-item"
            v-for="(todo,index) in todos">
                {{ todo.id }} {{ todo.title }}
                <button class="btn btn-danger btn-sm pull-right"
                 v-on:click="deleteTodo(index)"
                >刪除</button>
            </li>
          </ul>

          <form v-on:submit.prevent="addTodo(newTodo)">
            <div class="form-group">
                <input type="text" v-model="newTodo.title" class="form-control" placeholder="Add a list" />
            </div>
            <div class="form-group">
                <button class="btn btn-success">Add to do</button>
            </div>
          </form>

        </div>
      </div>
    </div>
  </div>
</div>
</body>

<!-- 注意:此處的引入文件須要放在body的後邊,須要DOM加載完後,才能獲取到#app-->
<script src="../assets/js/vue.js"></script>
<script>
  new Vue({
    el:'#app',
    data:{
      message:"Hello,Vue",
      todos:[
        {id:1,title:'Jack Chan'},
        {id:2,title:'Jet Lee'}
      ],
      newTodo:{id:null,title:""}
    },
    methods:{
      addTodo(newTodo){
        this.todos.push(newTodo)
        this.newTodo = {id:null,title:""}
      },
      deleteTodo(index){
        this.todos.splice(index,1)
      }
    }
  })

</script>
</html>

clipboard.png

注意:在輸入框輸入數據添加到列表時,須要使用v-on:submit.prevent="addTodo(newTodo)方法對錶單提交進行阻止,並使用v-model進行數據雙向綁定,當輸入框裏的數據變化時,Vue實例中的newTodo:{id:null,title:""}屬性數據也跟着變化,這樣就能夠將數據塞入到todos數組中。

2.計算屬性computed,v-bind

<!--  模板再也不簡單和清晰,因此,這裏引入computed,增長代碼可讀性
          <h1>My todos {{todos.length}}</h1>
          -->
          <h1>My todos {{todosCount}}</h1>

JS中的用法:

computed:{
      todosCount(){
        return this.todos.length;
      }

完整代碼:

<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>Vue js</title>
<link rel="stylesheet" type="text/css" href="../assets/css/bootstrap.min.css">
<style>
 .completed{
   color:#5c5b5c;
   text-decoration: line-through;
 }
</style>
</head>


<body>
  <header class="navbar navbar-fixed-top navbar-inverse">
    <div class="container">
    <h1>Vue開發</h1>
    </div>
  </header>

<div class="container" id="app" style="margin:100px;">
  <div class="row">
    <div class="col-md-offset-2 col-md-8">
      <div class="panel panel-default">
        <div class="panel-heading">
          welcome to Vue.js
        </div>
        <div class="panel-body">
          <!--  模板再也不簡單和清晰,因此,這裏引入computed,增長代碼可讀性
          <h1>My todos {{todos.length}}</h1>
          -->
          <h1>My todos {{todosCount}}</h1>

          <ul class="list-group">
            <li class="list-group-item"
            v-bind:class="{ 'completed' : todo.completed }"
            v-for="(todo,index) in todos">
                {{ todo.id }} {{ todo.title }}
                <button class="btn btn-warning btn-xs pull-right"
                v-bind:class="[todo.completed ? 'btn-danger' : 'btn-success']"
                 v-on:click="toggleCompletion(todo)"
                >
                {{ todo.completed ? 'undo' : 'complete' }}
                </button>

                <button class="btn btn-danger btn-xs pull-right"
                 v-on:click="deleteTodo(index)"
                >刪除</button>
            </li>
          </ul>

          <form v-on:submit.prevent="addTodo(newTodo)">
            <div class="form-group">
                <input type="text" v-model="newTodo.title" class="form-control" placeholder="Add a list" />
            </div>
            <div class="form-group">
                <button class="btn btn-success">Add to do</button>
            </div>
          </form>

        </div>
      </div>
    </div>
  </div>
</div>
</body>

<!-- 注意:此處的引入文件須要放在body的後邊,須要DOM加載完後,才能獲取到#app-->
<script src="../assets/js/vue.js"></script>
<script>
  new Vue({
    el:'#app',
    data:{
      message:"Hello,Vue",
      todos:[
        {id:1,title:'Jack Chan',completed:true},
        {id:2,title:'Jet Lee',completed:false}
      ],
      newTodo:{id:null,title:""}
    },
    computed:{
      todosCount(){
        return this.todos.length;
      }

    },
    methods:{
      addTodo(newTodo){
        this.todos.push(newTodo)
        this.newTodo = {id:null,title:""}
      },
      deleteTodo(index){
        this.todos.splice(index,1)
      },
      toggleCompletion(todo){
        todo.completed = !todo.completed;
      }
    }
  })

</script>


</html>

2、組件

組件(Component)是 Vue.js 最強大的功能之一。組件能夠擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素, Vue.js 的編譯器爲它添加特殊功能。在有些狀況下,組件也能夠是原生 HTML 元素的形式,以 is 特性擴展。

組件化的好處:
增長了代碼的可讀性,更重要的是增長了代碼的可重用性。

1)、全局組件

先註冊,而後再使用

<div id="example">
  <my-component></my-component>
</div>

// 註冊
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})
// 建立根實例
new Vue({
  el: '#example'
})

渲染:

<div id="example">
  <div>A custom component!</div>
</div>

注意事項:組件在註冊以後,即可以在父實例的模塊中以自定義元素 <my-component></my-component> 的形式使用。要確保在初始化根實例 以前 註冊了組件。

2)、局部註冊

沒必要在全局註冊每一個組件。經過使用組件實例選項註冊,可使組件僅在另外一個實例/組件的做用域中可用:

var Child = {
  template: '<div>A custom component!</div>'
}
new Vue({
  // ...
  components: {
    // <my-component> 將只在父模板可用
    'my-component': Child
  }
})

對上邊的實戰示例進行組件化封裝處理:
本案例,完成兩個組件化工做:
1:對列表進行組件封裝,注意在組件模板屬性參數的v-bind:todos="todos"傳遞
2:對錶單進行了組裝,注意data爲function返回

注意在 JavaScript對象和數組是引用類型,指向同一個內存空間,若是 prop 是一個對象或數組,在子組件內部改變它會影響父組件的狀態。

<!DOCTYPE html>

<!--
組件化的好處:
增長了代碼的可讀性,更重要的是增長了代碼的可重用性。

本案例,完成兩個組件化工做:
1:對列表進行組件封裝,注意在組件模板屬性參數的v-bind:todos="todos"傳遞
2:對錶單進行了組裝,注意data爲function返回

注意在 JavaScript 中對象和數組是引用類型,指向同一個內存空間,若是 prop 是一個對象或數組,在子組件內部改變它會影響父組件的狀態。
-->
<head>
<meta charset="UTF-8">
<title>Vue js</title>
<link rel="stylesheet" type="text/css" href="../assets/css/bootstrap.min.css">
<style>
 .completed{
   color:#5c5b5c;
   text-decoration: line-through;
 }
</style>
</head>


<body>
  <header class="navbar navbar-fixed-top navbar-inverse">
    <div class="container">
    <h1>Vue開發</h1>
    </div>
  </header>

<div class="container" id="app" style="margin:100px;">
  <div class="row">
    <div class="col-md-offset-2 col-md-8">
      <div class="panel panel-default">
        <div class="panel-heading">
          welcome to Vue.js
        </div>
        <div class="panel-body">
          <!--  模板再也不簡單和清晰,因此,這裏引入computed,增長代碼可讀性
          <h1>My todos {{todos.length}}</h1>
          -->
          <h1>My todos {{todosCount}}</h1>

          <!-- 這裏使用組件化封裝ul列表,這裏須要給組件綁定一個數據屬性,v-bind: 能夠簡寫爲一個冒號:-->
          <todo-items v-bind:todos="todos"></todo-items>
          <todo-form :todos="todos"></todo-form>

        </div>
      </div>
    </div>
  </div>
</div>
</body>

<!-- 列表組件 -->
<script type="text/x-template" id="todo-items-template">
  <ul class="list-group">
    <li class="list-group-item"
    v-bind:class="{ 'completed' : todo.completed }"
    v-for="(todo,index) in todos">
        {{ todo.id }} {{ todo.title }}
        <button class="btn btn-warning btn-xs pull-right"
        v-bind:class="[todo.completed ? 'btn-danger' : 'btn-success']"
         v-on:click="toggleCompletion(todo)"
        >
        {{ todo.completed ? 'undo' : 'complete' }}
        </button>

        <button class="btn btn-danger btn-xs pull-right"
         v-on:click="deleteTodo(index)"
        >刪除</button>
    </li>
  </ul>
</script>

<!-- 表單組件 -->
<script type="text/x-template" id="add-form-template">
  <form v-on:submit.prevent="addTodo(newTodo)">
    <div class="form-group">
        <input type="text" v-model="newTodo.title" class="form-control" placeholder="Add a list" />
    </div>
    <div class="form-group">
        <button class="btn btn-success">Add to do</button>
    </div>
  </form>
</script>

<!-- 注意:此處的引入文件須要放在body的後邊,須要DOM加載完後,才能獲取到#app-->
<script src="../assets/js/vue.js"></script>
<script>

// 組件化-列表
Vue.component('todo-items',{
  template:'#todo-items-template',
  props:['todos'],   // 定義一個屬性
  methods:{
    deleteTodo(index){
      this.todos.splice(index,1)
    },
    toggleCompletion(todo){
      todo.completed = !todo.completed;
    }
  }
})

// 組件化-表單
Vue.component('todo-form',{
  template:'#add-form-template',
  props:['todos'],

  // data爲function返回
  data(){
    return{
      newTodo:{id:null,title:"",completed:false}
    }
  },
  methods:{
    addTodo(newTodo){
      this.todos.push(newTodo)
      this.newTodo = {id:null,title:"",completed:false}
    }
  }
})

  new Vue({
    el:'#app',
    data:{
      message:"Hello,Vue",
      todos:[
        {id:1,title:'Jack Chan',completed:true},
        {id:2,title:'Jet Lee',completed:false}
      ],
    },
    computed:{
      todosCount(){
        return this.todos.length;
      }

    }
  })

</script>
</html>

3、vue-cli腳手架

vue-cli腳手架Github地址:https://github.com/vuejs/vue-cli

一、node安裝vue-cli腳手架

// 這裏使用淘寶的鏡像cnpm
$ cnpm install -g vue-cli

安裝好以後,能夠經過vue命令查看:
clipboard.png

二、命令安裝webpack項目

➜  Code vue init webpack vuejs-cli

clipboard.png

而後再執行上邊給出的提示命令:

cd vuejs-2.0-cli
npm install
npm run dev

執行完上邊的命令後,會打開瀏覽器的http://localhost:8080/#/頁面

clipboard.png

咱們看一下這個目錄下邊的文件:

clipboard.png

3.vue-router

vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,適合用於構建單頁面應用。vue的單頁面應用是基於路由和組件的,路由用於設定訪問路徑,並將路徑和組件映射起來。傳統的頁面應用,是用一些超連接來實現頁面切換和跳轉的。在vue-router單頁面應用中,則是路徑之間的切換,也就是組件的切換。

vue-router 快速入門
Vue路由的使用

<div class="list-group">
    <a class="list-group-item" v-link="{ path: '/home'}">Home</a>
    <a class="list-group-item" v-link="{ path: '/about'}">About</a>
</div>

4.JavaScript ES6中export及export default的區別

相信不少人都使用過export、export default、import,然而它們到底有什麼區別呢? 在JavaScript ES6中,export與export default都可用於導出常量、函數、文件、模塊等,你能夠在其它文件或模塊中經過import+(常量 | 函數 | 文件 | 模塊)名的方式,將其導入,以便可以對其進行使用,但在一個文件或模塊中,export、import能夠有多個,export default僅有一個。

具體事例,請看下邊的原文章:
JavaScript ES6中export及export default的區別

4、Vue請求api之vue-axios使用

axios github官方地址
vue-axios 擴展包GitHub地址

咱們在學習vue的API請求,因此,咱們用第二個特定的包vue-axios來安裝https://github.com/imcvampire...

1.安裝

npm install --save axios vue-axios

2.使用

安裝好以後,引入到使用的文件中

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'

Vue.use(VueAxios, axios)

示例:

Vue.axios.get(api).then((response) => {
  console.log(response.data)
})

this.axios.get(api).then((response) => {
  console.log(response.data)
})

5、laravel作後端API提供數據

這裏使用axios請求接口,會出現跨域的問題,不過,咱們能夠經過安裝https://github.com/barryvdh/laravel-cors庫來解決這個問題。

6、Vuex學習

一、什麼是Vuex?

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。---官方文檔

二、state理解

state 這樣概念初次接觸的時候可能會感受到有點模糊,簡單來講就是將 state 當作咱們項目中使用的數據的集合。而後,Vuex 使得 組件本地狀態(component local state)應用層級狀態(application state) 有了必定的差別。

  • component local state:該狀態表示僅僅在組件內部使用的狀態,有點相似經過配置選項傳入 Vue 組件內部的意思。

  • application level state:應用層級狀態,表示同時被多個組件共享的狀態層級。

假設有這樣一個場景:咱們有一個父組件,同時包含兩個子組件。父組件能夠很容易的經過使用 props 屬性來向子組件傳遞數據。

可是問題來了,當咱們的兩個子組件如何和對方互相通訊的? 或者子組件如何傳遞數據給他父組件的?在咱們的項目很小的時候,這個兩個問題都不會太難,由於咱們能夠經過事件派發和監聽來完成父組件和子組件的通訊。

然而,隨着咱們項目的增加:

  • 保持對全部的事件追蹤將變得很困難。到底哪一個事件是哪一個組件派發的,哪一個組件該監聽哪一個事件?

  • 項目邏輯分散在各個組件當中,很容易致使邏輯的混亂,不利於咱們項目的維護。

  • 父組件將變得和子組件耦合愈來愈嚴重,由於它須要明確的派發和監聽子組件的某些事件。

這就是 Vuex 用來解決的問題。 Vuex 的四個核心概念分別是:

  • The state tree:Vuex 使用單一狀態樹,用一個對象就包含了所有的應用層級狀態。至此它便做爲一個『惟一數據源(SSOT)』而存在。這也意味着,每一個應用將僅僅包含一個 store 實例。單狀態樹讓咱們可以直接地定位任一特定的狀態片斷,在調試的過程當中也能輕易地取得整個當前應用狀態的快照。

  • Getters:用來從 store 獲取 Vue 組件數據。

  • Mutators:事件處理器用來驅動狀態的變化。

  • Actions:能夠給組件使用的函數,以此用來驅動事件處理器 mutations

如何你暫時還不太理解這個四個概念,不用着急,咱們將在後面的項目實戰中詳細的解釋。

Vuex 應用中數據的流向(Vuex 官方圖)
clipboard.png

上邊的流程圖簡單解釋下:

Vuex 規定,屬於應用層級的狀態只能經過 Mutation 中的方法來修改,而派發 Mutation 中的事件只能經過 action。

從左到又,從組件出發,組件中調用 action,在 action 這一層級咱們能夠和後臺數據交互,好比獲取初始化的數據源,或者中間數據的過濾等。而後在 action 中去派發 Mutation。Mutation 去觸發狀態的改變,狀態的改變,將觸發視圖的更新。

注意事項

  • 數據流都是單向的

  • 組件可以調用 action

  • action 用來派發 Mutation

  • 只有 mutation 能夠改變狀態

  • store 是響應式的,不管 state 何時更新,組件都將同步更新

三、Vuex目錄結構

咱們來看一下建立的Vuex項目的目錄結構:

clipboard.png

  • components/ 文件夾用來存放咱們的 Vue 組件

  • vuex/ 文件夾存放的是和 Vuex store 相關的東西(state object,actions,mutators)

  • build/ 文件是 webpack 的打包編譯配置文件

  • config/ 文件夾存放的是一些配置項,好比咱們服務器訪問的端口配置等

  • dist/ 該文件夾一開始是不存在,在咱們的項目通過 build 以後纔會產出

  • App.vue 根組件,全部的子組件都將在這裏被引用

  • index.html 整個項目的入口文件,將會引用咱們的根組件 App.vue

  • main.js 入口文件的 js 邏輯,在 webpack 打包以後將被注入到 index.html 中

注:本博客Vuex部份內容轉自該博文:使用 Vuex + Vue.js 構建單頁應用,博文做者對Vuex理解的比較透徹,因此轉過來學習下。

四、將上邊實戰列表表單例子用Vuex重構

clipboard.png

先看Vuex的目錄結構:

clipboard.png

主要是在 src目錄下作組件重構:

先看列表組件Todos.vue:

<template>
  <div id="todos">

  <ul class="list-group">
    <li class="list-group-item"
    v-bind:class="{ 'completed' : todo.completed }"
    v-for="(todo,index) in todos">
        {{ todo.id }}
        <router-link :to="{ name: 'todo', params: { id: todo.id }}">{{ todo.title }}</router-link>
        <button class="btn btn-warning btn-xs pull-right margin-right-10"
        v-bind:class="[todo.completed ? 'btn-danger' : 'btn-success']"
         v-on:click="toggleCompletion(todo)"
        >
        {{ todo.completed ? 'undo' : 'complete' }}
        </button>

        <button class="btn btn-danger btn-xs pull-right margin-right-10"
         v-on:click="deleteTodo(todo, index)"
        >刪除</button>
    </li>
  </ul>

  <todo-form></todo-form>

</div>
</template>

<style>
  .completed{
    color:#5c5b5c;
    text-decoration: line-through;
  }
  .margin-right-10{
    margin-right: 10px;
  }
</style>

<script>
import TodoForm from './TodoForm';
  export default{
    name:'todos',
  //  props:['todos'],   // 定義一個屬性
  computed: {
   todos() {
     return this.$store.state.todos
   }
 },
    methods:{
      deleteTodo(todo, index){
        this.$store.dispatch('removeTodo', todo, index)
      },
      toggleCompletion(todo){
        this.$store.dispatch('completeTodo', todo)
      }
    },
    components:{
        TodoForm
      }
  }
</script>

表單組件TodoForm.vue

<template>
  <form v-on:submit.prevent="addTodo(newTodo)">
     <div class="form-group">
         <input type="text" v-model="newTodo.title" class="form-control" placeholder="Add a list" />
     </div>
     <div class="form-group">
         <button class="btn btn-success" type="submit">Add to do</button>
     </div>
   </form>
</template>

<style>
  .completed{
    color:#5c5b5c;
    text-decoration: line-through;
  }
  .margin-right-10{
    margin-right: 10px;
  }
</style>

<script>
  export default{
    //  props:['todos'],
     // 子組件數據屬性
     /*   Vuex 寫法,將 newTodo 放入到 state 中
     data(){
       return{
         newTodo:{id:null,title:"",completed:false}
       }
     },*/
     computed: {
      newTodo() {
        return this.$store.state.newTodo
      }
    },
     methods:{
       addTodo(newTodo){
         // this.todos.push(newTodo)
         // this.newTodo = {id:null,title:"",completed:false}
           this.$store.dispatch('saveTodo', newTodo)
       }
     }
  }
</script>

App.vue總組件:

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <!--
    <todos :todos="todos"></todos>
  -->

  <!-- 路由出口 -->
  <!-- 路由匹配到的組件將渲染在這裏 -->
  <router-view></router-view>
  </div>
</template>

<script>
import Hello from './components/Hello';
import Todos from './components/Todos';
export default {
  name: 'app',
  /*
  data(){
    return {
      todos:[
        {id:1,title:'Jack Chan',completed:true},
        {id:2,title:'Jet Lee',completed:false}
      ]
    }
  },
  */
  // 異步請求
  mounted(){
    /*
    this.axios.get('http://baidu.com').then(response =>{
      console.log(response.data)
    })*/
    this.$store.dispatch('getTodos')
  },
  computed:{
    todosCount(){
      return this.$store.todos.length;
    }
  },
  components: {
    Hello
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

main.js文件:

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'

// 引入axios請求API
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)

// 引入路由
import VueRouter from 'vue-router'
Vue.use(VueRouter)

// 引入vuex
import Vuex from 'vuex'
Vue.use(Vuex)

import Todos from './components/Todos';
import Todo from './components/Todo';

const routes = [
  { path: '/', component: Todos },
  { path: '/todo/:id', component: Todo, name:'todo'}
]

const router = new VueRouter({
  routes // (縮寫)至關於 routes: routes
})


/* ====================== Vuex說明 ==================== //
  store:能夠理解爲一個大的容器
  state: 至關於一個全局變量數據屬性
  mutations:裏邊的方法專門操做state裏邊的全局變量
  actions: 模型組件操做actions,獲取數據,而後觸發mutations
  小結:Vuex的核心,主要是理解其應用的場景,若是理清了這些思路,就能夠很容易進行開發了。
 另外咱們也能夠將store這變量拆分爲一個store.js文件,而後引用進來便可。
====================================================== */

// Vuex example
// store 能夠理解爲一個大的容器
const store = new Vuex.Store({

  state: {    // state 能夠理解爲全局的變量,能夠在任意組件中使用
    todos: [],
    newTodo:{id:null, title:"", completed:false}
  },

  // mutations裏邊的方法用來修改state裏的數據
  mutations: {
    get_todo_list(state, todos) {
      state.todos = todos;
    },
    complete_todo(state, todo){
      todo.completed = ! todo.completed
    },
    delete_todo(state, index){
      state.todos.splice(index, 1)
    },
    add_todo(state, todo){
      state.todos.push(todo)
    }
  },
  // actions 主要用來獲取客戶端的數據,而後將數據傳給 mutations 的方法
  actions:{
    getTodos(store){
      store.commit('get_todo_list', [{id : 1, title:"hello,world!"}])
    },
    completeTodo(state, todo){
      store.commit('complete_todo', todo)
    },
    removeTodo(store, todo, index){
      store.commit('delete_todo', index)
    },
    saveTodo(store, todo){
      store.commit('add_todo', todo)
      store.state.newTodo = {id:null,title:"",completed:false}
    },


  }
})

/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,
  template: '<App/>',
  components: { App },
  router
})

主頁面 index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>vuejs-2.0-cli</title>
    <link rel="stylesheet" href="https://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css">
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

本項目GitHub地址:
https://github.com/corwien/vu...


更多有關Vue.js學習的文章,請看這裏:
Vue.js——60分鐘快速入門
Vue.js——基於$.ajax實現數據的跨域增刪查改
Vuejs2.0 文檔攻略-介紹
使用 Vuex + Vue.js 構建單頁應用
使用Vue.js和Vuex實現購物車場景
Vuex學習

相關文章
相關標籤/搜索