Vue学习记录第五天

2018-11-28  本文已影响0人  大白熊_8133

slot

如果在自定义标签中直接写入内容,内容会被替换掉,为了避免这个问题,Vue提供了slot插槽标签

  <body>
    <div id="app">
      <modal m="1">
        <p>adaf</p>
        <h1 slot="title">大</h1>
        <p slot='content'>白熊</p>
        <button @click="fn">按钮</button>
      </modal>
      </div>
    <template id="modal">
      <div>
        <slot name="title">默认标题</slot>
        <slot name="content">默认内容</slot>
        <slot name="default">1111111</slot>
      </div>
    </template>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      let modal={
        template:"#modal"
      };
      let vm=new Vue(
      {
        el:"#app",
        components:{
          modal
        },
        methods:{
          fn(){
            alert(1)
          }
        }
      }
      )
      </script>
  </body>

默认值

<slot>标签中的内容为默认值,如果没有传递值,显示这个默认值,否则默认值会被替换掉
如果有很多没有指定默认值的slot,每一个都会被自定义标签内的内容替换掉

name

为slot标签附上name属性,为DOM上的自定义标签中内容附上slot属性,则可以为不同标签指定插槽
没有slot属性的都会使用default插槽

内容

自定义标签中的内容都属于父级,只有属性名属于组件,包括方法

父调用子组件的方法

复习

父传子利用属性
子级利用props接收,并且写入data(){}中
子传父利用方法
子级利用this.$emit()触发自定义方法,执行父级方法,改变父级的值

ref

前面第四天实例中的方法这部分有讲过,完全忘了

this.$refs ref用来给DOM元素或子组件注册引用信息,假如有一个标签ref="myp",通过this.$refs.myp可以进行调用

以下实例完成数据加载后,子组件隐藏

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <div id="app">
      <loading ref="load"></loading>
      <!--ref获取真实的dom-->
      </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      //父组件调用子组件的方法
      let loading={
        data(){
          return {flag:true}
        },
        template:'<div v-show="flag">加载中</div>',
        methods:{
          hide(){
            this.flag=false;
          }
        }
      };
      let vm=new Vue(
      {
        el:"#app",
        data:{},
        components:{
          loading
        },
        mounted:{
          //refs如果放在组件上,获取的是组件的实例,并不是组件的DOM元素
          this.$refs.load.hide()
 //this.$refs.load.$el.style.background="red" 改变当前元素背景颜色色
        }
      }
      )
      </script>
  </body>
</html>

mounted在整个生命周期中是把编译好的HTML挂载在DOM后执行的

组件的切换以及保持

component标签

这是vue自带标签

vue自带标签还有template,transition,slot
template:没有意义的标签
transition:动画标签,用enter,enter-to,enter-active,leave,leave-active,leave-to控制动画
slot:插槽

    <div id="app">
      <input type="radio" v-model="r" value="polarbear">polarbear
      <!--HTML中radio只要name相同,value不同就可以保证一组单选框了-->
      <input type="radio" v-model="r" value="penguin">penguin
      <keep-alive>
      <component :is="r"></component>
      </keep-alive>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      <script>
        let polarbear={
          template:'<div>polarbear</div>'
        }
        let penguin={
          template:"<div>penguin</div>"
        }
        let vm=new Vue(
        {
          el:"#app",
          components:{
            polarbear,
            penguin
          },
          data:{r:"polarbear"}
        }
        )
        </script>

component可以实现多个组件使用同一个挂载点,切换不同组件

is属性

component是利用动态绑定is属性,挂载不同组件的
没有is属性会报错
切换的时候会销毁前一个组件,挂载另一个组件

keep-alive标签

为了让切换时标签存入缓存,而不是销毁,使用keep-alive标签
keep-alive标签用于缓存,为的是后面的路由做准备,如果缓存了就不会再走每个组件的created,mounted

组件生命周期的一定补充

子组件和父组件同时拥有mounted方法,会先走哪一个?
mounted方法是挂载完调用的,所以需要等待子组件挂载完,再触发父级的挂载

dom异步渲染

 let child={
        data(){
          return {arr:[1,2,3]}
        },
      template:"<div>{{arr}}</div>",
      mounted(){
        this.arr=[4,5,6]
      }
      }
let vm=new Vue(
      {
        el:"#app",
        data:{

        },
        components:{
          child
        },
        mounted(){console.log(this.$refs.c.$el.innerHTML)
          })
        }
      }

这个时候显示的console.log出来的arr并不会是4,5,6。因为挂载完毕开始触发父级mounted的时候,数据还没有改变

nextTick

官网文档说明是下次DOM更新循环结束之后执行延迟回调,修改数据之后立即使用这个方法,获取更新后的DOM

完全没看懂
稍微查一下,大致意思就是

  1. DOM的更新是异步的,就是说Vue中数据变化之后,并不会立即变化
  2. 写在同一事件中的任务是同步的
  3. DOM会等待这些所有的任务执行完,再更新
    这就是为什么在实例中,调用DOM中的内容,会发现还没有更改

这时需要使用nextTick的回调函数,这个函数会在DOM更新完之后再调用
将Vue中 mounted改为

        mounted(){
          this.$nextTick(()=>{console.log(this.$refs.c.$el.innerHTML)
          })}

组件的循环

使用v-for也可以使组件循环,但是由于每一个组件是不一样的,所以必须赋予key元素,为了让他们都不一样,所以使用index
实例如下,是一个拥有标题,文章内容,作者的,使用了slot,组件,父传子,组件循环知识的panel

<!--组件的循环,key是必须的-->
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  </head>
  <body>
    <div id="app">
      <panel v-for="(article,index) in articles" :type="article.type" :key="index">
        <div slot="title"><span v-html="article.title"></span></div>
        <div slot="content">{{article.content}}</div>
        <div slot="footer" v-if="article.auth">{{article.auth}}</div>
      </panel>
      </div>
      <template id="p">
        <div class="panel" :class=[color]>
          <div class="panel-heading">
          <slot name="title"></slot>
          </div>
          <div class="panel-body">
          <slot name="content"></slot></div>
          <div class="panel-footer">
          <slot name="footer">作者:无名</slot></div>
          </div>
          </template>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    let panel={
      template:"#p",
      props:{
        type:{
          type:[String],
          default:"primary"
        }
      },
      data(){
        return {color:"panel-"+this.type}
      }
    }
    let vm=new Vue(
    {
      el:"#app",
      data:{
        articles:[
        {type:"primary",title:"<h1>大白熊</h1>",content:"这是一种哺乳动物",auth:"作者:polarbear"},
        {type:"danger",title:"<h1>企鹅</h1>",content:"这是一种鸟类",auth:"作者:penguin"},
        {type:"info",title:"<h1>海豹</h1>",content:"这是一种欧皇"}
        ]
      },
      components:{
        panel
      }
    }
    )
    </script>
  </body>
</html>

值得注意的点

  1. 不能写作 <slot class="panel-heading>这种形式,因为slot的样式会被替换掉
  2. props中的属性可以直接使用,但是不能改变,因为它的值是属于父组件的

EventBus(但是不用,就是为了面试了解一下,只适合极简单的形式)

同级组件,跨级组件之间相互传递信息很复杂
EventBus使用发布订阅,创建一个第三方实例,实现交互

  1. 在创建第一个组件时给EventBus绑定操作自己的方法
  2. 在第二个组件中,触发这个操作
  <body>
    <div id='app'>
      <brother1></brother1>
        <brother2></brother2>
      </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      let EventBus=new Vue;
      //创建一个第三方实例,实现交互
      let brother1={
        template:'<div>{{color}}<button @click="change1">变绿</button></div>',
        data(){
          return {color:"绿色",default:'绿色'}
        },
        created()
        {
          EventBus.$on('changered',(val)=>{this.color=val})
          //绑定事件
        },
        methods:{
          change1(){
            EventBus.$emit("changegreen",this.default)
          }
        }

      }
      let brother2={
        template:"<div>{{color}}<button @click='change2'>变红</button></div>",
        data(){
          return {color:"红色",default:'红色'}
        },
        created(){
          EventBus.$on("changegreen",(val)=>(this.color=val))
        },
        methods:{
          change2(){
            console.log(this.default)
            EventBus.$emit("changered",this.default)
          }
        }
      }
      let vm=new Vue(
      {
        el:"#app",
        components:{
          brother1,
          brother2
        }
      }
      )
      </script>
  </body>

路由

路由的特点

访问不同的路径,返回不同的结果
前端和后端是分离的,后端值负责提供接口供前端调用,跳转都是由前端自己处理的

hash与history
hash模式:通过#,服务器不会管#后面的内容,根本不会发送到服务器,所以不会404,但是不支持SEO
history: window.history.pushState(obj, title [, url]),强制跳转,window.history.replaceState(obj, title [, url]),路由替换,会把路由历史改变,使用history刷新会真的去服务器请求,有404错误
开发使用hash,上线使用history

多页面(SPA,single page application)

切换的是组件

vue-router

使用vue-router进行多页面切换,就是把组件映射到路由,告诉vue router在哪里渲染

实例步骤

JS

  1. 引入Vue和Vue Router
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="./vue-router.js"></script>

一定要把vue-router写在vue的后面,因为router是基于Vue的
我的Vue-router是从网上下载直接扔在同一个文件夹的

  1. 定义组件
      let home={template:"<div>首页</div>"};
      let list={template:"<div>列表页</div>"};
  1. 定义路由映射表
    配置路径和组件的关系,这个配置的关系就是页面级组件
      let routes=[
      //路由的映射表 配置路径和组件的关系
      {path:' ',component:home},//默认展示的路由
      //{path:'/*',component:home} 任意的都匹配到home
      {path:'/home',component:home},
      {path:'/list',component:list}
      //配置的关系就是页面级组件
      //访问'/home'就是home组件
      //因为是路径,所以这里不能不写/
      ]

- 这里routes是一个数组
- 路径不能不写/
这里的path如果为' ',则为默认展示的路由
如果path为'*',并且有redirect组件,则表示当前路径不在routes中跳转至redirect指定的路径

  1. 创建router实例,传routes配置
      let router=new VueRouter({
        //引入vue-router/自带VueRouter类
        routes:routes,
        //mode:"history",会改为history模式,默认hash模式
        linkActiveClass:'active'
        //更改默认样式的类名,默认叫router-link-active
      })

- 根据es6的规定,routes:routes可以直接缩写为routes,如果改名字了就不行了
- linkActiveClass是更改route-link的样式名,默认是router-link-active,这是router-link被激活时的样式,可以改变颜色之类的

  1. 创建Vue实例,通过router配置参数注入路由
      let vm=new Vue(
      {
        el:"#app",
        router:router,
        mode:"history"
        }
      }
      )

这里的Vue实例不再需要components
默认模式为hash,可以改变为hsitory

DOM

    <div id="app">
      <!--用a标签太局限了,只适用于hash-->
      <router-link to="/home" tag="button">首页</router-link>
      <router-link to="/list" tag="button">列表页</router-link>
      <!--不写/如果是二级路由就会出问题-->
      <!--默认为a标签,添加tag属性就会变化-->
      <router-view></router-view>
      <!--是一个vue-router定义的全局组件,可以直接使用-->
      <!--显示视图-->
      </div>

1. router-link

用于进行导航
- 默认会被渲染为a标签, 但是a标签太过于局限了,只适用于hash,使用tag属性,能够改变标签类型
- 通过to属性指定链接,链接要加/,否则如果是二级链接会出现问题

2. router-view

路由出口,路由匹配到的组件会渲染在此处
这是Vue-router定义的全局组件,用于显示视图

编程式导航

编程式导航是指利用js跳转页面

  1. 基于上述所学的Vue-router去创建一个导航栏
  2. 两个页面间可以通过按钮相互跳转

首先需要了解几个router实例方法

this.$router.push

相当于router-link标签中的to="url"
在history栈中添加一个新的记录

this.$router.replace

和push相似,但是不会像history栈添加新的记录,而是替换history记录

<router-link :to="..." replace>= router.replace(...)

this.$router.go

参数是一个整数,意思是在history记录中向前或向后多少步
负数为后退,正数为前进
记录不够用就报错

跳转的实现

在template中添加按钮,给每一个按钮绑定事件,在事件中通过this.$router.push()和 this.$router.go()进行页面跳转

  <body>
    <!--编程式导航,在js跳转页面-->
    <div id="app">
      <router-link :to="{path:'/home'" tag="a">首页</router-link>
      <router-link :to="{path:'/list'" tag="a">列表页</router-link>
      <router-view></router-view>
      </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="./vue-router.js"></script>
    <script>
      let home={
        template:"<div>首页<button @click='tolist'>去列表</button></div>",
        methods:{
            tolist(){
              //跳到列表页是一个方法,所以使用$router
              this.$router.push('/list') //强制跳转路径
              console.log(window.history)
              //this.$router.replace('/list') 路由替换,会把当前的历史替换掉
            }
          }};
      let list={
        template:"<div>列表页<button @click='back'>返回首页</button></div>",
        methods:{
          back(){
            this.$router.go(-1)
            //history.go(-1)返回一级
          }
        }
      };
      let routes=[
      {path:' ',component:home},//默认展示的路由
      //{path:'/*',component:home} 任意的都匹配到home
      {path:'/home',component:home},
      {path:'/list',component:list},
      //{path:'*',component:home} //都匹配不到的情况,但是这个地方路径不会变,只是切换组件
      {path:'*',redirect:'/home'} //路径变 组件也切换
      ]
      let router=new VueRouter(
      {
        routes:routes
      }
      )
      let vm=new Vue({
        el:"#app",
        router:router,
        mode:"history"
        //每个组件都会拥有一个名字为$router的属性(放的是方法) 还有一个名字为$route(放的都是属性)
        //不用加components
      })
    </script>
  </body>

路由的嵌套

创建一个拥有首页和详情页两个页面的路由,在详情页中又有两个路由,需要使用路由的嵌套

JS中嵌套

在routes中创建的父级路由中再创建路由表对象即可

      let routes=[
      {path:"/home",component:home},
      {
        path:"/detail",
        component:detail,
        children:[
        //children中的路径永远不带/,如果带/表示是1级路由
        {path:'profile',component:profile},
        {path:'about',component:about}
        ]
      }
      ]

一定要注意,这个二级路由中的路径不要带/

DOM中嵌套

路由的嵌套不要写在router-view中,写在template模板中,template模板中写的内容和之前单极路由的相似,不过是routes改为children,利用id引入组装中

    <div id="app">
      <router-link to="/home" tag="button">首页</router-link>
      <router-link to="/detail" tag="button">详情页</router-link>
      <!--路由的嵌套不能在这里写,要写在模板里-->
      <router-view></router-view>
    </div>
    <template id="detail">
      <div >
        <router-link to="/detail/profile" tag="button" style="margin-top:10px">个人中心</router-link><br>
        <!--这里的路径要写全部路径,不能只写二级路径-->
        <router-link to="/detail/about" tag="button" style="margin-top:10px">关于我</router-link>
        <router-view class="content"></router-view>
      </div>
    </template>

注意,这里的二级路由的路径,要写全部路径,否则找不到
全部程序

  <body>
    <div id="app">
      <router-link to="/home" tag="button">首页</router-link>
      <router-link to="/detail" tag="button">详情页</router-link>
      <!--路由的嵌套不能在这里写,要写在模板里-->
      <router-view></router-view>
    </div>
    <template id="detail">
      <div >
        <router-link to="/detail/profile" tag="button" style="margin-top:10px">个人中心</router-link><br>
        <!--这里的路径要写全部路径,不能只写二级路径-->
        <router-link to="/detail/about" tag="button" style="margin-top:10px">关于我</router-link>
        <router-view class="content"></router-view>
      </div>
    </template>
    <template id="home">
      <div>
        <img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1543393547132&di=49374d09562621a76c2570f91d82e7bd&imgtype=0&src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farchive%2F65c92640da76255ec058ab0d3691333135116875.jpg">
      </div>
    </template>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="./vue-router.js"></script>
    <script>
      let home={
        template:"#home"
      }
      let detail={
        template:"#detail"
      }
      let profile={
        template:"<div>profile</div>"
      }
      let about={
        template:"<div>about</div>"
      }
      let routes=[
      {path:"/home",component:home},
      {
        path:"/detail",
        component:detail,
        children:[
        //children中的路径永远不带/,如果带/表示是1级路由
        {path:'profile',component:profile},
        {path:'about',component:about}
        ]
      }
      ]
      let router=new VueRouter(
      {
        routes
      }
      )
      let vm=new Vue(
      {
        el:"#app",
        router,
      }
      )
      </script>
  </body>

带参数路由

可以在路径中添加参数,比如商品号为111,/item/111路径最后面就是传递的参数。
想要取出这个参数,需要在路由映射表的路径中添加 :c,会产生一个{c:1}的对象,存入this.$route.params, 可以通过this.$route.params.c取出来

<body>
    <div id="app">
      <router-link to="/article/1">商品1</router-link>
      <router-link to="/article/2">商品2</router-link>
      <router-link to="/article/3">商品3</router-link>
      <router-link to="/article/4">商品4</router-link>
      <router-view></router-view>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="./vue-router.js"></script>
    <script>
    //#/article/1 就是第一篇文章
    let article={template:"<div>第{{$route.params.c}}篇文章</div>"}
    let routes=[
    //路径参数 表示值必须要有但是值是随机的
    {path:'/article/:c',component:article}
    //:c表示随机的
    //会产生一个{c:1}的对象=>属于$route=>this.$route.params

    ]
    let router=new VueRouter(
    {
      routes
    }
    )
    let vm=new Vue(
    {
      el:"#app",
      router
    }
    )
    </script>
  </body>

这里组件只有一个,通过传递过来的参数改变内容

通过参数跳转

    <div id="app">
      <!--如果用对象作为to的属性,并且使用参数,必须给路由起名字,通过名字跳转-->
      <router-link :to="{name:'pro',params:{c:1}}">商品1</router-link>
      <router-link :to="{name:'pro',params:{c:2}}">商品2</router-link>
      <router-link :to="{name:'pro',params:{c:3}}">商品3</router-link>
      <router-link :to="{name:'pro',params:{c:4}}">商品4</router-link>
      <router-view></router-view>
    </div>

将参数写在路径中,注意这里的path不再是path而是name,必须在路由映射表中,给路由起名字,才能正常跳转
除此之外,还可以通过监控参数的变化而对组件进行一些操作,监控需要使用watch,监控参数的操作,而参数存入 this.$route,所以监控这个值的变化

  <body>
    <div id="app">
      <!--如果用对象作为to的属性,并且使用参数,必须给路由起名字,通过名字跳转-->
      <router-link :to="{name:'pro',params:{c:1}}">商品1</router-link>
      <router-link :to="{name:'pro',params:{c:2}}">商品2</router-link>
      <router-link :to="{name:'pro',params:{c:3}}">商品3</router-link>
      <router-link :to="{name:'pro',params:{c:4}}">商品4</router-link>
      <router-view></router-view>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="./vue-router.js"></script>
    <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
    <script>
    //#/article/1 就是第一篇文章
    let article={
      template:"<div>第{{$route.params.c}}篇文章</div>",
      watch:{

        this.$route(){
                  //路径参数发生变化,通过监控参数变化进行操作
        }
      }
    }
    let routes=[
    //路径参数 表示值必须要有但是值是随机的
    {path:'/article/:c',component:article,name:'pro'}
    //:c表示随机的
    //会产生一个{c:1}的对象=>属于$route=>this.$route.params

    ]
    let router=new VueRouter(
    {
      routes
    }
    )
    let vm=new Vue(
    {
      el:"#app",
      router
    }
    )
    </script>
  </body>

动画路由结合

Vue动画 Vue在插入,更新,移除DOM中,提供多种不同的应用过渡,可以直接使用Animation.css这个库
在head中添加
<link href="https://cdn.bootcss.com/animate.css/3.7.0/animate.css" rel="stylesheet">
然后再transition标签中添加 enter-active-class以及leave-active-class即可添加不同的进入离开动画

    <div id="app">
      <router-link to="/home">首页</router-link>
      <router-link to="/list">列表</router-link>
      <transition enter-active-class="animated bounceInRight"
                  leave-active-class="animated bounceOutRight">
        <keep-alive>
      <router-view></router-view>
      </keep-alive>
      </transition>
    </div>

但是会有一个非常严重的问题,动画不会在同一平面进行,列表会出现在首页下方,首页完全消失,列表跳到首页的位置
解决方法:1. 改变动画模式,在transition标签处添加mode属性,并且改为"out-in",先出去再进来 2. 使用position:absolute进行组件位置控制

上一篇下一篇

猜你喜欢

热点阅读