最近学习了一下Vue, 尝试实现一个自定义Tab组件, 效果如下:
支持动态添加tab项, 内容支持放入动态组件
效果图:
目录结构:
1. 使用vue-cli创建脚手架项目
2. 在components中创建C1,C2,C3,MyTab四个自定义组件
package.json
1 { 2 "name": "hello", 3 "version": "0.1.0", 4 "private": true, 5 "scripts": { 6 "serve": "vue-cli-service serve", 7 "build": "vue-cli-service build", 8 "lint": "vue-cli-service lint" 9 }, 10 "dependencies": { 11 "core-js": "^3.6.5", 12 "vue": "^2.6.11" 13 }, 14 "devDependencies": { 15 "@vue/cli-plugin-babel": "~4.5.0", 16 "@vue/cli-plugin-eslint": "~4.5.0", 17 "@vue/cli-service": "~4.5.0", 18 "babel-eslint": "^10.1.0", 19 "eslint": "^6.7.2", 20 "eslint-plugin-vue": "^6.2.2", 21 "vue-template-compiler": "^2.6.11" 22 }, 23 "eslintConfig": { 24 "root": true, 25 "env": { 26 "node": true 27 }, 28 "extends": [ 29 "plugin:vue/essential", 30 "eslint:recommended" 31 ], 32 "parserOptions": { 33 "parser": "babel-eslint" 34 }, 35 "rules": {} 36 }, 37 "browserslist": [ 38 "> 1%", 39 "last 2 versions", 40 "not dead" 41 ] 42 }
MyTab.vue
1 <script> 2 export default { 3 components: {}, 4 props: { 5 tabs: { 6 type: Array, 7 required: true, 8 default: function name() { 9 return [] 10 }, 11 }, 12 currentTab: { 13 type: String, 14 required: true, 15 }, 16 }, 17 render: function(h) { 18 var self = this 19 return h( 20 "div", // 标签名称 21 { 22 class: "tab-container", 23 }, 24 [ 25 h( 26 "ol", 27 { class: "title" }, 28 self.tabs.map(function name(t) { 29 return h( 30 "li", 31 { 32 class: { 33 active: self.isActive(t), 34 }, 35 on: { 36 click: function name() { 37 self.$emit("tabChange", t.title) 38 }, 39 }, 40 }, 41 t.title 42 ) 43 }) 44 ), 45 h( 46 "ol", 47 { class: "content" }, 48 self.tabs 49 .filter((t) => { 50 return self.isActive(t) 51 }) 52 .map(function name(t) { 53 return h("li", [h(t.com, { props: t.props })]) 54 }) 55 ), 56 ] 57 ) 58 }, 59 methods: { 60 isActive(tab) { 61 var cur = this.currentTab 62 if (cur == "") { 63 cur = this.tabs[0].title 64 } 65 return cur == tab.title 66 }, 67 }, 68 computed: {}, 69 } 70 </script> 71 <style scoped> 72 ol, 73 li { 74 padding: 0; 75 margin: 0; 76 list-style: none; 77 } 78 79 .tab-container { 80 border: solid 0px #f60; 81 width: 400px; 82 } 83 84 .tab-container .title li { 85 padding: 5px 10px; 86 background-color: cadetblue; 87 display: inline-block; 88 margin: 0 2px 1px 0; 89 color: #fff; 90 cursor: pointer; 91 } 92 93 .tab-container .title li:hover { 94 background-color: rgb(72, 142, 145); 95 } 96 97 .tab-container .title li.active { 98 background-color: rgb(43, 105, 107); 99 } 100 101 .tab-container .content li { 102 padding: 5px 10px; 103 background-color: rgb(195, 253, 255); 104 border: solid 2px rgb(51, 141, 148); 105 } 106 </style>
C1.vue
1 <template> 2 <p>{{ content }}</p> 3 </template> 4 <script> 5 export default { 6 props: { 7 content: { 8 type: String, 9 default: "none", 10 }, 11 }, 12 data() { 13 return {} 14 }, 15 } 16 </script> 17 <style scoped> 18 ol li { 19 border: solid 1px #abc; 20 padding: 5px 10px; 21 } 22 </style>
C2.vue
1 <template> 2 <ol> 3 <li v-for="tr in list" :key="tr">{{ tr }}</li> 4 </ol> 5 </template> 6 <script> 7 export default { 8 props: ["list"], 9 data1() { 10 return { 11 list: [1, 2, 3, 4, 5, 6], 12 } 13 }, 14 } 15 </script> 16 <style scoped> 17 ol li { 18 border: solid 1px #c0c0c0; 19 margin: 2px; 20 padding: 5px 10px; 21 } 22 </style>
C3.vue
1 <template> 2 <table> 3 <tr v-for="(tr, idx) in table" :key="tr[0] + ‘_‘ + idx"> 4 <td v-for="(td, idx2) in tr" :key="td + ‘_‘ + idx + ‘_‘ + idx2">{{ td }}</td> 5 </tr> 6 </table> 7 </template> 8 <script> 9 export default { 10 props: ["table"], 11 data1() { 12 return { 13 table: [ 14 [1, 2, 3, 4, 5, 6], 15 [2, 4, 5, 6, 7, 8], 16 [3, 4, 5, 6, 7, 8], 17 ], 18 } 19 }, 20 } 21 </script> 22 <style scoped> 23 table { 24 border: solid 1px #c0c0c0; 25 } 26 27 table tr td { 28 padding: 5px 10px; 29 border: solid 1px #c0c0c0; 30 } 31 </style>
App.vue
1 <template> 2 <div id="app"> 3 <div class="opt"> 4 <button @click="tabAdd">AddTab</button> 5 <button @click="tabRemove">RemoveTab</button> 6 <button>{{ index }}</button> 7 </div> 8 9 <MyTab :tabs="tabs" v-on:tabChange="tabChange" :currentTab="currentTab"> </MyTab> 10 </div> 11 </template> 12 13 <script> 14 //import HelloWorld from ‘./components/HelloWorld.vue‘ 15 import MyTab from "./components/MyTab.vue" 16 17 export default { 18 name: "App", 19 components: { MyTab }, 20 data() { 21 return { 22 index: 3, 23 currentTab: "textTab", 24 tabs: [ 25 { 26 title: "textTab", 27 com: this.$Com("C1"), 28 props: { 29 content: "一段文字而已..", 30 }, 31 }, 32 { 33 title: "listTab", 34 com: this.$Com("C2"), 35 props: { 36 list: [1, 2, 3, 4, 5, 6], 37 }, 38 }, 39 { 40 title: "tableTab", 41 com: this.$Com("C3"), 42 props: { 43 table: [ 44 [1, 2, 3, 4, 5, 6], 45 [2, 4, 5, 6, 7, 8], 46 [3, 4, 5, 6, 7, 8], 47 ], 48 }, 49 }, 50 ], 51 } 52 }, 53 methods: { 54 tabAdd() { 55 this.tabs.push({ 56 title: "tableTab" + this.index++, 57 com: this.$Com("C3"), 58 props: { 59 table: [ 60 [1 + this.index, 2, 3, 4, 5, 6], 61 [7 + this.index, 8, 9, 10, 11, 12], 62 ], 63 }, 64 }) 65 }, 66 tabRemove() { 67 if (this.index > 3) { 68 this.tabs.splice(this.tabs.length - 1, 1) 69 this.index-- 70 this.currentTab = this.tabs[0].title 71 } 72 }, 73 tabChange(currentTab) { 74 this.currentTab = currentTab 75 }, 76 }, 77 } 78 </script> 79 80 <style> 81 #app { 82 font-family: Avenir, Helvetica, Arial, sans-serif; 83 -webkit-font-smoothing: antialiased; 84 -moz-osx-font-smoothing: grayscale; 85 color: #2c3e50; 86 margin: 60px; 87 font-size: 14px; 88 } 89 90 .opt { 91 margin: 10px 0; 92 } 93 .opt button { 94 margin-right: 5px; 95 } 96 </style>
main.js
1 import Vue from "vue" 2 import App from "./App.vue" 3 4 Vue.config.productionTip = false 5 6 Vue.prototype.$Com = function(fileName) { 7 return Vue.component(fileName, () => import("./components/" + fileName + ".vue")) 8 } 9 10 new Vue({ 11 render: (h) => h(App), 12 }).$mount("#app")
参考1: https://cn.vuejs.org/v2/guide/components-dynamic-async.html
参考2: https://cn.vuejs.org/v2/guide/render-function.html
[Vue学习] 实现Tab选项卡效果, 动态增删Tab, 内容支持动态组件
原文:https://www.cnblogs.com/sanshizi/p/14691163.html