一、组件
在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。
但是如果每个页面都独自开发,这无疑增加了我们开发的成本。所以我们会把页面的不同部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。
在vue里,所有的vue实例都是组件
全局组件
Vue.component("",{
template:"",
data:""
});
我们用Vue.component();
,来定义一个全局组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<loltoulan></loltoulan><br><br>
<loltoulan></loltoulan><br><br>
<loltoulan></loltoulan><br><br>
<loltoulan></loltoulan><br><br>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
Vue.component("loltoulan",{
template:"<button v-on:click='add' @click.right.prevent='num--'>你点了我 {{ num }} 次,我记住了.</button>",
data(){
return{
num:0
}
},
methods:{
add:function () {
this.num++;
}
}
});
const model=new Vue({
el: "#app"
});
</script>
</body>
</html>
效果:
- 组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等
- 不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。
- 但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板
- 全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
- data必须是一个函数,不再是一个对象。
- 必须有根,也就是必须有 const model= new Vue({el: “#app”})
在上面的案例中我们发现:虽然公用的是同一个num,但是各个组件之中的值却没有发生关联,这就是因为,在Vue.component();
中data属性的值是一个函数,而不是一个对象,因为如果是一个对象的话,因为我们操作的都是同一个对象,就像v-model中双向绑定那样,具有同样的值,而函数则不一样,函数会再执行结束后立即释放,不会存在引用的问题。
局部组件
//定义我们的局部组件
const hello={};
const model=new Vue({ components:{ key: value}});
其key就是子组件名称
其value就是组件对象名
效果与刚才的全局注册是类似的,不同的是,这个counter组件只能在当前的Vue实例中使用
局部组件和全局组件不同的是,局部组件需要在Vue实例中进行注册,而且与全局组件不同的是,局部组件的创建方法,业务和全局组件不同
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<loltoulan></loltoulan><br><br>
<loltoulan></loltoulan><br><br>
<loltoulan></loltoulan><br><br>
<loltoulan></loltoulan><br><br>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
//定义我们的局部组件
const hello={
template:"<button v-on:click='add' @click.right.prevent='num--'>你点了我 {{ num }} 次,我记住了.</button>",
data(){
return{
num:0
}
},
methods:{
add:function () {
this.num++;
}
}
};
const model=new Vue({
el: "#app",
components:{
//在component中注册我们测局部组件
loltoulan : hello
}
});
</script>
</body>
</html>
组件通信
- 页面首先分成了顶部导航、左侧内容区、右侧边栏三部分
- 左侧内容区又分为上下两个组件
- 右侧边栏中又包含了3个子组件
各个组件之间以嵌套的关系组合在一起,那么这个时候不可避免的会有组件间通信的需求。
父向子通讯
就比如说我们在csdn的写作业面中,我们需要发布文章,我们点击发布文章之后会弹出
这样一张图,他有可能就会有一些向弹窗传递值的情况,这就是组件通讯。
在vue中组件通讯的方式如下:
局部组件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<toulan v-bind:name1="name" v-bind:num="num"></toulan>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
//定义我们的局部组件
const hello={
template:"<button v-on:click='num++' @click.right.prevent='num--' >" +
"你点了{{name1}}共{{ num }} 次,我记住你了.</button>",
props:{
name1:{
type:String,
required:true
},
num: {
type:Number,
default:0,
required: true
}
}
};
const model=new Vue({
el: "#app",
data :{
name:"Jacobshash",
num:0
},
components:{
//在component中注册我们测局部组件
toulan : hello
}
});
</script>
</body>
</html>
全局组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 此处的num仅仅只是为了父向子赋值所用-->
<loltoulan v-bind:number="num"></loltoulan>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
Vue.component("loltoulan",{
template:"<button v-on:click='add' @click.right.prevent='number--'>你点了我 {{ number }} 次,我记住了.</button>",
methods:{
add:function () {
this.number++;
}
},
props:["number"]
});
const model=new Vue({
el: "#app",
data:{
num:10
}
});
</script>
</body>
</html>
子向父通讯:
子组件接收到父组件属性后,默认是不允许修改的。怎么办?
既然只有父组件能修改,那么加和减的操作一定是放在父组件:
但是,点击按钮是在子组件中,那就是说需要子组件来调用父组件的函数,怎么做?
我们可以通过v-on指令将父组件的函数绑定到子组件上:
在子组件中定义函数,函数的具体实现调用父组件的实现,并在子组件中调用这些函数。当子组件中按钮被点击时,调用绑定的函数:vue提供了一个内置的this.$emit()函数,用来调用父组件绑定的函数
子组件中的事件只能调用子里面的方法,父里面的事件只能调用父里面的方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h2>num: {{num}}</h2>
<!--自定义事件-->
<counter :count="num" @inc="increment" @dec="decrement"></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component("counter", {
//调用方法
template: '\
<div>\
<button @click="plus">加</button> \
<button @click="reduce">减</button> \
</div>',
props: ['count'],
methods: {
plus() {
//通过调用特定方法,来进行父子组件的联系
this.$emit("inc");
},
reduce() {
this.$emit("dec");
}
}
});
var app = new Vue({
el:"#app",
data:{
//初始化num
num:0
},
// 父组件中定义操作num的方法,因为字中做出改变,默认是不能传递给父的
methods:{
increment(){
this.num++;
},
decrement(){
this.num--;
}
}
})
</script>
</body>
</html>
二、路由
可以通过不同的按钮实现不同的页面,而不刷新页面和地址栏
示例如下:
接下来,我们要做出相似的案例:
和Veujs从入门到应用(一)中的npm安装方法一样
首先,我们需要安装路由插件,如果我们是通过npm安装的,那么,我们就直接在
或者在创建工程的文件夹下,打开cmd窗口,输入命令
npm install vue-router --save
我们的文件夹下多了一个文件夹,如下:
安装成功之后,我们就开始
当然,我们这部分也是基于组件开发的,所以,我们可以通过,组件来进行创建
代码如下:
//因为我们这个组件的复用性不是很高,所以我们建议使用局部组件
//因为考虑到我们的登陆注册页面是比较宽广的,所以我们使用这种方式来编写模板页面
//值得注意的是,一个template中只能有一个根标签,也就是,一个同一级的标签
const loginForm = {
template: `
<div>
<h1>登录页面</h1>
用户名:<input type="text" /><br>
密 码:<input type="text" /><br>
<input type="submit" value="登录">
<input type="reset" value="重置">
</div>
`
};
//因为我们这个组件的复用性不是很高,所以我们建议使用局部组件
//因为考虑到我们的登陆注册页面是比较宽广的,所以我们使用这种方式来编写模板页面
//值得注意的是,一个template中只能有一个根标签,也就是,一个同一级的标签
const registerForm = {
template: `
<div>
<h1>注册页面</h1>
用 户 名:<input type="text" /><br>
密  码:<input type="text" /><br>
确认密码:<input type="text" /><br>
<input type="submit" value="登录">
<input type="reset" value="重置">
</div>
`
};
然后我们编写index中的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<span>登录</span>
<span>注册</span>
<hr/>
<div>
<!--<loginForm></loginForm>-->
<!--
疑问:为什么不采用上面的写法?
由于html是大小写不敏感的,如果采用上面的写法,则被认为是<loginform></loginform>
所以,如果是驼峰形式的组件,需要把驼峰转化为“-”的形式
-->
<!--显示组件-->
<login-form></login-form>
<register-form></register-form>
</div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<!--引入login.js-->
<script src="src/js/login.js"></script>
<!--引入register.js-->
<script src="src/js/register.js"></script>
<script type="text/javascript">
const vm = new Vue({
//绑定vue作用域
el: "#app",
//注册组件
components: {
loginForm:loginForm,
registerForm:registerForm
}
})
</script>
</body>
</html>
这时我们还没有引入路由组件的js文件
效果如下
我们期待的是,当点击登录或注册按钮,分别显示登录页或注册页,而不是一起显示。
但是,如何才能动态加载组件,实现组件切换呢?
虽然使用原生的Html5和JS也能实现,但是官方推荐我们使用vue-router模块。
让我们引入路由组件之后
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--router-link来指定跳转的路径-->
<h1><span><router-link to="/login">登录</router-link></span></h1>
<h1><span><router-link to="/register">注册</router-link></span></h1>
<hr/>
<div>
<!--vue-router的锚点-->
<router-view></router-view>
</div>
<!--<loginForm></loginForm>-->
<!--
疑问:为什么不采用上面的写法?
由于html是大小写不敏感的,如果采用上面的写法,则被认为是<loginform></loginform>
所以,如果是驼峰形式的组件,需要把驼峰转化为“-”的形式
npm install vue-router --save
-->
<!--<login-form></login-form>
<register-form></register-form>-->
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script src="src/js/login.js"></script>
<script src="src/js/register.js"></script>
<script>
const router = new VueRouter({
routes: [
{
path: "/login",
component: loginForm
},{
path: "/register",
component: registerForm
}
]
});
const app = new Vue({
el:"#app",
components:{
registerForm: registerForm,
loginForm:loginForm
},
router
})
</script>
</body>
</html>
效果: