个人自学前端26-Vue3-组件components,父传子props,子传父,组件混合,bus通信
作者:互联网
一.组件components
组件都是vm实例.它可以有自己的视图,自己的数据,自己的逻辑.
组件是为了复用布局以及逻辑.
- 组件
- 组件要注册
- 组件要挂载(组件的引用)
<div id='app'>
<!-- 一个组件挂载了3次.挂载时可以用单标签,也可以用双标签. -->
<!-- 挂载时,如果组件名是带驼峰的,你需要挂载的时候加- -->
<!-- 挂载的最终结果就是挂载的标签被组件的template指定的布局替换 -->
<!-- 组件挂载时,大写都会变小写 -->
<news></news>
<news-view></news-view>
<news-view></news-view>
<news-view></news-view>
</div>
<script>
const news = {
template: `
<div>
<h3>今天的新闻</h3>
<p>今天的新闻内容</p>
</div>
`
}
const newsView = {
template: `
<div>
<h3>今天的新闻</h3>
<p>今天的新闻内容</p>
</div>
`
}
// 全局注册 => 哪里都能用news组件
Vue.component('news', news);
new Vue({
el: '#app',
// 注册一个叫news的组件
// 局部注册 => 只能在当前组件视图中使用.
// 组件名如果是驼峰命名,需要在引用组件时,添加-
components: { news, "news-view": newsView }
})
</script>
-
组件的选项
newsView是组件的配置项.
template是组件的布局选项.(写组件的视图用的)
组件都是vue实例.可以写任意的Vue选项.除了el选项(例如data,methods,computed,filter,components...)组件的选项和new Vue的选项区别:
1:不用el选项.(写了没意义).
2:data必须写成函数.把数据对象return出来.const newsView = { template: ` <div> <h3 @click='fn'>{{title}}</h3> <p>今天的新闻内容</p> </div> `, data() { return { title: '今天的新闻' } }, methods: { fn() { alert(this.title) } } }
-
组件的组件
组件都是vue实例,隐藏它也可以写components选项
哪个组件的视图使用了数据,就在哪个组件内声明这个数据
哪个组件的视图挂载了其他组件A,就在哪个组件内声明这个A组件 -
v-for和组件
组件的template内必须有一个唯一的父元素包裹所有的布局.
如果组件的这个父元素需要v-for渲染.则应该把v-for写在组件挂载的地方.
<news v-for='d in 3'></news>
-
根组件
根组件. => 所有组件的唯一祖先组件。
new Vue时,如果没有写template选项,则vue会使用el所在的标签作为视图。
二.父传子props
父传子 => 让子组件复用时渲染了不同的内容(数据).
父传子 => 前子后父.
Vue => 单向数据流 => 从上往下的.(不能自下而上)
父组件数据变化 => 父组件视图更新 => :msg='msg'重新执行 => 新的msg的值传递给了子组件 => 子组件msg变化 => 子组件视图更新
const newsView = {
template: `
<div>
<h3>{{day}}的新闻标题</h3>
<p>{{day}}的新闻内容</p>
</div>
`,
// 声明一个props数据
props: ['day']
}
// 如果props的数据在赋值时,希望通过一个变量来赋值,则需要加:(v-bind)
// 子组件数据 => day
// 父组件数据 => day1,day2,day3
// 子组件在挂载时,子组件数据被父组件数据赋值.(父传子)
const App = {
template: `
<div id='app'>
<news-view v-bind:day='day1'></news-view>
<news-view :day='day2'></news-view>
<news-view :day='day3'></news-view>
</div>
`,
data() {
return {
day1: '今天',
day2: '明天',
day3: '后天',
}
},
components: { newsView },
}
在子组件中,是不能修改父组件传递的props数据.
但是可以操作应用类型的数据.
如果传递的是一个引用类型,则传递的是这个引用类型的引用.
我们不能在子组件中修改这个引用,但是可以修改这个引用背后的对象.
这样做修改的对象其实是父组件的对象,会导致父组件和子组件同时更新.
const myComponent = {
template: `
<div>
<h3>子组件msg---{{msg}}</h3>
<h3>子组件obj---{{obj.name}}</h3>
<button @click='fn'>按钮</button>
</div>
`,
props: ['msg', 'obj'],
methods: {
fn() {
// 可以通过this访问props数据.
// console.log(this.msg)
// 报错
// this.msg = Math.random();
// 不报错.
// this.obj.name = '超越';
// 报错.不能修改obj本身
// this.obj = 10000;
}
}
}
const App = {
template: `
<div id='app'>
<h3>App根组件msg--{{msg}}</h3>
<h3>App根组件obj--{{obj.name}}</h3>
<myComponent :msg='msg' :obj='obj' />
<button @click='fn'>父按钮</button>
</div>
`,
data() {
return {
msg: '父组件的数据msg',
obj: { name: '幂幂' },
}
},
methods: {
fn() {
this.msg = Math.random();
}
},
components: { myComponent },
}
三.子传父
- 父组件需要一个方法来接收子组件数据.
- 子组件触发父组件的这个方法.并传入子组件数据.
子组件如何触发父组件的方法?
- 把父组件的方法传递给子组件触发.(引用赋值传递)
const myComponent = {
template: `
<div>
<h3>子组件msg---{{msg}}</h3>
<button @click='fn'>按钮</button>
</div>
`,
props: ['getMsg'],
data() {
return {
msg: '子组件的msg'
}
},
methods: {
fn() {
// 调用父组件方法,并传递子组件的数据.
this.getMsg(this.msg);
}
}
}
const App = {
template: `
<div id='app'>
<h3>App根组件msg--{{msg}}</h3>
<!--父组件方法传递给子组件-->
<myComponent :getMsg='getMsg' />
</div>
`,
data() {
return {
msg: '',
}
},
methods: {
// 父组件用于接收子组件的方法。
getMsg(msg) {
this.msg = msg;
}
},
components: { myComponent },
}
- 自定义事件实现.(给子组件绑定一个自定义事件,这个自定义事件触发时调用父组件的方法)
const myComponent = {
template: `
<div>
<h3>子组件msg---{{msg}}</h3>
<button @click='fn'>按钮</button>
</div>
`,
data() {
return {
msg: '子组件的msg'
}
},
methods: {
fn() {
// $emit => 主动触发事件.
// 第一个参数是事件名, 第二个参数是传递给事件句柄的参数
this.$emit('myevent', this.msg);
}
}
}
const App = {
template: `
<div id='app'>
<h3>App根组件msg--{{msg}}</h3>
<!--给子组件绑定一个myevent自定义事件,这个自定义事件的事件句柄是getMsg-->
<myComponent @myevent='getMsg' />
</div>
`,
data() {
return {
msg: '',
}
},
methods: {
// 父组件用于接收子组件的方法。
getMsg(msg) {
this.msg = msg;
}
},
components: { myComponent },
}
四.如何在子组件修改父组件数据
- 把修改数据的逻辑方法写在父组件上.
const myComponent = {
template: `
<div>
<h3>子组件msg---{{msg}}</h3>
<button @click='show'>按钮</button>
</div>
`,
props: ['fn', 'msg'],
methods: {
show() {
this.fn(Math.random());
}
}
}
const App = {
template: `
<div id='app'>
<h3>App根组件msg--{{msg}}</h3>
<myComponent :fn='fn' :msg='msg' />
</div>
`,
data() {
return {
msg: '父组件的数据msg',
}
},
methods: {
// 用fn修改父组件的msg数据 => 这样父子组件可以同时更新。
// 只要让子组件能够触发父组件的这个fn.就可以实现子组件修改父组件的数据.
fn(msg) {
this.msg = msg
}
},
components: { myComponent },
}
- 在子组件中触发父组件的方法.(子传父)(Vue和React推荐的做法)
const myComponent = {
template: `
<div>
<h3>子组件msg---{{msg}}</h3>
<button @click='show'>按钮</button>
</div>
`,
props: ['msg'],
methods: {
show() {
this.$emit('myevent', Math.random())
}
}
}
const App = {
template: `
<div id='app'>
<h3>App根组件msg--{{msg}}</h3>
<myComponent @myevent='fn' :msg='msg' />
</div>
`,
data() {
return {
msg: '父组件的数据msg',
}
},
methods: {
// 用fn修改父组件的msg数据 => 这样父子组件可以同时更新。
// 只要让子组件能够触发父组件的这个fn.就可以实现子组件修改父组件的数据.
fn(msg) {
this.msg = msg
}
},
components: { myComponent },
}
五.组件混合
组件的公共选项部分
这部分需要通过组件的mixins选项引入.
如果组件本身就包含mx内的选项,则以组件的的选项为准.
const mx = {
computed: {
...mapState(['count'])
},
methods: {
...mapActions(['asyncPlus']),
...mapMutations(['reduce'])
}
}
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='asyncPlus(1)'>box--count++</button>
<button @click='reduce(1)'>box--count--</button>
</div>
`,
mixins: [mx],
}
六. bus通信
1. bus组件通信
任意组件的组件通信.
A组件传B组件
- B函数使用了一个方法接收A组件数据.
- A组件利用自定义事件调用B组件的这个方法,并传入A组件数据.
自定义事件一定需要给A组件添加吗?
不是,给任何组件添加自定义事件都可以.(只要方便获取这个组件)
因此我们在比较复杂的布局情况下,可以通过一个空的vue实例来充当一个中间组件.
给这个中间组件绑定自定义事件,通过这个中间组件触发自定义事件.
这个中间组件就充当了任意两个组件的数据运输工具.bus.
bus实现的通信,无法实现状态管理.
<script src="js/vue.js"></script>
<script>
// vm.$on => 添加一个自定义事件
// vm.$emit => 触发一个自定义事件
// vm.$off => 解绑自定义事件.
// 空的vue实例。
const bus = new Vue();
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='fn'>传递count给item</button>
</div>
`,
data() {
return { count: 666 }
},
methods: {
fn() {
bus.$emit('getcount', this.count);
}
}
}
const item = {
template: `
<div>
<h3>item组件---{{num}}</h3>
</div>
`,
data() {
return { num: 0 }
},
methods: {
getCount(count) {
this.num = count
}
},
created() {
bus.$on('getcount', this.getCount);
}
}
const App = {
template: `
<div id='app'>
<h3>App组件</h3>
<box />
<item />
</div>
`,
components: { box, item }
}
new Vue({
render: h => h(App),
}).$mount('#app');
</script>
2.子传父1
<script>
// 空的vue实例。
const bus = new Vue();
// <box @getcount='getcount' /> => 给box组件绑定自定义事件的一个简写
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='fn'>传递count给item</button>
</div>
`,
data() {
return { count: 666 }
},
methods: {
fn() {
this.$emit('getcount', this.count);
}
},
created() {
// 给当前的子组件绑定一个getcount事件,这个事件触发父组件的getcount方法
// this.$on('getcount', this.$parent.getcount);
}
}
const App = {
template: `
<div id='app'>
<h3>App组件---{{num}}</h3>
<box @getcount='getcount' />
</div>
`,
data() {
return {
num: 0
}
},
methods: {
getcount(count) {
this.num = count
}
},
components: { box }
}
new Vue({
render: h => h(App),
}).$mount('#app');
</script>
3. 子传父2 通过原型链
<script>
// 把控的vue实例bus对象,放在Vue的原型上。
// 这样所有组件实例都可以通过原型链访问到这个bus对象.
Vue.prototype.$bus = new Vue();
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='$bus.$emit("getcount", count)'>传递count给item</button>
</div>
`,
data() {
return { count: 666 }
},
}
const App = {
template: `
<div id='app'>
<h3>App组件---{{num}}</h3>
<box />
</div>
`,
data() {
return {
num: 0
}
},
components: { box },
created() {
// 在父组件created中给bus实例添加自定义事件
this.$bus.$on('getcount', (count) => {
this.num = count
});
}
}
new Vue({
render: h => h(App),
}).$mount('#app');
</script>
标签:count,26,const,子传父,App,msg,template,组件 来源: https://www.cnblogs.com/DarkCrow/p/15317055.html