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")

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