vue-cli3+技術棧實戰完整筆記(一)

 

 

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:自定義設置文件的配置,如跨域請求地址,配置後臺(相當於vue-cli3.0以下版本的build/serve-dev-serve.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中設置.slide-enter/leave-to/active樣式*/
<transition-group name="slide">  
    <router-view key="default"/>   
    <router-view key="email"/>
    <router-view key="tel"/>
</transition-group>

<style>
.slide-enter-active{  //動畫執行的時間
	transition: all 0.3s;
}
.slide-enter,.slide-leave-to{  //進入和離開時的動畫
	transform: translate3d(100%,0,0);
}

</style>

    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',然後全局設置:
        

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
  },
  plugins:[saveInLocal]      //加載插件
})

  11.2 state.js全局狀態參數管理: 
        定義全局參數:

const state={
   appName:"我是全局參數,在組件內都可傳遞值"
}
export default state

   調用全局參數值:
            方法1:
                在某個組件內{{this.$store.state.appName}}就可獲取該值了,或通過可在該組件寫入到計算屬性中computed

<p>{{appName}}</p>
computed:{
    appName(){
        return this.$store.state.appName
    }
}

        方法2:
         

<p>{{appName}}</p>

import {mapState} from "vuex"
     computed:{
         ...mapState(["appName"])  //這2種寫法一樣。...表示展開一個對象。
//       ...mapState({
//              appName:state=>state.appName
//       })
}
               


    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

<p>{{userName}}</p>

computed:{
    userName(){
         return this.$store.state.user.userName
    }
}
              

       方法2:

<p>{{userName}}</p>

import {mapState} from "vuex"
computed:{
     ...mapState(["userName"])  //這3種寫法一樣。...表示展開一個對象。
//   ...mapState({
//          userName:state=>state.user.userName
//   })
//   ...mapState("user",{        //傳入模塊名
//          userName:state=>state.userName
//   })
}
              


                
            注意:在模塊狀態管理中如果有命令空間,即
                export default{
                    namespaced:true,   //設置命名空間爲true,使得模塊更加密閉,不受到外界的干擾
                    state,
                    mutations,
                    actions
                }

            方法3:

 <p>{{userName}}</p>

import {createNamespacedHelpers} from "vuex"
const {mapState}=createNamespacedHelpers("user")  //參數user是命令空間的名稱(模塊名,user.js)
computed:{
     ...mapState(["userName"])  //這2種寫法一樣。...表示展開一個對象。
//   ...mapState({              //不許要在傳入模塊名了
//         userName:state=>state.userName  
//    })
}
               

    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             

<p>{{appNameVersion}}</p>

computed:{
    appNameVersion(){
       return this.$store.getters.appNameVersion
    }
}
               

           方法2:
        

<p>{{appNameVersion}}</p>

import {mapGetters} from "vuex"
   computed:{
       ...mapGetters(["appNameVersion"])  //這3種寫法一樣。...表示展開一個對象。
//     ...mapGetters({
//           userName:state=>state.appNameVersion   
//      })
}
                

   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:

mport {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({
     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裏面設置跨域代理請求         

module.exports = {
    lintOnSave:false, //是否校驗eslint
    productionSourceMap:false,  //打包時是否生成.map文件
    outputDir: 'www',  //打包輸出的文件名
    publicPath: './',  //打包輸出的位置
    devServer: {    //跨域代理
        open: true, 
        host: '0.0.0.0',
        port: 8082,
        https: false,
        hotOnly: false, 
        proxy: {  //可設置多個跨域代理
            '/api': {//以/api開始的路由的baseUrl由target:'http://xxx.xxx.x.xxx:8083'代理      
                target: 'http://xxx.xxx.x.xxx:8083',          
                changeOrigin: true,
                ws: false,
                secure: false,
                pathRewrite:{
                    '^/api':''
                }
            },
            '/ace': {//以/ace開始的路由的baseUrl由target:'http://xxx.xxx.x.xxx:123'代理      
                target: 'http://xxx.xxx.x.xxx:123',          
                changeOrigin: true,
                ws: false,
                secure: false,
                pathRewrite:{
                    '^/ace':''
                }
            }
        }
    }
}

             在發送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'元素domwidth,height,top,bottom,right....等信息的對象。        
}


18. 在組件引入外部css的3種方法:
        在script裏面:import "../index.css"
        在style裏面:@import "../index.css"

        在style裏面:@import url("../index.css")

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章