VUE组件
一.组件基础
1.概念
组件类似于Vue实例,但是没有el挂载,而且data里面必须是函数(为了保证组件的复用不相互影响)
2.组件的定义和注册使用
定义:组件中定义模板(注意:模板中必须要包含一个根节点,最外层需要包住)
var component1=({
data(){
return {
count:0,
}
},
template:`<button @click='count++'>{{count}}</button>`
})
注册:全局注册和局部注册
Vue.component("buttoncount",component1);//全局
var vm=new Vue({
el:"#app",
data:{
},
components:{
"buttoncount":component1,
}
})
//局部注册,组件名全部使用小写不易犯错
3.关于组件与父级之间的数据传递
父传子:props
组件里面的props取一个自定义名字,那么template里面也要使用这个定义的名字,然后把vue实例的需要绑定的在组件上绑定
props绑定一整个对象,且props可以近行语法的校验
post: {
id: 1,
title: 'My Journey with Vue'
}
<blog-post v-bind="post"></blog-post>
等价于
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
校验:
props:{
number:Number,
age:{
type:Number,
default:18,
required:true,//是否是必须的
validator(value){
return value>18 &&value<60;
}
}
},
props是一个单向数据流,所以不要再子组件中试图改变他,如果要改变需要先定义好
①这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
②这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
但是在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。
非prop的特性
①替换合并已有的特性
<input type="date" class="form-control">
<bootstrap-date-input
data-date-picker="activated"
class="date-picker-theme-dark"
></bootstrap-date-input>
class和style的特性会合起来,而其他的一些比如type的就会被替换掉
②禁用特性继承
<div id="app">
<custom-input
v-bind:value="customInputText"
v-on:input="customInputText = $event"
></custom-input>
<!-- object{} -->
<custom-input
v-bind:value="customInputText"
v-on:input="customInputText = $event"
:a="a"
></custom-input>
<!-- {a:1} -->
</div>
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`,
mounted:function(){
console.log(this.$attrs);
}
})
const app = new Vue({
el:'#app',
data:{
customInputText:"customInputText",
a:'1'
}
});
当在子组件中加入inheritAttrs:false
时a属性就不会自动加到根元素上了。
总之一句话:$attrs存储非prop特性,inheritAttrs控制vue对非prop特性默认行为
也可以使用插槽(具名插槽)来传递数据
var component1=({
template:`
<div>
<p>插槽代码</p>
<slot name="default"></slot>
<slot name="top"></slot>
<slot name="late"></slot>
</div>
`,
})
<ppt>
<p slot="top">hello</p>
<span slot="late">word</span><span slot="default">e</span>
</ppt>
子传父:$emit
后面跟着的参数$event是当前的对象
$emit后面跟着的自定义方法如果写成了input,那么久相当于是input事件,就是双向绑定了
<textx @input="leftcon=$event"></textx>
<textx v-model="leftcon"></textx>
input输入框的绑定就是双向绑定的所以可以用v-model来绑定
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
//绑定了value值即input的值,绑定了input事件,传递的数据是当前对象的value值,就形成了v-model
4.is的使用和keep-alive
is可以在组件之间切换,但是切换的时候vue会重新创建实例,会反复重渲染导致性能问题,所以使用keep-alive可以在它们第一次被创建的时候缓存下来,使用keep-alive 要求被切换到的组件都有自己的名字
<div id="app">
<button v-for="tab in tabs" :key="tab.name" @click="zjs=tab">{{tab.name}}</button>
<component :is="zjs.component"></component>
//使用keep-alive
<keep-alive>
<component v-bind:is="zjs.component"></component>
</keep-alive>
</div>
<script>
var tabs = [
{
name: 'Home',
component: {
template: '<div>Home component</div>'
}
},
{
name: 'Posts',
component: {
template: '<div>Posts component</div>'
}
},
{
name: 'Archive',
component: {
template: '<div>Archive component</div>',
}
}
]
var vm=new Vue({
el:"#app",
data:{
tabs,
zjs:tabs[0],
}
})
</script>
解析html的时候,有些元素,诸如 <li>
、<tr>
和 <option>
,只能出现在其它某些特定的元素内部。
当你要在table里面插入组件的时候就需要用到is
<table>
<tr is="blog-post-row"></tr>//组件名
</table>
5. .sync的使用(类似model的双向绑定)
功能是:当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定。
this.$emit('update:title', newTitle)
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
//等同于下面的这段
<text-document v-bind:title.sync="doc.title"></text-document>
<template>
<div class="details">
<myComponent :show.sync='valueChild' style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"></myComponent>
<button @click="changeValue">toggle</button>
</div>
</template>
<script>
import Vue from 'vue'
Vue.component('myComponent', {
template: `<div v-if="show">
<p>默认初始值是{{show}},所以是显示的</p>
<button @click.stop="closeDiv">关闭</button>
</div>`,
props:['show'],
methods: {
closeDiv() {
this.$emit('update:show', false); //触发 input 事件,并传入新值
}
}
})
export default{
data(){
return{
valueChild:true,
}
},
methods:{
changeValue(){
this.valueChild = !this.valueChild
}
}
}
</script>
6.异步组件:会异步解析你的组件定义缓存起来,在这个组件需要被渲染的时候使用(通过异步的promise)
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回调传递组件定义
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})
Vue.component(
'async-webpack-example',
// 这个 `import` 函数会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
new Vue({
// ...
components: {
'my-component': () => import('./my-async-component')
}
})
关于异步,说一下vue+webpack实现异步加载的三种方法
1.
const Home = resolve => {
import("@/components/home/home.vue").then( module => {
resolve(module)
}
}
2.
const router = new Router({
routes: [
{
path: '/home',
component: (resolve)=> {
require(['../components/home/home'], resolve) // 省去了在上面去import引入
}
}
]
})
3.
const Home = r => require.ensure([], () => r(require('../components/home/home')), 'home');
const router = new Router({
routes: [
{
path: '/home/home',
component: Home,
name: 'home' ,
}
]
})
处理加载状态:
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
7.处理边界情况