美文网首页
Vue学习记录第五天

Vue学习记录第五天

作者: 大白熊_8133 | 来源:发表于2018-11-28 14:13 被阅读0次

    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进行组件位置控制

    相关文章

      网友评论

          本文标题:Vue学习记录第五天

          本文链接:https://www.haomeiwen.com/subject/zximqqtx.html