一、模塊化與組件化
- 模塊化的定義
模塊化在Node中是一個規范,定義一些模塊的相關的規則,從代碼角度上來說,方便做區別,如果不使用模塊化,寫在js文件中不利於后期維護和擴展,從代碼的層面上就把相關的功能脫離出來,所以模塊化從從代碼的角度觸發,分析項目,把項目中一些功能類型的代碼,單獨抽離為一個個的模塊,那么為了保證大家以相同的方式去封裝模塊,於是我們就創造了CommentJS規范
- 模塊化的優點
在我們項目中,如果需要是實現相同的功能,就不需要再重寫,所以模塊化從一定程度上提高我們的開發效率,有一些相關模塊,直接調用就可以了,方便項目開發,和后期的維護與擴展
- 組件化:
把頁面中樣式差不多的東西抽為單獨的小組件,把需要經常復用的東西封裝為一個單獨的,今后需要用的時候直接拿就可以,不用再重寫,從ui的角度觸發去考慮問題,把頁面中代碼結構類似的區域抽離出來,封裝成一個單獨的小組件 ;前端的組件化,方便UI組件的重用;
- 組件化的優點:
隨着項目規模的發展,我們手中的組件會越來越多,我們今后一個頁面的ui,幾乎都可以從手中拿現成的組件拼接出來,方便項目的開發和維護
二、創建全局組件的方式
1. 創建全局組件的方式一
- 先調用
Vue.extend()
得到組件的構造函數
var com1 = Vue.extend({
template: '<h2>這是創建的第一個全局組件</h2>'
// template 屬性,表示這個組件的 UI 代碼解構
})
- 通過
vue.component('組件的名稱',組件的構造函數)
來注冊全局組件
Vue.component('mycom1', com1)
//com1就是組件的構造函數
注意:
組件的名稱如果是駝峰命名,那么引入的時候表名稱得加連字符-
1.如果 Vue.component('myCom1','com1')
對應的組件標簽是<my-com1></my-com1>
2. 如果是Vue.component('myCom1Test','com1')
對應組件標簽為:<my-com1-test></my-com1-tses>
3. 如果Vue.component('my-com1','com1')
對應組件標簽為:<my-com1></my-com1>
- 把注冊好的組件名稱,以標簽的形式引入到vm實例區域的頁面中即可
<div id="app">
<!-- 引入全局的vue組件-->
<mycom1></mycom1>
</div>
來吧展示:
2. 創建全局組件的方式二
- 直接使用
vue.component('組件的名稱','組件對象')
// Vue.component 的第二個參數,既接收 一個 組件的構造函數, 同時,也接受 一個對象
Vue.component('mycom2', {
template:'<h2>這是直接使用 Vue.component 創建出來的組件</h2>'
})
- 把注冊好的組件名稱,以標簽的形式引入到vm實例區域的頁面中即可
<div id="app">
<mycom2></mycom2>
</div>
來吧展示:
注意:
1.template 屬性中,不能單獨放一段文本,必須用標簽包裹起來;
2. 如果在 template 屬性中,想要放多個元素了,那么,在這些元素外,必須有唯一的一個根元素進行包裹;
Vue.component('mycom2', {
template:'<div><p>嘿嘿嘿嘿嘿</p><h2>這是直接使用 Vue.component 創建出來的組件</h2><h1>哈哈哈哈</h1> </div>'
})
3. 創建全局組件的方式三
- 給
template
添加一個id選擇器
Vue.component('mycom3', {
template: '#tpl'
})
- 定義一個 template 標簽元素
使用 Vue 提供的 template 標簽,可以定義組件的UI模板解構
<div id="app">
<mycom3></mycom3>
</div>
<template id="tpl">
<h2>這是創建全局組件的第三種方式</h2>
</template>
注意:
template標簽中里面也必須有唯一的一個根元素進行包裹
也就是如果沒有根元素包裹,那么下面代碼是執行不出來了會報錯
<template id="tmpl">
<h2>這是創建全局組件的第三種方式</h2>
<p>喲喲喲喲喲喲</p>
</template>
正確寫法:
<template id="tmpl">
<div>
<h2>這是創建全局組件的第三中方式</h2>
<p>嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿</p>
</div>
</template>
既然是全局組件,那么就可以重復調用,栗子:
<div id="app">
<mycom3></mycom3>
</div>
<div id="app2">
<mycom3></mycom3>
</div>
<template id="tmpl">
<h2>這是創建全局組件的第三中方式</h2>
</template>
<script>
Vue.component('mycom3', {
template: '#tmpl'
})
var vm = new Vue({
el: '#app',
});
var vm2 = new Vue({
el: '#app2',
});
</script>
三、創建私有組件
創建一個私有組件
<div id="app">
<mycom4></mycom4>
</div>
var vm = new Vue({
el: '#app',
data: {},
methods: {},
components: {
// 定義實例中私有組件的 組件的名稱 和組件的 解構
'mycom4': {
template: '<h6>這是定義的私有組件</h6>'
}
}
});
創建多個私有組件:
<div id="app">
<mycom4></mycom4>
<mycom5></mycom5>
</div>
components:{
mycom4:{
template:'<h2>這是我定義的私有組件1</h2>'
},
mycom5:{
template:'<h2>這是我定義的私有組件2</h2>'
}
}
四、組件中相應數據和展示方法
Vue.component('mycom', {
template: '<h3 @click="show">這是自定義的全局組件 ------ {{ msg }}</h3>',
data: function () { //
// 在 組件中,可以有自己的私有數據
// 但是,組件的 data 必須是一個 function
// 並且內部 return 一個 數據對象
return {
msg: '我是組件的內部data'
}
},
methods: { // 嘗試定義組件的私有方法
show() {
// console.log('出發了組件的私有show方法!')
alert('我是組件內部的方法')
}
}
})
思考:
為什么要把 組件中的 data 定義為一個function呢?
因為這樣做的話,每當我們在頁面上引用一次組件,必然會先調用 這個 data function,
從而得到一個當前組件私有的 數據對象;
五、切換組件
1. 結合flag標識符和 v-if
與 v-else
實現組件的切換
<div id="app">
<!-- 使用按鈕去控制顯示login和res-->
<input type="button" value="顯示登錄" @click="flag=true"/>
<input type="button" value="顯示注冊" @click="flag=false"/>
<login v-if="flag"></login>
<!-- 當flag:true的時候顯示login-->
<!-- 當flag:false的時候顯示res-->
<res v-else="flag"></res>
</div>
<script>
Vue.component('login', {
template: '<h2>登錄</h2>'
})
Vue.component('res', {
template: '<h2>注冊</h2>'
})
// 創建 Vue 實例,得到 ViewModel
var vm = new Vue({
el: '#app',
data:{
flag:false
},
methods:{}
})
</script>
2. 切換多個組件
<div id="app">
<!--想要顯示哪個組件就在:is屬性的后面傳入(字符串)=====> '組件的名字'-->
<component :is="'com1'"></component>
<component :is="'com3'"></component>
</div>
Vue.component('com1', {
template: '<h2>我是第1個組件</h2>'
})
Vue.component('com2', {
template: '<h2>我是第2個組件</h2>'
})
Vue.component('com3', {
template: '<h2>我是第3個組件</h2>'
})
Vue.component('com4', {
template: '<h2>我是第4個組件</h2>'
})
進行多組件的切換
<div id="app">
<!--點擊a鏈接,修改component的值
component標簽結合is屬性進行組件的切換-->
<a href="#" @click="comname='com1'">顯示組件1</a>
<a href="#" @click="comname='com2'">顯示組件2</a>
<a href="#" @click="comname='com3'">顯示組件3</a>
<a href="#" @click="comname='com4'">顯示組件4</a>
<component :is="comname"></component>
</div>
var vm = new Vue({
el: '#app',
data:{
comname:'com1'
},
//當vue解析文件到component標簽時,如果有:is屬性就會解析后面的字符串"comname"
//然后去data中尋找這個變量
//comname:'com1'
//正好是一個字符串的變量的名稱,就會顯示名稱叫com1的組件
methods:{}
})
3.為組件切換添加動畫
<transition>
<component :is="comname"></component>
</transition>
<style>
.v-enter,
.v-leave-to{
opacity:0;
transform: translate(100px);
}
.v-enter-active,
.v-leave-active{
transition: all 0.4s ease;
}
</style>
如上圖效果顯示,有標准流的影響,所以要脫離標准流的影響,讓離開的組件脫離標准流
<style>
.....
.v-enter-active,
.v-leave-active{
transition: all 0.4s ease;
position: absolute;
}
</style>
如圖動畫效果是先進入再離開,如果想要實現先離開再進入,則只需要在transition
中添加mode="out-in"
<transition mode="out-in"> -
<component :is="comname"></component>
</transition>
如果想要實現離開往左走,進入往右走的效果,則:
<style>
.v-enter {
/* 即將進入時候的坐標 */
opacity: 0;
transform: translateX(100px);
}
.v-leave-to {
/* 離開時候,最終到達的位置 */
opacity: 0;
transform: translateX(-100px);
}
</style>
六、父組件通過屬性綁定向子組件傳遞數據
- 把要傳遞給子組件的數據,作為自定義屬性,通過
v-bind
綁定到子組件身上
<com :sonmsg="pmsg"></com>
- 在子組件中,不能直接使用父組件傳遞過來的數據,需要先用
props
來接收一下
props: ['sonmsg']
- 在接收父組件傳遞過來的
props
的時候,一定要和父組件中傳遞過來的自定義屬性名稱保持一致
template: '<h2>我是子組件-----{{sonmsg}}</h2>',
具體代碼如下:
<body>
<div id="app">
<com :sonmsg="pmsg"></com>
<!-- 以屬性綁定的方式將父組件中的值傳遞給子組件-->
<!-- 這里相當於把 '我是父組件中的數據'放在這邊 也就是 msg123='我是父組件中的數據'-->
<!-- 但是如果子組件想用msg需要在子組件中定義一下-->
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
pmsg:'我是父組件中的數據'
},
methods: {},
components: { // 定義私有組件
'com': { // 在Vue中,默認,子組件無法直接獲取父組件中的數據
template: '<h2>我是子組件-----{{sonmsg}}</h2>',
props: ['sonmsg']
// 在Vue中,只有 props 是數組,其它帶 s 后綴的屬性都是 對象
}
}
});
</script>
</body>
七、父組件向子組件傳遞對象
- 把要傳遞給子組件的對象,作為自定義屬性,通過
v-bind
綁定到子組件身上
<com1 :msgobj123="msgObj"></com1>
- 在子組件中,不能直接使用父組件傳遞過來的對象,需要先用
props
來接收一下
props: ['msgobj123']
- 在接收父組件傳遞過來的
props
的時候,一定要和父組件中傳遞過來的自定義屬性名稱保持一致
template: '<h3>后面傳遞的是父組件中對象 ---- {{ JSON.stringify(msgobj123) }}</h3>'
具體代碼如下:
<body>
<div id="app">
<com1 :msgobj123="msgObj"></com1>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
msgObj: {
id:1,
name:'千夏Chinatsu',
age:18
}
},
methods: {},
components: {
'com1': {
template: '<h3>后面傳遞的是父組件中對象 ---- {{ JSON.stringify(msgobj123) }}</h3>',
props: ['msgobj123']
}
}
});
</script>
</body>
八、父組件向子組件傳遞方法
- 把要傳遞給子組件的方法,通過
v-on
綁定事件到子組件身上
<com v-on:func="show()"></com>
- 在子組件中,不能直接使用父組件傳遞過來的方法,需要先用
$emit()
來接收一下
this.$emit('func')
- 在接收父組件傳遞過來的
$emit()
中,一定要和父組件中傳遞過來的方法名稱保持一致
具體代碼:
<body>
<div id="app">
<com v-on:func="show()"></com>
</div>
<script>
var vm = new Vue({
el:'#app',
data:{},
methods:{
show(){
console.log('觸發了父組件中的show()方法')
}
},
components: {
'com': {
template: '<input type="button" value="這是子組件的按鈕" @click="btnClick()"/>',
methods:{
btnClick(){
// console.log('hhhh')
this.$emit('func')
}
}
}
}
})
</script>
</body>
總結:
1.如果要向子組件傳遞 data 中的數據,則 使用 屬性綁定的形式v-bind:
2. 如果要向子組件傳遞 methods 中的 方法,則 使用 事件綁定的形式v-on:
九、子組件向父組件傳遞數據
<body>
<div id="app">
<!-- 方式一:-->
<com v-on:func="show"></com>
<!-- $emit('func',1)后面要傳遞參數-->
<!-- 方式二:-->
<!-- <com v-on:func="show(2)"></com>-->
<!-- 然后$emit('func')后面就不用傳遞參數-->
</div>
<script>
var vm = new Vue({
el:'#app',
data:{},
methods:{
show(arg){
console.log('觸發了父組件中的show()方法' + arg)
// '--------'
}
},
components: {
'com': {
template: '<input type="button" value="這是子組件的按鈕" @click="btnClick()"/>',
data:function(){
return{
sonmsg:'這是子組件中的數據'
}
},
methods:{
btnClick(){
this.$emit('func','嘿嘿嘿嘿嘿')
}
}
}
}
})
</script>
</body>
所以可以直接在show()方法中傳入子組件中的data數據
methods:{
show(arg){
console.log('觸發了父組件中的show()方法' +'--------'+ arg)
// '--------'
}
},
components: {
'com': {
template: '<input type="button" value="這是子組件的按鈕" @click="btnClick()"/>',
data:function(){
return{
sonmsg:'這是子組件中的數據'
}
},
methods:{
btnClick(){
// console.log('hhhh')
//this.$emit('func','嘿嘿嘿嘿嘿')
this.$emit('func',this.sonmsg)
}
}
}
}
把子組件傳遞過來的數據,保存到 父組件的 data 中
methods: {
show(arg) {
// console.log('觸發了父組件中的show()方法' +'--------'+ arg)
// 把子組件傳遞過來的數據,保存到 父組件的 data 中
this.msgFormSon = arg
console.log(this.msgFormSon)
}
},
總結:
子組件向父組件傳值,本質上,還是調用了父組件傳遞過來的方法
只不過,子組件在調用的時候,把 數據 當作參數,傳給這個方法了;
十、練習列表案例(結合父子組件傳值)
<body>
<div id="app">
<!-- 評論框區域 -->
<cmt-box @func="addNewCmt"></cmt-box>
<ul>
<cmt-item v-for="(item, i) in list" :item="item" :key="i"></cmt-item>
</ul>
</div>
<script>
Vue.component('cmt-item', {
template: `<li>
<h3>評論人:{{ item.name }}</h3>
<h5>評論內容:{{ item.content }}</h5>
</li>`,
props: ['item']
})
Vue.component('cmt-box', {
template: `<div>
<label>評論人:</label>
<br>
<input type="text" v-model="name">
<br>
<label>評論內容:</label>
<br>
<textarea v-model="content"></textarea>
<br>
<input type="button" value="發表評論" @click="postComment">
</div>`,
data: function () {
return {
name: '',
content: ''
}
},
methods: {
postComment() { // 發表評論
// console.log('ok')
const cmt = { name: this.name, content: this.content }
// 子組件中,調用父組件傳遞過來的方法,然后可以把 子組件的數據,當作參數,傳遞給父組件的方法去使用
this.$emit('func', cmt)
this.name = this.content = ''
// console.log(cmt)
}
}
})
var vm = new Vue({
el: '#app',
data: {
list: [
{ name: 'zs', content: '沙發' },
{ name: 'ls', content: '板凳' },
{ name: 'qqq', content: '涼席' },
{ name: 'eee', content: '磚頭' }
]
},
methods: {
addNewCmt(cmt) { // 添加新評論
// console.log(cmt)
this.list.push(cmt)
}
}
});
</script>
</body>
十一、使用ref屬性來獲取頁面中的元素
<body>
<div id="app">
<input value="按鈕" type="button" @click="show()"/>
<!--<h2 id="myh2">{{msg}}</h2>-->
<!--加入ref屬性-->
<h2 id="myh2" ref="hhh">{{msg}}</h2>
</div>
<script>
var vm = new Vue({
el:'#app',
data:{
msg:'嘿嘿嘿嘿嘿'
},
methods:{
show(){
//原生想要獲取h2中的數據的方法
// var res = document.getElementById('myh2').innerHTML
//console.log(res)
console.log(this)
console.log(this.$refs.hhh)
console.log(this.$refs.hhh.innerHTML)
}
}
})
</script>
</body>
在h2標簽中沒有加入ref
屬性的打印console.log(this)
結果
在h2標簽加入ref
屬性的打印console.log(this)
結果
所以可以通過ref
可以很方便的來獲取元素
console.log(this)
console.log(this.$refs.hhh)
console.log(this.$refs.hhh.innerHTML)
十二、使用ref屬性來獲取頁面中的組件
所以可以根據msg去修改組件內部的數據或者調用子組件中的方法
<body>
<div id="app">
<input value="按鈕" type="button" @click="getCom()" />
<com1 ref="xxxxx"></com1>
</div>
<script>
Vue.component('com1', {
template:'<h2>這是一個小組件---{{msg}}</h2>',
data:function () {
return {
msg:'這是組件內部數據'
}
},
methods:{
show(){
console.log('子組件中的方法被調用了')
}
}
})
var vm = new Vue({
el:'#app',
data:{
msg:'這是父組件的數據'
},
methods:{
getCom(){
// console.log(this)
this.$refs.xxxxx.msg = '組件內部數據被修改了'
this.$refs.xxxxx.show()
}
}
})
//頁面上可以為很多元素創建ref的引用
</script>
</body>
十三、在vue組件中data和props的區別
data
在組件中,要被定義成一個function,並且要返回一個對象props
在組件中,要被定義成數組,其中,數組的值都是字符串名,表示從父組件傳遞過來的數據props
中的數據,不要直接拿來修改,如果想要修改,必須在data上重新定義一個屬性,然后把屬性的值從this.props
直接拿過來data
上的數據,都是組件自己私有的,數據都是可讀可寫的props
都是外界傳遞過來的數據,數據只能讀取,不能重新寫入