组件化开发
引子
组件化开发是Vue里卖弄非常重要的一个思想
-
我们将一个完整的页面分成很多个组件
-
每个组件都用于实现页面的一个功能块
-
而每一个组件又可以进行细分
Vue.js
-
提供了一种抽象的,可以让我们开发出一个个可复用的小组件来构造我们的应用
-
任何的应用都会被抽象成一颗组件树
-
基础
所谓的组件化开发,实际上就是把可复用的代码抽成一个模板,能在多个地方重复利用,就像函数
-
定义组件
const cpn = Vue.extend({ // 1. 创造一个组件 template: ` <div> <a href="https:">666</a> <p>我是p标签</p> </div>` })
Vue.extend()
创建的是一个组件构造器template
代表了我们自定义组件的模板,也就是要显示的HTML代码- 这种方法在 Vue2.X 已经看不到了,后面的语法糖写法也是基于这种方法实现的
-
注册组件
// 2.1 注册一个组件,该组件是全局组件,可以在多个Vue实例下使用 Vue.component('my-pn', cpn) // 2.2 注册局部组件 const cpn = Vue.extend({ // 1. 创造一个组件 template: ` <div> <a href="https:">666</a> <p>我是p标签</p> </div>` }) var app = new Vue({ el: '#app', data: { message: 0, }, components: { // my-pn 使用组件时的标签名 'my-pn': cpn } })
- 第一个参数是 组件名,在DOM里面我们的自定义标签名
- 第二个参数是 组件构造器
-
使用组件
<div id="app"> <my-pn></my-pn> <!-- 使用自定义组件 --> <my-pn></my-pn> </div>
父组件/子组件
-
子组件
const son = Vue.extend({ template: ` <div> <a href="https:">我是子组件</a> <p>我是p标签</p> </div>` })
-
父组件
const father = Vue.extend({ template: ` <div> <a href="https:">我是父组件</a> <p>我是p标签</p> <sonCpn></sonCpn> </div>`, components: { 'sonCpn': son } })
-
Root组件
var app = new Vue({ el: '#app', data: { message: 0, }, components: { // my-pn 使用组件时的标签名 'my-cpn': father } })
-
使用
<div id="app"> <my-cpn></my-cpn> </div>
注册组件语法糖
-
注册全局组件
// 直接使用compoment注册,不需要写组件构造器, // 源码实际上把{}里面的内容自动帮我们构造了组件构造器,所以我们能使用语法糖的写法 Vue.component('my-cpn', { template: ` <div>我是一个div标签</div> ` })
-
注册局部组件
var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' }, components: { 'my-cpn': { template: `<a href="">我是一个a标签</a>` } } })
组件模板抽离
在body页面上写,把模板单独抽离出来成为一个标签
-
写法一
把模板单独写到一个 script 标签中,在写组件的时候把 script 标签用选择器选中
<script type="text/x-template" id="son"> <div> <h2>今天天气真好</h2> <p>我是一个p</p> </div> </script> <script> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' }, components: { 'my-cpn': { template: '#son' } } }) </script>
-
写法二
写到 template标签 里面
<div id="app"> <my-cpn></my-cpn> </div> <template id="son"> <div> <h2>今天天气真好</h2> <p>我是一个p</p> </div> </template> <script> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' }, components: { 'my-cpn': { template: '#son' } } }) </script>
组件data参数
我们想要组件里面的模板也能使用 {{}}
这样的语法,我们可以再组件的 data 属性里面写!
组件是可复用的 Vue 实例对象,他能接收的参数也包括 el,data,methods,computed这样的
-
组件的复用
<div id="app"> <my-cpn></my-cpn> <my-cpn></my-cpn> <my-cpn></my-cpn> </div> <template id="son"> <div> <button @click="counter++">你已经点了我 {{counter}} 次了</button> </div> </template> <script src="VueJs/vue.js"></script> <script> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' }, components: { 'my-cpn': { // ① template: '#son', data: function() { // ② return { counter: 0 } } } } }) </script>
①:创建了一个Vue 实例的子组件,这个子组件其实也是一个 Vue 实例
②:每一个组件实例在被创建的时候,都会调用 data 这个函数,返回一个独一无二,专门用来储存他自己业务和数据的对象,不会受到其他组件的影响
为什么data要用函数
- 我们定义组件的
data
的时候,不能返回一个对象,它必须是一个函数
-
组件是要能被复用的,每个组件的实例都应该有他自己的数据和业务逻辑
-
每个实例都应该可以维护一份被返回对象的独立的拷贝的数据
-
如果
data
不是一个函数,而是一个对象
,每次创建组件实例,所有实例引用的数据都会是同一个data
-
如果 Vue 没有这条规则,点击一个按钮就可能会像如下代码一样影响到 其它所有组件实例:
我们用函数来模拟下这个现象,加深对这个data的理解
-
如果所有的实例用的都是一个 data 数据,会变成灾难
const obj = { name: 'xyb', age: 20, sex: 'M' } function People() { return obj } let a = People(); let b = People(); let c = People(); a.age = 888 console.log(a); // 1. 这里三个值的age都会被修改 console.log(b); // 2. 非常不方便管理,一个数据变动,全部数据都变动 console.log(c); // 3. 三个值执行的都是同一个对象
-
各自的实例有各自管理的数据,各司其职,互不干扰
function People() { return { name: 'xyb', age: 20, sex: 'M' } } let a = People(); // a 会指向 People 函数返回的新对象的内存地址上 let b = People(); // b 会指向 People 函数返回的新对象的内存地址上 let c = People(); // c 会指向 People 函数返回的新对象的内存地址上 a.age = 888 console.log(a); // 1. 这里只有a的值会被修改 console.log(b); // 2. 用函数返回一个新对象,就能管理各自的数据 console.log(c); // 3. 如果data是一个对象,那么所有组件实例都会指向同一个对象
父子组件通信
- 我们在开发的时候,通常都是在最大的组件,也就是
root
(最大的Vue实例) 组件里面请求数据,然后在一个个小组件里面渲染出来,不可能说每一个小组件都各自来一次请求数据,服务器给我们返回的数据肯定是一个层层包裹的大数据,需要我们手动把数据分发给小组件进行渲染
组件通信的方式
-
父组件通过 props 向子组件传递数据
-
子组件通过 事件 向父组件发送消息
props
子组件用来接收父组件传入值的重要属性
// 1. 创建 Vue 实例,创建子组件,把数据在data中定义好 var app = new Vue({ el: '#app', data: { books: ['《星际穿越》', '《弱点》', '《一条狗的使命》'], price: [20, 30, 45, 16] }, components: { // ① cpn1: { template: ` <div> {{books}} // 这里的 books 是来自 props 里的 {{price}} // ③ </div>`, data() { return { counter: 0, } }, props: ['books', 'price'] // ② } } }) // 2. 重要的部分 <div id="app"> // :打开了一条通道,能把父(root)组件里的price绑定到子组件上,必须要用props接收 <cpn1 :books="books" :price="price"> // ④ </cpn1> </div>
①:在root组件上创建一个子组件
cpn1
②:用来接收父组件绑定到模板上的值。可以是数组/对象
③:从组件的 props 里面找数据,找不到报错
④:
:books="books"
**: ** 作为一条子组件和父组件的通道,前面一个 books 是子组件 props 里面接收的值后面的 books,如果父组件里面的 data 已经定义了,那么就会把定义的数据传入子组件,否者为字符串 books