本節目錄css
Axios 是一個基於 promise 的 HTTP 庫,能夠用在瀏覽器和 node.js 中,promise是es6裏面的用法。html
axios能作的事情:官網地址前端
從瀏覽器中建立 XMLHttpRequests 從 node.js 建立 http 請求 支持 Promise API 攔截請求和響應 轉換請求數據和響應數據 取消請求 自動轉換 JSON 數據 客戶端支持防護 XSRF
咱們先看一下promise對象,看文檔。vue
axios是基於promise對象的,axios的用法相似於jQuery的鏈式用法,就是.then().catch().then().catch()連着用等等,每一個方法都至關於給你返回了一個promise對象,接着能夠調用這個對象的其餘方法node
好,那麼首先如今安裝一下axios,兩種方式:webpack
直接下載安裝:
$ npm install axios //在我們模塊化開發的項目中,必定要使用這個來下載 cdn的方式引入: <script src="https://unpkg.com/axios/dist/axios.min.js"></script> //這個作簡單的演示的時候能夠用
其實用法官網寫的很詳細了,這裏你能夠去直接看官網,我這裏就簡單看一下get請求和post請求的使用方法:ios
get請求的寫法:es6
// 爲給定 ID 的 user 建立請求 axios.get('/user?ID=12345') //get方法then方法等都給你返回了一個promise對象,接着能夠調用這個對象的方法 .then(function (response) { //response請求成功接收的數據 console.log(response); }) .catch(function (error) { //error請求錯誤捕獲的異常 console.log(error); }); // 可選地,上面的請求能夠這樣作 axios.get('/user', { //請求的查詢參數還能夠這樣寫 params: { ID: 12345 } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });
post請求的寫法:web
axios.post('/user', { //後面這個大括號裏面寫請求體裏面的請求數據,就是jQuery的ajax裏面的data屬性 firstName: 'Fred', lastName: 'Flintstone' }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });
好,接下來在們上篇博客最後經過webpack作的那個項目來演示一下axios的使用,首先在咱們的項目目錄下執行下載安裝axios的指令:ajax
npm install axios -S //爲何要下載項目下呢,由於未來打包項目推送到線上的時候,打包咱們的項目,全局的那些環境你是打包不上的,你線上環境裏面可能沒有咱們下載的這些環境或者說工具,因此直接在咱們的項目環境中下載,打包的時候一同打包給線上
那麼接下來axios這個東西怎麼用呢,咱們是否是可能在好多個組件中都要發送請求啊,首先看一下咱們開發時的結構:
那這個怎麼玩呢,原型鏈技術:
vue對象實例在咱們的mian.js裏面引用的,那麼咱們就在main.js裏面引入axios,而且封裝給vue實例,那麼其餘組件就能用了(繼承),在mian.js裏面加上下面兩句:
import Axios from 'axios' Vue.prototype.$http = Axios; //Vue.prototype.名字(這個名字隨便起,通常是叫$http或者$https,那麼一看就明白,你這是在日後端發送請求)
看mian.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'
//引入axios,封裝axios,注意不用使用Vue.use()昂,人家官網也沒說讓你用,用use的通常是在vue的基礎上封裝的一些框架或者模塊,可是這個axios不是vue的,是一個新的基於es6的http庫,在哪一個語言裏面均可以用這個庫,它是一個獨立的內容,vue裏面通常不用jQuery的ajax,人間jQuery也是前端的一個框架能夠這麼說,人家vue也是前端的框架,二者作的事情差很少,人家不想用jQuery的 import Axios from 'axios' Vue.prototype.$http = Axios; //Vue.prototype.名字(這個名字隨便起,通常是叫$http或者$https,那麼一看就明白,你這是在日後端發送請求) import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); import '../static/global/index.css' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
而後咱們啓動咱們的項目,執行npm run dev,而後在咱們的組件中使用一下axios,我在咱們以前建立的那個Course.vue組件中使用了一下,也算是寫一下前面佈置的那個練習吧,你們看看代碼,看看你能看懂不:
<template> <!--<div>--> <!--這是Course頁面--> <!--</div>--> <div> <div class="categorylist"> <span v-for="(item,index) in categoryList" :key="item.id" :class="{active:currentIndex===index}" @click="clickCategoryHandler(index,item.id)">{{ item.name }}</span> </div> <div class="course"> <ul> <li v-for="(course,index) in courseList" :key="course.id"> <h3>{{ course.name }}</h3> <!-- 這裏就拿個name的值演示一下效果 --> </li> </ul> </div> </div> </template> <script> export default { name: 'Course', data(){ return{ categoryList:[],//用來接收請求回來的數據,通常都和咱們後端返回回來的數據的數據類型對應好,因此我寫了個空列表(空數組),那麼在使用這個數據屬性的地方就變成了你請求回來的數據 currentIndex:0, //爲了讓咱們上面渲染的第一個span標籤有個默認樣式 courseList:[], //存放免費課程頁裏面的課程分類標籤頁對應的數據 courseId:0, //用來記錄用戶點擊的哪一個課程標籤,根據這個id動態的日後端請求數據 } }, //發送請求獲取這個組件的數據,咱們隨便找個網站,找個數據接口昂,好比這裏用的是https://www.luffycity.com/courses這個網址裏面的組件的數據接口,咱們訪問這個網址,你在瀏覽器調試臺的network的地方能看到,它發送了兩個請求,一個是請求了這個網址https://www.luffycity.com/api/v1/course_sub/category/list/,一個是請求了這個網址https://www.luffycity.com/api/v1/courses/?sub_category=0&ordering= //好,那麼咱們就經過axios來請求一下這兩個接口的數據,本身寫一個getCategoryList方法,封裝請求邏輯,在created鉤子函數中使用一下,直接在created裏面寫請求邏輯也是能夠的,只不過結構不太清晰而已 //咱們發現,咱們要請求的這兩個url'https://www.luffycity.com/api/v1/courses/?sub_category=0&ordering='和https://www.luffycity.com/api/v1/course_sub/category/list/,你會發現這些url的前面部分https://www.luffycity.com/api/v1/都是同樣的,能不能作一個公用的部分存起來啊,省去了不少的代碼,好,axios也提供了一個對應的方法defaults.baseURl,怎麼用這個東西呢,在咱們引入axios的方法配置,也就是在咱們的main.js裏面配置,看咱們的main.js內容 methods:{ //獲取標題欄的數據 getCategoryList(){ // this.$https.get('https://www.luffycity.com/api/v1/course_sub/category/list/'),配置了公用的url,因此咱們下面就寫這個公用url的後面的部分就能夠了 // var _this = this; 能夠先將this保存下來,下面的方法中也能夠不寫成箭頭函數了,直接使用_this變量就是咱們的當前組件對象 this.$https.get('course_sub/category/list/') .then((response)=>{ //注意咱們寫箭頭函數,由於寫普通函數的話,裏面使用this的時候,this指向的當前調用該方法的對象,就是axios,而咱們像讓this指向定義當前對象的父類,也就是咱們的當前組件對象,因此要改變一下this的指向,不用箭頭函數也能搞定,把this先保存一下,賦值給一個變量 // console.log(response); //響應數據的對象 // console.log(response.data.data); //響應回來的數據 // console.log(response.data.error_no); //響應數據的狀態 if (response.data.error_no === 0){ this.categoryList = response.data.data; let obj = { //由於咱們的免費課程裏面的子組件的標籤頁最前面還有個'所有'功能,因此把它加上 id:0, name:'所有', category:0, hide:false, }; this.categoryList.unshift(obj);//數組頭部追加元素 //數據刪除任意一個指定的元素,按照索引來刪除對應元素,splice(2,2,'a')方法還記得嗎,三個參數,第一個2是從索引2開始刪,第二個2,是刪除幾個,第三個'a',第三個參數能夠不寫,就成了單純的刪除操做,是刪除元素的地方替換成'a',忽然想起來了,就在這裏提一嘴 } }) .catch((error)=>{ console.log('獲取數據失敗,錯誤是:',error);//後端返回的錯誤 }) }, //獲取標題欄中免費課程的標籤頁列表數據 getCourseList(){ // this.$https.get('courses/?sub_category=0&ordering=') 經過this.courseId的值來改變sub_category=0的這個0,來發送動態的請求,獲取不一樣課程標籤對應的不一樣的數據,別忘了用反引號,模板字符串,鍵盤上tab鍵上面那個鍵 this.$https.get(`courses/?sub_category=${this.courseId}&ordering=`) .then((response)=>{ // console.log(response); // console.log(response.data); // console.log(response.data.data); if (response.data.error_no === 0){ this.courseList = response.data.data; } }) .catch((error)=>{ console.log('標題欄中免費課程的標籤頁列表數據獲取失敗,錯誤是:',error) }) }, //點擊span標籤變css樣式的,而且發送另一個這個標籤對應的請求,請求的url是https://www.luffycity.com/api/v1/courses/?sub_category=0&ordering=,這些url的數據是咱們點擊免費課程那個組件的時候發送的請求,返回給咱們的,而後咱們渲染到這個咱們這裏點擊的每一個span標籤的 clickCategoryHandler(val,course_id){ this.currentIndex = val; this.courseId = course_id; //將用戶點擊的課程標籤的id賦值給咱們設置的這個數據屬性,未來經過這個數據屬性來動態的發送請求,執行下面的方法來發送請求 this.getCourseList(); //根據用戶點擊的課程標籤傳過來的courseid的值來動態獲取對應的數據 } }, //免費課程頁面剛加載完就發送了下面的兩個請求,拿到了數據,渲染在頁面上了,因此咱們經過一個created鉤子函數搞一下 created(){ this.getCategoryList(); this.getCourseList(); } } </script> <style scoped> span{ padding: 0 20px; } span.active{ color:blue; } </style>
裏面涉及到了mian.js文件的一個配置,我把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' import Axios from 'axios'
//看這裏,看這裏!!! Vue.prototype.$https = Axios; //Vue.prototype.名字(這個名字隨便起,通常是叫$http或者$https,那麼一看就明白,你這是在日後端發送請求) //給Axios設置一個未來發送請求時的公用的url,那麼未來經過axios請求網址時,就只須要寫這個公用url的後面的部分 Axios.defaults.baseURL = 'https://www.luffycity.com/api/v1/'; //axios官網上有 //引入element-ui import ElementUI from 'element-ui'; //引入element-ui的css樣式,這個須要單獨引入 import 'element-ui/lib/theme-chalk/index.css'; //給Vue添加一下這個工具 Vue.use(ElementUI); import '../static/global/index.css' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
vuex官網地址:https://vuex.vuejs.org/zh/guide/
vuex是什麼呢?官方說法是這樣的:Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
其實vuex簡單來說就是一個臨時數據倉庫,可以幫咱們保存各個組件中的數據,也就是每一個組件都能使用這個倉庫裏面的數據,只要修改了數據倉庫裏面的數據,使用了倉庫中這個數據的組件中的這個數據都會跟着變化,很是適合各組件的一個傳值,以前咱們使用組件之間的傳值都是用的父子組件傳值或者平行組件傳值的方式,這個vuex幫咱們作了一個更方便的數據倉庫來實現組件傳值和各組件的數據共享,可是小型項目通常用它比較合適,大型項目仍是用組件傳值的方式,由於這個倉庫對於大型項目來說,比較笨重,共享數據量太大了也會對頁面性能有所影響,因此咱們今天學的這個vuex不是說必需要用的,仍是看需求和業務場景的。
來看一個圖:
經過上面這個圖我相信你們應該很清楚vuex是作什麼的,好,既然涉及到組件往裏面存值、取值、修改值的操做,那麼咱們就來看看怎麼完成這些操做。
vuex就至關於一個倉庫(store),首先咱們說一下vuex的五大悍將:
state:狀態,也就是共享數據
mutations:更改 Vuex 的 store 中的狀態的惟一方法是提交 mutation。Vuex 中的 mutation 很是相似於事件:每一個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是咱們實際進行狀態更改的地方,而且它會接受 state 做爲第一個參數,可是全部修改數據的操做經過mutations都是同步(串行)操做,目的應該是爲了保證數據安全,你們應該可以發現,多個組件可能都會執行修改一個共享數據的操做(異步操做,定時器,ajax都是異步的),那麼異步操做共享數據,就容易出現數據混亂的問題,因此凡是經過mutations的數據操做都變成了同步狀態。
actions:Action 相似於 mutation,不一樣在於:Action 提交的是 mutation,也就是提交的是mutations中的函數(方法),而不是直接變動狀態(共享數據)。Action 能夠包含任意異步操做,也就是說異步操做能夠同時提交給actions,經過dispatch方法,可是action提交數據的操做必須通過mutations,經過commit方法提交給mutations,又變成了同步,爲了數據安全可靠不混亂。也就是說操做數據的方式有兩個: 1.操做數據--> commit -->mutations(同步) 2.操做數據--> dispatch -->actions(異步)--> commit -->mutations(同步),若是將異步任務直接交給mutations,就會出現數據混亂不可靠的問題。
getters:至關於store的計算屬性
modules:模塊
getters和modules不經常使用,咱們後面的項目也用不到,因此這裏就不作詳細的解釋了,咱們只要學習前三個悍將(state、mutations、actions),這些悍將都是寫在咱們建立的store對象裏面的屬性,具體他們幾個怎麼搞,先看圖:
首先下載安裝,項目目錄下在終端執行一下下面的指令:
npm i vuex -S
因爲vuex這個倉庫全部的組件都要使用,因此咱們須要將它掛載到咱們的全局vue實例裏面,vue實例咱們是在main.js裏面引入並建立的,因此咱們看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' import Axios from 'axios' Vue.prototype.$https = Axios; Axios.defaults.baseURL = 'https://www.luffycity.com/api/v1/'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); import '../static/global/index.css' Vue.config.productionTip = false; //引入vuex import Vuex from 'vuex' Vue.use(Vuex); //必須use一下 //建立vuex的store實例,這個實例就是咱們的vuex倉庫 const store = new Vuex.Store({ //這裏面使用vuex的五大悍將 state:{ num:1, //state狀態有個num=1,也就是有個共享數據num=1,組件中使用它 }, mutations:{ //mutations裏面的函數第一個參數就是上面這個state屬性,其餘的參數是調用這個這函數時給傳的參數 setNum(state,val){ // console.log(val); state.num += val; }, }, //此時尚未用到actions異步提交倉庫的一些數據操做的任務 actions:{ } }); /* eslint-disable no-new */ new Vue({ el: '#app', router, store, // 必須將建立的vuex.Store對象掛載到vue實例中,那麼至關於給咱們的vue對象添加了一個$store屬性,未來組件中經過this.$store就能調用這個對象,this雖然是組件對象,可是組件對象都是至關於繼承了vue對象,還記得router嗎,使用router的時候有兩個對象,經過vue對象調用,this.$router,this.$route,和它相似的用法 components: { App }, template: '<App/>' });
而後咱們建立兩個組件,一個叫作Home.vue,一個叫作Son.vue,這個Son組件是Home組件的子組件,咱們就玩一下這兩個組件對vuex倉庫的一個共享數據的一系列操做,下面完成了組件從倉庫中取值(必須經過組件的計算屬性完成這個事情),還有經過mutations來完成的一個同步方式修改數據的操做,看代碼:
Home.vue文件的代碼以下:
<template> <div> 這是Home頁面 <h1>我是父組件中的 {{ myNum }}</h1> <Son></Son> </div> </template> <script> //引入子組件 import Son from '../son/Son' export default { name: 'Home', data() { return { } }, //掛載Son子組件 components:{ Son, }, //組件中想操做store中的數據,必須經過計算屬性來監聽vuex的store中的state裏面的數據,組件中才能獲取到並使用store中的這個數據 computed:{ //在模板語法中使用{{ myNum }},就能用myNum對應函數裏面的返回值 myNum:function () { // console.log(this);//還記得這個this指的是誰嗎,就是當前的組件對象,組件是否是都至關於繼承於咱們的vue對象,而store又掛載到了咱們的vue實例上,那麼經過組件對象.$store就能得到我們的vuex倉庫store return this.$store.state.num //獲取到咱們store中的state裏面的數據 } }, } </script> <style> </style>
Son.vue文件代碼以下:咱們在子組件中來個button按鈕,點擊一下這個按鈕,就給store倉庫中的數據num加1,而後Home組件中使用了這個num地方的num值也自動跟着發生改變,實現了一個父子組件傳值的效果。
<template> <div> <h2>我是子組件 {{ sonNum }}</h2> <button @click="addNum">同步修改num+1</button> </div> </template> <script> export default { name:'Son', data(){ return{ } }, //Son子組件中使用一下store中的數據 computed:{ sonNum:function () { console.log(this); return this.$store.state.num } }, methods:{ //給button綁定了一個點擊事件,這個事件作的事情是點擊一下,給store中的state中的num + 1 addNum(){ //不能直接修改store中的數據,這是vuex限定死的,必須經過mutations裏面的方法來修改 this.$store.commit('setNum',1) //注意,commit裏面的第一個參數是咱們在vuex的store對象中的mutations裏面定義的方法,第二個參數1就是要傳給mutations中的setNum函數的數據,也就是第二個參數val }, } } </script> <style></style>
而後咱們看頁面效果:
這就是咱們直接經過mutations中的函數完成的一個同步修改數據的操做,
下面咱們再來一個經過mutations中的函數完成作一個異步修改數據的操做,看一下上面說的會形成數據不可靠的問題的效果:
首先看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' import Axios from 'axios' Vue.prototype.$https = Axios; Axios.defaults.baseURL = 'https://www.luffycity.com/api/v1/'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); import '../static/global/index.css' Vue.config.productionTip = false; //引入vuex import Vuex from 'vuex' Vue.use(Vuex); //必須use一下 //建立vuex的store實例,這個實例就是咱們的vuex倉庫 const store = new Vuex.Store({ //這裏面使用vuex的五大悍將 state:{ num:1, //state狀態有個num=1,也就是有個共享數據num=1,組件中使用它 }, mutations:{//異步數據操做要執行的對應的mutations中的函數 setNumAsync(state,val){ setTimeout(()=>{ //還記得嗎,定時器是個異步任務,你頁面隨便操做,它本身玩本身的 state.num += val; },1000) } }, actions:{ } }); /* eslint-disable no-new */ new Vue({ el: '#app', router, store, // 必須將建立的vuex.Store對象掛載到vue實例中,那麼至關於給咱們的vue對象添加了一個$store屬性,未來組件中經過this.$store就能調用這個對象,this雖然是組件對象,可是組件對象都是至關於繼承了vue對象,還記得router嗎,使用router的時候有兩個對象,經過vue對象調用,this.$router,this.$route,和它相似的用法 components: { App }, template: '<App/>' });
Home.vue中的代碼沒有變化,仍是以前的代碼,不用改,因此咱們直接看Son.vue文件中的代碼以下:
<template> <div> <h2>我是子組件 {{ sonNum }}</h2> <button @click="addNumAsync">異步修改num+5</button> </div> </template> <script> // <button @click="addNum">同步修改num+1</button> export default { name:'Son', data(){ return{ } }, //Son子組件中使用一下store中的數據 computed:{ sonNum:function () { console.log(this); return this.$store.state.num } }, methods:{ //給button綁定了一個點擊事件,這個事件作的事情是點擊一下,給store中的state中的num + 1 addNum(){ //不能直接修改store中的數據,這是vuex限定死的,必須經過mutations裏面的方法來修改 this.$store.commit('setNum',1) //注意,commit裏面的第一個參數是咱們在vuex的store對象中的mutations裏面定義的方法,第二個參數1就是要傳給mutations中的setNum函數的數據,也就是第二個參數val }, //異步提交數據加1操做 addNumAsync(){ this.$store.commit('setNum',5) //每次都加5 } } } </script> <style></style>
效果以下:
因此,咱們提交異步任務操做數據的時候,必須遵循人家vuex說的,異步的任務先提交給actions,再有actions提交給mutations,那麼未來,同步的數據操做,咱們也是先給actions,再給mutations,在mutations中進行數據操做,看代碼,將異步和同步操做放到actions中:
mian.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' import Axios from 'axios' Vue.prototype.$https = Axios; Axios.defaults.baseURL = 'https://www.luffycity.com/api/v1/'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); import '../static/global/index.css' Vue.config.productionTip = false; //引入vuex import Vuex from 'vuex' Vue.use(Vuex); //必須use一下 //建立vuex的store實例,這個實例就是咱們的vuex倉庫 const store = new Vuex.Store({ //這裏面使用vuex的五大悍將 state:{ num:1, //state狀態有個num=1,也就是有個共享數據num=1,組件中使用它 }, mutations:{ //mutations裏面的函數第一個參數就是上面這個state屬性,其餘的參數是調用這個這函數時給傳的參數 // setNum(state,val){ // // console.log(val); // state.num += val; // }, muNum(state,val){ // console.log(val); state.num += val; }, //在actions中調用這個方法,仍是mutations中這個方法來進行數據操做 muNumAsync(state,val){ state.num += val; } }, actions:{ //同步數據操做,咱們也經過actions來提交給mutations setNum(context,val){ context.commit('muNum',val); }, //異步數據操做要執行的對應的actions中的函數,actions中的函數在調用mutations中的函數,actions中的函數的第一個參數是咱們的store對象 setNumAsync(context,val){ setTimeout(()=>{ //還記得嗎,定時器是個異步任務,你頁面隨便操做,它本身玩本身的 context.commit('muNumAsync',val); },1000) } } }); /* eslint-disable no-new */ new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' });
Home.vue組件沒有改動,因此我只把改動的Son.vue組件的代碼拿出來了,看代碼:
<template> <div> <h2>我是子組件 {{ sonNum }}</h2> <button @click="addNum">同步修改num+1</button> <button @click="addNumAsync">異步修改num+5</button> </div> </template> <script> // <button @click="addNum">同步修改num+1</button> export default { name:'Son', data(){ return{ } }, //Son子組件中使用一下store中的數據 computed:{ sonNum:function () { console.log(this); return this.$store.state.num } }, methods:{ //給button綁定了一個點擊事件,這個事件作的事情是點擊一下,給store中的state中的num + 1 addNum(){ //同步的數據操做,也經過dispatch方法調用actions中的函數,將數據操做提交給actions中的函數 this.$store.dispatch('setNum',1) }, //異步提交數據加1操做 addNumAsync(){ //爲了必須經過actions來提交數據操做,這裏面咱們就不能直接commit了,而是用dispatch方法調用actions中的函數,將數據操做提交給actions中的函數 // this.$store.commit('setNum',5) this.$store.dispatch('setNumAsync',5) //每次都加5,setNumAsync是actions中的函數 } } } </script> <style></style>
而後看頁面效果:
而後看一下上面代碼的邏輯圖:
vuex用起來比較麻煩,可是對於組件傳值來講方便了一些,哈哈,也沒太大感受昂,不要緊,用熟練了就行了,如今再回去看一下vuex的那個圖,應該就清晰了。好,這就是咱們講到的vuex倉庫store,咱們使用vuex來搞一搞異步操做,完成的事情就是點擊對應課程,展現課程詳細信息:
先看目錄結構:
而後咱們直接上代碼了
Coures.vue代碼以下:
<template> <!--<div>--> <!--這是Course頁面--> <!--</div>--> <div> <div class="categorylist"> <span v-for="(item,index) in categoryList" :key="item.id" :class="{active:currentIndex===index}" @click="clickCategoryHandler(index,item.id)">{{ item.name }}</span> </div> <div class="course"> <ul> <li v-for="(course,index) in courseList" :key="course.id"> <h3 @click="showDetail(course.id)">{{ course.name }}</h3> <!-- 點擊它,跳轉到對應課程的詳情數據頁面 --> </li> </ul> </div> </div> </template> <script> export default { name: 'Course', data(){ return{ categoryList:[],//用來接收請求回來的數據,通常都和咱們後端返回回來的數據的數據類型對應好,因此我寫了個空列表(空數組),那麼在使用這個數據屬性的地方就變成了你請求回來的數據 currentIndex:0, //爲了讓咱們上面渲染的第一個span標籤有個默認樣式 courseList:[], //存放免費課程頁裏面的課程分類標籤頁對應的數據 courseId:0, //用來記錄用戶點擊的哪一個課程標籤,根據這個id動態的日後端請求數據 } }, methods:{ getCategoryList(){ this.$https.get('course_sub/category/list/') .then((response)=>{ if (response.data.error_no === 0){ this.categoryList = response.data.data; let obj = { id:0, name:'所有', category:0, hide:false, }; this.categoryList.unshift(obj);//數組頭部追加元素 } }) .catch((error)=>{ console.log('獲取數據失敗,錯誤是:',error);//後端返回的錯誤 }) }, //獲取標題欄中免費課程的標籤頁列表數據 getCourseList(){ this.$https.get(`courses/?sub_category=${this.courseId}&ordering=`) .then((response)=>{ if (response.data.error_no === 0){ this.courseList = response.data.data; } }) .catch((error)=>{ console.log('標題欄中免費課程的標籤頁列表數據獲取失敗,錯誤是:',error) }) }, //點擊span變色,獲取對應的數據 clickCategoryHandler(val,course_id){ this.currentIndex = val; this.courseId = course_id; this.getCourseList(); }, //第一步:查看課程詳細信息,獲取詳細信息數據,第二步要配置路由了 showDetail(val){ //跳轉路由,加載對應組件 this.$router.push({ name:'coursedetail', //傳的params參數 params:{ cid:val, } }); } }, created(){ this.getCategoryList(); this.getCourseList(); } } </script> <style scoped> span{ padding: 0 20px; } span.active{ color:blue; } </style>
路由配置信息index.js文件代碼以下:
import Vue from 'vue' import Router from 'vue-router' // import HelloWorld from '@/components/HelloWorld' //@還記得嗎,表示src文件夾的根路徑 //引入組件 import Course from '@/components/Course/Course' import Home from '@/components/Home/Home' import CourseDetail from '@/components/CourseDetail/CourseDetail' // console.log(Course); //給Vue添加vue-router功能,使用別人提供的功能都要用Vue來use一下 Vue.use(Router) //建立路由對象 export default new Router({ mode:'history', //去掉瀏覽器地址欄的#號 //配置路由信息 routes: [ { path: '/', // redirect:'Home' //直接跳轉Home名字對應的path路徑上 redirect:'/home' }, { path: '/home', name: 'Home', component: Home }, { path: '/course', name: 'Course', component: Course }, //第二步配置路由! { path: '/course/:cid/payment_info/', //動態params的動態路由 name: 'coursedetail', component: CourseDetail } ] })
課程詳細信息插件CoureDetail.vue文件代碼以下:
<template> <div> 我是課程詳情組件 <h3> {{ courseDetailShow }} <!-- 簡單展現了詳細信息數據中的一個name數據 --> </h3> </div> </template> <script> export default { name:'coursedetail', data(){ return{} }, //第三步,調用actions中的函數,將courseid做爲參數給他 created(){ let cid = this.$route.params.cid; this.$store.dispatch('getDetailAsync',cid); }, //第七步:在模板中使用vuex倉庫中修改後的數據 computed:{ courseDetailShow(){ console.log(this.$store.state.detailList); return this.$store.state.detailList; } } } </script> <style></style>
mian.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' import Axios from 'axios' Vue.prototype.$https = Axios; Axios.defaults.baseURL = 'https://www.luffycity.com/api/v1/'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); import '../static/global/index.css' Vue.config.productionTip = false; //引入vuex import Vuex from 'vuex' Vue.use(Vuex); //必須use一下 //建立vuex的store實例,這個實例就是咱們的vuex倉庫 const store = new Vuex.Store({ //這裏面使用vuex的五大悍將 state: { num: 1, detailList:'' }, mutations: { muNum(state, val) { // console.log(val); state.num += val; }, muNumAsync(state, val) { state.num += val; }, //第六步:修改state中的數據 getCourseDetail(state, val) { // state.detailList = val; state.detailList = val.data.name; //這裏我只是簡單的獲取了一下詳情信息中的name屬性的數據 console.log('?????',state.detailList) } }, actions: { setNum(context, val) { context.commit('muNum', val); }, setNumAsync(context, val) { setTimeout(() => { context.commit('muNumAsync', val); }, 1000) }, //第四步:提交獲取課程詳細信息的數據操做 getDetailAsync(context, val) { Axios.get(`course/${val}/payment_info/`) .then((response) => { // console.log(response.data); //第五步:調用mutations中的方法 context.commit('getCourseDetail', response.data); }) .catch((error) => { console.log(error); }); } } }); /* eslint-disable no-new */ new Vue({ el: '#app', router, store, // 必須將建立的vuex.Store對象掛載到vue實例中,那麼至關於給咱們的vue對象添加了一個$store屬性,未來組件中經過this.$store就能調用這個對象,this雖然是組件對象,可是組件對象都是至關於繼承了vue對象,還記得router嗎,使用router的時候有兩個對象,經過vue對象調用,this.$router,this.$route,和它相似的用法 components: {App}, template: '<App/>' });
代碼流程圖以下:
再補充一點:
將Home組件組成全局組件,在main.js中作,看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' import Axios from 'axios' Vue.prototype.$https = Axios; Axios.defaults.baseURL = 'https://www.luffycity.com/api/v1/'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); import '../static/global/index.css' Vue.config.productionTip = false; //引入vuex import Vuex from 'vuex' //將Home組件作成全局組件的方法,看這裏看這裏!!!! import Home from './components/Home/Home' Vue.component(Home.name,Home); Vue.use(Vuex); //必須use一下 //建立vuex的store實例,這個實例就是咱們的vuex倉庫 const store = new Vuex.Store({ //這裏面使用vuex的五大悍將 state: { num: 1, detailList:'' }, mutations: { muNum(state, val) { // console.log(val); state.num += val; }, muNumAsync(state, val) { state.num += val; }, //第六步:修改state中的數據 getCourseDetail(state, val) { // state.detailList = val; state.detailList = val.data.name; console.log('?????',state.detailList) } }, actions: { setNum(context, val) { context.commit('muNum', val); }, setNumAsync(context, val) { setTimeout(() => { context.commit('muNumAsync', val); }, 1000) }, //第四步:提交獲取課程詳細信息的數據操做 getDetailAsync(context, val) { Axios.get(`course/${val}/payment_info/`) .then((response) => { // console.log(response.data); //第五步:調用mutations中的方法 context.commit('getCourseDetail', response.data); }) .catch((error) => { console.log(error); }); } } }); /* eslint-disable no-new */ new Vue({ el: '#app', router, store, // 必須將建立的vuex.Store對象掛載到vue實例中,那麼至關於給咱們的vue對象添加了一個$store屬性,未來組件中經過this.$store就能調用這個對象,this雖然是組件對象,可是組件對象都是至關於繼承了vue對象,還記得router嗎,使用router的時候有兩個對象,經過vue對象調用,this.$router,this.$route,和它相似的用法 components: {App}, template: '<App/>' });
在Course.vue組件中使用這個組件
<template> <!--<div>--> <!--這是Course頁面--> <!--</div>--> <div> <div class="categorylist"> <span v-for="(item,index) in categoryList" :key="item.id" :class="{active:currentIndex===index}" @click="clickCategoryHandler(index,item.id)">{{ item.name }}</span> </div> <div class="course"> <ul> <li v-for="(course,index) in courseList" :key="course.id"> <h3 @click="showDetail(course.id)">{{ course.name }}</h3> <!-- 點擊它,跳轉到對應課程的詳情數據頁面 --> </li> </ul> </div> <div> <!-- 看這裏看這裏,全局組件不須要掛載,直接在template中使用 --> <Home></Home> </div> </div> </template> <script> export default { name: 'Course', data(){ return{ categoryList:[],//用來接收請求回來的數據,通常都和咱們後端返回回來的數據的數據類型對應好,因此我寫了個空列表(空數組),那麼在使用這個數據屬性的地方就變成了你請求回來的數據 currentIndex:0, //爲了讓咱們上面渲染的第一個span標籤有個默認樣式 courseList:[], //存放免費課程頁裏面的課程分類標籤頁對應的數據 courseId:0, //用來記錄用戶點擊的哪一個課程標籤,根據這個id動態的日後端請求數據 } }, methods:{ getCategoryList(){ this.$https.get('course_sub/category/list/') .then((response)=>{ if (response.data.error_no === 0){ this.categoryList = response.data.data; let obj = { id:0, name:'所有', category:0, hide:false, }; this.categoryList.unshift(obj);//數組頭部追加元素 } }) .catch((error)=>{ console.log('獲取數據失敗,錯誤是:',error);//後端返回的錯誤 }) }, //獲取標題欄中免費課程的標籤頁列表數據 getCourseList(){ this.$https.get(`courses/?sub_category=${this.courseId}&ordering=`) .then((response)=>{ if (response.data.error_no === 0){ this.courseList = response.data.data; } }) .catch((error)=>{ console.log('標題欄中免費課程的標籤頁列表數據獲取失敗,錯誤是:',error) }) }, //點擊span變色,獲取對應的數據 clickCategoryHandler(val,course_id){ this.currentIndex = val; this.courseId = course_id; this.getCourseList(); }, //第一步:查看課程詳細信息,獲取詳細信息數據,第二步要配置路由了 showDetail(val){ //跳轉路由,加載對應組件 this.$router.push({ name:'coursedetail', //傳的params參數 params:{ cid:val, } }); } }, created(){ this.getCategoryList(); this.getCourseList(); } } </script> <style scoped> span{ padding: 0 20px; } span.active{ color:blue; } </style>
最後給你們說一些作單頁面應用的問題:
經過vue這種框架作的單頁面應用,若是沒有作vue服務器渲染(相似於django的模板渲染),SEO很難搜索到你的網站,爬蟲也不能直接在你的頁面上爬出數據,可是即使是單頁面應用,前端頁面的數據不是前端寫死的,就是從接口獲取的,咱們找接口就好了,接口的數據不能徹底知足你,你在爬頁面上的。
那SEO問題怎麼辦,nodejs服務器(前端作),,或者後端服務器配合渲染,前端和後端都要作vue,相互渲染來完成頁面解析,讓SEO能搜索到你全部的頁面,有缺陷,若是在公司寫這種單頁面應用,必定要注意SEO的問題,那有這麼多問題,怎麼還用單頁應用啊,單頁應用能夠提升用戶體驗,防止白屏現象,頁面更好維護。
那麼SEO是什麼呢,我找了一些資料給你們,看看就明白了,頁面越多,百度收錄的機會越大
解決SEO的大概思路,看圖:
還有一點跟你們說一下,之後咱們經過腳手架的webpack模塊建立完項目以後,下面這幾個必需要有,沒有的本身npm安裝一下:
直接看代碼吧:
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' Vue.config.productionTip = false; //平行組件傳值,經過bus對象,而後咱們將bus對象掛載到Vue對象上,那麼其餘組件經過this.$bus就能用這個公交車對象了 let bus = new Vue(); Vue.prototype.$bus = bus; /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' });
App.vue組件代碼以下:
<template> <div id="app"> <router-view/> </div> </template> <script> export default { name: 'App' } </script> <style> </style>
Home.vue組件代碼以下:
<template> <div> 我是Home組件 <!--<Son title="Jaden"></Son> 傳靜態數據給子組件 --> <Son :title="title" :msg="msg" @handler="handlerClick"></Son> <!-- 傳動態數據給子組件 --> <button @click="busHandler">經過bus對象傳值</button> </div> </template> <script> import Son from './Son' export default { name:'Home', data(){ return{ title:'test1', msg:'test2', } }, components:{ Son, }, methods:{ handlerClick(val){ this.msg = val; }, //平行組件傳值 busHandler(){ this.$bus.$emit('homeClick',2); }, } } </script> <style></style>
Son.vue組件代碼以下,Son.vue組件是Home組件的父級組件,咱們除了作了父子、子父組件傳值外還作了平行組件傳值,都在這幾個文件的代碼裏面了
<template> <div> 我是Son組件 {{ msg }} -- {{ title }} -- {{ number }} </div> </template> <script> export default { name:'Son', data(){ return{ number:0, } }, props:['msg','title'], created(){ this.$emit('handler',1); this.$bus.$on('homeClick',(val)=>{ this.number = val; }) } } </script> <style></style>
router路由配置信息的index.js內容以下:
import Vue from 'vue' import Router from 'vue-router' import Home from '@/components/Home/Home' Vue.use(Router); export default new Router({ mode:'history', routes: [ { path: '/', redirect:{name:'Home'} }, { path: '/', name: 'Home', component: Home } ] })