Vue介紹
Vue (讀音 /vjuː/,相似於 view) 是一套用於構建用戶界面的漸進式框架。與其它大型框架不一樣的是,Vue 被設計爲能夠自底向上逐層應用。Vue 的核心庫只關注視圖層,不只易於上手,還便於與第三方庫或既有項目整合。另外一方面,當與現代化的工具鏈以及各類支持類庫結合使用時,Vue 也徹底可以爲複雜的單頁應用提供驅動。javascript
Vue官網https://cn.vuejs.orgcss
在使用時咱們能夠下載vue.js,並在頁面中引用html
<script type="text/javascript" src="vue.js"></script>
聲明式渲染
Vue.js 的核心是一個容許採用簡潔的模板語法來聲明式地將數據渲染進 DOM 的系統前端
<div id="app"> {{ message }} </div> <script> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } }) </script>
當咱們引入了Vue.js後,此時window下就掛載了一個全局的Vue函數,咱們使用他來new一個對象,此對象包含一個參數,這個參數是json對象,期中包括el(找到對應的標籤對象),data(數據屬性)vue
此時咱們就能夠在標籤中利用{{}}來渲染數據了java
咱們已經成功建立了第一個 Vue 應用!看起來這跟渲染一個字符串模板很是相似,可是 Vue 在背後作了大量工做。如今數據和 DOM 已經被創建了關聯,全部東西都是響應式的。咱們要怎麼確認呢?打開你的瀏覽器的 JavaScript 控制檯 (就在這個頁面打開),並修改app.message
的值,你將看到上例相應地更新node
除了文本插值,咱們還能夠像這樣來綁定元素特性:python
<div id="app-2"> <span v-bind:title="message"> 鼠標懸停幾秒鐘查看此處動態綁定的提示信息! </span> </div> var app2 = new Vue({ el: '#app-2', data: { message: '頁面加載於 ' + new Date().toLocaleString() } })
這裏咱們遇到了一點新東西。你看到的 v-bind
特性被稱爲指令。指令帶有前綴 v-
,以表示它們是 Vue 提供的特殊特性。可能你已經猜到了,它們會在渲染的 DOM 上應用特殊的響應式行爲。在這裏,該指令的意思是:「將這個元素節點的 title
特性和 Vue 實例的message
屬性保持一致」。jquery
若是你再次打開瀏覽器的 JavaScript 控制檯,輸入 app2.message = '新消息'
,就會再一次看到這個綁定了 title
特性的 HTML 已經進行了更新。webpack
這裏的v-bind等同於一個:號
條件渲染
v-if
在 Vue 中,咱們使用 v-if
指令實現判斷的功能
<h1 v-if="ok">Yes</h1>
也能夠用 v-else
添加一個「else 塊」:
<h1 v-if="ok">Yes</h1> <h1 v-else>No</h1>
其中ok的值咱們能夠在data數據屬性中定義
v-else
元素必須緊跟在帶 v-if
或者 v-else-if
的元素的後面,不然它將不會被識別
<div v-if="Math.random() > 0.5"> Now you see me </div> <div v-else> Now you don't </div>
v-else-if
,顧名思義,充當 v-if
的「else-if 塊」,能夠連續使用:
<div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else-if="type === 'C'"> C </div> <div v-else> Not A/B/C </div>
相似於 v-else
,v-else-if
也必須緊跟在帶 v-if
或者 v-else-if
的元素以後
v-show
另外一個用於根據條件展現元素的選項是 v-show
指令。用法和v-if大體同樣:
<h1 v-show="ok">Hello!</h1>
不一樣的是帶有 v-show
的元素始終會被渲染並保留在 DOM 中。v-show
只是簡單地切換元素的 CSS 屬性 display
注意,v-show
不支持 <template>
元素,也不支持 v-else
v-if和v-show的區別
v-if
是「真正」的條件渲染,由於它會確保在切換過程當中條件塊內的事件監聽器和子組件適當地被銷燬和重建。
v-if
也是惰性的:若是在初始渲染時條件爲假,則什麼也不作——直到條件第一次變爲真時,纔會開始渲染條件塊。
相比之下,v-show
就簡單得多——無論初始條件是什麼,元素老是會被渲染,而且只是簡單地基於 CSS 進行切換。
通常來講,v-if
有更高的切換開銷,而 v-show
有更高的初始渲染開銷。所以,若是須要很是頻繁地切換,則使用 v-show
較好;若是在運行時條件不多改變,則使用 v-if
較好。
列表渲染
用 v-for
把一個數組對應爲一組元素
咱們用 v-for
指令根據一組數組的選項列表進行渲染。v-for
指令須要使用item in items
形式的特殊語法,items
是源數據數組而且 item
是數組元素迭代的別名
<ul id="example-1"> <li v-for="item in items"> {{ item.message }} </li> </ul> var example1 = new Vue({ el: '#example-1', data: { items: [ { message: 'Foo' }, { message: 'Bar' } ] } })
結果:
在 v-for
塊中,咱們擁有對父做用域屬性的徹底訪問權限。v-for
還支持一個可選的第二個參數爲當前項的索引。
<ul id="example-2"> <li v-for="(item, index) in items"> {{ parentMessage }} - {{ index }} - {{ item.message }} </li> </ul> var example2 = new Vue({ el: '#example-2', data: { parentMessage: 'Parent', items: [ { message: 'Foo' }, { message: 'Bar' } ] } })
結果:
你也能夠用 of
替代 in
做爲分隔符,由於它是最接近 JavaScript 迭代器的語法
一個對象的v-for
你也能夠用 v-for
經過一個對象的屬性來迭代
<ul id="v-for-object" class="demo"> <li v-for="value in object"> {{ value }} </li> </ul> new Vue({ el: '#v-for-object', data: { object: { firstName: 'John', lastName: 'Doe', age: 30 } } })
結果:
你也能夠提供第二個的參數爲鍵名:
<div v-for="(value, key) in object"> {{ key }}: {{ value }} </div>
第三個參數爲索引:
<div v-for="(value, key, index) in object"> {{ index }}. {{ key }}: {{ value }} </div>
一段取值範圍的 v-for
v-for
也能夠取整數。在這種狀況下,它將重複屢次模板
<div> <span v-for="n in 10">{{ n }} </span> </div>
結果:
v-for
on a <template>
相似於 v-if
,你也能夠利用帶有 v-for
的 <template>
渲染多個元素。好比
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider"></li> </template> </ul>
v-for
with v-if
當它們處於同一節點,v-for
的優先級比 v-if
更高,這意味着 v-if
將分別重複運行於每一個 v-for
循環中。當你想爲僅有的一些項渲染節點時,這種優先級的機制會十分有用,以下
<li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo }} </li>
事件處理
監聽事件
能夠用 v-on
(等同於@)指令監聽 DOM 事件,並在觸發時運行一些 JavaScript 代碼
<div id="example-1"> <button v-on:click="counter += 1">Add 1</button> <p>The button above has been clicked {{ counter }} times.</p> </div>
var example1 = new Vue({
el: '#example-1',
data: {
counter: 0
}
})
事件處理方法
然而許多事件處理邏輯會更爲複雜,因此直接把 JavaScript 代碼寫在 v-on
指令中是不可行的。所以 v-on
還能夠接收一個須要調用的方法名稱
<div id="example-2"> <!-- `greet` 是在下面定義的方法名 --> <button v-on:click="greet">Greet</button> </div> var example2 = new Vue({ el: '#example-2', data: { name: 'Vue.js' }, // 在 `methods` 對象中定義方法 methods: { greet: function (event) { // `this` 在方法裏指向當前 Vue 實例 alert('Hello ' + this.name + '!') // `event` 是原生 DOM 事件 if (event) { alert(event.target.tagName) } } } }) // 也能夠用 JavaScript 直接調用方法 example2.greet() // => 'Hello Vue.js!'
方法寫在methods中,綁定時若是方法沒有參數則能夠不加括號
事件修飾符
在事件處理程序中調用 event.preventDefault()
或 event.stopPropagation()
是很是常見的需求。儘管咱們能夠在方法中輕鬆實現這點,但更好的方式是:方法只有純粹的數據邏輯,而不是去處理 DOM 事件細節。
爲了解決這個問題,Vue.js 爲 v-on
提供了事件修飾符。以前提過,修飾符是由點開頭的指令後綴來表示的
<!-- 阻止單擊事件繼續傳播 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件再也不重載頁面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修飾符能夠串聯 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修飾符 --> <form v-on:submit.prevent></form> <!-- 添加事件監聽器時使用事件捕獲模式 --> <!-- 即元素自身觸發的事件先在此到處理,而後才交由內部元素進行處理 --> <div v-on:click.capture="doThis">...</div> <!-- 只當在 event.target 是當前元素自身時觸發處理函數 --> <!-- 即事件不是從內部元素觸發的 --> <div v-on:click.self="doThat">...</div>
使用修飾符時,順序很重要;相應的代碼會以一樣的順序產生。所以,用v-on:click.prevent.self
會阻止全部的點擊,而 v-on:click.self.prevent
只會阻止對元素自身的點擊
按鍵修飾符
在監聽鍵盤事件時,咱們常常須要檢查常見的鍵值。Vue 容許爲 v-on
在監聽鍵盤事件時添加按鍵修飾符:
<!-- 只有在 `keyCode` 是 13 時調用 `vm.submit()` --> <input v-on:keyup.13="submit">
記住全部的 keyCode
比較困難,因此 Vue 爲最經常使用的按鍵提供了別名:
<!-- 同上 --> <input v-on:keyup.enter="submit"> <!-- 縮寫語法 --> <input @keyup.enter="submit">
系統修飾鍵
能夠用以下修飾符來實現僅在按下相應按鍵時才觸發鼠標或鍵盤事件的監聽器。
注意:在 Mac 系統鍵盤上,meta 對應 command 鍵 (⌘)。在 Windows 系統鍵盤 meta 對應 Windows 徽標鍵 (⊞)。在 Sun 操做系統鍵盤上,meta 對應實心寶石鍵 (◆)。在其餘特定鍵盤上,尤爲在 MIT 和 Lisp 機器的鍵盤、以及其後繼產品,好比 Knight 鍵盤、space-cadet 鍵盤,meta 被標記爲「META」。在 Symbolics 鍵盤上,meta 被標記爲「META」或者「Meta」
<!-- Alt + C --> <input @keyup.alt.67="clear"> <!-- Ctrl + Click --> <div @click.ctrl="doSomething">Do something</div>
鼠標按鈕修飾符
這些修飾符會限制處理函數僅響應特定的鼠標按鈕
一些例子

頁面上的音樂播放
在h5中有一個用於播放音樂的標籤audio
咱們先在目錄中導入咱們提早準備好的音樂文件
而後寫頁面
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <style> *{ padding: 0; margin: 0; } ul{list-style: none} li{ border-bottom: 1px solid red; } </style> </head> <body> <div id="music"> <audio :src="currentSong" autoplay controls autoloop @ended="nextSong()"></audio> <ul> <li v-for="(item,i) in songs" @click="currentHandler(item,i)"> <h3>做者:{{item.author}}</h3> <p>{{item.name}}</p> </li> </ul> <button>上一首</button> <button @click="nextSong()">下一首</button> </div> <script type="text/javascript" src="vue.js"></script> <script> var songs = [ {id:1,src:'./audios/1.mp3',name:"la la Land",author:'Ryan'}, {id:2,src:'./audios/2.mp3',name:"The Best of",author:'Skillof'}, {id:3,src:'./audios/3.mp3',name:"It My Life",author:'Bon'}, {id:4,src:'./audios/4.mp3',name:"Tender",author:'Blur'} ]; // MVVM Model==>數組 數據庫 View==>document 標籤 VM // 數據的雙向綁定 m<=>v var music = new Vue({ el:"#music", data:{ // 數據屬性 songs:songs, currentSong:"./audios/1.mp3", index:0 }, methods:{ currentHandler(item,i){ this.index = i; // this.currentSong = item.src; this.currentSong = this.songs[i].src }, nextSong(){ this.index++; this.currentSong = this.songs[this.index].src; // 判斷數組越界,實現上一首 } } }) </script> </body> </html>
能夠看到具體播放哪一首歌取決於audio標籤的src屬性
咱們給每個li標籤綁定一個點擊事件,點擊時就將目前的index切換爲該歌曲的index,再經過index取到songs數組中對應歌曲的src,賦值給currentSong,這樣就完成了經過點擊切換歌曲
下一首按鈕一樣綁定了點擊事件,只要點擊就將index加1,不過這裏要考慮數組越界問題,須要添加判斷
audio標籤的autoplay屬性表示自動播放,controls則表示控制檯,能夠有調節音量等按鈕,autoloop表示自動循環,當一首歌播放完畢後,會觸發@ended,這裏咱們給他也綁定了下一首的功能
最後效果:
計算屬性
模板內的表達式很是便利,可是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板太重且難以維護。例如:
<div id="example"> {{ message.split('').reverse().join('') }} </div>
在這個地方,模板再也不是簡單的聲明式邏輯。你必須看一段時間才能意識到,這裏是想要顯示變量 message
的翻轉字符串。當你想要在模板中屢次引用此處的翻轉字符串時,就會更加難以處理。
因此,對於任何複雜邏輯,你都應當使用計算屬性
例子:
<div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div> var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // 計算屬性的 getter reversedMessage: function () { // `this` 指向 vm 實例 return this.message.split('').reverse().join('') } } })
結果:
Original message: "Hello"
Computed reversed message: "olleH"
這裏咱們聲明瞭一個計算屬性 reversedMessage
。咱們提供的函數將用做屬性vm.reversedMessage
的 getter 函數:
console.log(vm.reversedMessage) // => 'olleH' vm.message = 'Goodbye' console.log(vm.reversedMessage) // => 'eybdooG'
你能夠打開瀏覽器的控制檯,自行修改例子中的 vm。vm.reversedMessage
的值始終取決於 vm.message
的值。
你能夠像綁定普通屬性同樣在模板中綁定計算屬性。Vue 知道 vm.reversedMessage
依賴於vm.message
,所以當 vm.message
發生改變時,全部依賴 vm.reversedMessage
的綁定也會更新。並且最妙的是咱們已經以聲明的方式建立了這種依賴關係:計算屬性的 getter 函數是沒有反作用 (side effect) 的,這使它更易於測試和理解
計算屬性的 setter
計算屬性默認只有 getter ,不過在須要時你也能夠提供一個 setter :
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] } } }
如今再運行 vm.fullName = 'John Doe'
時,setter 會被調用,vm.firstName
和vm.lastName
也會相應地被更新
例子:
<div id="computed"> <h2>{{message}}</h2> <h4>{{getMessage}}</h4> <button @click="clickHandler()"></button> </div> <script type="text/javascript" src="vue.js"></script> <script> var com = new Vue({ el:"#computed", data:{ message:"luffycity" }, methods:{ clickHandler(){ // this.message = "youngman"; // set this.getMessage = "youngman"; } }, computed:{ // 計算屬性默認只有getter 做用:實時監聽咱們的數據變化 // getMessage(){ // return this.message // } getMessage:{ get:function () { return this.message }, set:function (newValue) { this.message = newValue; } } } }) </script>
經過計算屬性實現播放音樂
<div id="music"> <audio :src="currentSong" autoplay controls autoloop @ended="nextSong()"></audio> <ul> <li v-for="(item,i) in songs" @click="currentHandler(item,i)"> <h3>做者:{{item.author}}</h3> <p>{{item.name}}</p> </li> </ul> <button>上一首</button> <button @click="nextSong()">下一首</button> </div> <script type="text/javascript" src="vue.js"></script> <script> var songs = [ {id:1,src:'./audios/1.mp3',name:"la la Land",author:'Ryan'}, {id:2,src:'./audios/2.mp3',name:"The Best of",author:'Skillof'}, {id:3,src:'./audios/3.mp3',name:"It My Life",author:'Bon'}, {id:4,src:'./audios/4.mp3',name:"Tender",author:'Blur'} ]; // MVVM Model==>數組 數據庫 View==>document 標籤 VM // 數據的雙向綁定 m<=>v var music = new Vue({ el:"#music", data:{ // 數據屬性 songs:songs, // currentSong:"./audios/1.mp3", index:0 }, methods:{ currentHandler(item,i){ this.index = i; // this.currentSong = item.src; // this.currentSong = this.songs[i].src }, nextSong(){ this.index++; // this.currentSong = this.songs[this.index].src; // 判斷數組越界,實現上一首 } }, computed:{ currentSong(){ return this.songs[this.index].src } } }) </script>
能夠發現,只要修改了index,對應的計算屬性currentSong就會自動更新
表單輸入綁定
你能夠用 v-model
指令在表單 <input>
及 <textarea>
元素上建立雙向數據綁定。它會根據控件類型自動選取正確的方法來更新元素。儘管有些神奇,但 v-model
本質上不過是語法糖。它負責監聽用戶的輸入事件以更新數據,並對一些極端場景進行一些特殊處理
v-model
會忽略全部表單元素的 value
、checked
、selected
特性的初始值而老是將 Vue 實例的數據做爲數據來源。你應該經過 JavaScript 在組件的 data
選項中聲明初始值
<input v-model="message" placeholder="edit me"> <p>Message is: {{ message }}</p> <span>Multiline message is:</span> <p style="white-space: pre-line;">{{ message }}</p> <br> <textarea v-model="message" placeholder="add multiple lines"></textarea>
效果:
在文本區域插值 (<textarea></textarea>
) 並不會生效,應用 v-model
來代替
修飾符
在默認狀況下,v-model
在每次 input
事件觸發後將輸入框的值與數據進行同步 (除了上述輸入法組合文字時)。你能夠添加 lazy
修飾符,從而轉變爲使用 change
事件進行同步:
<!-- 在「change」時而非「input」時更新 --> <input v-model.lazy="msg" >
若是想自動將用戶的輸入值轉爲數值類型,能夠給 v-model
添加 number
修飾符:
<input v-model.number="age" type="number">
這一般頗有用,由於即便在 type="number"
時,HTML 輸入元素的值也總會返回字符串
若是要自動過濾用戶輸入的首尾空白字符,能夠給 v-model
添加 trim
修飾符:
<input v-model.trim="msg">
相關軟件介紹
webpack:打包機,將項目中的js,css,img,font,html等進行捆綁 編譯成一個.js進行加載
nodejs: Node.js 的包管理器npm 一門後端語言 寫咱們服務器代碼
npm: 比如python中的pip,node package manager,供全部的前端開發者使用的包都在這裏面
babel: 將咱們es6的代碼在各類瀏覽器中兼容
npm相關命令
npm init --yes 初始化 不加--yes須要本身選擇 npm install jquery --save 下載項目依賴時用--save 若是沒有項目依賴則--save-dev npm install bootstrap@3 --save 下載指定版本的bootstrap npm uninstall jquery 卸載jquery
構建vue項目
安裝nodejs
npm是nodejs的包管理工具,咱們能夠經過他來下載一些咱們須要的包
首先咱們要下載nodejs,官網爲https://nodejs.org/en/
,下載完進行安裝時須要注意,若是你沒有安裝在默認的路徑下,須要將你的安裝路徑添加到環境變量,否則沒法直接使用node和npm命令
安裝完成後,咱們就可使用這兩個命令了
安裝Vue-cli
Vue 提供一個官方命令行工具,可用於快速搭建大型單頁應用。該工具爲現代化的前端開發工做流提供了開箱即用的構建配置。只需幾分鐘便可建立並啓動一個帶熱重載、保存時靜態檢查以及可用於生產環境的構建配置的項目
# 全局安裝 vue-cli npm install --global vue-cli
安裝完成後咱們就可使用vue命令了
使用vue list能夠查看一些可用的模板
其中pwa是用於移動端的,而webpack和webpack-simple就是咱們立刻要使用的
npm初始化工做目錄
如今咱們就能夠建立一個工做目錄,並在cmd中進入該目錄進行初始化了
初始化完成後會在工做目錄中生成一個package.json文件內容如上圖所示
而後咱們就可使用npm install命令安裝包了,安裝完的包會放在工做目錄下的node_modules目錄下
搭建Vue項目
經過咱們的vue-cli搭建咱們的vue項目,首先建立一個目錄並進入,而後執行vue init webpack-simple + 項目名,若是不加項目名會默認使用目錄名
完成後就能夠根據他的提示進行操做了,首先cd到項目目錄中,而後使用npm install下載依賴包
能夠看到此時的package.json文件爲
{ "name": "my-project", "description": "A Vue.js project", "version": "1.0.0", "author": "", "license": "MIT", "private": true, "scripts": { "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" }, "dependencies": { "vue": "^2.5.11" }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ], "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-preset-env": "^1.6.0", "babel-preset-stage-3": "^6.24.1", "cross-env": "^5.0.5", "css-loader": "^0.28.7", "file-loader": "^1.1.4", "vue-loader": "^13.0.5", "vue-template-compiler": "^2.4.4", "webpack": "^3.6.0", "webpack-dev-server": "^2.9.1" } }
dependencies就是項目依賴,而devDependencies是其它的一些依賴,因爲這些依賴包通常都比較大,咱們在上傳項目到github上時是會忽略這些包的,當咱們從github上下載了別人的項目要使用時能夠先執行npm install,這樣就會根據package.json文件自動下載依賴了
最後運行項目使用npm run dev,而後咱們就能夠經過http://localhost:8080/來訪問咱們的項目了
總結
生成vue的項目 模板: simple,webpack-simple,webpack(最後要使用的模板) vue init webpack-simple 項目名 根據提示 安裝模板 不要安裝sass cd 當前目錄 npm install ===> vue.js 整個項目的依賴 npm run dev 啓動咱們的vue項目 vue is not a module若是出現這種錯誤代表未下載依賴
Vue項目中的組件和功能
項目的入口文件
上面咱們已經建立了一個vue項目,如今咱們能夠看看這個項目的目錄結構
這裏的main.js就是項目的入口文件
這裏要提到一些概念
webpack: entry 入口文件的地址 output 出口 loader babel-base-loader,vue-loader css-loader style-loader plugins js壓縮 css壓縮 html壓縮 圖片壓縮等
咱們能夠看看main.js的內容
// es6的模塊導入方式 // 全部的模塊不要添加路徑 // 第三方模塊 只能引入名字 有import就有export(拋出) import Vue from 'vue' // 導入 import App from './App.vue' new Vue({ el: '#app', render: h => h(App) })
這裏導入的vue就是vue模塊,當導入第三方模塊時直接使用名字就好了,而導入一個文件時,須要加路徑,這裏導入App時就是從文件中導入的
這裏能夠看到咱們其實項目中只用到了一個Vue對象
import導入時,導入的模塊中必定有export(拋出)
父組件
在main.js中直接導入的組件能夠稱爲父組件
這裏咱們能夠看看App.vue文件(這種.vue結尾的文件其實就是一個組件)中的內容
<!-- 組件都是.vue的文件 --> <!-- 寫咱們頁面的結構 --> <template> <div id="app"> <h3>{{msg}}</h3> <ul> <li v-for="(item,index) in favs"> {{item}} </li> </ul> <!-- 插入子組件 --> <Vheader :hfavs="getAllDatas" @addHandler="add"></Vheader> <Vmarked></Vmarked> <!-- 如何從父組件傳遞數據到子組件,使用vue提供的一個屬性props,props必定要驗證自定義的屬性 --> <!-- 子組件往父組件傳值,使用自定義事件,使用$emit()來觸發自定義的函數 --> </div> </template> <!-- 處理咱們頁面的業務邏輯 --> <script> // 導入子組件 import Vheader from "./Vheader" import Vmarked from "./Vmarked" export default{ name:"App", data(){ return { msg: "今天學習組件", favs: ["抽菸", "喝酒", "燙頭"] } }, methods:{ add(a){ // alert(a) this.favs.push(a) } }, computed:{ getAllDatas(){ return this.favs } }, // 父組件插入子組件 掛載子組件 把子組件掛載到父組件上 components:{ Vheader: Vheader, Vmarked } } </script> <!-- 樣式 --> <style></style>
這個文件中的內容咱們從新寫了一下,能夠看到主要分爲三個部分template部分寫的是頁面結構(html),script部分寫的是頁面的業務邏輯(js),style部分寫的是樣式(css)
在script中咱們就要寫exprot default,這裏麪包括組件的名字和數據屬性等信息,須要注意的是,這裏的寫法和咱們上面提到的有些不同
子組件
在父組件中咱們還能夠導入子組件,固然首先須要寫一個子組件的文件,好比Vheader.vue,而後在父組件中經過以下方式導入
<script> // 導入子組件 import Vheader from "./Vheader" import Vmarked from "./Vmarked" export default{ name:"App", data(){ return { msg: "今天學習組件", favs: ["抽菸", "喝酒", "燙頭"] } }, methods:{ add(a){ // alert(a) this.favs.push(a) } }, computed:{ getAllDatas(){ return this.favs } }, // 父組件插入子組件 掛載子組件 把子組件掛載到父組件上 components:{ Vheader: Vheader, Vmarked } } </script>
這裏的components就是用來掛載子組件的,在開頭咱們先經過import Vheader from "./Vheader"將寫好的子組件導入,而後將這個子組件放到components中,最後在template中經過以下方式使用
<template> <div id="app"> <h3>{{msg}}</h3> <ul> <li v-for="(item,index) in favs"> {{item}} </li> </ul> <!-- 插入子組件 --> <Vheader></Vheader>
這樣咱們在頁面上就能看到父組件和子組件的內容了
markdown功能
咱們能夠再寫一個子組件,在頁面上實現一個markdown功能
首先要下載marked模塊
npm install marked --save
而後寫一個新的組件Vmarked.vue
<template> <div class="mark"> <div class="markdown"> <textarea v-model="msg"> </textarea> </div> <div class="box" v-html="currentMarked"></div> </div> </template> <script> import Marked from "marked" export default{ name:"mark", data(){ return { msg:"" } }, methods:{ }, computed:{ currentMarked(){ // 默認只有getter return Marked(this.msg) } }, components:{ } } </script> <style scoped> .markdown,.box{ width:50%; height: 300px; border: 1px solid gray; float: left; box-sizing: border-box; } textarea{ width: 100%; height:300px; } </style>
在這個組件中咱們讓用戶在textarea框中輸入,而後經過計算屬性,將輸入的值經過marked轉換爲咱們須要的格式,最後在頁面上展現,可是須要注意的是,這裏轉換成的html代碼沒法直接在頁面上顯示,須要經過v-html才能展現
v-html
雙大括號會將數據解釋爲普通文本,而非 HTML 代碼。爲了輸出真正的 HTML,你須要使用v-html
指令:
<p>Using mustaches: {{ rawHtml }}</p> <p>Using v-html directive: <span v-html="rawHtml"></span></p>
效果
在父組件中導入
完成子組件後只須要在父組件中導入
<!-- 組件都是.vue的文件 --> <!-- 寫咱們頁面的結構 --> <template> <div id="app"> <h3>{{msg}}</h3> <ul> <li v-for="(item,index) in favs"> {{item}} </li> </ul> <!-- 插入子組件 --> <Vheader :hfavs="getAllDatas" @addHandler="add"></Vheader> <Vmarked></Vmarked> <!-- 如何從父組件傳遞數據到子組件,使用vue提供的一個屬性props,props必定要驗證自定義的屬性 --> <!-- 子組件往父組件傳值,使用自定義事件,使用$emit()來觸發自定義的函數 --> </div> </template> <!-- 處理咱們頁面的業務邏輯 --> <script> // 導入子組件 import Vheader from "./Vheader" import Vmarked from "./Vmarked" export default{ name:"App", data(){ return { msg: "今天學習組件", favs: ["抽菸", "喝酒", "燙頭"] } }, methods:{ add(a){ // alert(a) this.favs.push(a) } }, computed:{ getAllDatas(){ return this.favs } }, // 父組件插入子組件 掛載子組件 把子組件掛載到父組件上 components:{ Vheader: Vheader, Vmarked } } </script> <!-- 樣式 --> <style></style>
組件的style
在style中能夠寫css樣式,可是當父組件和子組件都有相同的標籤時,可能形成css樣式的混亂,通常狀況下子組件會覆蓋父組件的,這時咱們可使用下面的參數
<!-- scoped注入,當前的樣式只對當前的html有用 --> <style scoped> </style>
scoped參數使當前的式樣只對當前的html有用
父子組件通訊
父子組件之間的數據傳遞是一種單向數據流,從父組件流向子組件
父傳子
當子組件要用父組件的數據時,能夠在template中插入子組件時,給子組件綁定屬性,屬性的值就是要傳遞的數據
<!-- 插入子組件 --> <Vheader :hfavs="getAllDatas" @addHandler="add"></Vheader>
子組件中使用props方法接收數據,接收到的數據必定要驗證
<script> import url from "./assets/logo.png" export default{ name:"vheader", data(){ return { url: url } }, props:{ // 必定要驗證 驗證數據屬性的類型 hfavs:Array }, methods:{ addOneFav(){ // 使用$emit()方法來觸發自定義事件 // 第一個參數是自定義的函數名 this.$emit("addHandler",12323); // this.hfavs.push("泡妹") } } } </script>
驗證完成後就能夠在子組件中使用了
<template> <div class="vheader"> <img :src="url" alt=""> <ul> <li v-for="(item,index) in hfavs"> {{item}} </li> </ul> <button @click="addOneFav">添加</button> </div> </template>
props
咱們能夠爲組件的 prop 指定驗證規則。若是傳入的數據不符合要求,Vue 會發出警告。這對於開發給他人使用的組件很是有用
type
能夠是下面原生構造器:
- String
- Number
- Boolean
- Function
- Object
- Array
- Symbol
子傳父
當子組件要向父組件傳遞數據時,咱們要給template中插入的子組件自定義一個觸發事件
<!-- 插入子組件 --> <Vheader :hfavs="getAllDatas" @addHandler="add"></Vheader>
這裏咱們定義了一個addHandler的觸發事件,而後在子組件中咱們使用this.$emit()觸發這個自定義事件
<template> <div class="vheader"> <img :src="url" alt=""> <ul> <li v-for="(item,index) in hfavs"> {{item}} </li> </ul> <button @click="addOneFav">添加</button> </div> </template> <script> import url from "./assets/logo.png" export default{ name:"vheader", data(){ return { url: url } }, props:{ // 必定要驗證 驗證數據屬性的類型 hfavs:Array }, methods:{ addOneFav(){ // 使用$emit()方法來觸發自定義事件 // 第一個參數是自定義的函數名 this.$emit("addHandler",12323); // this.hfavs.push("泡妹") } } } </script>
這裏$emit的第一個參數是咱們自定義的觸發事件的名字,第二個參數就是咱們要傳遞的數據
當父組件的觸發事件觸發後執行相應的函數,這個函數能夠接收一個參數,這個參數就是子組件傳遞的數據
<script> // 導入子組件 import Vheader from "./Vheader" import Vmarked from "./Vmarked" export default{ name:"App", data(){ return { msg: "今天學習組件", favs: ["抽菸", "喝酒", "燙頭"] } }, methods:{ add(a){ // alert(a) this.favs.push(a) } }, computed:{ getAllDatas(){ return this.favs } }, // 父組件插入子組件 掛載子組件 把子組件掛載到父組件上 components:{ Vheader: Vheader, Vmarked } } </script>
圖片當文件導入
在咱們的子組件Vheader中有一個img標籤,一開始咱們寫死了他的src屬性,可是通常狀況下咱們不該該寫死他,因此咱們能夠把這個圖片看成一個模塊導入,而後定義一個url來接收
<template> <div class="vheader"> <img :src="url" alt=""> <ul> <li v-for="(item,index) in hfavs"> {{item}} </li> </ul> <button @click="addOneFav">添加</button> </div> </template> <script> import url from "./assets/logo.png" export default{ name:"vheader", data(){ return { url: url } }, props:{ // 必定要驗證 驗證數據屬性的類型 hfavs:Array }, methods:{ addOneFav(){ // 使用$emit()方法來觸發自定義事件 // 第一個參數是自定義的函數名 this.$emit("addHandler",12323); // this.hfavs.push("泡妹") } } } </script>
vue-router
使用vue-router路由時須要先進行安裝
npm install vue-router
安裝完成後要在main.js中導入
import Vue from 'vue' import App from './App.vue' import VueRouter from 'vue-router' // 0. 若是使用模塊化機制編程,導入Vue和VueRouter,要調用 Vue.use(VueRouter) // 1. 定義(路由)組件。 // 導入路由組件 import Index from './Index' import Luffy from './Luffy' Vue.use(VueRouter) // 建立 router 實例,而後傳 `routes` 配置 const router = new VueRouter({ routes:[ { path: '/', component: Index }, { path: '/luffy', component: Luffy } ] }) new Vue({ el: '#app', router, render: h => h(App) })
能夠看到導入後咱們又導入了兩個路由組件
index
<template> <div class="index"> <h3>我是首頁</h3> </div> </template> <script> export default{ name:'index', data(){ return { } } } </script> <style> </style>
luffy
<template> <div class="luffy"> <h4>我是路飛</h4> </div> </template> <script> export default{ name:'luffy', data(){ return { } } } </script> <style> </style>
而後咱們建立了一個router實例,實例中包含咱們路徑對應的組件,最後將router實例添加到Vue實例中
在App組件中使用時
<template> <div id="app"> <img src="./assets/logo.png"> <h1>{{ msg }}</h1> <h2>Essential Links</h2> <ul> <router-link v-for='(item,index) in urls' :to="item.href" :class='{active:currentIndex==index}' @click.native='clickHandler(index)' >{{item.name}}</router-link> <!-- 給router-link添加事件 會阻止click事件的觸發,須要加上.navtive就能夠了。加上.navtive 纔是真正點擊了a標籤的click事件,在組件中不加.native 不會觸發原生的事件。注意了注意了 --> <!-- <router-link to="/luffy">路飛學城</router-link> --> </ul> <!-- 路由出口 全部路由匹配的組件都會被渲染到這裏 --> <router-view></router-view> </div> </template> <script> export default { name: 'app', data () { return { msg: 'Welcome to Your Vue.js App', urls:[ {href:'/',name:"首頁"}, {href:'/luffy',name:"路飛學城"} ], currentIndex:0 } }, methods:{ clickHandler(index){ console.log(index) this.currentIndex = index; } } } </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; } h1, h2 { font-weight: normal; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } a.active{ color: yellow; } </style>
<router-view></router-view>是路由的出口,全部路由匹配的組件都會被渲染到這裏
<router-link> 默認會被渲染成一個 `<a>` 標籤,他的to對應的就是a標籤的href屬性,在給該標籤綁定事件時須要注意,添加.native
建立webpack模板的項目
與上面建立項目同樣,首先建立項目目錄,而後執行初始化
建立完成後使用npm run dev運行項目
使用webpack模板建立的項目目錄結構和咱們上面建立的項目有些區別
能夠看到有專門放組件的目錄,還有一個放路由模塊的目錄,看看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' import router from './router' new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
這裏直接導入了App組件和router路由,看看路由目錄的index.js中拋出了什麼
import Vue from 'vue' import Router from 'vue-router' // import HelloWorld from '@/components/HelloWorld' import Vmain from '@/components/Vmain' import Vmarked from '@/components/Vmarked' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Vmain', component: Vmain }, { path: '/marked', name: 'Vmarked', component: Vmarked } ] })
能夠看到拋出的就是咱們上面路由中須要的Router實例,這樣就實現了分離,不用將路由的邏輯寫到main.js中了
在路由中咱們還定義了兩個新的組件內容,分別是首頁的內容和個人筆記內容,如今咱們經過App,Vmain和Vmarked三個組件來構建咱們的基本頁面
App
<template> <div id="app"> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#"> <img src="./assets/logo.png" style="width: 50px;height: 50px;margin-top: -15px"> </a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li :class="{active:currentIndex==index}" v-for = "(item,index) in urls" @click='clickHandler(index)'> <router-link :to="item.url">{{item.name}}</router-link> </li> </ul> <form class="navbar-form navbar-right"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search"> </div> <button type="submit" class="btn btn-default">Submit</button> </form> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <router-view></router-view> </div> </template> <script> import 'bootstrap/dist/css/bootstrap.min.css' var $ = require('jquery'); export default { name: 'App', data(){ return { urls:[ {url:'/',name:'首頁'}, {url:'/marked',name:"個人筆記"} ], currentIndex:0 } }, methods:{ clickHandler(index){ this.currentIndex = index; } }, } </script> <style> </style>
這裏咱們使用到了bootstrap,須要先下載npm install bootstrap@3 --save,在導入bootstrap時,只須要直接import文件就行,可是須要注意,這裏忽略了node_modules目錄
而後在App中經過router-link和router-view來關聯兩個路由組件
Vmain
<template> <div class="main"> <div class="container"> <div class="row"> <div class="col-md-12"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">個人首頁</h3> </div> <div class="panel-body"> <ul> <li>個人筆記</li> <li>個人博客</li> </ul> </div> </div> </div> </div> </div> </div> </template> <script> export default{ name:'Vmain', data(){ return { } }, methods:{ }, components:{ }, computed:{ } } </script> <style> </style>
Vmarked
<template> <div class="marked"> <div class="container"> <div class="row"> <div class="col-md-12"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">個人markdown</h3> </div> <div class="panel-body"> <!-- 左邊: 筆記列表 --> <div class="col-md-3"> <Vnotelist></Vnotelist> </div> <!-- 右邊:markdown編輯器 --> <div class="col-md-9"> <Vmarkdown></Vmarkdown> </div> </div> </div> </div> </div> </div> </div> </template> <script> import Vnotelist from './Vnotelist' import Vmarkdown from './Vmarkdown' export default{ name:'Vmarked', data(){ return { } }, methods:{ }, components:{ Vnotelist, Vmarkdown }, computed:{ } } </script> <style> </style>
在Vmarked中咱們又引入了兩個子組件,分別是左側的筆記列表和右側的markdown內容
分別來看看這兩個子組件的內容,先看Vnotelist
<template> <div class="notelist"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">個人筆記列表</h3> </div> <div class="panel-body"> <ul> <!-- <li v-for='(item,index) in getAllDatas'> {{item.content}} </li> --> <!-- <VnoteItem v-for='(item,index) in getAllDatas' :item = 'item'></VnoteItem> --> <VnoteItem :getAllDatas = 'getAllDatas'></VnoteItem> </ul> </div> </div> </div> </div> </template> <script> import VnoteItem from './VnoteItem' export default{ name:'Vnotelist', data(){ return { } }, methods:{ }, components:{ VnoteItem }, computed:{ getAllDatas(){ return this.$store.state.allNotes; } } } </script> <style> </style>
這裏咱們看到在這個組件中咱們須要使用到後端傳來的數據,這個數據應該在頁面加載完就從後端請求來,因此咱們須要在App組件中須要在載入完成後向後臺發送請求,這裏咱們要知道vue的聲明週期中有一個鉤子函數mounted,在頁面加載完後就會執行這個函數
App.vue
<script> import 'bootstrap/dist/css/bootstrap.min.css' var $ = require('jquery'); export default { name: 'App', data(){ return { urls:[ {url:'/',name:'首頁'}, {url:'/marked',name:"個人筆記"} ], currentIndex:0 } }, methods:{ clickHandler(index){ this.currentIndex = index; } }, mounted(){ // console.log('當前組件加載完成了'); // axios vue推薦我們使用這個插件發起ajax console.log($) var _this = this; $.ajax({ url:'http://127.0.0.1:7428/api/comments', type:'get', success:function(allNotes) { console.log(_this.$store); _this.$store.state.allNotes = allNotes; }, error:function(err) { console.log(err); } }) } } </script>
首先咱們導入jquery,須要先下載npm install jquery --save,而後咱們在mounted中向後臺發送ajax請求,得到數據,可是這裏的數據是在App中,而須要使用數據的組件Vnodelist和App沒有關聯,要怎麼拿到數據能,這裏就要引入咱們的新知識vuex了
Vuex
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能
要使用他咱們須要先下載npm install vuex --save,而後咱們在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' import router from './router' // 使用vuex 先下載再導入 import Vuex from 'vuex' // 確保下面這句代碼必定要寫 Vue.use(Vuex) Vue.config.productionTip = false import $ from 'jquery' // 建立store 商店 const store = new Vuex.Store({ // 狀態 state: { // 數據屬性 allNotes: [], note:{ id:'', title:'', date:'', content:'', markdown:'' } }, // 修改狀態惟一的方法就是提交mutations mutations: { // 同步操做 // 起名字 ADDONENOTE:function(state,newAllDatas){ state.allNotes = newAllDatas; } }, actions:{ // 異步操做 addOneNote:function(context,json) { // 發起ajax請求 日後臺 添加一條筆記 // $.ajax({ url:"http://127.0.0.1:7428/api/comments", type:'post', data:json, success:function(newAllDatas) { context.commit('ADDONENOTE',newAllDatas) } }) } } }) /* eslint-disable no-new */ new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })
首先導入vuex,而後使用vue.use使用他,最後使用Vuex.Store建立一個store實例對象,在這個對象裏,有state,這就是咱們的數據屬性,不論在哪一個組件中咱們均可以經過this.$store.state取到這個狀態中的值,因此咱們在App的mounted中發送ajax請求獲取到值後直接更新這個store中的數據就好了
mounted(){ // console.log('當前組件加載完成了'); // axios vue推薦我們使用這個插件發起ajax console.log($) var _this = this; $.ajax({ url:'http://127.0.0.1:7428/api/comments', type:'get', success:function(allNotes) { console.log(_this.$store); _this.$store.state.allNotes = allNotes; }, error:function(err) { console.log(err); } }) }
這裏須要注意一下,ajax中的this並非咱們要的,可使用箭頭函數或者上面的方式獲取,有了這個之後咱們在Vnodelist中就能夠經過計算屬性來監聽他了
computed:{ getAllDatas(){ return this.$store.state.allNotes; } }
在Vnodelist中咱們還有一個子組件,就是每個列表的item,這裏咱們有不少中方法把數據傳給他,也能夠直接在子組件中經過vuex獲取數據
<template> <div class="notelist"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">個人筆記列表</h3> </div> <div class="panel-body"> <ul> <!-- <li v-for='(item,index) in getAllDatas'> {{item.content}} </li> --> <!-- <VnoteItem v-for='(item,index) in getAllDatas' :item = 'item'></VnoteItem> --> <VnoteItem :getAllDatas = 'getAllDatas'></VnoteItem> </ul> </div> </div> </div> </div> </template>
VnoteItem
<template> <div> <li v-for='(item,index) in getAllDatas'> <!-- { "id": 1492672679281, "title": "vue的核心用法3", "date": "2017-4-23", "content": "計算屬性", "markdown": "### 計算屬性" } --> <h4>{{item.title}}</h4> <p>{{item.content}}</p> <button class="btn btn-danger">刪除</button> </li> </div> </template> <script> export default{ name:'VnoteItem', data(){ return { } }, props:{ getAllDatas:Array }, computed:{ } } </script> <style> </style>
咱們從後臺獲取的數據其實就是一個數組中包含多個對象的形式,對象的內容如上面的註釋,這樣咱們就能夠在頁面上展現咱們從數據庫得到的數據了
到這裏Vmarked的左側部分就完成了,如今咱們來看看右側的markdown內容
Vmarkdown
<!-- 頁面的結構 --> <template> <div class="mark"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">個人markdown</h3> 個人標題: <input type="text" v-model='titleHandler'/> <button class="btn btn-success" @click='addOneNote'>提交</button> </div> <div class="panel-body"> <div class="markdown"> <textarea v-model='msgHandler'> </textarea> </div> <div class="box" v-html='currentMarked'> </div> </div> </div> </div> </template> <!-- 頁面的業務邏輯 --> <script> import Marked from 'marked' export default{ name:'Vmarkdown', data(){ return { msg:'' } }, methods:{ /* { "id": 1492672679281, "title": "vue的核心用法3", "date": "2017-4-23", "content": "計算屬性", "markdown": "### 計算屬性" } */ // 最小值 到最大值 (max-min) + Math.random()*min 100---300 // 添加一條筆記 addOneNote(){ var content = document.getElementsByClassName('box')[0].innerText; var json = { id:Math.random(), title:this.titleHandler, date: (new Date()).toLocaleString(), content:content, markdown:this.msgHandler } console.log(json) this.$store.dispatch('addOneNote',json) // success:function(){ // } } }, computed:{ currentMarked(){ // 默認只有getter return Marked( this.$store.state.note.markdown) }, getDatas(){ return this.$store.state.allNotes; }, titleHandler:{ get:function() { return this.$store.state.note.title }, set:function(newValue) { console.log(newValue) this.$store.state.note.title = newValue; } }, msgHandler:{ get:function() { return this.$store.state.note.markdown; }, set:function(newValue) { this.$store.state.note.markdown = newValue } } }, components:{ } } </script> <!-- 樣式 --> <style scoped> .markdown,.box{ width: 50%; height: 300px; border:1px solid gray; float: left; box-sizing: border-box; } textarea{ width:100%; height: 100%; border:none; } </style>
這裏的功能和咱們上面寫的markdown差很少,咱們還增長了標題的輸入框以及一個提交按鈕,當點擊提交時,須要將新寫的數據提交到數據庫,並在左側的列表中顯示,這裏的標題input框咱們使用v-model來取值,他的值咱們利用數據屬性來監聽,當用戶輸入時,至關於觸發了數據屬性的set功能,在這裏咱們在vuex的store中定義一個空的對象來存放值,同理textarea框中的內容也這樣存放
// 建立store 商店 const store = new Vuex.Store({ // 狀態 state: { // 數據屬性 allNotes: [], note:{ id:'', title:'', date:'', content:'', markdown:'' } }
這樣標題欄中輸入的值一直會實時更新進去,當輸入完全部內容後,點擊提交時,咱們要觸發一個事件,要向後臺提交數據,而且要將新的數據在store中更改,使得左側展現的數據能實時更新,點擊提交後,咱們先取到全部的內容生成一個對象,而後將這個對象傳給後臺,這裏又要用到新的只是,在vuex的store中有兩個方法Mutations和actions
Mutation
更改 Vuex 的 store 中的狀態的惟一方法是提交 mutation。Vuex 中的 mutation 很是相似於事件:每一個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是咱們實際進行狀態更改的地方,而且它會接受 state 做爲第一個參數
const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 變動狀態 state.count++ } } })
你不能直接調用一個 mutation handler。這個選項更像是事件註冊:「當觸發一個類型爲 increment
的 mutation 時,調用此函數。」要喚醒一個 mutation handler,你須要以相應的 type 調用 store.commit 方法:
store.commit('increment')
這裏的store咱們在組件中使用時就是this.$store,從這裏能夠看出咱們上面在更改store中state狀態時實際上是有問題的,沒有使用mutation
action
Action 相似於 mutation,不一樣在於:
- Action 提交的是 mutation,而不是直接變動狀態。
- Action 能夠包含任意異步操做。
讓咱們來註冊一個簡單的 action:
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } })
Action 函數接受一個與 store 實例具備相同方法和屬性的 context 對象,所以你能夠調用 context.commit
提交一個 mutation,或者經過 context.state
和 context.getters
來獲取 state 和 getters。當咱們在以後介紹到 Modules時,你就知道 context 對象爲何不是 store 實例自己了。
實踐中,咱們會常常用到 ES2015 的參數解構來簡化代碼(特別是咱們須要調用 commit
不少次的時候):
actions: { increment ({ commit }) { commit('increment') } }
Action 經過 store.dispatch
方法觸發:
store.dispatch('increment')
經過上面的介紹,咱們發現當咱們得到了新的數據對象後其實就是要實現一個action方法來發送ajax請求,而後將得到的新內容經過mutation來更新到store中,具體內容以下
methods:{ /* { "id": 1492672679281, "title": "vue的核心用法3", "date": "2017-4-23", "content": "計算屬性", "markdown": "### 計算屬性" } */ // 最小值 到最大值 (max-min) + Math.random()*min 100---300 // 添加一條筆記 addOneNote(){ var content = document.getElementsByClassName('box')[0].innerText; var json = { id:Math.random(), title:this.titleHandler, date: (new Date()).toLocaleString(), content:content, markdown:this.msgHandler } console.log(json) this.$store.dispatch('addOneNote',json) // success:function(){ // } } }
在main.js中咱們定義store中mutation和action
// 建立store 商店 const store = new Vuex.Store({ // 狀態 state: { // 數據屬性 allNotes: [], note:{ id:'', title:'', date:'', content:'', markdown:'' } }, // 修改狀態惟一的方法就是提交mutations mutations: { // 同步操做 // 起名字 ADDONENOTE:function(state,newAllDatas){ state.allNotes = newAllDatas; } }, actions:{ // 異步操做 addOneNote:function(context,json) { // 發起ajax請求 日後臺 添加一條筆記 // $.ajax({ url:"http://127.0.0.1:7428/api/comments", type:'post', data:json, success:function(newAllDatas) { context.commit('ADDONENOTE',newAllDatas) } }) } } })
這樣就實現了一個簡單的新增數據功能