ToDoList
使用前两天所学的知识,可以做一个很简单的小demo:ToDoList。代码如下:
<div id="root">
<input v-model="inputValue"/>
<button @click="addList">提交</button>
<ul>
<li v-for="(item,index) of list" :key="index">{{item}}</li>
</ul>
</div>
<script>
new Vue({
el:"#root",
data:{
inputValue:"",
list:[]
},
methods:
{
addList:function()
{
if(this.inputValue)
{
this.list.push(this.inputValue);
this.inputValue="";
}
}
}
})
</script>
简单描述一下实现的过程:首先通过点击提交按钮,触发addList事件,这个事件会把inputValue拉入list中,而inputValue肯定要求就是输入框的value这时候我们就利用双向绑定,把输入框的value值和inputValue进行绑定。最后形成了list这个数组。再通过v-for列表渲染这个list就能得出这个demo了。
ToDoList的组件拆分
组件
组件是什么?
简单来讲组件就是页面的一个部分,当你的页面十分庞大的时候,你可以将它拆分为几个小部分,也就是几个小组件。这样的话,这样的话,维护整个页面的难度就被大大降低。这就是组件开发的思想。
注册组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<script>
const cpn = Vue.extend({//步骤一创建组件构造器对象
template:
`<div>
<h1>我是猪</h1>
</div> `
});
Vue.component("cpn",cpn);//注册全局组件
const app = new Vue({
el:"#app",
})
</script>
</body>
</html>
第一步:创建组件构造器对象
使用Vue的extend方法创建一个组件构造器对象,它的参数是一个对象,就是组件的全部信息。
const cpn = Vue.extend({//步骤一创建组件构造器对象
template://这里我只给组件添加了template 选项。
`<div>//这里使用的是es6的字符串模板的语法。这样可以换行。
<h1>我是猪</h1>
</div> `
});
第二步,注册组件
注册组件,其实有两种注册方式
- 注册全局组件。
只需要Vue.component("组件名(也就是使用时组件的标签名)",对应的组件组件构造器对象);
example:
Vue.component("cpn",cpn);//注册全局组件
那么在它下面注册的vue实例都可以直接用它了,对他们都是可见的,全局的。
- 注册局部组件
注册局部组件更简单。创建vue实例或者vue组件的时候,有个options叫components。这个属性就是用来注册局部组件的,在这里面的注册的组件都可以在vue实例或者vue组件中使用。
example:
const cpn2 = Vue.extend({//创建组件构造器对象
template:
`<div>
<h1>你才是</h1>
</div> `
});
const app = new Vue({
el:"#app",
components:
{
cpn2:cpn2//注册局部组件
}
})
components是一个对象。它的属性名,就是组件的标签名。它的属性值就是对应组件的组件构造器对象。 当你注册之后你就可以使用该组件了
第三步,使用组件
- 全局组件
全局组件注册好之后,在它下面注册的vue实例是可以无条件用它的。 - 局部组件 局部组件在组件或实例中注册好之后,是可以在该实例或者组件中,任意使用的,但是它的使用范围仅限这个区间。因为他是局部的
注册组件的语法糖写法
在vue2.x中,其实刚刚那种三步式的写法已经很少了,都是使用语法糖的形式注册组件。其实也很简单,就是将第一步第二步融为一步来操作。跳过创建组件构造器的写法。但是其实vue内部还是在调用Vue.extend()方法。
- 注册全局组件的语法糖写法
Vue.component("cpn",{
template:``,
...
})
- 注册局部组件的语法糖写法
new Vue({
el:"#app",
components:{
cpn:{
template:``,
...
}
}
})
组件模板的分离写法
通常情况下,组件的模板可能很长,如果注册的时候不剥离出来可能代码就会显得很冗余没有结构,所以我们会把组件的模板剥离出来。有两种方式剥离组件的模板。
- 使用script标签进行剥离。
注意此时的类型应该是type="text/x-template"
<script type="text/x-template" id="cpn">//设置id属性
<div>
<h1>我是猪</h1>
</div>
</script>
<script>
const cpn = Vue.extend({
template:"#cpn"//template引用id属性
});
Vue.component("cpn",cpn);
const app = new Vue({
el:"#app",
})
如何将我们剥离出来的这个script标签和我们的组件联系起来这里是用id属性,通过id属性将两者联系起来
- 使用template标签。
其实同上面的差不太多。同样是通过id属性,关联起来。
<template id="cpn">
<div>
<h1>我是猪</h1>
</div>
</template>
<script>
const cpn = Vue.extend({
template:"#cpn"
});
Vue.component("cpn",cpn);
const app = new Vue({
el:"#app",
})
</script>
注意事项
1.全局组件要写在vue实例创建前才能在该实例对应的挂载元素内使用它。它只能渲染实例写在它后面的元素。如果一个元素内使用它但是它对应的vue实例创建在这个组件之前,不仅渲染不出来,而且会报错:
<div id="root" >
<todo-item></todo-item>
</div>
<div id="root1">
<todo-item></todo-item>
</div>
<script>
new Vue({
el:"#root1",
})
Vue.component("todo-item",{
template:"<li>item</li>"
})
new Vue(
{
el:"#root",
}
)
</script>
比如这里的#root1
只渲染出#root的组件
并且出现报错:
2. 模板必须有一个根元素且仅有一个根元素。(这一点在前面已经提及)
组件的一些问题
组件的其他属性和vue实例一样,但是data属性是不一样的。
vue实例中的data属性是一个对象。而组件中的data属性是一个函数。要达到实例相同的效果,只要返回一个对象就可以了。
Vue.component("my-component",{
template:"<button @click='add3'>" +
"{{message}}</button>",
data:function(){
return {
message:"hello world"
}
}
});
为什么和vue实例不一样呢?不觉得多此一举嘛。
是为了让每一个组件都有独自的data数据,而不是像vue实例一样共享一个data数据。
举个栗子:
(这是我自己写的一个小栗子)这个例子就是模拟的data是一个对象的情况,因为component的data直接写是一个对象是会报错的,只能通过函数返回同一个对象
<body>
<div id="root" >
<todo-item></todo-item>
<todo-item></todo-item>
<todo-item></todo-item>
</div>
<script>
var data =
{
counter:0
}
Vue.component("todo-item",{
template:"<li @click='add'>{{counter}}</li>",
data:function()
{
return data;
},
methods:{
add:function()
{
this.counter++;
}
}
})
new Vue({
el:"#root"
})
</script>
</body>
首先我要让组件共享数据,怎么做到呢?
首先template里面的data不能直接是对象,必须是函数,为了让函数返回相同的对象,我先在外面定义一个data对象,再在函数内返回data,那么导致他们的data是同一个data,就模拟出了实例的共享数据的情形。你会发现不管点击哪个组件。都会导致所有组件的数据的counter都会改变
因为他们改变的是同一个对象的counter属性。组件肯定是希望他互不干扰的,不然就会出现很多麻烦。所以组件的数据是不能共享的,不然就能通过其他组件改变他。所以我们需要用函数返回一个对象。这样每个对象是不一样的。很容易理解:{}!={};而data={};data===data,函数里面返回的是不同的地址,导致他返回的是不同的对象。每个组件只能改变自身的数据。所以我们这里只需要把data替换成{cuonter:0}即可。
DOM模板
主要是HTML标签有些元素只能包裹特定元素,比如table标签,select标签,ul标签,ol标签。还有些标签只能放在特定的标签里面,比如li标签,option标签,tr标签。
所以这类代码是会报错的:
<table><my-row></my-row></table>
因为my-row标签在html的渲染规则中是不能出现在table标签内部的。这时候要使用一个特殊的属性is。
<table><tr is="my-row"></tr></table>