美文网首页
Vue 轮子

Vue 轮子

作者: 天才在右 | 来源:发表于2019-07-11 16:33 被阅读0次

    使用JSbin学习
    首先写一个tabs组件 然后用轮子代替组件

    html

    <html>
    <head>
      <meta charset="utf-8">
      <title>Vue轮子</title>
      <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    </head>
    <body>
      <div id="app"></div>
    </body>
    </html>
    

    JS

    Vue.component('tabs',{
      props:['selectedTab'],
       template:
        `
          <div class="tabs">
            <slot>
          </div>
        `
    })
    Vue.component('tabs-navs',{
      name:'tabs-navs',
      template:
      `
       <div class="tabs-navs">
         <slot>
       </div>
      `
    })
    Vue.component('tabs-navs-item',{
      props:["name"],
      template:
      `
       <div class="tabs-navs-item">
       </div>
      `
    })
    Vue.component('tabs-panes',{
      name:'tabs-panes',
      template:
      `
        <div class="tabs-panes">
          <slot>
        </div>
      `
    })
    Vue.component('tabs-panes-item',{
      props:["name"],
      template:
      `
        <div class="tabs-panes-item">
        </div>
      `
    })
    var vm = new Vue({
      el:"#app",
      data:{
        selectedTab:1
      },
      template:
      `
       <tabs selectedTab="1">
         <tabs-navs>
           <tabs-navs-item name="tab1">tab1</tabs-navs-item>
           <tabs-navs-item name="tab2">tab2</tabs-navs-item>
         </tabs-navs>
         <tabs-panes>
           <tabs-panes-item name="tab1">内容1</tabs-panes-item>
           <tabs-panes-item name="tab2">内容2</tabs-navs-item>
         </tabs-panes>
       </tabs>
      `
    })
    

    注意

    • 节点不能挂载在<html>或者<body>之类root标签之上 因为VUE 会生成新的标签将其代替
    • name属性是为了区别不同的标签(个人理解性记忆 类似于不同标签的id 便于区分)
    • JS 中用上述代码写完 template中 如果标签之后还有子元素 则需要加上<slot>标签 进一步显示
      然而上述写法中 tab1 tab2 内容1 内容2 却都未在output中显示 尝试在<tabs-navs-item>和<tabs-panes-item>中的template中都加上<slot>标签 结果 tab1 tab2 内容1 内容2均显示了

    所以 在template编译中 文本中输入的字符也想要显示 那么在每个标签的template中都需加上<slot>标签

    (其中<slot>是否要写成<slot/> 但我写成<slot> 没有报错 功能没有影响)
    JS代码

    Vue.component('tabs',{
      props:['selectedTab'],
       template:
        `
          <div class="tabs">
            <slot>
          </div>
        `
    })
    Vue.component('tabs-navs',{
      name: 'tabs-navs',
      template:
      `
       <div class="tabs-navs">
         <slot>
       </div>
      `
    })
    Vue.component('tabs-navs-item',{
      props:["name"],
      template:
      `
       <div class="tabs-navs-item">
         <slot>   //需要加才能显示相应内容
       </div>
      `
    })
    Vue.component('tabs-panes',{
      name: 'tabs-panes',
      template:
      `
        <div class="tabs-panes">
          <slot>
        </div>
      `
    })
    Vue.component('tabs-panes-item',{
      props:["name"],
      template:
      `
        <div class="tabs-panes-item">
          <slot>  //需要加才能显示相应内容
        </div>
      `
    })
    var vm = new Vue({
      el:"#app",
      data:{
        selectedTab:1
      },
      template:
      `
       <tabs selectedTab="1">
         <tabs-navs>
           <tabs-navs-item name="tab1">tab1</tabs-navs-item>
           <tabs-navs-item name="tab2">tab2</tabs-navs-item>
         </tabs-navs>
         <tabs-panes>
           <tabs-panes-item name="tab1">内容1</tabs-panes-item>
           <tabs-panes-item name="tab2">内容2</tabs-navs-item>
         </tabs-panes>
       </tabs>
      `
    })
    

    尝试用每层递进传递selectedTab 即爷爷传给爸爸 爸爸传给儿子

    JS代码

    Vue.component('tabs',{
      props:['selectedTab'],
       template:
        `
          <div class="tabs">
            <slot>
          </div>
        `,
      mounted(){       
        this.$children.forEach((e)=>{
          if(e.$options.name==='tabs-navs'){
            e.selectedTab = this.selectedTab
          }
        })
      }       // 爷爷传递selectedTab
    })    
    Vue.component('tabs-navs',{
      name:'tabs-navs',
      props:['selectedTab'],  // 爸爸接收selectedTab
      template:
      `
       <div class="tabs-navs">
         <slot>
       </div>
      `,
      mounted(){
        this.$children.forEach((e)=>{
          e.selectedTab = this.selectedTab
        })
      }     //爸爸传递selectedTab
    })
    Vue.component('tabs-navs-item',{
      props:["name","selectedTab"],   //儿子接受selectedTab
      template:`
       <div class="tabs-navs-item" :class={active}>
         <slot>
       </div>
      `,
      computed:{
       active(){
        return this.selectedTab === this.name
       }
      }
    })
    

    发现没有显示active 那打个log看看this.selectedTab到底是个啥

    打出了undefined
    那接着打log 看看爸爸和爷爷谁先mounted咯
    真刺激 爸爸比爷爷先mounted
    虽然写代码的时候是先写了爷爷再写爸爸儿子,但是Vue是先把儿子造出来放到爸爸里面 然后 再把爸爸渲染出来放到爷爷里面 再把爷爷渲染出来放到页面当中
    额。。。。所以没有办法让爷爷把selectedTab传给爸爸了 因为那个时候爸爸已经被渲染好了

    既然mounted不行 那我用computed

    没有更新。。。直接赋值是不会更新的吗?
    既然没法更新 就得换个思路了 告辞

    那就试试不直接传值 调用爸爸的一个方法看看

    先后顺序没错 现在看来可行
    那就如法炮制 爷爷对爸爸 爸爸对儿子 写下来 同理 <tabs-panes-item>标签中的一样写下来

    最后得到JS代码

    Vue.component('tabs',{
      props:['selectedTab'],
       template:
        `
          <div class="tabs">
            <slot>
          </div>
        `,
      mounted(){
        this.$children.forEach((e)=>{
          if(e.$options.name==='tabs-navs'){
            e.setSelectedTab(this.selectedTab)   //调用爸爸一个方法
          }else if(e.$options.name==='tabs-panes'){
            e.setSelectedTab(this.selectedTab)
          }
        })
      }
    })
    Vue.component('tabs-navs',{
      name:'tabs-navs',
      data(){
        return {
          selectedTab:undefined
        }
      },
      template:
      `
       <div class="tabs-navs">
         <slot>
       </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
          this.$children.forEach((e)=>{
            e.setSelectedTab(tab) //同理 调用儿子的方法
          })
        }
      }
    })
    Vue.component('tabs-navs-item',{
      props:["name"],
      data(){
        return{
          selectedTab: undefined
        }
      },
      template:`
       <div class="tabs-navs-item" :class={active}>
         <slot>
       </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
        }
      },
      computed:{
       active(){
        return this.selectedTab === this.name
       }
      }
    })
    Vue.component('tabs-panes',{
      name:'tabs-panes',
      data(){
        return {
          selectedTab:undefined
        }
      },
      template:
      `
        <div class="tabs-panes">
          <slot>
        </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
          this.$children.forEach((e)=>{
            e.setSelectedTab(tab)
          })
        }
      }
    })
    Vue.component('tabs-panes-item',{
      props:["name"],
      data(){
        return{
          selectedTab: undefined
        }
      },
      template:
      `
        <div class="tabs-panes-item" :class = {active}>
          <slot>
        </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
        }
      },
      computed:{
       active(){
        return this.selectedTab === this.name
       }
      }
    })
    var vm = new Vue({
      el:"#app",
      data:{
        selectedTab:'1'
      },
      template:
      `
       <tabs selectedTab="tab1">
         <tabs-navs name="tabs-navs">
           <tabs-navs-item name="tab1">tab1</tabs-navs-item>
           <tabs-navs-item name="tab2">tab2</tabs-navs-item>
         </tabs-navs>
         <tabs-panes name="tabs-panes">
           <tabs-panes-item name="tab1">内容1</tabs-panes-item>
           <tabs-panes-item name="tab2">内容2</tabs-navs-item>
         </tabs-panes>
       </tabs>
      `
    })
    

    这里用的是data没有用props了

    好了 接下来就只差触发事件 事件监听 点击切换状态

    子元素上触发事件 父元素上监听 发现未打出log 父元素并未监听到事件 这样不可行

    看来Vue的事件是不会冒泡的(只能手动冒泡操作) 只能监听子元素本人了
    JS代码

    Vue.component('tabs',{
      props:['selectedTab'],
       template:
        `
          <div class="tabs">
            <slot>
          </div>
        `,
      mounted(){
        this.$children.forEach((vm)=>{
          if(vm.$options.name==='tabs-navs'){
            vm.setSelectedTab(this.selectedTab)
            vm.$on('update:selectedTab',(e)=>{
              console.log('爷爷知道selectedTab为'+e)
            this.$emit('update:selectedTab',e)
          })
          }else if(vm.$options.name==='tabs-panes'){
            vm.setSelectedTab(this.selectedTab)
          }
        })
      }
    })
    Vue.component('tabs-navs',{
      name:'tabs-navs',
      data(){
        return {
          selectedTab:undefined
        }
      },
      template:
      `
       <div class="tabs-navs">
         <slot>
       </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
          this.$children.forEach((e)=>{
            e.setSelectedTab(tab)
          })
        }
      },
      mounted(){
        this.$children.forEach((e)=>{
          
          e.$on('update:selectedTab',(e)=>{
            console.log('爸爸知道事件触发了'+e)
            this.$emit('update:selectedTab',e)
          })
        })
      },
      created(){
       this.$on('update:selectedTab',()=>{
        
        }
       )
    }
    })
    Vue.component('tabs-navs-item',{
      props:["name"],
      data(){
        return{
          selectedTab: undefined
        }
      },
      template:`
       <div class="tabs-navs-item" :class={active} @click = "onClick">
         <slot>
       </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
        },
        onClick(){
          this.$emit('update:selectedTab',this.name) //触发事件
          console.log('儿子触发事件')
        }
      },
      computed:{
       active(){
        return this.selectedTab === this.name
       }
      }
    })
    Vue.component('tabs-panes',{
      name:'tabs-panes',
      data(){
        return {
          selectedTab:undefined
        }
      },
      template:
      `
        <div class="tabs-panes">
          <slot>
        </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
          this.$children.forEach((e)=>{
            e.setSelectedTab(tab)
          })
        }
      }
    })
    Vue.component('tabs-panes-item',{
      props:["name"],
      data(){
        return{
          selectedTab: undefined
        }
      },
      template:
      `
        <div class="tabs-panes-item" :class = {active}>
          <slot>
        </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
        }
      },
      computed:{
       active(){
        return this.selectedTab === this.name
       }
      }
    })
    var vm = new Vue({
      el:"#app",
      data:{
        value: 'tab1'
      },
      template:
      `
       <tabs :selectedTab="value" @update:selectedTab = "value=$event">
         <tabs-navs name="tabs-navs">
           <tabs-navs-item name="tab1">tab1</tabs-navs-item>
           <tabs-navs-item name="tab2">tab2</tabs-navs-item>
         </tabs-navs>
         <tabs-panes name="tabs-panes">
           <tabs-panes-item name="tab1">内容1</tabs-panes-item>
           <tabs-panes-item name="tab2">内容2</tabs-navs-item>
         </tabs-panes>
       </tabs>
      `
    })
    

    现在发现点击tab2没有变化 原来是我们只在tabs的mounted做了操作 而在其updated的时候也应该做相应的操作

    添加相应的JS代码

    Vue.component('tabs',{
      props:['selectedTab'],
       template:
        `
          <div class="tabs">
            <slot>
          </div>
        `,
      mounted(){
        this.$children.forEach((vm)=>{
          if(vm.$options.name==='tabs-navs'){
            vm.setSelectedTab(this.selectedTab)
            vm.$on('update:selectedTab',(e)=>{
              console.log('爷爷知道selectedTab为'+e)
            this.$emit('update:selectedTab',e)
            })
          }else if(vm.$options.name==='tabs-panes'){
            vm.setSelectedTab(this.selectedTab)
          }
        })
      },
      updated(){           //任何更新后都应做相应的渲染
        this.$children.forEach((vm)=>{
          if(vm.$options.name==='tabs-navs'){
            vm.setSelectedTab(this.selectedTab)
            }else if(vm.$options.name==='tabs-panes'){
            vm.setSelectedTab(this.selectedTab)
          }
        })
      }
    })
    

    删除测试的log 以下是Vue轮子完整代码
    JS

    Vue.component('tabs',{
      props:['selectedTab'],
       template:
        `
          <div class="tabs">
            <slot>
          </div>
        `,
      mounted(){
        this.$children.forEach((vm)=>{
          if(vm.$options.name==='tabs-navs'){
            vm.setSelectedTab(this.selectedTab)
            vm.$on('update:selectedTab',(e)=>{
            this.$emit('update:selectedTab',e)
            })
          }else if(vm.$options.name==='tabs-panes'){
            vm.setSelectedTab(this.selectedTab)
          }
        })
      },
      updated(){
        this.$children.forEach((vm)=>{
          if(vm.$options.name==='tabs-navs'){
            vm.setSelectedTab(this.selectedTab)
            }else if(vm.$options.name==='tabs-panes'){
            vm.setSelectedTab(this.selectedTab)
          }
        })
      }
    })
    Vue.component('tabs-navs',{
      name:'tabs-navs',
      data(){
        return {
          selectedTab:undefined
        }
      },
      template:
      `
       <div class="tabs-navs">
         <slot>
       </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
          this.$children.forEach((e)=>{
            e.setSelectedTab(tab)
          })
        }
      },
      mounted(){
        this.$children.forEach((e)=>{
          
          e.$on('update:selectedTab',(e)=>{
            this.$emit('update:selectedTab',e)
          })
        })
      },
      created(){
       this.$on('update:selectedTab',()=>{
        
        }
       )
    }
    })
    Vue.component('tabs-navs-item',{
      props:["name"],
      data(){
        return{
          selectedTab: undefined
        }
      },
      template:`
       <div class="tabs-navs-item" :class={active} @click = "onClick">
         <slot>
       </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
        },
        onClick(){
          this.$emit('update:selectedTab',this.name) //触发事件
        }
      },
      computed:{
       active(){
        return this.selectedTab === this.name
       }
      }
    })
    Vue.component('tabs-panes',{
      name:'tabs-panes',
      data(){
        return {
          selectedTab:undefined
        }
      },
      template:
      `
        <div class="tabs-panes">
          <slot>
        </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
          this.$children.forEach((e)=>{
            e.setSelectedTab(tab)
          })
        }
      }
    })
    Vue.component('tabs-panes-item',{
      props:["name"],
      data(){
        return{
          selectedTab: undefined
        }
      },
      template:
      `
        <div class="tabs-panes-item" :class = {active}>
          <slot>
        </div>
      `,
      methods:{
        setSelectedTab(tab){
          this.selectedTab = tab
        }
      },
      computed:{
       active(){
        return this.selectedTab === this.name
       }
      }
    })   
    ///上面是轮子///
    var vm = new Vue({
      el:"#app",
      data:{
        value: 'tab1'
      },
      template:
      `
       <tabs :selectedTab="value" @update:selectedTab = "value=$event">
         <tabs-navs name="tabs-navs">
           <tabs-navs-item name="tab1">tab1</tabs-navs-item>
           <tabs-navs-item name="tab2">tab2</tabs-navs-item>
         </tabs-navs>
         <tabs-panes name="tabs-panes">
           <tabs-panes-item name="tab1">内容1</tabs-panes-item>
           <tabs-panes-item name="tab2">内容2</tabs-navs-item>
         </tabs-panes>
       </tabs>
      `
    })
    

    HTML

    <html>
    <head>
      <meta charset="utf-8">
      <title>Vue轮子</title>
      <script src="https://cdn.bootcss.com/vue/2.6.9/vue.min.js"></script>
    </head>
    <body>
      <div id="app"></div>
    </body>
    </html>
    

    css

    .active{
      background: red;
    }
    

    jsbin预览链接
    jsbin源码链接(进入点击run with js按钮)

    相关文章

      网友评论

          本文标题:Vue 轮子

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