Vue 轮子
2019-07-11 本文已影响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了
好了 接下来就只差触发事件 事件监听 点击切换状态
看来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;
}