1. 程式人生 > >vue-cli3.0技術棧筆記

vue-cli3.0技術棧筆記

0. 基於vue ui建立vue cli3.0專案:參考:https://blog.csdn.net/qq_42231156/article/details/82343793。

1.專案檔案vueCli3.0下:
    pulic檔案下:favicon.ico是網址上方標題的的小圖示。
    index.html:是入口檔案。
    src:專案的主檔案:
                assets:是存放靜態資源(如,圖片,css等)的檔案。
          img:存放圖片檔案。
                iconfont:圖示字型檔案。

                          css:

存放公共css檔案。

                          js:存放第三方js檔案。
                components:可複用的元件。
                views:頁面元件。
                api:專案的ajax請求請求。
                config:專案的配置檔案。
                         index.js:配置地址,在需要的地方import config  from "./config/index.js"引入配置物件即可。
                directive:存放vue的自定義指令。
                         index.js:


                lib:
                        tools.js:存放與業務無關的,純粹的工具方法,如封裝的通用js方法。
                        util.js:存放與業務結合有關的,如定義的路徑變數。
               router:存放路由有關的,將router.js移入該檔案內。
                        index.js::存放引入檔案,配置路由例項。如下圖:

import Vue from 'vue'
import Router from 'vue-router'
import routes from "./router.js"
import {setTitle} from "@/lib/util.js"
Vue.use(Router)
const router=new  Router({
	routes
})
const IS_LOGIN=true   //根據儲存在cookie的登入資訊判斷是否登入的判斷
router.beforeEach((to,form,next)=>{  //router例項的beforeEach方法是註冊一個全域性前置守衛,從from路由物件到to路由物件,即禁止在沒有登入情況下,在網址欄輸入admin會跳轉到admin頁面。
	to.meta && setTitle(to.meta.title)  //設定頁面網址上面的標題
	if(to.name !=="login"){    //如果即將跳轉的頁面不是登入頁面,如跳轉的是admin頁面
		if(IS_LOGIN) {   //根據是否已經登入,判斷是否可以跳轉到adminy頁面
			next()      //如果已即登入,就直接跳轉
		}else{
			next({name:'login'})    //如果沒有登入,就跳轉到登入頁面
		} 
	}else{   //如果即將跳轉的頁面是登入頁面
		next()
	}
})

router.beforeResolve((to,form,next)=>{  //router例項的beforeResolve方法是註冊一個全域性守衛,從from路由物件到to路由物件,即頁面跳轉前所有鉤子執行完最後執行該函式 ,

})

router.afterEach((to,form)=>{  //router例項的afterEach方法是註冊一個全域性後置守衛,從from路由物件到to路由物件,即頁面跳轉之後執行,
	//loading=false
})

export default router

/**
 * 1. 導航被觸發
 * 2. 在失活的元件(即將離開的頁面元件)裡呼叫離開守衛 beforeRouteLeave
 * 3. 呼叫全域性的前置守衛 beforeEach
 * 4. 在重用的元件裡呼叫 beforeRouteUpdate
 * 5. 呼叫路由獨享的守衛 beforeEnter
 * 6. 解析非同步路由元件
 * 7. 在被啟用的元件(即將進入的頁面元件)裡呼叫 beforeRouteEnter
 * 8. 呼叫全域性的解析守衛 beforeResolve
 * 9. 導航被確認
 * 10. 呼叫全域性的後置守衛 afterEach
 * 11. 觸發DOM更新
 * 12. 用建立好的例項呼叫beforeRouterEnter守衛裡傳給next的回撥函式
 */



                        router.js::單獨存放處理路由檔案,配置路由列表。如下圖:

import Home from './views/Home.vue'
export default [
    {
      path: '/',
      alias:'/home_page',  //為路由取別名,即訪問"/home_page"和訪問"/"和通過name:"home",訪問的效果是一樣的
      name: 'home',   
      component: Home,    //普通寫法
      beforEnter:(to,from,next)=>{   //該元件獨有的守衛
      	if(from.name="login"){
	      	alert("這是從登陸頁面跳轉過來的")
	    }else{
	      	alert("這不是從登陸頁面跳轉過來的")
	    }
	    next()   //一定要在最後執行next(),否則不會跳轉
      }
    },
    {
      path: '/about',
      name: 'about',      
      component: () => import(./views/About.vue')  //這樣寫,有懶載入的作用,即該頁面顯示時才會載入。
    },
    {
   	  path: '/argu/:name',    //動態路由傳參:高階寫法,動態載入路由,name是引數
   	  props:true,             //表示允許元件props:{}中接受name引數值,然後可以直接渲染在頁面{{name}}
      component: () => import('./views/Argu.vue')  
    },
     {
   	  path: '/parent',    //巢狀路由
      component: () => import( './views/Argu.vue'),
      children:[
	      {
	      	path:"child1",  //注意只有父級需要'/'
	      	component: () => import('./views/child1.vue'),
	      },
	      {
	      	path:"child2",
	      	component: () => import('./views/child2.vue'),
	      },
      ]
    },
    {
		path: '/name_router',   //命名檢視
		components: {
		    default:() => import('./views/about.vue') ,   //如果<router-view/> 沒有name屬性值,預設對應該路由
		    email:() => import('./views/parent.vue') ,    //<router-view name="email"/> 對應該路由
		    tel:() => import('./views/argu.vue')       //<router-view tel="tel"/> 對應該路由
		} 
	},
	{
		path: '/home',    //重定向路由
		redirect:"/",   //3種重定向的方法
//		redirect:{
//		   name:"home1"
//		},
//		redirect:to =>{    //如根據引數確定跳轉到哪個頁面
////		console.log(to)
////		return "/"
//			return {
//				name:"home1"
//		    }
//		}
	
	},
	 {
   	  path: '/login',      	       
      component: () => import('./views/login.vue')  
    },
	{
		path:"*",   //404頁面一定要寫在最後,因為是從上到下匹配路由。
		component: () => import('./views/error404.vue'),
	}
  ]


               store::存放狀態管理的檔案。store.js移入到該檔案內,修改為index.js。記得修改main.js的入口檔案
                        index.js:
                        mutations.js:
                        getters.js:   
相當於計算屬性,對state引數的計算
                        state.js:
                        actions.js:
                        mudels:
存放模組檔案。
                              user.js:如存放使用者的登入資訊        
               mock:  模擬返回的資料,在後臺介面還麼有完成,前端通過模擬資料。參考:http://mockjs.com/
   vue.config.js:自定義設定檔案的配置,如跨域請求地址,路徑簡寫src為@,設定專案的基本路徑。
            
2. vscode編譯器的:新增配置編譯器的檔案:
    在src同級下.editorconfig:
        root=true
        [*]     //表示該編譯器配置針對所有檔案
        charset=utf-8
        indent_style=tabs  縮排鍵
        indent-size=2     縮排的大小        
    然後在編譯器中安裝editorConfig for VS Code,然後就可執行新增的配置編譯器的檔案了
    
    
3. router-link與router-view:router-link是封裝了的a標籤。router-view渲染路由檢視,兩者效果一樣。
命名路由:即路由有name屬性name: 'home',<router-link to="/about"></router-link> 相當於命名路由 <router-link :to="{name:'about'}"></router-link>。
    3.1 配置路由列表的5種方法:

a.為路由取別名:

    import Home from './views/Home.vue'  //如果有 name: 'home',表示是命名路由。
    {
        path: '/',
        alias:'/home_page',  //取別名,即訪問"/home_page"和訪問"/"和通過name:"home",訪問的效果是一樣的
        name: 'home',        //命名路由
        component: Home    //普通載入頁面模組
    },        
b.頁面懶載入頁面模組:即該頁面顯示時才會載入。不需要像import Home from './views/Home.vue'一樣提前載入。
    {
        path: '/about',
        name: 'about',
        component: () => import('./views/About.vue')  
    },
c.動態路由載入傳參:動態載入路由,name是引數,{{$route.params.name}}呼叫引數name的值。
    {
        path: '/argu/:name',
        props:true,             //表示允許元件props:{}中接受name引數值,並直接渲染在頁面{{name}}
        name: 'argu',
        component: () => import( './views/argu.vue')  
    },
d.巢狀路由: <div class="parent"> <router-view/> </div>  。
    {
       path: '/parent',    //巢狀路由
       component: () => import('./views/Argu.vue'),
       children:[
           {
               path:"child1",   //注意只有父級需要'/'
               component: () => import( './views/child1.vue'),
           },
           {
               path:"child2",
               component: () => import('./views/child2.vue'),
           },
       ]
    }
e. 命名檢視:同時存在多個路由跳轉時<router-view/> <router-view name="email"/> <router-view name="tel"/> 。
    {
        path: '/name_router',
        components: {
            default:() => import('./views/about.vue') ,   //如果<router-view/> 沒有name屬性值,預設對應該路由。
            email:() => import('./views/parent.vue') ,    //<router-view name="email"/> 對應該路由。
            tel:() => import('./views/argu.vue')       //<router-view name="tel"/> 對應該路由。
        } 
    },
f. 重定向:重新定義跳轉的路徑,如本來是訪問 '/home',重新繫結跳轉到"/"。
    {
        path: '/home',
        redirect:"/",   //3種重定向的方法
//      redirect:{
//          name:"home1"
//      },
//      redirect:to =>{    //如根據引數確定跳轉到哪個頁面       
////         return "/"
//           return {
//               name:"home1"
//           }
//     }            
    }          

 
 4. js操作路由(即程式設計式的導航)進行頁面跳轉: 
    a. 返回/前進一頁:返回:this.$router.go(-1)、this.$router.back()。前進:this.$router.go(1)。
    b. 跳轉到其他頁:
        this.$router.push("/parent")。
        this.$router.push({name:"parent",query:{name:"ace"}),即瀏覽歷史紀錄儲存著,query是引數。
        this.$router.push({path:`/argu/${name}`})    ,es6帶引數跳轉,針對 path: '/argu/:name',該路由。
        this.$router.push({path:"/parent",params:{name:"ace"}) , 帶引數跳轉。
    c. 用其他頁替換本頁:this.$router.replace("/about")或this.$router.replace({name:"parent"}),即瀏覽歷史紀錄沒有了。 

5. 路由傳值:
    5.1 基於動態路由的頁面(path: '/argu/:name')傳值。 

{
    path: '/argu/:name',
    props:true,             //表示允許Argu.vue元件中props:{}中接受name引數值,然後可以直接渲染在頁面{{name}}
    component: () => import( './views/argu.vue' )  
},

    5.2 基於普通頁面傳參,物件模式傳參。
    

{
    path: '/about',
    props:{
        food:"香蕉"
    },                      //表示允許about.vue元件中props:{}中接受food引數值,然後可以直接渲染在頁面{{food}}
    component: () => import( './views/argu.vue')  
},

   5.3 基於普通頁面傳參,函式模式傳參。   

{
    path: '/parent',
    props: route=>{
        return {
            food:route.query.food
        }
    },                      //表示允許parent.vue元件中props:{}中接受food引數值,然後可以直接渲染在頁面{{food}}
    component: () => import( './views/argu.vue')  
}

6. 導航守衛:如根據是否登入或登入者的許可權跳轉不同的頁面。
    6.1 全域性守衛:即在全域性設定一個守衛。在router/index.js中配置全域性守衛
    

const router=new  Router({
    routes
})    
const IS_LOGIN=true   //是否登入的判斷        
router.beforeEach((to,form,next)=>{  //router例項的beforeEach方法是註冊一個全域性前置守衛,從from路由物件到to路由物件,
    if(to.name !=="login"){    //如果即將跳轉的頁面不是登入頁面
        if(IS_LOGIN) {
            next()      //如果已即登入,就直接跳轉
        }else{
            next({name:'login'})    //如果沒有登入,就跳轉到登入頁面
        } 
    }else{   //如果即將跳轉的頁面是登入頁面
        if(IS_LOGIN) {
            next({name:'home'})      //如果已即登入,就直接跳轉首頁,{name:'home'}也可是'/home'
        }else{
            next()    //如果沒有登入,就直接跳轉
        } 
    }
})    
router.beforeResolve((to,form,next)=>{  //router例項的beforeResolve方法是註冊一個全域性守衛,從from路由物件到to路由物件,即頁面跳轉前所有鉤子執行完最後執行該函式         
})        
router.afterEach((to,form)=>{  //router例項的afterEach方法是註冊一個全域性鉤子,從from路由物件到to路由物件,即頁面跳轉之後執行,
            //loading=false
})

    6.2 元件獨享守衛:即該元件獨有的守衛。   如 在router/router.js中path:"/"中配置元件守衛 。       

{
    path: '/',
    component: Home,    //普通寫法
    beforEnter:(to,from,next)=>{   //該元件獨有的守衛
        if(from.name="login"){
            alert("這是從登陸頁面跳轉過來的")
        }else{
            alert("這不是從登陸頁面跳轉過來的")
        }
     next()   //一定要在最後執行next(),否則不會跳轉
},

    6.3  在元件裡面的3種守衛,如在login.vue元件裡面與生命週期同級:     

#a.beforeRouteEnter:即將跳轉到當前頁面,但是頁面還沒有渲染,所有裡面的this不指向例項vue該元件
beforeRouteEnter(to,from,next){   
   console.log(to.name)
   next(vm=>{
       console.log(vm)      //而這裡的vm就是該元件的例項了。
   })
}
#b.beforeRouteLeave:即將離開當前頁面時執行,如即將離開編輯頁面,彈出提醒框,提醒你是否儲存編輯內容。
beforeRouteLeave(to,from,next){     //此時元件是已經渲染了,this可以執行vue例項
   const leave=confirm("你確定要離開本頁面麼?")
   if(leave){
        next()
    }else{
        next(false)
    }    //false表示不發生頁面跳轉
}
#c.beforeRouteUpdate:即路由發生改變,元件(複用元件)被複用時,執行。如同一個頁面,在url上修改了引數之後,該頁面被複用了,就會執行
beforeRouteUpdate(to,from,next){   //此時元件是已經渲染了,this可以執行vue例項
    console.log(to.name)
}


    注意:整個導航守衛的流程:
        a. 導航被觸發:即url路由地址發生改變。
        b. 在失活的元件:即將離開的頁面元件,裡呼叫離開守衛函式(beforeRouteLeave)。
        c. 呼叫全域性的前置守衛:即函式beforeEach。
        d0. 如果跳轉的是重/複用的元件裡呼叫:在複用/重用的元件裡呼叫函式beforeRouteUpdate。
        d1. 如果跳轉的是新的元件呼叫:在新的元件裡呼叫beforeRouteEnter。
        e. 呼叫路由獨享的守衛:即router/router.js裡面的函式beforEnter 。
        f. 解析非同步路由元件:
        g. 在被啟用的元件裡(即將進入的頁面):呼叫beforeRouteEnter。
        h. 呼叫全域性的解析守衛:即呼叫beforeResolve。
        i. 導航被確認。
        j. 呼叫全域性的後置守衛:afterEach。
        k. 觸發DOM的渲染。
        l. 用建立好的例項呼叫beforeRouteEnter守衛傳給next()的回撥函式。

7. 通過頁面元件的meta:欄位設定每個頁面的window.document.title標題。
    7.1 如在login.vue元件中:

export default{
     meta:{   //與生命週期平級。
         title:"我是登入頁面標題"
     }
}

    7.2 然後在router/index.js的beforeEach中設定 to.meta.title && setTitle(to.meta.title),import {setTitle} from "@/lib/util.js"

router.beforeEach((to, from, next) => {   	
	//設定docutment.title的標題內容
	to.meta && setMetaTitle(to.meta.title)  
	var IS_LOGIN=true;   //是否登入的判斷,getCookie("isLogin")可以有cookie中是否有登入資訊決定    
	if(IS_LOGIN){
		next()
	}else{
		if(to.name==="login"){
			next()
		}else{
			next({name:'login'})
		}
	}
})

     7.3 在lib/util.js中定義一個設定url上方的標題的函式。

export const setTitle =(title)=>{
    window.document.title=title ||  "title不存在時的預設值"
}


8. 設定路由跳轉時的動畫效果:
    8.1 靜態設定動畫效果:  

/*一組路由檢視,設定動畫效果,必須寫key值。然後在css中設定.router-enter/leave-to/active樣式*/
<transition-group name="router">  
    <router-view key="default"/>   
    <router-view key="email"/>
    <router-view key="tel"/>
</transition-group>

    8.2 動態設定動畫效果:     

 //一組路由檢視,設定動畫效果,必須寫key值。然後在css中設定.router-enter/leave-to/active樣式。
<transition-group :name="transitionName">  
   <router-view key="default"/>   
   <router-view key="email"/>
   <router-view key="tel"/>
</transition-group>
data(){
   return{
       transitionName:""
   }
},
watch:{
   "$route"(to){   //to表示當前頁面,表示如果在url傳入引數transitionName存在如值為"router",則將其賦值給this.transitionName
       to.query &&  to.query.transitionName && (this.transitionName =to.query.transitionName)
    }
}

9. 父元件與子元件的傳值:
    9.1 父傳值給子元件:props傳值
        父元件:

<div class="parent">
     <son-assembly :sendVal="value"></son-assembly>
</div> 

        子元件:           

<div class="son">
    {{value}}
</div>
props:{
   son:{
      type:[String,Number],
      default:"123"
   }
}     

     9.2 子傳值給父元件:自定義事件傳值
        子元件:        

<div class="son"></div>
this.$emit("aceHandle",val)

        父元件:           

<div class="parent"  @aceHandle="myHandle"></div>
myHandle(val){
    console.log("這是自定義事件傳值",val)
}

   9.3 父子元件之間相互呼叫彼此的方法:

        父元件呼叫子元件的方法:this.$refs.son.方法名。

        子元件呼叫父元件的方法:方法一:this.$emit("aceHandle",val)。方法二:this.$parent.方法名。

        注意:在vue中@input="inputHandle" 表示input輸入值改變時觸發的內建事件。inputHandle(e){console.log("輸入框的值是",e,target.val)}

10.bus傳值,狀態管理:bus即空的vue例項,如用於簡單場景下的兄弟元件之間傳值。
    10.1 src下建立bus/index.js檔案。
        import Vue from "vue"
        const Bus=new Vue()
        export default Bus
    10.2 在main.js引入bus,並新增到vue的原型物件裡,然後在任何地方都可以不需要在引入,直接使用this.$bus.
        import Bus from './bus/index.js'
        Vue.prototype.$bus=Bus
    10.3 從兄弟元件A傳值給兄弟元件B:
        A元件:
            this.$bus.$emit("myHandle",val)
        B元件:
            mouted(){
                this.$bus.$on("myHandle",(val)=>{  //監聽自定義事件myHandle
                    //在這裡處理接收到B元件傳遞過來的值
                })
            }

11. vuex傳值,狀態管理:src下建立store檔案用於vuex狀態管理
    11.1 index.js入口檔案的管理: 然後在main.js中引入import store from './store/index.js',然後全域性設定new Vue({store}).$mount('#app')
        import Vue from 'vue'
        import Vuex from 'vuex'
        import state from "./state"      //全域性狀態引數管理
        import getters from "./getters"   //相當於計算屬性,對state引數的計算
        import mutations from "./mutations"   
        import actions from "./actions"   
        import user from "./mudule/user.js"    //單獨某個模組如使用者模組user的狀態管理
        
        Vue.use(Vuex)
        
        export default new Vuex.Store({
          state ,   //es6語法相當於state:state
          getters,
          mutations ,
          actions,
          mudules:{
              user
          }
        })
    11.2 state.js全域性狀態引數管理: 
        定義全域性引數:
            const state={
                appName:"我是全域性引數,在元件內都可傳遞值"
            }
            export default state
        呼叫全域性引數值:
            方法1:
                在某個元件內{{this.$store.state.appName}}就可獲取該值了,或通過可在該元件寫入到計算屬性中computed
                computed:{
                    appName(){
                        return this.$store.state.appName
                    }
                }
                <p>{{appName}}</p>
            方法2:
                import {mapState} from "vuex"
                computed:{
                    ...mapState(["appName"])  //這2種寫法一樣。...表示展開一個物件。
//                    ...mapState({
//                        appName:state=>state.appName
//                    })
                }
                <p>{{appName}}</p>
    11.3 user.js 獲取模組中user.js裡面的狀態引數管理:
        定義單獨某個模組中全域性引數:
            const state={
                userName:"我是user模組的引數值"
            }
            const mutations={}
            const actions={}
            export default{
                state,
                mutations,
                actions
            }        
        呼叫單獨某個模組全域性引數值:
            方法1:
                在某個元件內{{this.$store.state.user.userName}}就可獲取該值了,或通過可在該元件寫入到計算屬性中computed
                computed:{
                    userName(){
                        return this.$store.state.user.userName
                    }
                }
                <p>{{userName}}</p>
            方法2:
                import {mapState} from "vuex"
                computed:{
                    ...mapState(["userName"])  //這3種寫法一樣。...表示展開一個物件。
//                    ...mapState({
//                        userName:state=>state.user.userName
//                    })
//                    ...mapState("user",{        //傳入模組名
//                        userName:state=>state.userName
//                    })
                }
                <p>{{userName}}</p>
                
            注意:在模組狀態管理中如果有命令空間,即
                export default{
                    namespaced:true,   //設定名稱空間為true,使得模組更加密閉,不受到外界的干擾
                    state,
                    mutations,
                    actions
                }

           方法3:
                import {createNamespacedHelpers} from "vuex"
                const {mapState}=createNamespacedHelpers("user")    //引數user是命令空間的名稱(模組名,user.js)
                computed:{
                    ...mapState(["userName"])  //這2種寫法一樣。...表示展開一個物件。
//                    ...mapState({              //不許要在傳入模組名了
//                        userName:state=>state.userName  
//                    })
                }
                <p>{{userName}}</p>
    
    11.4 getters.js相當於元件的computed計算屬性,是對state狀態的計算處理。 在模組如user.js中使用getters和使用state方法一樣
        定義getters(計算屬性):
            const getters={
                appNameVersion:(state)=>{    //依賴於state.js
                    return state.appName+"v2.0"
                }
            }
            export default getters
        呼叫getters(計算屬性的結果):
            方法1:
                在某個元件內{{this.$store.getters.appNameVersion}}就可獲取該值了,或通過可在該元件寫入到計算屬性中computed
                computed:{
                    appNameVersion(){
                        return this.$store.getters.appNameVersion
                    }
                }
                <p>{{appNameVersion}}</p>
            方法2:
                import {mapGetters} from "vuex"
                computed:{
                    ...mapGetters(["appNameVersion"])  //這3種寫法一樣。...表示展開一個物件。
//                    ...mapGetters({
//                        userName:state=>state.appNameVersion   
//                    })
                }
                <p>{{appNameVersion}}</p>
    
    11.5 mutations.js修改state狀態引數的值;
        在mutations定義修改state的事件:
            import vue from "vue"
            const mutations={
                set_app_name(state,params){    //state表示store/state.js,params是要修改state狀態中引數的新值,可能是物件,或字串
                    state.appName=params      //引數時字串
            //        state.appName=params.appName    //引數時物件
                },
                set_app_version(state){     //如果stata.js中沒改屬性引數,這個表示給state.js中新增version並賦值v2.0
                    vue.set(state,"version","v2.0")    
                }
                
            }
            export default mutations
        在元件裡呼叫上面定義的事件:
            方法1:
                this.$store.commit("set_app_name","我是state.js裡新修改的appName的值")  //引數是字串
                this.$store.commit("set_app_name",{appName:"我是state.js裡新修改的appName的值"})  //引數是物件
                this.$store.commit({type:"set_app_name",appName:"我是state.js裡新修改的appName的值"})  //引數是物件,且事件也包含在物件裡
            方法2:
                在元件的方法裡
                    import {mapMutations} from "vuex"
                    methods(){
                        ...mapMutations([
                            "set_app_name",
                            "set_app_version"
                        ]),
                        Handle(){
                            this.set_app_version("newAppName");
                            this.set_app_name()
                        }
                    }
        注意:mutations和getters和actions在模組裡面是在模組裡且沒有命令空間限制,
            會預設將模組中的mutations和getters和actions註冊在全域性store檔案下的mutations.js和getters.js和actions.js,如下寫,且不需要傳入模組名,如"user"
                        ...mapMutations([
                            "set_app_name",
                            "set_app_version"
                        ]),    
            但是如果有命令空間限制namespaced:true,   //設定名稱空間為true,使得模組更加密閉,不受到外界的干擾
                        ...mapMutations("user",[    //需要傳入模組
                            "set_app_name",
                            "set_app_version"
                        ]),    
                        

                        
                        
    11.6 action.js非同步修改state.js的狀態值,如通過獲取後臺資料,將state.js的值修改成後臺獲取的資料:
        定義非同步修改狀態值的方法:
            import {getAppName} from "@/api/app.js"
            const action={   //非同步操作狀態的改變,如通過接受介面的api返回的資料,從而改變state.js的狀態值
            //    updateAppName({commit}){   //es6寫法:{commit},相當於func(obj){const commit=obj.commit}
            //        getAppName().then(res=>{
            //            console.log(res)
            ////            const {info:{appName}}=res;   //es6的
            ////            commit("set_app_name",appName);
            //            commit("set_app_name",res.info.appName);   //通過commit關聯mutations.js中的是set_app_name()方法,從而非同步修改state.js狀態值
            //        }).catch(err){
            //            console.log(err)
            //        }
            //    }
                async updateAppName({commit}){   //es8的語法,與上面執行結果一樣,只是將非同步變成同步。
                    try{
                        const {info:{appName}} =await getAppName()
                        commit("set_app_name",appName);
                    }catch(err){
                        console.log(err)
                    }
                }
            
            }
            export default action

        呼叫非同步修改的狀態值的方法:
            方法1:
                import {mapAction} from "vuex"
                    methods(){
                        ...mapAction([
                            "updateAppName"
                        ]),
                        Handle(){                
                            this.updateAppName()
                        }
                    }            
            方法2:
                this.$store.dispatch("updateAppName",val)
    11.7 在模組中定義    action方法        
        user.js        
            const state={}
            const mutations={}
            const actions={
                updateUserName({commit,state,rootState,dispatch}){    //commit用於提交到mutation,state值當前檔案下state,rootState值store資料夾下state.js
                    rootState.appName
                }
            }
            export default{
                state,
                mutations,
                actions
            }    
    11.8 api/app.js模擬後臺介面返回資料:
        export const getAppName=()=>{
            return new Promise((resolve,reject)=>{
                const err=null;
                setTimeout(()=>{  //模擬介面操作請求
                    if(!err) resolve({code:200,info:{appName:"newAppName"}})
                    else reject(err)
                })
            })
        }

12. vuex進階-store外掛----持久本地儲存:
    新建檔案:store/plugin/saveInLocal.js
        //定義一個持久化存在在本地的state狀態管理的外掛,即使每次重新整理,也不會影響修改後的值
        export default state=>{
            console.log('store初始化時執行該函式');
            if(localStorage.state){store.replaceState(JSON.stringify(localStorage.state))}  //如此本地已經儲存了,每次重新整理頁面(即初始化store時),
                                                                                //就用mutation提交後的本地儲存替換提交前的
            store.subscribe((mutation,state)=>{
                console.log("每次有提交mutation時就執行");  //每次提交mutation時,時都將state值儲存在本地
                localStorage.state=JSON.stringify(state);  //state 是一個物件,轉換為json字串,儲存在本地
            })
        }
    在store/index.js中:
        import saveInLocal from './plugin/saveInLocal.js' 
        export default new Vuex.Store({
          state ,   //es6語法相當於state:state
          mutations ,
          getters,
          actions,
          mudules:{
              user
          },
          plugins:[saveInLocal]      //載入外掛
        })
13. store下的嚴格模式:在嚴格模式下,不能直接修改state裡面的值(this.$store.state.user.userName="newName"),需要在mutation提交中修改,否則會報錯(雖然也會修改)
    在store/index.js下:
        export default new Vuex.Store({
          strict:true,    //是否開啟嚴格模式,process.env.NODE_ENV ==="devalopment"    ,在開發環境下是嚴格模式,否則不是
          state ,   //es6語法相當於state:state
          mutations ,
          getters,
          actions,
          mudules:{
              user
          },
          plugins:[saveInLocal]      //載入外掛
        })

14. v-model:如果繫結的屬性不是元件data(){return{} }裡面的屬性,也是state裡面的屬性,就存在問題,因為state裡面的屬性時需要在mutation中修改。
    v-model:的本質就是:在元件上繫結一個屬性傳值,再繫結一個@input事件監聽,當輸入的值有變化時,就直接替換掉原來的值,
    解決雙休繫結state值的方法:
        方法1:<input   :value="stateVal" @input="changeStateVal"/>
        ...mapMutations({"SET_STATE_VALUE"})
        changeStateVal(val){
            this.SET_STATE_VALUE(val)   //在mutations.js中設定:SET_STATE_VALUE(state,val){state.stateVal=val} ,在state.js中設定stateVal:"abc"
        }
        方法2:<input   v-model="stateVal"/>
        ...mapMutations({"SET_STATE_VALUE"})
        computed:{
            stateVal:{   //計算屬性裡stateVal是物件不是方法,有set()和get()方法
                get(){
                    return this.$store.state.stateVal;
                },
                set(val){
                    this.SET_STATE_VALUE(val)
                }
            }
        }
        
15. ajax請求:
    15.1解決跨域問題:
        方法1:在vue.config.js裡面設定跨域代理請求         

devServer:{       //設定跨域,即將本檔案內任何沒有匹配到的靜態檔案,都指向跨域地址 
                     //http://xxx.xxx.x.xxx:30337
   proxy:'http://xxx.xxx.x.xxx:30337'
}

             在傳送axios請求裡執行:            

 axios.post("/login",{
      uname:"Ace",
      upwd:"1234"
}).then(data=>{
    //返回資料的處理
})

        方法二:在後臺設定跨越。

   在後臺(如node的app.js)設定跨域:            

app.all("*",(req,res,next)=>{   //*代表所有請求。
    req.header("Access-Contral-Allow-Origin","*");
    req.header("Access-Contral-Allow-Headers","X-Requested-Width,Content-Type");
    req.header("Access-Contral-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
    next();
})

             在傳送axios請求裡執行:            

axios.post("http://localhost:3000/login",{
    uname:"Ace",
    upwd:"1234"
}).then(data=>{

})

      15.2 封裝axios:
        15.2.1 請求/響應攔截:建立檔案lib/axios.js:

/*
 *  1. 引入axios,建立一個類,封裝新的axios。
 *  2. "@/config/index.js"  export const baseURL=process.env.NODE_ENV==='procution'?'http://127.207.1.460':'' ;
 * */
import axios from "axios"
import iview from "iview"

class HttpRequest{   
	constructor(baseUrl){ //建立constructor方法,設定預設值
		this.baseUrl=baseUrl;
		this.queue={};   //佇列空物件,用於存放所有未請求的url,當佇列為空時,表示所有url請求完,這樣可以只需要一次新增loading載入效果,不需要重複新增loading效果
	}
	getInsideConfig(){  //建立一個方法,配置全域性通用配置如url,methods,headers等,返回一個內部的配置
		const config={
			baseUrl:this.baseUrl,
			headers:{
				'platform':'pc',
				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
			}
		}
		return config
	}
	distroy (url) {
	    delete this.queue[url]
	    if (!Object.keys(this.queue).length) {
	    	iview.Spin.hide()
	    }
	}
	interceptors(instance,url){  //攔截器,引數axios的例項 ,url是用於存放queue空佇列中
		instance.interceptors.request.use(config=>{  //axios請求攔截器,config是請求成功時的處理函式,error是請求出現錯誤時的處理函式 
			//新增請求前的控制,如新增loading....載入效果,如iview的Spin標籤
			if(!Object.keys(this.queue).length){  //如果this.queue佇列所有的Key值組成的陣列為空的情況下,即
				iview.Spin.show()  //顯示iview中的載入動畫效果
			}
			this.queue[url]=true;   //將請求的url放入佇列中
			return config
		},error=>{
			return Promise.reject(error)  //請求失敗時,直接丟擲這個錯誤
		})
		instance.interceptors.response.use(res=>{  //axios響應攔截器,對返回的結果的處理篩選 
			delete this.queue[url]   //響應成功後就刪除該佇列對應的url
			this.distroy(url)
			return res
//			const {data,status}=res   //對響應結果進行篩選,只需要data和status值
//			return {data,status}
		},error=>{
			iview.Spin.hide()
			this.distroy(url)
			delete this.queue[url]   //響應失敗後就刪除該佇列對應的url
			return Promise.reject(error)  //響應失敗時,直接丟擲這個錯誤
		})
	}
	request(options){  //建立一個請求request方法,options是單獨傳入請求的配置 如引數
		const instance=axios.create();  //建立一個axios例項
		options=Object.assign(this.getInsideConfig(),options);    //將內部的配置與傳入的配置合併成新的options物件
		this.interceptors(instance,options.url);   //axios例項為引數的攔截器
		return instance(options);   //返回axios的例項,配置引數是合併後的引數
	}
}

export default HttpRequest

        15.2.2 在config/index.js配置基礎資訊:用於存放專案的配置url。

export const baseUrl=process.env.NODE.ENV==="production"
    ?"http://www.ace.com"   //如果是執行環境,就寫執行環境的介面
    :"" //如果是在開發環境下,且已經設定了本地跨域代理 devServer:{proxy:'http://localhost:4000'},
         //這裡設定空字串,如果沒有設定本地跨域代理這裡就寫'http://localhost:4000'

        15.2.3 在api/index.js例項化axios       

import HttpRequest from "@/lib/axios"
const axios=new HttpRequest()  //建立一個HttpRequest例項
export default axios

        15.2.4 在api/user.js中定義login.vue元件的登入介面:
           


import axios from "./index.js"
import iView from 'iview'


export var getUserInfo=({name})=>{ 
    return axios.request({
        url:"/login",
        methods:"post",
		timeout: 30000,
		data:{
            name:name
        }
    })
}

export var sendAxiosHttp=(baseUrl,sendMethod,timeOut,params,func)=>{  
	axios.request({
		url:baseUrl,
		methods:"post",
		timeout: 30000,
		params:params
	}).then(data=>{		
	if(data.status==200){							
		var dataVal=data.data;
		if(dataVal.returnCode=="0"){	//成功獲取資料						
			func(data)
		}else if(dataVal.returnCode=="-9995"){  //後臺判斷登入超時,重新登入
			var host=window.location.protocol+"//"+window.location.host;
			window.location.href = host + "/login";	//xxx/index.html這是釋出的時候預設的登入頁面
			return;
		}else{			
			iView.Message.info(dataVal.returnMessage)					
		}
	}else{
		iView.Message.info("登入介面,網路故障!")			
	}}).catch( (error)=> {
		alert(error);
	});
}

        15.2.5 在login.vue元件裡引入api/user.js中定義好的登入介面方法,然後呼叫:
           

import {getUserInfo} from "@/api/user.js"
getInfo(){
   getUserInfo({uname:"Ace"}).then(res=>{
        console.log(res)
   })
}


16. 元件的ID命名,插槽的實現,DOM獲取,以及父元件呼叫子元件的方法:    
    子:
        <div>
            <slot name="slot1"></slot>
            <span :id="myId" ref="child"> 我是子元件</span>
            <slot name="slot2"></slot>
        </div>
        
    父:
        <div>
            <v-child ref="childSpan"></v-child>
        </div>    
        
        
    16.1 如何給元件命名id不會衝突其他元件的id名:this._uid在專案中,每個元件都有獨一無二的_uid,給裡面元件命名時,帶上這個獨一無二的_uid就會避免與其他元件命名衝突。
        computed:{
            myId(){
                return `child_${this._uid}`
            }
        }
    16.2 怎麼獲取子元件dom裡面的內容(原生js獲取和ref獲取):
        ref獲取:this.$refs.child.innerText
        原生js獲取:document.getElementById(myId).innerText
    16.3 父元件怎麼呼叫子元件的方法:
        this.$refs.childSpan.getChildInfo()
17. vue中操作dom元素:通過資料操作dom的width/height/top/bottom....
    17.1 通過資料操作dom的width/height/top/bottom....
        <div ref="div" :style={left:divLeft;width:`${divWidth}px` }>
            <span @mousedown="downHandle"></span>
        </div>
        computed:{
            divLeft(){
                return ``
            },
            divWidth(){
                return `clac(30%-4px)`   //css3的屬性clac()計算出百分比和px的結果
            }
        }
    17.2 滑鼠按下移動事件:
        downHandle(e){
            document.addEventListener("mousemove/mouseup",mousemoveHandle/mouseupHanlde) ;  //給doucment繫結滑鼠移動、鬆開事件
            e.pageX   //是獲取觸發downHandle的是滑鼠距離頁面左邊的距離
            this.$refs.div.getBoundingClientRect() ;  //返回ref='div'元素dom的width,height,top,bottom,right....等資訊的物件。        
        }
18. 在元件引入外部css的2種方法:
        在script裡面:import "../index.css"
        在style裡面:@import "../index.css"