组件化
开发大型应用的时候,页面往往拆分成为多个部分,这些部件可能会被多个页面使用,所以有必要对这些部件进行组件化。
在Vue里,所有的Vue实例都是组件。
全局组件
通过Vue.component()
来定义一个全局组件。
- 组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等
- 不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。
- 但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板
- 全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
- data必须是一个函数,不再是一个对象。
使用示例:
<body>
<div id="app">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
</body>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
Vue.component("counter",{
template:'<button @click="num++">点击次数:{{num}}</button>',
data(){
return{
num:0
}
}
})
var app = new Vue({
el: "#app",
data: {
num:0
},
})
</script>
组件复用
在上面的代码中,我们使用全局组件的时候,
<div id="app">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
我们点击按钮,发现它们的值相互之间不干涉,为什么呢?
在定义全局组件的时候,我们是用函数来定义data数据的,因此每个实例可以维护一份被返回对象的独立的拷贝:
data(){
return{
num:0
}
}
局部组件
一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着Vue的加载而加载。因此,对于一些并不频繁使用的组件,我们会采用局部注册。
使用示例:
<body>
<div id="app">
<hello1></hello1>
<hello1></hello1>
<hello1></hello1>
</div>
</body>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const hello = {
template: '<button @click="num++">点击次数:{{num}}</button>',
data(){
return{
num: 0
}
}
}
const app = new Vue({
el: "#app",
data: {
num: 0
},
components:{
hello1: hello
}
})
</script>
- components就是当前vue对象子组件集合。
- 其key就是子组件名称
- 其值就是组件对象名
hello1: hello
如果属性名和值相同可以只写名
- 效果与刚才的全局注册是类似的,不同的是,这个hello1组件只能在当前的Vue实例中使用。
组件通信
通常一个单页应用会以一棵嵌套的组件树的形式来组织:
- 页面首先分成了顶部导航、左侧内容区、右侧边栏三部分
- 左侧内容区又分为上下两个组件
- 右侧边栏中又包含了3个子组件
各个组件之间以嵌套的关系组合在一起,那么这个时候不可避免的会有组件间通信的需求。
父向子通信
- 父组件使用子组件时,自定义属性(属性名任意,属性值为要传递的数据)
- 子组件通过props接收父组件数据,通过自定义属性的属性名
使用示例:
<body>
<div id="app">
<hello1 v-bind:num1="num" title="title"></hello1>
</div>
</body>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const hello = {
template: '<button @click="num1++">点击次数:{{num1}}</button>',
props: ["num1"]
}
const app = new Vue({
el: "#app",
data: {
num:0
},
components: {
hello1: hello
}
})
</script>
props验证
验证父组件传递的数据模型是否是子组件需要的:
const hello = {
template: '<button @click="num1++">点击次数:{{num1}}</button>',
props: {
num1: {
type: Number,
default: 0,
required: true
}
}
}
定义一个复杂类型,例如数组,验证示例:
<body>
<div id="app">
<hello :items="items"></hello>
</div>
</body>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const hello = {
template: '<ul><li v-for="(item,index) in items" >第{{index+1}}个按钮是:{{item}}</li><br></ul>',
props: {
num1: {
type: Number,
default: 0,
required: true
},
items:{
type: Array,
default: [],
required: true
}
}
}
const app = new Vue({
el: "#app",
data: {
// num: "abc"
num: "123",
items:["变大","变小","变长"]
},
components: {
// hello1: hello //组件名的key和value一样时,可只写一个
hello
}
})
</script>
PS1:v-for
只能使用在<ul><li></li></ul>
的<li>
的类似结构里,不能直接作用与<li>
里。
PS2:子组件的自定义属性,必须使用v-bind
进行绑定
子向父通信
- 在上面的案例中,通过父向子通信,可以将num的初始值传递到子模板,它的值可以修改,但是父模板的num并没有改变,下面案例将演示如何修改子模版的数据,同时修改父模板的数据。
- 子组件调用这个
$emit()
方法,然后调用父模板的方法,间接修改数据。
<body>
<div id="app">
<hello :num1="num" @incres1="incres()"></hello>
</div>
</body>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const hello = {
template: '<button @click="subIncre()">点击+1,现在是:{{num1}}</button>',
props: {
num1: {
type: Number,
default: 0,
required: true
},
},
methods: {
subIncre(){
this.$emit("incres1");
}
}
}
const app = new Vue({
el: "#app",
data: {
// num: "abc"
num: 0,
},
components: {
// hello1: hello //组件名的key和value一样时,可只写一个
hello
},
methods:{
incres(){
this.num++
}
}
})
</script>
分析上面代码:数据的改变实际上是子组件调用父模板的方法间接修改的数据。
路由
vue-router简介与安装
使用vue-router和vue可以非常方便的实现 复杂单页应用的动态路由功能。
官网:https://router.vuejs.org/zh-cn/
使用npm安装:npm install vue-router --save
在index.html中引入依赖:
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
使用步骤
(1)创建router对象
(2)编写路由规则
(3)在父组件中引入router对象
需求
一个页面,包含登录和注册,点击不同按钮,实现登录和注册页切换,不能同时显示。
实现
(1)安装vue-router
(2)编写子组件
为了复用性,在开发中会将组件放入一个独立的JS文件,并将组件放进一个文件夹统一管理。
登录组件login.js
const loginForm = {
//template内只能有一个根标签
template: `
<form id="loginForm">
用 户 名:<input type="text"></br>
密  码:<input type="password"></br>
<input type="button" value="登录">
</form>
`
}
注册组件register.js
const registerForm = {
//template内只能有一个根标签
template: `
<form id="registerForm">
用 户 名:<input type="text"></br>
密  码:<input type="password"></br>
确认密码:<input type="password"> </br>
<input type="button" value="注册">
</form>
`
}
(3)编写index.html主页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件路由</title>
</head>
<body>
<div id="app">
<span><router-link to="/login">登录</router-link></span> 
<span><router-link to="/register">注册</router-link></span>
<hr>
<router-view></router-view>
</div>
</body>
<script src="../node_modules/vue/dist/vue.min.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script src="js/loginForm.js"></script>
<script src="js/registerForm.js"></script>
<script>
//创建router对象
const router = new VueRouter({
routes: [ //编写路由规则
{
path:"/login", //请求路径,以“/”开头,不可省略
component:loginForm //组件名称
}, {
path: "/register",
component: registerForm
}
]
})
const app = new Vue({
el: "#app",
router //引入上面定义的router对象 ,可以省略在component中注册的路由组件
})
</script>
</html>
案例目录结构