Vue组件化

Note

组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项

非单文件组件

即一个文件中有若干组件

基本使用

基本配置

基本配置和实例对象相同,只有两个注意:

data: {
	n: 1,
	opacity: 2
},

改变data时data内的元素随之改变,而设置为函数,将对象作为返回值:

data() {
	return {
		n: 1,
		opacity: 2
	}
}

每次调用都会传入一个全新的对象,不会改变原有数据

使用组件

  1. 创建组件:
    使用extend方法,组件要包含数据和结构(html语句)
    使用标签template定义结构,` `模板字符串语法
const school = Vue.extend({
	template: `
	<div>
		<h1>{{name}}</h1>
		<h1>{{age}}</h1>
	</div>
	`,
	data() {
		return {
			name: "Orange",
			age: 18
		}
	}
})
  1. 在实例中注册组件

使用

// 局部
const vm = new Vue({
	el: '#root',
	components: {
		school,
	}
});
// 全局
Vue.component('name',{
	school,
})

注册名和临时变量名相同时可简写
3. 编写组件标签

<div id="root">
	<school></school>
</div>

脚手架环境下可使用自闭合标签

<school/>

组件命名

<school></school>
<School></School>
  1. 使用-连接
const tmp = Vue.extend({})
const vm = new Vue({
	el: '#root',
	components: {
		'my-school':tmp,
	}
});

注意JS语法要求属性的key含有-时必须用''包裹
对应标签

 <my-school></my-school>
  1. 所有单词首字母大写(需要Vue脚手架支持)
const school = Vue.extend({
	name:"ShowName"
})

简写方法

可以直接不调用extend()方法:

const school = {
	template: `
	<div>
		<h1>{{name}}</h1>
		<h1>{{age}}</h1>
	</div>
	`,
	data() {
		return {
			name: "Orange",
			age: 18
		}
	}
}

组件嵌套

组件内部也可以使用components属性配置子组件,由于是在内部配置,只能在该组件内部调用(即template内)

<body>
    <div id="root">
        <school></school>
    </div>
    <script>
        const student = {
            template: `
            <div>
                <h3>{{name}}</h3>
            </div>
            `,
            data() {
                return {
                    name: "Orange"
                }
            }
        };
        const school = {
            template: `
            <div>
                <h1>{{address}}</h1>
                <student></student>  <!-- 内部调用子组件  -->
            </div>
            `,
            data() {
                return {
                    address: "StairWay to Heaven"
                }
            },
            components:{
                student
            }
        };
        const vm=new Vue({
            el:"#root",
            components:{
                school,
            }
        })
    </script>
</body>

在标准化开发中,通常使用app组件管理所有组件,实例对象只管理app组件,同时将结构放在实例对象中用template定义,使逻辑清晰:

<body>
    <div id="root">    
    </div>
    <script>
        const student = {
            template: `
            <div>
                <h3>{{name}}</h3>
            </div>
            `,
            data() {
                return {
                    name: "Orange"
                }
            }
        };
        const school = {
            template: `
            <div>
                <h1>{{address}}</h1>
                <student></student>
            </div>
            `,
            data() {
                return {
                    address: "StairWay to Heaven"
                }
            },
            components:{
                student
            }
        };
        const hello={
            template: `<p>hello</p>`,
        }
        const app={
            template:`
            <div>
                <hello></hello>
                <school></school>
            </div>
            `,
            components:{
                school,
                hello
            }
        }
        const vm=new Vue({
            el:"#root",
            template:`<app></app>`,
            components:{
                app,
            }
        })
    </script>
</body>

注意:Vue2中的template只允许一个根标签,即2个及以上的标签要用一个div包裹

关于VueComponent

  1. school组件本质是一个名为VueComponent的构造函数,且是由Vue.extend()生成的。

  2. 只需要写<school/><school></school>,Vue解析时会创建school组件的实例对象,即Vue执行:new VueComponent(options)

  3. 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent

  4. 关于this指向:

    1. 组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是VueComponent实例对象
    2. new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是Vue实例对象
  5. 一个重要的内置关系:

    • VueComponent.prototype.__proto__ === Vue.prototype
    • 为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。

单文件组件

单文件组件 — Vue.js
模块化开发的思想,和上面的类似,只不过每个组件放在单独的文件夹内
一般文件结构:

脚手架

使用官方脚手架Vue CLI,见资源汇总
几个陌生语法:

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- <%= BASE_URL %>指的是本文件夹 是Vue脚手架的特有语法-->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- 配置页面标题,是webpack的语法,将package.json中name属性的值作为标题 -->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <!-- 浏览器不支持JS时,noscript包裹的内容就会被渲染 -->
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>

  </body>
</html>

scoped属性

修饰组件的<style>标签,官方文档中作了详细的解释:HTML&CSS

在渲染页面时,所有组件的style都会同时渲染,此时就可能会出现同名的问题,同名时依照后来的覆盖前面的样式的原则,此时使用scoped属性修饰<style>标签说明改样式为局部样式,不会影响其他组件

<style scoped></style>

但是这个属性不适合用在App组件中

render()函数

render()在使用精简版的vue时被启用,此时template不会被解析,需要使用该函数
render()有一个返回值,是一个函数,参数为html模板,包含标签和html内容

new Vue({
  render: h => h(App),
})

上方的代码就是render的简单实现,标准写法为:

new Vue({
  render(createElement) {
    return createElement('h1', 'Hello');
  }
})

此时会在页面上渲染<h1>Hello<\h1>,由于函数中没有使用this,所以可以写为箭头函数,又参数只有一个,可以不加(),又App中同时包含标签和内容,所以只传入了一个参数

ref

官方文档:API-ref
key一样,ref是一个特殊属性,被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素
如果用在子组件上,引用就指向组件实例:

<div id="app">
	<h1 ref="hello">Hello</h1>
	<School ref="sch"></School>
	<button @click="ShowELement" ref="btn">Get Element</button>
</div>

使用方法可以简单的查看ref的指向:

methods: {
	ShowELement() {
		console.log(this.$refs);
	}
}

这里的方法是组件的方法,所以this指向组件实例对象vc,参见Vue组件化#关于VueComponent

配置项

脚手架配置项

props

props可以是数组或对象,用于接收来自父组件的数据。props可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。
基于数组时要写成字符串

props: ['title', 'likes', 'isPublished', 'commentIds', 'author']

基于对象时可以使用以下选项:

每个数据也要写成对象

name:{
	type:String, //name的类型是字符串
	required:true, //name是必要的
},
age:{
	type:Number,
	default:99 //默认值
},

注意事项

  1. 父组件传入的类型默认都是String类型,若想使用数字等,需要使用:(v-bind)
<School name="A" :age='18' address="a"></School>
  1. 父组件传入的数据不允许修改
  2. props中的数据优先级高于data,即优先接收props中的数据根据这一特性可以在data中配置临时元素实现对props中的元素的修改(不是修改原有元素)
data() {
	console.log(this)
	return {
		msg:'我是一个尚硅谷的学生',
		myAge:this.age
	}
},
methods: {
	updateAge(){
		this.myAge++
	}
},
//接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
props:{
	age:{
		type:Number,
		default:99 //默认值
	},
}

mixin

API-mixin

mixins选项接收一个混入对象的数组。这些混入对象可以像正常的实例对象一样包含实例选项,这些选项将会被合并到最终的选项中,使用的是和 Vue.extend() 一样的选项合并逻辑

使用方法:

export const mix = {
	methods: {
		showName() {
			alert(this.name);
			console.log('mix')
		}
	},
}

导入时也相应的改变写法:

import { mix } from '../mixin'
Vue.mixin(hunhe)
Vue.mixin(hunhe2)

导入的组件将会适用于本工程的所有Vue实例对象(vm)和组件实例对象(vc)

注意:

插件

插件 — Vue.js

export default {
	install(Vue) {
		//自定义全局过滤器
		Vue.filter('SliceString', function (value) {
			return value.slice(0, 4);
		});
		//自定义全局指令
		Vue.directive('focus', {
			// 当被绑定的元素插入到 DOM 中时……
			inserted: function (el) {
				// 聚焦元素
				el.focus()
			}
		});
		//...
	}
}
import plugins from './plugins.js'
Vue.use(plugins)

就可以正常使用所有在插件中定义的方法,不需要其他操作

Note

如果遇到报错:"import ... =" 只能在 TypeScript 文件中使用,只需在配置文件settings.json中添加:"javascript.validate.enable":false即可
参见这篇博客

$nextTick()

$nextTick()
在DOM更新后执行回调函数

组件自定义事件

组件自定义事件适用于子组件==>父组件

使用实例对象的函数$emit() 触发事件

$emit()

vm.$emit( eventName, […args] )
<Student @myEvent="demo"></Student>

只有一个参数时:

demo(name) {
	console.log('student name is ' + name)
}
this.$emit('myEvent', this.name);

有多个参数时:

demo(name,...a) {
	console.log('student name is ' + name + a)
}
getStudentName() {
	this.$emit('myEvent', this.name,1,2,3);
}

...a是es6中的写法,表示其他所有参数均存放在数组a中

绑定自定义事件

在子组件中,创建待绑定的函数,同时传入参数
下面为button标签绑定了点击事件的函数getStudentName()

<button @click="getStudentName">click</button>
methods: {
	getStudentName() {
		this.$emit('myEvent', this.name);
	}
},

使用方法$emit()绑定触发的函数并传入参数
相应的,在父组件中,绑定对应的事件和方法:

<Student @myEvent="demo"></Student>
methods: {
	demo(name) {
		console.log('student name is ' + name)
	}
},
<Student ref="student"></Student>
mounted() {
	this.$refs.student.$on('myEvent', this.demo)
}

使用ref将子组件添加到父组件的ref对象上,使用$refs()方法访问,因为是组件,可以使用组件实例方法$on()绑定事件
实际上二者的逻辑一致,都是为标签绑定方法

解绑自定义事件

使用$off()解绑自定义事件

vm.$off( [event1, event2] )

使用数组形式解绑多个事件
或者什么都不传,解绑所有自定义事件

注意事项

this.$refs.student.$on('myEvent', this.demo)

Vue中的一般规则是v-on与谁绑定,this就是谁,上面的代码this正常应该为Student组件实例对象,但是特别的,如果该元素在本实例对象中定义,那么this指向本实例对象,demo在本实例对象中定义了,所以this可以访问到demo()

<Student ref="student" @click.native="demo"></Student>