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
下圖不只歸納了MVVM模式(Model-View-ViewModel),還描述了在Vue.js中ViewModel是如何和View以及Model進行交互的。前端
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
先來看一個簡單的示例,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
每一個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 })
每一個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、ready
和destroyed
,全部被調用的生命週期鉤子經過this
指向調用它的Vue實例,一些用戶可能會疑惑在Vue.js的世界中有沒有「控制器」的概念,答案是沒有。組件的自定義邏輯會被分割到這些生命週期鉤子中。
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: '隨便其餘什麼人吃的東西' } ] } })
這只是一個假設的例子,可是咱們已經設法將應用分割成了兩個更小的單元,子單元經過 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!
咱們先來看一下這個比較經典的問題,當初在學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)
一個組件下只能有一個並列的 div
,能夠這麼寫,因此複製官網示例的時候只要複製 div
裏面的內容就好。
可是不能這樣寫:
第二。數據要寫在 return 裏面而不是像文檔那樣子寫
錯誤的寫法:
組件使用 :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 新手徹底填坑攻略——從環境搭建到發佈
在瞭解了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>
添加、刪除表單數據
<!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>
注意:在輸入框輸入數據添加到列表時,須要使用v-on:submit.prevent="addTodo(newTodo)
方法對錶單提交進行阻止,並使用v-model
進行數據雙向綁定,當輸入框裏的數據變化時,Vue實例中的newTodo:{id:null,title:""}
屬性數據也跟着變化,這樣就能夠將數據塞入到todos數組中。
<!-- 模板再也不簡單和清晰,因此,這裏引入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>
組件(Component)是 Vue.js 最強大的功能之一。組件能夠擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素, Vue.js 的編譯器爲它添加特殊功能。在有些狀況下,組件也能夠是原生 HTML 元素的形式,以 is 特性擴展。
組件化的好處:
增長了代碼的可讀性,更重要的是增長了代碼的可重用性。
先註冊,而後再使用
<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> 的形式使用。要確保在初始化根實例
以前
註冊了組件。
沒必要在全局註冊每一個組件。經過使用組件實例選項註冊,可使組件僅在另外一個實例/組件的做用域中可用:
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>
vue-cli腳手架Github地址:https://github.com/vuejs/vue-cli
// 這裏使用淘寶的鏡像cnpm $ cnpm install -g vue-cli
安裝好以後,能夠經過vue
命令查看:
➜ Code vue init webpack vuejs-cli
而後再執行上邊給出的提示命令:
cd vuejs-2.0-cli npm install npm run dev
執行完上邊的命令後,會打開瀏覽器的http://localhost:8080/#/頁面
咱們看一下這個目錄下邊的文件:
vue-router
是Vue.js官方的路由插件,它和vue.js是深度集成的,適合用於構建單頁面應用。vue的單頁面應用是基於路由和組件的,路由用於設定訪問路徑,並將路徑和組件映射起來。傳統的頁面應用,是用一些超連接來實現頁面切換和跳轉的。在vue-router單頁面應用中,則是路徑之間的切換,也就是組件的切換。
<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>
相信不少人都使用過export、export default、import,然而它們到底有什麼區別呢? 在JavaScript ES6中,export與export default都可用於導出常量、函數、文件、模塊等,你能夠在其它文件或模塊中經過import+(常量 | 函數 | 文件 | 模塊)名的方式,將其導入,以便可以對其進行使用,但在一個文件或模塊中,export、import能夠有多個,export default僅有一個。
具體事例,請看下邊的原文章:
JavaScript ES6中export及export default的區別
axios github官方地址
vue-axios 擴展包GitHub地址
咱們在學習vue的API請求,因此,咱們用第二個特定的包vue-axios
來安裝https://github.com/imcvampire...。
npm install --save axios vue-axios
安裝好以後,引入到使用的文件中
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) })
這裏使用axios
請求接口,會出現跨域的問題,不過,咱們能夠經過安裝https://github.com/barryvdh/laravel-cors
庫來解決這個問題。
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。---官方文檔
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 官方圖)
上邊的流程圖簡單解釋下:
Vuex 規定,屬於應用層級的狀態只能經過 Mutation 中的方法來修改,而派發 Mutation 中的事件只能經過 action。
從左到又,從組件出發,組件中調用 action,在 action 這一層級咱們能夠和後臺數據交互,好比獲取初始化的數據源,或者中間數據的過濾等。而後在 action 中去派發 Mutation。Mutation 去觸發狀態的改變,狀態的改變,將觸發視圖的更新。
注意事項
數據流都是單向的
組件可以調用 action
action 用來派發 Mutation
只有 mutation 能夠改變狀態
store 是響應式的,不管 state 何時更新,組件都將同步更新
咱們來看一下建立的Vuex項目的目錄結構:
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的目錄結構:
主要是在 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學習