组件
全局组件
<body> <div id="app"> <component-test></component-test> </div> <script type="text/template" id="template-test"> <div> 全局组件 {{count}} </div> </script> <script> Vue.component('component-test',{ template:"#template-test", data(){ return { count:0 } }, }) var vm = new Vue({ el: "#app", }) </script> </body>
局部组件
<body> <div id="app"> <compa></compa> </div> <script> const compb = { template: '<div>compb</div>' } const compa = { template: ` <div> compa <compb></compb> </div> `, components: { compb } } var vm = new Vue({ el: "#app", components: { compa } }) </script> </body>
父传子
通过prop向子组件传递数据
<body> <div id="app"> <component-test :mytitle="title" :mylist="list"></ccomponent-test> //@key </div> <script type="text/template" id="template-test"> <div> <h2>{{mytitle}}</h2> <ul> <li v-for="user in mylist"> {{user.username}} </li> </ul> </div> </script> <script> Vue.component("component-test",{ props:['mytitle','mylist'],//@key template:"#template-test" }) var vm = new Vue({ el:"#app", data:{ title:"parent-title", list:[ {username:'hanye',age:20}, {username:'hansai',age:30} ] } }) </script> </body>
子传父
通过$emit 事件泡发
<body> <div id="app"> {{parent_count}} <component-test @countchange="handleChange"></component-test> //@key </div> <script type="text/template" id="template-test"> <div> <h2>{{count}}</h2> <button @click="increment">+</button> </div> </script> <script> Vue.component('component-test',{ template:"#template-test", data(){ return { count:0 } }, methods:{ increment(){ this.count++ this.$emit('countchange',this.count)//@Key } } }) var vm = new Vue({ el:"#app", data:{ parent_count:0 }, methods:{ handleChange(num){ this. parent_count = num } } }) </script> </body>
非父子
非父子组件之间进行通信,用 中央事件总线 eventbus
中央总线的特点,只要订阅了,触发的时候就能收到通知
<body> <div id="app"> <component-a></component-a> <component-b></component-b> </div> <script> var eventbus = new Vue();//空vue实例 就是中央事件总线 Vue.component('component-a',{ template:`<div>组件a</div>`, mounted(){ eventbus.$on("message",function(msg){ //@key console.log(msg);//wanglei }) } }) Vue.component('component-b',{ template:` <button @click="handleclick">组件b 点击</button>`, methods:{ handleclick(){ eventbus.$emit("message","收到通知了吗") //@key } } }) var vm = new Vue({ el:"#app", data:{ username:"wanglei" }, }) </script> </body>
动态组件
通过 Vue 的 component 元素加一个特殊的 is
属性来实现
<div id="app"> <!-- 动态组件 --> <component v-bind:is="currentTap"></component> <button v-for="tab in tabs" @click="handleChange(tab)"> {{tab}} </button> </div> <script> Vue.component('tap-position', { template: '<div>position</div>' }) Vue.component('tap-search', { template: '<div>search</div>' }) var vm = new Vue({ el: "#app", data: { currentTap: 'tap-position', tabs: ['tap-position', 'tap-search'] }, methods: { handleChange(tabs) { this.currentTap = tabs; } } }) </script> </body>
keep-alive
在动态组件上使用keep-alive
当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题
重新创建动态组件的行为通常是非常有用的,但是在这个案例中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 `` 元素将其动态组件包裹起来
<body> <div id="app"> <!-- 动态组件keep-alive --> <keep-alive> <component v-bind:is="currentTap"></component> </keep-alive> <button v-for="tab in tabs" @click="handleChange(tab)"> {{tab}} </button> </div> <script> Vue.component('tap-position', { template: '<div>position <input type="text"/></div>' }) Vue.component('tap-search', { template: '<div>search</div>' }) var vm = new Vue({ el: "#app", data: { currentTap: 'tap-position', tabs: ['tap-position', 'tap-search'] }, methods: { handleChange(tabs) { this.currentTap = tabs; } } }) </script> </body>
在组件上使用v-model
这个组件内的 input必须:
- 将其
value
attribute 绑定到一个名叫value
的 prop 上 - 在其
input
事件被触发时,将新的值通过自定义的input
事件抛出
- 自己实现的v-model
<body> <div id="app"> <h1>{{username}}</h1> <!-- <component-test v-model="username"></component-test> --> <!-- 这2句是等价的 --> <component-test :value="username" @input="handleInput"></component-test> </div> <script> Vue.component('component-test',{ props:['value'], template:` <input type="text" :value="value" @input="handleclick">`, data(){ return { count:0 } }, methods:{ handleclick(event){ this.$emit('input',event.target.value) } } }) var vm = new Vue({ el:"#app", data:{ username:"wanglei" }, methods:{ handleInput(username){ this.username = username } } }) </script> </body>
- v-model
<body> <div id="app"> <h1>{{username}}</h1> <component-test v-model="username"></component-test> </div> <script> Vue.component('component-test',{ props:['value'], template:` <input type="text" :value="value" @input="handleclick">`, data(){ return { count:0 } }, methods:{ handleclick(event){ this.$emit('input',event.target.value) } } }) var vm = new Vue({ el:"#app", data:{ username:"wanglei" } }) </script> </body>
自定义组件
自定义组件的v-model
一个组件上的 v-model
默认会利用名为 value
的 prop 和名为 input
的事件,但是像单选框、复选框等类型的输入控件可能会将 value
attribute 用于不同的目的。model
选项可以用来避免这样的冲突,改变默认值
<body> <div id="app"> {{checkvalue}} <component-checkbox v-model="checkvalue"></component-checkbox> </div> <script> Vue.component('component-checkbox',{ //改变默认值 model:{ prop:'checked',//默认值是:value event:'change'//默认值:input }, props:['checked'], template:`<input type="checkbox" :checked="checked" @change="handleChange"/>`, methods:{ handleChange(event){ this.$emit("change",event.target.checked) } } }) var vm = new Vue({ el:"#app", data:{ checkvalue:true } }) </script> </body>
.sync修饰符
- sync 代码
<body> <div id="app"> {{name}} <component-input :myname.sync="name"></component-input> </div> <script> Vue.component('component-input',{ template:`<input type="text" @change="$emit('update:myname',$event.target.value)"/>` }) var vm = new Vue({ el:"#app", data:{ name:'' } }) </script> </body>
- 上面sync的代码实现的功能和下面这些代码一样
<body> <div id="app"> {{name}} <component-input @change="handleChange"></component-input> </div> <script> Vue.component('component-input',{ template:`<input type="text" @change="$emit('change',$event.target.value)"/>` }) var vm = new Vue({ el:"#app", data:{ name:'' }, methods:{ handleChange(val){ this.name = val } } }) </script> </body>
插槽
具名插槽
意思就是起了名字的插槽
<body> <div id="app"> <component-a> <template #header> <h1>header</h1> </template> <template #main> <h1>main</h1> </template> <h1>footer</h1> </component-a> </div> <script type="text/template" id="template-a"> <div> <header> <slot name="header"></slot> </header> <main> <slot name="main"></slot> </main> <footer> <slot></slot> </footer> </div> </script> <script> Vue.component('component-a',{ template:"#template-a" }) var vm = new Vue({ el:"#app", }) </script> </body>
作用域插槽
有时让插槽内容能够访问子组件中才有的数据是很有用的
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的
<body> <div id="app"> <component-user v-slot:default="obj"> //default是插槽的名字,default 可以省略掉 {{obj.myname}} </component-user> </div> <script> Vue.component('component-user',{ template:`<div> <slot :myname="name"></slot> //@key </div>`, data(){ return { name:'hanye' } } }) var vm = new Vue({ el:"#app", data:{ name:'' } }) </script> </body>
后备内容
意思其实就是默认值
<body> <div id="app"> <my-button>确定</my-button> </div> <script> Vue.component('my-button', { template: ` <button> <slot>submit</slot> //如果不传就是submit </button> ` }) var vm = new Vue({ el: "#app", data: { username: 'hanye' } }) </script> </body>
prop
<body> <div id="app"> <component-a a="123" :b="123" :c="[1,2]" :d="{name:'wanglei',age:'20'}" :f="handle" h="banana"> </component-a> </div> <script> Vue.component('component-a', { props:{ a:String, b:{ required:true, type:Number }, c:Array, f:Function, d:{ type:Object, default:function(){ return {message:'hello'} } }, i:{ required:false, type:Number, default:100 }, h:{ required:true, validator:function(value){ if(value.indexOf('banana') > -1) { return true; } else { return false; } } } }, template: '<div>test</div>' }) var vm = new Vue({ el: "#app", methods:{ handle(){ } } }) </script> </body>
处理边界情况
访问根 和父级组件实例
意思是子组件拿父组件的数据
通过parent
<body> <div id="app"> <compa></compa> </div> <script> const compb = { template:`<div> 访问跟组件的数据: {{$root.username}} <br/> 访问父元素的数据: {{$parent.title}} </div>`, mounted(){ console.log(this.$root.username); console.log(this.$parent.username); } } const compa = { template:`<div > <compb></compb> </div>`, components:{ compb }, data(){ return { title:"compa-title" } } } var vm = new Vue({ el:"#app", data:{ username:"wanglei" }, components:{ compa } }) </script> </body>
访问子组件实例
意思是父组件拿子组件的数据
尽管存在 prop 和事件,有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,你可以通过 ref
这个 attribute 为子组件赋予一个 ID 引用
<body> <div id="app"> <compa ref="refcompa"></compa> </div> <script> const compa = { template:`<div > compa </div>`, data(){ return { title:"compa-title" } } } var vm = new Vue({ el:"#app", data:{ username:"wanglei" }, components:{ compa }, mounted(){ console.log(this.$refs.refcompa.title); } }) </script> </body>
依赖注入
provide
选项允许我们指定我们想要提供给后代组件的数据/方法,然后在任何后代组件里,我们都可以使用 inject
选项来接收指定的我们想要添加在这个实例上的属性
(注意不是响应式)
<body> <div id="app"> <compa ></compa> </div> <script> const compa = { inject:['username'],//@key template:`<div > {{username}} </div>`, } var vm = new Vue({ el:"#app", data:{ username:"wanglei2" }, components:{ compa }, provide(){ //@key return { username:this.username } } }) </script> </body>
访问元素
<body> <div id="app"> <input type="text" ref="myinput"> //@key </div> <script> var vm = new Vue({ el: "#app", mounted() { console.log(this.$refs.myinput); } }) </script> </body>
组件的生命周期
<body> <div id="app"> {{username}} <compa></compa> </div> <script> var compa = { template:`<div>组件a</div>`, beforeCreate(){ console.log("child beforeCreate"); }, created(){ console.log("child created"); }, beforeMount(){ console.log("child beforeMounted"); }, mounted(){ console.log("child mounted"); }, beforeUpdate(){ console.log("child beforeUpdate"); }, updated(){ console.log('child updated') }, beforeDestroy(){ console.log("child beforeDestory"); }, destroyed(){ console.log("child destoryed"); } } var vm = new Vue({ el:"#app", data:{ username:"wanglei" }, components:{ compa }, beforeCreate(){ console.log("root beforeCreate"); }, created(){ console.log("root created"); }, beforeMount(){ console.log("root beforeMounted"); }, mounted(){ console.log("root mounted"); this.username="guoguo" setTimeout(() => { vm.$destroy(); }, 1000) }, beforeUpdate(){ console.log("root beforeUpdate"); }, updated(){ console.log('root updated') }, beforeDestroy(){ console.log("root beforeDestory"); }, destroyed(){ console.log("root destoryed"); } }) </script> </body> 打印出来的结果是: root beforeCreate root created root beforeMounted child beforeCreate child created child beforeMounted child mounted root mounted root beforeUpdate root updated root beforeDestory child beforeDestory child destoryed root destoryed
组件之间传递数据的总结
props emit
缺点:如果组件嵌套层次多的话,数据传递比较繁琐
provide inject(依赖注入)
缺点:不支持响应式
root, parent refs
eventbus
缺点:数据不支持响应式