[toc]
前端框架—Vue组件化
在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。但是如果每个页面都独自开发,这无疑增加了我们开发的成本。所以我们会把页面的不同部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。在vue里,所有的vue实例都是组件。
组件(Component)是 Vue.js 最强大的功能之一。
组件可以扩展 HTML 元素,封装可重用的代码。
1. 全局组件
我们通过Vue的component方法来定义一个全局组件。
组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等。
不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。
==但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板。==
全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
data必须是一个函数,不再是一个对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 全局组件</title > <script src ="node_modules/vue/dist/vue.js" > </script > </head > <body > <div id ="app" > <counter > </counter > <counter > </counter > <counter > </counter > </div > </body > <script > Vue .component ("counter" ,{ template :'<button @click="count++">你点击了我{{count}}次,我记住了.</button>' , data ( ){ return { count :0 , } } }); var vue = new Vue ({ el :"#app" , data :{ wd :"" , }, }); </script > </html >
1.2 组件的复用
定义好的组件,可以任意复用多次:
1 2 3 4 5 6 <div id ="app" > <counter > </counter > <counter > </counter > <counter > </counter > </div >
效果:
你会发现每个组件互不干扰,都有自己的count值。怎么实现的呢?
组件的data属性必须是函数!
当我们定义这个 <counter>
组件时,它的data 并不是像之前直接提供一个对象:
取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
1 2 3 4 5 data : function ( ) { return { count : 0 } }
2. 局部组件
一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着Vue的加载而加载。因此,对于一些并不频繁使用的组件,我们会采用局部注册。
1 2 3 4 5 6 7 8 9 const counter = { template :'<button v-on:click="count++">你点了我 {{ count }} 次,我记住了.</button>' , data ( ){ return { count :0 } } };
然后在Vue中使用它:
1 2 3 4 5 6 var app = new Vue ({ el :"#app" , components :{ counter :counter } })
components
就是当前vue对象子组件集合。
效果与刚才的全局注册是类似的,不同的是,这个counter组件只能在当前的Vue实例中使用。
2.1 定义局部组件的另一种写法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <body > <div id ="app" > <Navbar > </Navbar > </div > <script src ="node_modules/vue/dist/vue.js" > </script > <script > new Vue ({ el : '#app' , data : { }, components : { 'Navbar' : { template : '<ul><li>首页</li><li>学员管理</li></ul>' } } }) </script > </body >
3. 组件通信
通常一个单页应用会以一棵嵌套的组件树的形式来组织:
页面首先分成了顶部导航、左侧内容区、右侧边栏三部分
左侧内容区又分为上下两个组件
右侧边栏中又包含了3个子组件
各个组件之间以嵌套的关系组合在一起,那么这个时候不可避免的会有组件间通信的需求。
3.1 props(父向子传递)
父组件使用子组件时,自定义属性(属性名任意,属性值为要传递的数据)。
子组件通过props接收父组件数据
,通过自定义属性的属性名。
父组件使用子组件,并自定义了title属性:
父组件app
子组件introduce
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <div id ="app" > <introduce title ="大家好我是涛哥" /> </div > <script src ="./node_modules/vue/dist/vue.js" > </script > <script type ="text/javascript" > Vue .component ("introduce" ,{ template :'<h1>{{title}}</h1>' , props :['title' ] }) var app = new Vue ({ el :"#app" }) </script >
3.2 props验证
我们定义一个子组件,并接收复杂数据:
props
:定义需要从父组件中接收的属性。
items
:是要接收的属性名称。
type
:限定父组件传递来的必须是数组。
default
:默认值。
required
:是否必须。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 组件通信-复杂数据类型</title > <script src ="node_modules/vue/dist/vue.js" > </script > </head > <body > <div id ="app" > <introduce :users ="users" > </introduce > </div > </body > <script > Vue .component ("introduce" ,{ template :' <ul>\n' + ' <li v-for="(user,index) in users" :key="index">\n' + ' {{index+1}}--{{user.name}}--{{user.age}}--{{user.gender}}\n' + ' </li>\n' + ' </ul>' , data ( ){ return {}; }, props :{ users :{ type :Array , default :[], required :true } } }); var vue = new Vue ({ el : "#app" , data : { users :[ {name :'柳岩' , gender :'女' , age : 21 }, {name :'晓哥' , gender :'男' , age : 18 }, {name :'范冰冰' , gender :'女' , age : 24 }, {name :'刘亦菲' , gender :'女' , age : 18 }, {name :'古力娜扎' , gender :'女' , age : 25 } ] }, methods : {} }); </script > </html >
当 props 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
3.2.1 type类型
注意:子组件模板有且只有一个根标签
3.3 动态静态传递
给 props 传入一个静态的值:
1 <introduce title ="大家好,我是锋哥" />
给 prop 传入一个动态的值: (通过v-bind从数据模型中,获取title的值)
1 <introduce :title ="title" />
静态传递时,我们传入的值都是字符串类型的,但实际上任何类型的值都可以传给一个 props。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <script src ="node_modules/vue/dist/vue.js" > </script > </head > <body > <div id ="app" > <blog-post v-bind:like ="post.like + post.dislike" > </blog-post > </div > <script > Vue .component ("blog-post" ,{ template :"<h1>{{like}}</h1>" , data ( ){ return { }; }, props :['like' ] }); var vue = new Vue ({ el :"#app" , data :{ post :{ like :1021 , dislike :"haha" } } }) </script > </body > </html >
3.4 子向父的通信:$emit
来看这样的一个案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <div id ="app" > <h2 > num: {{num}}</h2 > <counter :num ="num" > </counter > </div > <script src ="./node_modules/vue/dist/vue.js" > </script > <script type ="text/javascript" > Vue .component ("counter" , { template :'\ <div>\ <button @click="num++">加</button> \ <button @click="num--">减</button> \ </div>' , props :['num' ] }) var app = new Vue ({ el :"#app" , data :{ num :0 } }) </script >
子组件接收父组件的num属性
子组件定义点击按钮,点击后对num进行加或减操作
我们尝试运行,好像没问题,点击按钮试试:
1 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "num"
子组件接收到父组件属性后,默认是不允许修改的。怎么办?
既然只有父组件能修改,那么加和减的操作一定是放在父组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var app = new Vue ({ el :"#app" , data :{ num :0 }, methods :{ increment ( ){ this .num ++; }, decrement ( ){ this .num --; } } })
但是,点击按钮是在子组件中,那就是说需要子组件来调用父组件的函数,怎么做?
我们可以通过v-on指令将父组件的函数绑定到子组件上:
1 2 3 4 <div id ="app" > <h2 > num: {{num}}</h2 > <counter :count ="num" @inc ="increment" @dec ="decrement" > </counter > </div >
在子组件中定义函数,函数的具体实现调用父组件的实现,并在子组件中调用这些函数。当子组件中按钮被点击时,调用绑定的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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" ); } } })
vue提供了一个内置的this.$emit()函数,用来调用父组件绑定的函数
效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 组件通信--子到父</title > <script src ="node_modules/vue/dist/vue.js" > </script > </head > <body > <div id ="app" > <h2 > num:{{num}}</h2 > <counter :num ="num" @increment ="increment" @decrement ="decrement" > </counter > </div > </body > <script > Vue .component ("counter" ,{ template :' <div>\n' + ' <button @click="plus">加</button>\n' + ' <button @click="reduce">减</button>\n' + ' </div>' , data ( ){ return {}; }, methods :{ plus ( ){ this .$emit("increment" ); }, reduce ( ){ this .$emit("decrement" ); } }, props :['num' ] }); var vue = new Vue ({ el : "#app" , data : { num :0 }, methods : { increment ( ){ this .num ++; }, decrement ( ){ this .num --; } } }); </script > </html >
☆