vue.js 组件知识总结
组件局部注册
<div id="app">
hello vue
<my-component></my-component>
</div>
<script>
var Child = {
template:'<div>局部注册组件</div>'
};
new Vue({
el:'#app',
components:{
'my-component':Child
}
})
</script>
is 属性挂载组件
常见:ul、select、table
(字符串模板除外:.vue文件)
例:在table里使用组件无效,添加一个is属性。在is下往下写无效。
<div id="app">
<table border="1">
<tbody is="my-com"></tbody>
</table>
</div>
<script>
Vue.component('my-com',{
template:'<tr><td>1</td><td>2</td></tr>'
});
new Vue({
el:'#app'
})
</script>
props
数组和对象是引用类型,指向同一片内存空间,所以props是数组和对象时,修改子组件内容会影响到父组件。默认双向绑定。
数据验证
Vue.component('my-component',{
props:{
propA:Number, //必须是数字类型
propB:[String,Number],
propC:{//布尔值,如果没定义,默认是true
type:Boolean,
default:true
},
propD:{//数字,而且是必传
type:Number,
required:true
},
propE:{//如果是数组或对象,默认值必须是一个函数来返回
type:Array,
default:function (){
return [];
}
},
propF:{//自定义一个验证函数
validator:function(value){
return value > 10;
}
}
}
})
自定义事件
v-on 监听自定义事件外,也监听DOM事件。用.native
修饰符表示监听的是一个原生事件,监听的是该组件的根元素。
<my-component v-on:click.native="handleClick"></my-component>
使用 v-model
- 接受一个value属性;
- 在有新的value时触发input事件
<div id="app">
{{ value }}
<my-com v-model="value" @click="minus"></my-com>
<!-- 父向子:执行减操作 -->
<button @click="minus">-1</button>
</div>
<script>
// 点击add加一 把const++通过input事件把const传递出去
Vue.component('my-com', {
props: {
// 这个必须叫value,并与input一一对应。不懂为啥?
value: {
type: Number
}
},
template: '<div> {{ const1 }} <button @click="add">+1</button></div>',
data() {
return {
const1: this.value
}
},
methods: {
add() {
this.const1++;
this.$emit('input', this.const1); //必须为input
}
},
watch:{
value(val){
this.const1 = val;
}
}
});
new Vue({
el: '#app',
data: {
value: 1
},
methods: {
minus() {
this.value--;
},
}
})
</script>
非父子组件通信
推荐使用一个空 Vue 实例作为中央事件总线(bus),也就是一个中介。
<div id="app">
{{ message }}
<my-coma></my-coma>
</div>
<script>
// 创建空的bus实例
const bus = new Vue({
//可以继续扩展bus
//data,methods...
});
// 注册一个组件
Vue.component('my-coma',{
template:'<button @click="passEvent">click</button>',
methods:{
passEvent(){
bus.$emit('msgInfo','come from children ');
}
}
});
new Vue({
el:'#app',
data:{
message:''
},
mounted(){
bus.$on('msgInfo',(msg)=>{
this.message = msg;
})
}
})
</script>
非父子组件通信--父链、子链
this.$parent
可以在子组件中取到父组件的东西
this.$children
可以拿到app 实例里的所有子组件,如果有很多子组件,可以得到一个数组。
修改父(子)组件内容,最好不这么做!
非父子组件通--子组件索引
子组件索引只在组件渲染完成后填充,非响应式的,仅作为直接访问子组件的应急方案,应当避免在模板、计算属性中使用自定义子组件索引。
加载两个相同的组件,用ref 区分,如果需要for循环,可以动态绑定:ref="a"
<my-coma ref='a'></my-coma>
<my-coma ref='b'></my-coma>
取值:
this.$refs.a
slot 内容分发
当需要让组件组合使用时,要内容分发,混合父组件内容和子组件模板就叫内容分发。
<div id="app">
<my-com>
<!-- message生命周期由父组件或父实例来决定的,包括作用域,和my-com本身没关系。 -->
{{ message }}
</my-com>
</div>
<script>
//hello world:
// hello来自‘my-com’组件本身的内容,
// world:父组件通过<slot>来充当的这部分内容
Vue.component( 'my-com',{
//slot 标签挂载内容,
template:'<div>hello <slot></slot></div>',
});
new Vue({
el:'#app',
data:{
message:'world'
}
})
</script>
具名slot
在一个组件里,设置多个slot来区分显示的内容。(按照slot顺序展示:你好 朋友)
<my-com>
<div slot="a">
你好
</div>
<div slot="b">
朋友
</div>
</my-com>
script
<script>
Vue.component( 'my-com',{
template:'<div>hello <slot name="a"></slot><slot name="b"></slot></div>',
});
new Vue({
el:'#app',
})
</script>
slot展示默认内容
如果组件里没有内容,就会显示slot里的默认内容。内容作用域只与当前组件(my-com)相关,与父组件无关。
<div id="app">
<my-com></my-com>
</div>
script
Vue.component( 'my-com',{
template:'<div>hello <slot>这里是默认</slot></div>',
});
访问slot
rander函数用的多
mounted(){
this.$slots.default //取默认内容
this.$slots.a //取a的内容
}
组件的高级用法
递归组件
满足条件:
- 必须给组件设置一个name。
- 在一个合适的时间结束组件的递归,否则报超栈的错误。
<div id="app">
<my-com :count="1"></my-com>
</div>
<script>
Vue.component('my-com',{
name:'myCon',
props:{
count:{
type:Number,
default:1
}
},
template:'<div><my-com :count="count +1" v-if="count<3"></my-com>{{ count }}</div>',
});
new Vue({
el:'#app',
})
</script>
内联模板(少用)
给组件标签使用inline-template
特性,组件就会把它的内容当做模板,而不是当做内容分发。会替代子组件的template来显示。
<div id="app">
<my-com inline-template>
<div>
{{message}}
</div>
</my-com>
</div>
script
结果为:你好
<script>
Vue.component( 'my-com',{
// template:'<div>hello <slot>这里是默认内容</slot></div>',
data(){
return{
message:'你好'
}
}
});
new Vue({
el:'#app',
data:{
message:'world'
},
})
</script>
{{message}}作用域为组件内的data,有些难理解,少用吧~
异步组件
组件比较庞大时,使用异步组件
- 按需加载,可以节省首次加载的时间,提高速度,也算是一个性能优化。
- 那么一个组件可能会被使用多次,按需加载的话也不会加载多次,第一次加载完成就会缓存下来,和webpack是一样的,所以不用担心
<div id="app">
<my-com :count="1"></my-com>
</div>
<script>
Vue.component('my-com',function(resolve,reject){
window.setTimeout(function(){
resolve({
template:'<div>from 异步加载组件</div>'
})
},3000)
});
new Vue({
el:'#app',
})
</script>
$nextTick
等组件渲染完后使用
this.$nextTick( () => {
});
vue 如何观察数据变化?
并不是直接更新DOM,而是开启一个检查队列,缓存在一次事件循环中,缓存中会排除重复数据来避免不必要计算或DOM的操作,在下一次循环tick中,vue才会刷新队列,执行实际的操作。如果使用for循环动态改变100次数据,实际上只触发1次
检测:
vue 优先使用promise检测,不支持会使用HTML5新特性 Mutation Observer 的方法,都不支持采用setTimeout
x-template
前提是你没有使用webpack
<div id="app">
<my-com></my-com>
<script type="text/x-template" id="my">
<div>...</div>
</script>
</div>
手动挂载实例
一般使用 new vue 创建实例
new Vue({
el:'#app'
})
在特殊情况下也可以动态创建 vue 实例。
vue提供两个API:
extend : 基础vue 构造器,创建了一个子类。参数包含组件选项的对象
$mount :挂载 (有很多参数,参见官网)
<div id="app">
hello Vue
</div>
<script>
// 用extend 定义组件内容
const myComponent = Vue.extend({
template:'<div> from extend </div>'
});
//通过 $mount 手动挂载在指定DOM里(通过工厂函数方法),支持class和id选择器
new myComponent().$mount('#app');
</script>
结果:from extend 覆盖 hello vue
问答模块
1.watch 与 computed 区别
watch:单纯监听某些东西变化,执行一些操作。
computed:根据已有的 data 或 props 的改变,来动态输出一个内容.常见的:排序、过滤、千位运算符等计算属性。
2.bus 怎么避免全局污染?
在 webpack 中,bus是一个bus.js文件,通过模块化就不会全局污染。
import bus from './bus.js'
3.mixins混合
把部分vue的配置merge到实例里,如:data、methods、props、计算属性、生命周期等内容,形成复用。
mixins:[]
组件解决UI的复用,mixins混合解决组件内部配置的复用
4.远程搜索(需要补充)
数据过多不建议使用select数据下拉。
5.keep-alive (需要补充)
让组件做缓存
文章内容参考:
Vue.js 实战之组件篇:https://segmentfault.com/l/1500000009448056/play
说说Vue的异步组件:https://juejin.im/entry/599562f36fb9a0249716d299
vue.js官网:https://cn.vuejs.org/v2/guide/components.html