Vue学习day5.md

一、脚手架搭建的项目中组件使用

1、定义组件

  • 在components目录下新建xxx.vue文件

  • 在xxx.vue文件下写入代码

   Myhead.vue中
   
   <template>
        <div>
            这个是myhead组件
        </div>
    </template>
    <script>
    export default {
    
    
        
   }
   </script>

2、使用组件

①引入组件,②注入到components,③页面调用

   // 组件模板
   <template>
      <div>
         我的第一个vue项目
         <!-- 3:调用 -->
         <Myhead></Myhead>
      </div>
   </template>
   // js
    <script>
   // 1:引入
   import Myhead  from './components/Myhead'
   export default {
    
    
        components:{
    
      //2:注入
          Myhead
        }
   }
   </script>

二、组件数据通信

父向子传值 子向父传值 非父子传值
1.父组件调用子组件,给子组件自定义属性;并在父组件中定义要传的数据值 1.父组件调用子组件时,给子组件自定义事件 1.main.js中定义一个公共区域 (新的vue实例)
2.子组件通过props接收父组件传来的自定义属性 2.在父组件中的methods中定义自定义事件对应的函数 2.在A组件中给公共区域自定义事件.$on(‘事件名’,function(v){})–自定义接收值
3.子组件视图上直接使用该自定义属性 3.在子组件中定义要传的数据值,并通过this.$emit(‘事件名’,参数)触发自定义事件传值 3.在B组件中触发公共区域的自定义事件.$emit(‘事件名’,参数)–触发传值

1、父向子通信

  • 1.父组件调用子组件,给子组件自定义属性

    vue是单向数据流,子组件不能修改props中的属性,但是在父组件中修改,传到子组件的数据也会跟着变化

   //父组件
   <子组件  自定义属性1=""  自定义属性2=""></子组件>
   
   //绑定属性
   <子组件  :自定义属性1="变量"></子组件>
  • 2.子组件通过props接收父组件传来的自定义属性
   <script>
     export default {
    
    
       props:['自定义属性属性1','自定义属性属性2']
     }
   </script>
  • 3.子组件视图上直接使用该自定义属性
   {
   
   {属性1}}
   <li v-for="item in 属性1"></li>

父组件

   // 组件模板
   <template>
      <div>
       
         <!-- 1  自定义属性 -->
         <Myhead :mycon="num" :mycom="flag" :dataarr="arr[0]"></Myhead>

         <Myhead :dataarr="arr[1]"></Myhead>
     <hr/>
        <Myhead v-for="(item,index) in arr" :key="index" :dataarr="item"></Myhead>
     

      </div>
   </template>
    // js
   <script>
    import Myhead  from './components/Myhead'
    export default {
    
    
        data(){
    
    
            return {
    
    
              flag:true,
              num:25,
              arr:[
                {
    
    
                  title:"title1",
                  list:[
                    '父组件中的数据1',
                    '父组件中的数据11'
                  ]
                },
                {
    
    
                  title:"title1",
                  list:[
                    '父组件中的数据2',
                    '父组件中的数据22'
                  ]
                }
              ]
            }
        },
       components:{
    
      
          Myhead
       }
    }
    </script>
    // css
    <style>
    </style>

子组件

   <template>   
   <div>
         <div class="list">
             <!-- 3:直接使用属性 -->
             {
    
    {
    
    mycon}} ----{
    
    {
    
    mycom}}
             
             <h3>{
    
    {
    
    dataarr.title}}</h3>
             <ul>
                 <li v-for="(item,index) in dataarr.list" :key="index">
                     {
    
    {
    
    item}}
                 </li>
             </ul>
         </div>
      </div>
   </template>
   <script>

   export default {
    
    
      props:['mycon','mycom','dataarr'],  //2:通过props接收父组件传来的数据
      data(){
    
    
          return {
    
    
             arr:[]
          }
      }

   }
   </script>
   <style>
   </style>

props验证

  • 为了使组件内部使用数据的可靠性,我们对外部传递进来的数据进行校验

  • 实现校验- 方式1

   //方式1
   props:['属性1''属性2']
  • 实现校验- 方式2
   //方式2   (String,Number,Boolean,Array,Object Date Function Symbol)
   props:{
    
    
     属性1:类型 ,    //只有类型检测
     属性2[类型1,类型2] 
   }
  • 实现校验- 方式3
   //方式3  
   props:{
    
    
     属性1{
    
    
        type:类型 ,
        default:默认值,
        required:true/false,
        validator:function(val){
    
    
          //validator函数一定要有返回值,返回true表示验证通过,false验证不通过 控制台有警告
        }
     }
     
   }

2、子向父通信

  • 1.父组件调用子组件时,给子组件自定义事件
   //父组件
   <Child @自定义事件名="函数"></Child>
  • 2.在父组件中的methods中定义自定义事件对应的函数
   //父组件
   methods:{
        函数名(){} 
   }
  • 3.在子组件中通过this.$emit(‘事件名’,参数)触发自定义事件
   //子组件
   this.$emit('事件名',参数)

父组件

   <template>
     <div>
      {
   
   {m}}
        <hr/>
        <!-- 1:自定义事件  @abc="函数名" -->
       <Child @abc="fn"></Child>
     </div>
   </template>
  
   <script>
   import Child from './components/Child'
   export default {
     
     
      data(){
     
     
        return {
     
     
          m:""
        }
      },
      components:{
     
     
        Child
      },
      methods:{
     
     
        fn(val){
     
        //2:父组件中定义自定义事件对应的函数fn
          console.log("wo 被触发了")
         //  val就是触发abc事件传递过来的参数
         this.m=val;
        }
      }
   }
   </script>

子组件

   <template>
      <div>
          {
   
   {msg}}
          <button @click="change">触发abc</button>

      </div>
   </template>
   <script>
   export default {
     
     
      data(){
     
     
           return {
     
     
               msg:"子组件==成都"
           }
       },
       created(){
     
     
           // 3:触发自定义事件的
           // this.$emit('事件名',参数)
           // console.log(this)
           // this.$emit('abc',this.msg)
       },
       methods:{
     
     
           change(){
     
     
                this.$emit('abc',this.msg)
            }
      }
   }
   </script>

3、非父子通信

  • 中央事件总线:eventbus
    在这里插入图片描述

需求:非父子组件A和B,从B组件向A组件传值

  • 1.main.js中定义一个公共区域 (新的vue实例)
   // 1:创建公共区域(新的vue实例)

   var eventbus=new Vue();
   
   // 2:公共区域挂载到Vue的原型上面(每一个vue实例都可以访问它原型上的成员)
   
   Vue.prototype.$bus=eventbus;
  • 2.在A组件中给公共区域自定义事件–自定义接收值
   <template>
       <div>
           {
   
   {m}}
       </div>
   </template>
   <script>
   export default {
     
     
       data(){
     
     
           return {
     
     
              m:"123"
           }
       },
       created(){
     
     
           // // console.log(this,111)
           // var that=this;
           // // 公共区域自定义事件--接收值
          // this.$bus.$on('abc',function(v){
     
     
           //     console.log("我被触发了",v)
           //     console.log(this)
           //    that.m=v
           // })
           //--------------
           // console.log(this,111)
          
           // 公共区域自定义事件--接收值
           this.$bus.$on('abc',(v)=>{
     
     
               console.log("我被触发了",v)
               console.log(this)
              this.m=v
           })
       }
   }
   </script>
  • 3.在B组件中触发公共区域的自定义事件–触发传值
   <template>
       <div>
           <button @click="fn">传值</button>
       </div>
   </template>
   <script>
   export default {
    
    
       data(){
    
    
           return {
    
    
              msga:"a向b传值" 
           }
       },
       methods:{
    
    
           fn(){
    
    
               // 触发公共区域的自定义事件传值
              
               this.$bus.$emit('abc',this.msga)
           } 
       }
   }
   </script>

三、ref

ref博客学习链接

  • ref 被用来给元素或子组件注册引用信息
ref 注册在普通dom元素上 ref加载子组件上 ref加载循环渲染
获取到的是dom元素 可以获取或使用组件的所有数据和方法 利用v-for和ref获取一组数组
  • 利用v-for和ref获取一组数组

注意:

1:ref需要在dom渲染完成后才会有,使用的时候确保dom已经渲染完成,比如

mounted钩子函数中可以使用

2:ref如果是循环出来的,ref的值是一个数组,要拿到单个的ref只需要循环就可以了

   <template>
     <div>
         <!--1 ref加在普通的dom元素上  -->
         <h2 ref="myh2">成都</h2>
         <input type="text" ref="myinput" />
         <button @click="fn">获取h2</button>

         <!--2 ref应用在循环渲染 -->
        <ul>
           <li ref="list" v-for="(item,index) in arr" :key="index">
              {
    
    {
    
    item}}
           </li>
        </ul>
        <button @click="fn1">获取li元素</button>

        <!-- 3ref设置在子组件上 -->
        <Myref ref="myref"></Myref>
        <button @click="fn2">获取子组件</button>
     </div>
   </template>

   <script>
        import Myref from './components/Myref'
        export default {
    
    
              data(){
    
    
                   return {
    
    
                      arr:['aaa','bbb','cccc']
                  }
              },
              methods:{
    
    
                   fn(){
    
    
                        // 所有的ref的注册信息都会注册到组件的$refs对象上
                        console.log(this.$refs)
                        console.log(this.$refs.myh2.innerHTML)
                   },
                   fn1(){
    
    
                        // ref注册到循环渲染的元素上,得到的是一个数组[li,li,li]
                        console.log(this.$refs.list[0].innerHTML)
                   }, 
                   fn2(){
    
    
                        console.log(this.$refs.myref.msg)
                        this.$refs.myref.msg="hello  晋老师"
                   }
             },
             components:{
    
    
                     Myref
              }
       }
    </script>

四、vue中使用jquery

外部链接 npm
在index.html中,通过script标签直接引入外部链接 局部使用、全局使用
  • npm
   cnpm install  jquery -D
    
   //局部使用(哪个组件用,哪个组件引用)
   import $ from 'jquery'
   
   //全局使用(所有的组件都可以用)
   //main.js
   import $ from 'jquery'
   Vue.prototype.$=$
   //在每一个组件中使用
   this.$("#box").hide()

五、插槽(Slot)

  • 插槽用于将所携带的内容,插入到指定的某个位置

  • 父组件向子组件传html标签

1、匿名插槽

  • 子组件
   <template>
      <slot></slot>
      <div>我是程序员</div>
   </template>
  • 父组件
   //msg数据在父组件中定义
   <Mychild>
       <p>{
    
    {
    
    msg}}</p>   
   </Mychild>

2、具名插槽

  • 子组件
   <template>
      <slot name="foot">
           默认值
       </slot>
   </template>
  • 父组件
   //msg数据在父组件中定义
   <Mychild>
       <div slot="foot">
         我是插槽foot要的内容
       </div>
   </Mychild>

3、作用域插槽(难)

子组件做循环(循环的结构从父组件传来)或者某一部分他的dom结构应该由外部传递进来的时候,使用作用域插槽

使用作用域插槽,子组件会向父组件的作用域里传递数据,父组件使用插槽,要包含在template中

   v-bind:自定义prop名="数据"    //子组件中传值

   <template  v-slot:插槽名字="props">
       {
    
    {
    
    props.prop名}}
   </template>
  • 子组件
   <template>
        <slot name="one" :msg="msg" :tit="name"></slot>
        <slot name="two"></slot>
   </template>
    <script>
       export default {
    
    
          data(){
    
    
             return {
    
    
                 msg:"nihao",
                 name:"张三"
             }
          }
       }
   </script>
  • 父组件
   //msg数据在父组件中定义
   <Mychild>
       <template v-slot:one="props">
          <div>{
    
    {
    
    props.msg}}---{
    
    {
    
    props.tit}}</div>
       </template>
   </Mychild>
   <script>
       import Mychild from './components/Mychild'
       export default {
    
    
          data(){
    
    
             return {
    
     }
          }
       }
   </script>

六、路由

  • 路由:根据不同的url地址,映射不同的组件内容
  • Vue是单页面应用(SPA):single page application单页面应用,只有一个页面,不断切换内部展示的内容

1、路由(vue-router)安装

  • 1.安装vue-router(进入项目cmd中安装)
   npm/cnpm  i  vue-router --save
  • 2.配置路由(在main.js中)

    ①引入vue-router
    ②给vue安装路由插件
    ③配置—路由映射关系
    ④实例化路由对象
    ⑤把路由对象注入到根实例中

   import Vue from 'vue'
   import App from './App.vue'
   
   // 1:引入vue-router
   import VueRouter from 'vue-router'

   // 引入组件
   import Index from './components/Index'
   import Mya from './components/Mya'

   // 2:给vue安装路由插件,让每一个组件中都可以使用router-link和router-view组件
   Vue.use(VueRouter);
   
   // 3: 路由配置---路由映射关系
   var routes=[
       {
    
    
         path:'/',
         component:Index
   },
   {
    
    
         path:'/mya',
         component:Mya
    }
   ]
   //  4:实例化路由对象
   var router=new VueRouter({
    
    routes:routes})

   Vue.config.productionTip = false

   new Vue({
    
    
        el: '#app',
   // 5:把路由对象注入到根实例中
   // 在每一个组件中都可以使用this.$router和this.$route
        router:router,
        components: {
    
     App },
        template: '<App/>'
   })
  • 3.在组件中使用router-link 和router-view组件
   <router-link to="/about">关于我们</router-link>
    渲染成   <a href="/about">关于我们</a>
   <router-view></router-view>
   路由出口   网址上访问  /about  router-view的位置上映射该路由对应的组件

2、路由的封装

  • 1.src目录下新建router/index.js, 在index.js中写入如下代码
   import Vue from 'vue'
   // 1:引入vue-router
   import VueRouter from 'vue-router'

   // 2:给vue安装路由插件,让每一个组件中都可以使用router-link和router-view组件
   Vue.use(VueRouter);


   // 引入组件
   import Index from '../components/Index'
   import Mya from '../components/Mya'
   import Myb from '../components/Myb'

   // /:3: 路由配置---路由映射关系
   var routes=[
       {
    
    
         path:'/',
         component:Index
    },
    {
    
    
         path:'/mya',
         component:Mya
    },
    {
    
    
         path:'/myb',
         component:Myb
    }
    ]
  
    //  4:实例化路由对象
    var router=new VueRouter({
    
    routes:routes})

    //   导出router,vuerouter实例
    export default router;
  • 2.在main.js中导入router
   // 把路由实例导入
   import  router from './router'
   new Vue({
    
    
       el: '#app',
       // 5:把路由对象注入到根实例中
       // 在每一个组件中都可以使用this.$router和this.$route
       // router:router,
       router,
       components: {
    
     App },
       template: '<App/>'
})
  • 3.组件中使用router-link和router-view组件即可

3、路由重定向–404配置

  • redirect
   var routes=[
         {
    
    
           path:'/',
           component:Index
    },
    {
    
    
           path:'*',     //*是所有的,上面没有匹配到就都渲染这里
           // redirect:'/'   //redirect重定向   
           // 还可以映射到到一个组件中404
           component:Notfound    
    }
   ]

4、路由懒加载

  • component:()=>import(’’)
   var routes=[
       {
    
    
         path:'/',
         component:()=>import('../components/Index')
    },
    {
    
    
         path:'/mya',
         component:()=>import('../components/Mya')
    },
    {
    
    
         path:'/myb',
         component:()=>import('../components/Myb')
    },
    {
    
    
         path:'*',     //*是所有的,上面没有匹配到就都渲染这里
         // redirect:'/'   //redirect重定向   
         // 还可以映射到到一个组件中404
         component:()=>import('../components/Notfound')    
    }
   ]

5、编程式导航

  • 声明式导航:html中的router-link组件实现页面跳转

  • 编程式导航:通过js代码实现页面跳转

   向history历史记录中添加记录
   
   <router-link to="/"></router-link>
   
   this.$router.push('/mya')
   this.$router.push({
    
    path:'/mya'})
   this.$router.push({
    
    path:'prodetail',query:{
    
    id:1}})    //只能是query数据
   this.$router.push({
    
    name:'detail',params:{
    
    newsid:2}})   //对应params数据
   不会在history中添加记录
   this.$router.replace('/mya')
   历史记录的前进及后退
   this.$router.go(-1)  //后退
   this.$router.go(1)  //前进

6、动态路由

动态路由网址如:http://www.abc.com/newsdetial/102, 那么怎么配置这样的路由呢?

  • newsdetial:表示新闻详情路由
  • 102 表示新闻id

1.路由规则页面

   var routes=[{
    
    
       path:'/detail/:newsid',    //   匹配detail/5
       component:()=>import('../components/Detail')
    }]

2.从某组件中跳转到Detail组件中

   <router-link to="/detail/1"></router-link>

3.Detail.vue组件中获取动态参数

   this.$route.params.newsid
  • 点击首页新闻–跳转到对应新闻的详情页
   <template>
      <div>index这是首页
        <ul >
            <li v-for="(item,index) of list" :key="index"><router-link  :to="'/detail/'+(index+1)">{
    
    {
    
    item}} </router-link></li>
            
        </ul>
      </div>
   </template>

   <script>
   export default {
    
    
       data(){
    
    
           return{
    
    
               list:['新闻1','新闻2','新闻3'],
           }
        }
    }
    </script>
  • 进入对应新闻的详情页–要获取动态id–确定是哪条新闻
   <template>
      <div>
          这是新闻页{
    
    {
    
     obj.title }}
      </div>
   </template>
   <script>
   export default {
    
    
       data() {
    
    
            return {
    
    
               list: [{
    
    
                       id: 1,
                       title: "新闻1详情",
               },
              {
    
            id: 2,
                       title: "新闻2详情",
              },
              {
    
    
                       id: 3,
                       title: "新闻3详情",
              },
             ],
           obj: "", //展示具体的新闻内容--根据动态id
       };
     },
     created() {
    
    
           console.log(this.$route.params.newsid);
           var num = this.$route.params.newsid;
           var index = this.list.findIndex((item, index) => {
    
    
                     return item.id == num;
           });
           this.obj = this.list[index];
        },
    };
   </script>

7、路由参数

  • 路由中传递 参数除了上文params外,还可以使用query数据
    如:http://www.xx.com/p?id=8

1.路由规则页面

   var routes=[
      {
    
    
        path:'/detail',    //  匹配 detail---直接匹配
        component:()=>import('../components/Detail')
      }
   ]

2.从某组件中跳转到Prodetail组件中

   <router-link to="/detail?newsid=8"> 
   
   //传入动态id
   <router-link :to="'/detail?newsid='+item.id">

3.Prodetail.vue组件中获取路由参数

   this.$route.query.newsid
  • 点击首页新闻–跳转到对应新闻的详情页
   <template>
      <div>index这是首页
        <ul >
            <li v-for="(item,index) of list" :key="index"><router-link  :to="'/detail?newsid='+(index+1)">{
    
    {
    
    item}} </router-link></li>
            
        </ul>
      </div>
   </template>

   <script>
   export default {
    
    
       data(){
    
    
           return{
    
    
               list:['新闻1','新闻2','新闻3'],
           }
        }
    }
    </script>
  • 进入对应新闻的详情页–要获取动态id–确定是哪条新闻
   <template>
      <div>
          这是新闻页{
    
    {
    
     obj.title }}
      </div>
   </template>
   <script>
   export default {
    
    
       data() {
    
    
            return {
    
    
               list: [{
    
    
                       id: 1,
                       title: "新闻1详情",
               },
              {
    
            id: 2,
                       title: "新闻2详情",
              },
              {
    
    
                       id: 3,
                       title: "新闻3详情",
              },
             ],
           obj: "", //展示具体的新闻内容--根据动态id
       };
     },
     created() {
    
    
           console.log(this.$route.query.newsid);
           var num = this.$route.query.newsid;
           var index = this.list.findIndex((item, index) => {
    
    
                     return item.id == num;
           });
           this.obj = this.list[index];
        },
    };
   </script>
  • 设置动态路由,路由参数params(detial/102)和query(detail?newsid=3)方式对比
params query
路由规划: path:’/detail/:newsid’ path:’/detai’
传入动态id 在router-link :to="’/detail/’+item.id" 在router-link :to="’/detail?newsid=’+item.id"
获取动态参数 this.$route.params.newsid this.$route.query.newsid

8、嵌套路由

  • 路由配置
    给Myb组件配置二级路由
   var routes=[
        {
    
    
         path:'/myb',
         component:()=>import('../components/Myb'),
         children:[
            {
    
    
             path:'/myb/aa',
             component:()=>import('../components/Myaa'),
            },
            {
    
    
             path:'/myb/bb',
             component:()=>import('../components/Mybb'),
            }
         ]
      },
   ]
  • 给谁设置二级路由,就在他的组件里写router-link和router-view
    (例子在 Myb组件中)
   <template>
       <div>
      
           <router-link to="/myb/aa">aa</router-link>
           <router-link to="/myb/bb">bb</router-link>

        <div class="box">
            <router-view></router-view>
        </div>

       </div>
   </template>

9、路由进阶

  • 1.路由模式

    hash模式 # 默认模式

    history模式

   var router=new VueRouter(
       {
    
      routes,
          mode:'history'     //去掉#
       }
   )
  • 2.命名路由
   var routes=[
       {
    
    
         path:'/mya',
         name:'minea',   //命名路由
         component:()=>import('../components/Mya')
       }
   ]
  • 3.路由跳转
   //1种
   <router-link :to="{name:'detail',params:{newsid:item.id}}">
                xxxxxx
   </router-link>
   
   //2种
   <router-link :to="{name:'minea'}">
            Mya
   </router-link>
 
   //3种
   this.$router.push({
    
    name:'detail',params:{
    
    newsid:2}})
  • 4.命名视图
    一个组件里有多个router-view时,我们要给router-view定义name属性
   <router-link to="/myb/bb">bb</router-link>
          <!-- 默认的 -->
   <router-view></router-view>
   
   <div class="box">
         <router-view name="a"></router-view>
   </div>
   
   <router-view name="b"></router-view>
   //路由规则中配置
   var routes=[
   {
    
    
      path:'/myb',
      component:()=>import('../components/Myb'),
      children:[
         {
    
    
          path:'/myb/aa',
          // component:()=>import('../components/Myaa'),
          components:{
    
    
             default:()=>import('../components/Myaa'),
              a:()=>import('../components/Login'),
              b:()=>import('../components/Mya')
           }
         },
         {
    
    
          path:'/myb/bb',
          component:()=>import('../components/Mybb'),
         }
      ] 
     }
   ]

七、路由守卫

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航

全局守卫 路由独享守卫 组件内的守卫
守卫所有的路由—进入每一个路由之前执行 写在哪个路由上,守卫的就是那个路由 在路由组件内直接定义路由导航守卫

1、全局守卫

  • 守卫所有的路由—进入每一个路由之前执行
  • main.js中实现(还可以把该全局守卫放到router/index.js文件中)
   import  router from './router'
   router.beforeEach((to, from, next) => {
    
    
   //商城项目进入购物车页面要先进行判断 ,登录成功next()登录不成功next('/login')
   console.log(to)
   // console.log(to.path,to.name);
   if(to.path=='/mine'){
    
    
       next('/login')
    }else{
    
    
        next()
    }
    // next()
   })

to:即将要进入的目标 路由对象
from:当前导航正要离开的路由
next:一定要调用该方法来 resolve 这个钩子 进入导航

2、路由独享守卫

  • 写在哪个路由上,守卫的就是那个路由
  • 可以在路由配置上直接定义 beforeEnter 守卫
   var routes=[
       {
    
    
        path:'/mine',
        component:()=>import('../components/view/Mine'),
        beforeEnter(to,from,next){
    
    
          // console.log(to)
          next();
        }
      }
   ]

3、组件内的守卫

  • 在路由组件内直接定义以下路由导航守卫
    beforeRouteEnter
    beforeRouteUpdate (2.2 新增)
    beforeRouteLeave
   <script>
   export default {
    
    
      beforeRouteEnter (to, from, next) {
    
      //进入路由之前
         console.log("进入路由之前")
         console.log(this) ;   //undefined
         next()
         // 在渲染该组件的对应路由被 confirm 前调用
         // 不!能!获取组件实例 `this`
        // 因为当守卫执行前,组件实例还没被创建
        // 你可以通过传一个回调给 next来访问组件实例
         next(vm => {
    
    
         // 通过 `vm` 访问组件实例
         console.log(vm)
      })
     },
      beforeRouteUpdate (to, from, next) {
    
    
         console.log("我被调用了")
        // 在当前路由改变,但是该组件被复用时调用
        // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
       // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
     },
      beforeRouteLeave (to, from, next) {
    
    
         // 导航离开该组件的对应路由时调用
        // 可以访问组件实例 `this`
        console.log("我离开了")
        next()
      }
   }
   </script>

八、vuex-状态管理

  • 解决的复杂组件之间的数据通信问题

  • vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享,它采用集中式存储管理应用的所有组件的状态

优点

能够在vuex中集中管理共享的数据,易于开发和后期维护
能够高效的实现组件之间的数据共享,提高开发效率
存储在vuex中的数据是响应式的,能够实时保持数据和页面的同步

1、vuex的使用

  • 1.安装vuex的依赖包
   cnpm install vuex --save
  • 2.导入vuex包(src目录下新建store目录,store目录下新建index.js文件)
  • 3.创建store对象
   //index.js

   import Vue from 'vue'
   //1: 导入vuex包
   import Vuex from 'vuex'
   Vue.use(Vuex)

   // 2创建store对象
   export default new Vuex.Store({
    
    
    
   })
  • 4.将store对象挂载到vue实例中
   import store from './store'
   new Vue({
    
    
       el: '#app',
       store,
       components: {
    
     App },
       template: '<App/>'
   })

2、vuex的核心概念

state mutation action getter
存放数据 变更数据 处理异步 获取数据
this.$store.state.data this.$store.commit(‘add’) this.$store.dispatch(‘addAsync’) this.$store.getters.getcount
…mapState([‘data’]) …mapMutations([‘add’,‘addN’]) …mapActions([‘addAsync’,‘addNAsync’]) …mapGetters([‘getcount’]),

state

  • state提供唯一的公共数据源,所有共享的数据都要统一放到Store的state中进行存储
   export default new Vuex.Store({
    
    
     //store数据源,提供唯一公共数据
     state:{
    
    
       count:20
     }
   })
  • 组件访问state中数据方式
this.$store.state.全局数据名称 从vuex中按需导入mapState函数

1.方式一: this.$store.state.全局数据名称

   <template>
     <div>
       count的值是{
    
    {
    
    $store.state.count}}
     </div>
   </template>
   
   <script>
   export default {
    
    
     mounted(){
    
    
       console.log(this.$store.state.count)
     }
   }
   </script>

2.方式二:从vuex中按需导入mapState函数

   // 1:从vuex中按需导入mapState函数
   import {
    
    mapState} from 'vuex'
   computed:{
    
    
        // 2将全局数据,映射为当前组件的计算属性
        ...mapState(['count'])
    }
   <template>
     <div>
       {
    
    {
    
    count}}
     </div>
    </template>
    
    <script>
    // 1:从vuex中按需导入mapState函数
    import {
    
    mapState} from 'vuex'
    export default {
    
    
      computed:{
    
    
        // 2将全局数据,映射为当前组件的计算属性
        ...mapState(['count'])
        // 等同于
        // count(){
    
    
        //     return this.$store.state.count;
        // }
      }
    }
   .
   </script>

mutation

  • mutation用于变更store中的数据

    • 只能通过mutation变更store中的数据,不可以直接操作store中的数据
    • 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化
  • 定义mutation

   export default new Vuex.Store({
    
    
     state:{
    
    
       count:20
     },
     mutations:{
    
      //定义mutation 变更store中的数据
        add(state){
    
    
         //变更状态
          state.count++;
       }
     }    
   })
  • 组件中触发mutation中的方法(第一种):this.$store.commit(‘add’)
   <template>
     <div>
       count的值是{
    
    {
    
    $store.state.count}}
       <button @click="add">自增1</button>
     </div>
   </template>
   <script>
    export default {
    
    
      mounted(){
    
    
        console.log(this.$store.state.count)
      },
      methods:{
    
    
        add(){
    
    
        //    触发mutation
         this.$store.commit('add')
         } 
       }
      }
   </script>
  • 组件中触发mutation中的方法(第二种):将指定的mutations函数,映射为当前组件的methods函数
   <template>
     <div>
       {
    
    {
    
    count}}
       <button @click="add">自增1</button>
     </div>
   </template>
   <script>
    // 1:从vuex中按需导入mapState,mapMutations函数
    import {
    
    mapState,mapMutations} from 'vuex'
    export default {
    
    
      computed:{
    
    
        // 2将全局数据,映射为当前组件的计算属性
        ...mapState(['count'])
        // 等同于
        // count(){
    
    
        //     return this.$store.state.count;
        // }
      },
      methods:{
    
    
        //将指定的mutations函数,映射为当前组件的methods函数
        ...mapMutations(['add'])
      }
    }
   </script>
  • 触发mutations时传递参数
    • 定义传参的mutation
   export default new Vuex.Store({
    
    
     state:{
    
    
       count:20
     },
     mutations:{
    
      //变更store中的数据   step形参
       addN(state,step){
    
    
           state.count +=step
       }
     }    
   })
  • 组件中调用并传参(第一种):this.$store.commit(‘addN’,实参)
   //传递多个值用对象传递
   this.$store.commit('addN',实参)

   //代码
   <template>
     <div>
       count的值是{
    
    {
    
    $store.state.count}}
       <button @click="add">自增1</button>
       <button @click="addN(8)">自增N</button>
     </div>
   </template>
   <script>
    export default {
    
    
      mounted(){
    
    
        console.log(this.$store.state.count)
      },
      methods:{
    
    
        add(){
    
    
             //    触发mutation
             this.$store.commit('add')
        },
        addN(n){
    
    
             //    触发mutation传递参数,传多个写在对象里面
             this.$store.commit('addN',n)
       }
      }
    }
   </script>
  • 组件中调用并传参(第二种)
   <template>
     <div>
       {
    
    {
    
    count}}
       <button @click="add">自增1</button>
       <button @click="addN(9)">自增N</button>
     </div>
   </template>
   <script>
   // 1:从vuex中按需导入mapState,mapMutations函数
   import {
    
    mapState,mapMutations} from 'vuex'
   export default {
    
    
     computed:{
    
    
        // 2将全局数据,映射为当前组件的计算属性
        ...mapState(['count'])
        // 等同于
        // count(){
    
    
        //     return this.$store.state.count;
        // }
     },
     methods:{
    
    
        //将指定的mutations函数,映射为当前组件的methods函数
        ...mapMutations(['add','addN'])
     }
   }
   </script>

action

  • action用于处理异步任务,和逻辑操作
  • 如果通过异步操作或者执行逻辑操作变更数据,必须通过action,而不能使用mutation,但是在action中还是通过触发mutation的方式间接变更数据
  • 定义action
   const store=new Vuex.Store({
    
    
     state:{
    
    },
     mutations:{
    
    },
     actions:{
    
    
        addAsync(context){
    
    
            setTimeout(()=>{
    
    
              //在actions中,不能直接修改state中的数据
              //必须通过context.commit()触发某个mutation才行
              context.commit('add')
            },1000)
        }
     }
   })

   //代码
   import Vue from 'vue'
   //1: 导入vuex包
   import Vuex from 'vuex'
   Vue.use(Vuex)

   // 2创建store对象
   export default new Vuex.Store({
    
    
     //store数据源,提供唯一公共数据
     state:{
    
    
       count:20
     },
     mutations:{
    
      //变更store中的数据
       // 只有mutation中定义的函数,才有权利修改state中的数据
       add(state){
    
    
         state.count++;
        // 不要在mutations函数中执行异步操作
        // setTimeout(()=>{
    
    
        //     state.count++;
        // },1000)

       },
       addN(state,step){
    
    
           state.count +=step
       }
     },
     actions:{
    
    
        addAsync(context){
    
    
          setTimeout(()=>{
    
    
             //在actions中,不能直接修改state中的数据
             //必须通过context.commit()触发某个mutation才行
             context.commit('add')
          },1000)
        }
      }
   })
  • 组件中访问action中的函数(第一种):this.$store.dispatch(‘函数名’)
   <template>
     <div>
       count的值是{
    
    {
    
    $store.state.count}}
       <button @click="add">自增1</button>
       <button @click="addN(8)">自增N</button>

       <button @click="addAsync">自增1+async</button>
     </div>
   </template>
   <script>
   export default {
    
    
     mounted(){
    
    
        console.log(this.$store.state.count)
     },
     methods:{
    
    
       add(){
    
    
        //    触发mutation
             this.$store.commit('add')
       },
       addN(n){
    
    
        //    触发mutation传递参数,传多个写在对象里面
        this.$store.commit('addN',n)
       },
       addAsync(){
    
    
        //    触发action中的函数
        this.$store.dispatch('addAsync')
       }
     }
   }
   </script>
  • 组件中访问action中的函数(第二种):从vuex中按需导入mapActions函数
   // 1:从vuex中按需导入mapActions函数
   import {
    
    mapState,mapMutations,mapActions} from 'vuex'
   //将指定的actions函数,映射为当前组件的methods函数
   methods:{
    
    
    ...mapActions(['addAsync'])
   }
    
   //代码

   <template>
      <div>
         {
    
    {
    
    count}}
         <button @click="add">自增1</button>
         <button @click="addN(9)">自增N</button>

         <button @click="addAsync">自增1+async</button>
      </div>
   </template>
   <script>
   // 1:从vuex中按需导入mapState,mapMutations函数
   import {
    
    mapState,mapMutations,mapActions} from 'vuex'
   export default {
    
    
     computed:{
    
    
        // 2将全局数据,映射为当前组件的计算属性
        ...mapState(['count'])
        // 等同于
        // count(){
    
    
        //     return this.$store.state.count;
        // }
      },
      methods:{
    
    
        //将指定的mutations函数,映射为当前组件的methods函数
        ...mapMutations(['add','addN']),
        ...mapActions(['addAsync'])
      }
   }
   </script>   
  • 触发action异步任务时携带参数
  • 定义action
   const store=new Vuex.Store({
    
    
     state:{
    
    },
     mutations:{
    
    },
     addNAsync(context,step){
    
    
            setTimeout(()=>{
    
    
                //在actions中,不能直接修改state中的数据
                //必须通过context.commit()触发某个mutation才行
                context.commit('addN',step)
              },1000)
        }
     })

    //代码
    import Vue from 'vue'
    //1: 导入vuex包
    import Vuex from 'vuex'
    Vue.use(Vuex)

    // 2创建store对象
    export default new Vuex.Store({
    
    
      //store数据源,提供唯一公共数据
      state:{
    
    
        count:20
      },
      mutations:{
    
      //变更store中的数据
        // 只有mutation中定义的函数,才有权利修改state中的数据
       add(state){
    
    
         state.count++;
        // 不要在mutations函数中执行异步操作
        // setTimeout(()=>{
    
    
        //     state.count++;
        // },1000)

         },
         addN(state,step){
    
    
           state.count +=step
         }
       },
       actions:{
    
    
         addAsync(context){
    
    
            setTimeout(()=>{
    
    
              //在actions中,不能直接修改state中的数据
              //必须通过context.commit()触发某个mutation才行
              context.commit('add')
            },1000)
         },
         addNAsync(context,step){
    
    
            setTimeout(()=>{
    
    
                //在actions中,不能直接修改state中的数据
                //必须通过context.commit()触发某个mutation才行
                context.commit('addN',step)
              },1000)
         }
     }
   })
  • 组件中访问action中的函数(第一种)
   <template>
     <div>
       count的值是{
    
    {
    
    $store.state.count}}
       <button @click="add">自增1</button>
       <button @click="addN(8)">自增N</button>

       <button @click="addAsync">自增1+async</button>
       <button @click="addNAsync(5)">自增N+async</button>
     </div>
   </template>
   <script>
   export default {
    
    
     mounted(){
    
    
        console.log(this.$store.state.count)
     },
     methods:{
    
    
       add(){
    
    
             //    触发mutation
             this.$store.commit('add')
       },
       addN(n){
    
    
             //    触发mutation传递参数,传多个写在对象里面
             this.$store.commit('addN',n)
       },
       addAsync(){
    
    
             //    触发action中的函数
             this.$store.dispatch('addAsync')
       },
       addNAsync(n){
    
    
           this.$store.dispatch('addNAsync',n)
       }
     }
   }
   </script>
  • 组件中访问action中的函数(第2种)
   <template>
     <div>
       {
    
    {
    
    count}}
       <button @click="add">自增1</button>
       <button @click="addN(9)">自增N</button>

        <button @click="addAsync">自增1+async</button>

        <button @click="addNAsync(3)">自增N+async</button>
      </div>
   </template>
   <script>
   // 1:从vuex中按需导入mapState,mapMutations函数
   import {
    
    mapState,mapMutations,mapActions} from 'vuex'
   export default {
    
    
     computed:{
    
    
        // 2将全局数据,映射为当前组件的计算属性
        ...mapState(['count'])
        // 等同于
        // count(){
    
    
        //     return this.$store.state.count;
        // }
     },
     methods:{
    
    
        //将指定的mutations函数,映射为当前组件的methods函数
        ...mapMutations(['add','addN']),
        ...mapActions(['addAsync','addNAsync'])
     }
   }
   </script>

getter

  • getter用于对store中的数据进行加工处理形成新的数据,类似于vue中的计算属性

  • store中数据发生变化,getter的数据也会跟着变化

  • 定义getter

   export default new Vuex.Store({
    
    
     getters:{
    
    
        getcount:state=>{
    
    
            return '当前最新的数量是【'+ state.count +'】';
        }
     }
   })
  • 使用getters的第一种方式
   {
    
    {
    
    $store.getters.getcount}}
  • 使用getters的第二种方式
   import {
    
    mapGetters} from 'vuex'
     computed:{
    
    
            ...mapGetters(['getcount']),
   }

九、axios

  • 易用、简洁且高效基于promise的http库,可以用于浏览器和node.js
  • axios官网
  • 慕课网学习视频
  • axios特性:支持node端和浏览器端,支持Promise,丰富的配置项
从浏览器中创建 XMLHttpRequests
从 node.js 创建 http 请求
支持 Promise API
拦截请求和响应
转换请求数据和响应数据
取消请求
自动转换Json数据
客户端支持防御XSRF

1、axios使用

   //安装
   cnpm  install  axios  -S
   //每个组件里单独引入
   import axios from axios

2、常用的方法

get方法

  • axios.get(请求地址,配置对象).then(res=>{ }).catch(err=>{})
  //不传参数
  axios.get('url').then(res=>{
    
    }).catch(err=>{
    
    })

  //传参数 第一种
  axios.get('url?id=5&name=张三').then(res=>{
    
    }).catch(err=>{
    
    })

  //传参数 第二种
  axios.get('url',{
    
    params:{
    
    参数1:值,参数2:值}})
       .then(res=>{
    
    })
       .catch(err=>{
    
    })

post方法

   axios.post('/user')
        .then((res) =>{
    
    console.log(res);})
        .catch((error)=> {
    
    console.log(error);});

   axios.post('/user', {
    
    参数1:,参数2:})
        .then((res) =>{
    
    console.log(response);})
        .catch((error)=> {
    
    console.log(error);});

all方法

  • axios.all([函数1(),函数2(),函数3()]) 一个函数返回一个结果,多个函数返回多个结果
  • 可以实现发送多次请求,请求成功之后再做处理
  • 在项目开发中,一个页面的数据需要请求多个接口地址,那我们就执行多并发请求
   //执行多并发请求
   // axios.all([函数1(),函数2(),函数3()]) 一个函数返回一个结果,多个函数返回多个结果
   // axios.spread((结果1,结果2,结果3)=>{}) 去获取每个函数返回的结果
   // 语法:
   axios.all([函数1(),函数2(),函数3()])
        .then(axios.spread((结果1,结果2,结果3)=>{
    
    }))

3、axios Api

  • 可以通过向 axios 传递相关配置来创建请求
   axios({
    
    配置对象})
   axios({
    
    
     url:"地址",
     method: 'post',   //请求方式
     data: {
    
          //传递的参数
       参数1:,
       参数2:}
   })
   .then(res=>{
    
       //res是axios返回的请求对象  res.data才是后端返回的数据})
   .catch(err=>{
    
    console.log(err)})

4、axios的create方法

  • create方法不是用来请求数据的,而是用来创建一个axios实例对象,定义一些初始化配置
   let $axios=axios.create({
    
    配置对象})

   let $axios=axios.create({
    
    
     baseURL:'http://xxxx.com/aaa',
     timeout:3000  //请求时长
   })

   $axios.get('/getuser',{
    
    params:{
    
    参数1:值}})
         .then(res=>{
    
    })
         .catch(err=>{
    
    })

5、拦截器

  • 拦截器 :发送请求之前做一些事情,返回数据之前做一些事情
   // 请求拦截器   修改请求头 【在请求头上加token】
   $axios.interceptors.request.use(function (config) {
    
    
     console.log(55555)
     // 请求发送之前做一些事情
     return config;  //一定要return config 请求无法发送
   }, function (error) {
    
    
     // 对请求错误做些什么
     return Promise.reject(error);
   });

   // 响应拦截器   返回数据的校验  状态是否正确  信息的提取
   $axios.interceptors.response.use(function (response) {
    
    
     // 对响应数据做点什么
     return response;
   }, function (error) {
    
    
     // 对响应错误做点什么
     return Promise.reject(error);
   });

6、axios的封装

  • 1.src目录下面新建http目录,目录下新建index.js文件
   import axios from 'axios'

   //初始化配置
   const $axios=axios.create({
    
    
     baseURL:'http://localhost:4000',
     timeout:3000
   })


   // 请求拦截器   修改请求头 【在请求头上加token】
   $axios.interceptors.request.use(function (config) {
    
    
   
      // 请求发送之前做一些事情
      return config;  //一定要return config 请求无法发送
   }, function (error) {
    
    
      // 对请求错误做些什么
      return Promise.reject(error);
   });

   // 响应拦截器   返回数据的校验  状态是否正确  信息的提取
   $axios.interceptors.response.use(function (response) {
    
    
     // 对响应数据做点什么
     return response;
   }, function (error) {
    
    
     // 对响应错误做点什么
     return Promise.reject(error);
   });

   export default $axios;
  • 2.main.js文件中引入
   import $axios from './http'
   Vue.prototype.$axios=$axios
  • 3.每一个组件中使用
   this.$axios.get(url).then(res=>{
    
    })
   this.$axios.post(url).then(res=>{
    
    })
   //this.$axios没有all方法    要使用all方法需要导入import axios from 'axios'  再使用  axios.all

十、jsonserver模拟数据

  • 安装
   npm install json-server -g
  • 新建一个文件夹myjson,在该文件夹下面新建data.json文件
   {
    
    
     "arr":[
        {
    
    
         "id":1,
         "title":"111111111"
        },
        {
    
    
          "id":2,
          "title":"222222"
         },
         {
    
    
          "id":3,
          "title":"33333333"
         }
      ],
      "user":[
         {
    
    
          "id":1,
          "name":"张三"
        }
      ]
    }
  • 执行命令行
   //在文件夹中进入cmd
   json-server -w data.json -p 4000

十一、跨域问题

  • 因为浏览器的同源策略,不能访问其他网站的资源
  • 同源策略:协议相同,域名相同,端口号相同
  • 解决方法
jsonp script的src属性
iframe
cors 服务端
后端允许跨域

vue里面如何解决跨域问题

  • webpack配置跨域代理 —生产环境不存在
  • config文件下面的index.js 中找 proxyTable
   proxyTable: {
    
       // 【设置代理!!!】
     '/api': {
    
      //使用"/地址名"来代替"请求网址" 
      target: 'http://localhost:3000/',   //源地址 
      changeOrigin: true,  //改变源 
      pathRewrite: {
    
      // 路径重写
         '^/api': '' // 当请求的路径开头有  /地址名  的时候就替换成请求网址
      }
   } 
  • 组件中请求要修改链接,配置文件更改后一定要重新启动项目
   axios.get('/api/xxx/yyy').then(res=>{
    
    })

十二、Vant

   //安装
   cnpm i vant -S
  • 引入 main.js
   import Vant from 'vant';
   import 'vant/lib/index.css';
   Vue.use(Vant);

十三、token

  • token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码是否正确,并作出相应的提示,这时就有了token
  • token是服务端生成的一串字符串,来作为客户端进行请求的一个令牌,当第一次登陆后服务端生成一个token然后将token返回给客户端,以后客户端只需要带着这个token去请求数据就可以了,不需要再次带上用户名和密码
  • 使用token的目的:减轻服务器的压力,减少了频繁的查询数据库
  • 工作原理
    在这里插入图片描述

十四、项目打包

  • vue 项目中执行命令
   npm run build
  • 根目录下面生成一个dist目录,把dist目录交给后端,但是这一次我们把dist目录复制到接口目录下,打开app.js文件,添加语句
   app.use(express.static('dist'))

浏览器打开http://localhost:3000/login就可以访问项目了

十五、vue-cli脚手架升级

1、 如果安装旧版本 卸载

   npm uninstall vue-cli -g
  • 安装
   npm install @vue/cli -g
  • 查看版本
   vue -V
   or
   vue --version

2、初始化项目

   vue create 项目名
  • 启动项目
   yarn serve

3、跨域配置

  • 在vue的项目根目录下新建vue.config.js文件(一定是vue.config.js)
   module.exports={
    
    
     devServer:{
    
    
        proxy:{
    
    
            '/api': {
    
      //使用"/地址名"来代替"请求网址" 
            target: 'http://localhost:3000/',   //源地址 
            changeOrigin: true,  //改变源 
            pathRewrite: {
    
      // 路径重写
              '^/api': '' // 当请求的路径开头有  /地址名  的时候就替换成请求网址
            } 
        }
        }
     }
   }

十六、过滤器

文本格式化—内容显示

  • 局部定义
   new Vue({
    
    
     filters:{
    
      //局部定义过滤器  val
       名字:function(val){
    
    
         return val+"111"
       }
     }showprice(val,zk){
    
    
       if(!val){
    
    
         return;
       }
       return val*zk/10;
     }
   })

   //定义过滤器可以传其他参数
  • 使用过滤器
   {
    
    {
    
     price | 过滤器的名字 }}    //price 作为过滤器的第一个参数
   {
    
    {
    
     price | 过滤器的名字(第二个参数) }} 
  • 全局定义(main.js中定义)
   // Vue.filter(过滤器的名字,()=>{
    
    

   // })


   Vue.filter('isTrue',function(val){
    
    
      if(val==1){
    
    
         return true
      }else{
    
    
         return false;
      }
   })
  • 组件中使用
   {
    
    {
    
     price | 过滤器的名字 }}    //price 作为过滤器的第一个参数
  • filters的封装
    1.src目录下新建filters/index.js文件
   const isNullOrEmpty=function(val){
    
    
        if(val==null || val=="" ||typeof(val)==undefined){
    
    
            return "hello"
        }else{
    
    
            return "nihao"
        }
    }
    const isTrue=function(val){
    
    
        if(val==1){
    
    
                 return true
               }else{
    
    
                 return false;
               }
    }

    export {
    
    
        isTrue,
        isNullOrEmpty
    }

2.main.js文件中导入

   import * as filters from './filters'
   //Object.keys把有键值对的对象转换为数组
   Object.keys(filters).forEach(key=>{
    
    
      console.log(key)
      Vue.filter(key,filters[key])
   })

3.组件中直接使用过滤器

   {
    
    {
    
    name | isNullOrEmpty}} 

猜你喜欢

转载自blog.csdn.net/qq_41008567/article/details/108015372