組件化開發(fā) & 根組件
App.vue
<template>
<div class="App">
<div class="box" @click="fn"></div>
</div>
</template>
<script>
// 導(dǎo)出的是當(dāng)前組件的配置項(xiàng)
// 里面可以提供 data(特殊) methods computed watch 生命周期八大鉤子
export default {
created () {
console.log('我是created')
},
methods: {
fn () {
alert('你好')
}
}
}
</script>
<style lang="less">
/* 讓style支持less
1. 給style加上 lang="less"
2. 安裝依賴包 less less-loader
yarn add less less-loader -D (開發(fā)依賴)
*/
.App {
width: 400px;
height: 400px;
background-color: pink;
.box {
width: 100px;
height: 100px;
background-color: skyblue;
}
}
</style>
普通組件的注冊(cè)使用
局部注冊(cè)
components/HmFooter.vue
<template>
<div class="hm-header">
我是hm-header
</div>
</template>
<script>
export default {
}
</script>
<style>
.hm-header{
height: 100px;
line-height: 100px;
text-align: center;
font-size: 30px;
background-color: #cdcf48;
}
</style>
App.vue
<template>
<div class="App">
<!-- 使用,直接當(dāng)成html標(biāo)簽使用 -->
<!-- 組件命名規(guī)范,大駝峰命名法 -->
<!-- 頭部組件 -->
<HmHeader></HmHeader>
<!-- 主體組件 -->
<HmMain></HmMain>
<!-- 底部組件 -->
<HmFooter></HmFooter>
</div>
</template>
<script>
// 導(dǎo)入
import HmFooter from './components/HmFooter.vue';
import HmHeader from './components/HmHeader.vue';
import HmMain from './components/HmMain.vue';
export default {
// 局部注冊(cè)
components:{
// '組件名':'組件對(duì)象
HmHeader:HmHeader,
HmFooter,// 同名可簡寫
HmMain,
}
}
</script>
<style>
.App{
width: 600px;
height: 700px;
background-color:rgb(0, 234, 255);
padding: 20px;
margin: 0 auto;
}
</style>
全局注冊(cè)
HmButton.vue
<template>
<button class=".hm-button">通用按鈕</button>
</template>
<script>
export default {
}
</script>
<style>
.hm-button{
height: 50px;
line-height: 50px;
padding: 0 20px;
background-color: #ca2a50;
border-radius: 5px;
}
</style>
main.js
// 文件核心作用:導(dǎo)入App.vue,基于App.vue創(chuàng)建結(jié)構(gòu)渲染index.html
import Vue from 'vue'
import App from './App.vue'
// 1.導(dǎo)入的代碼,都寫在上面,書寫規(guī)范
import HmButton from './components/HmButton'
Vue.config.productionTip = false
// 2.進(jìn)行全局注冊(cè)
// Vue.component(組件名,組件注冊(cè))
Vue.component('HmButton', HmButton)
new Vue({
render: (createElement) => {
return createElement(App)
}
}).$mount('#app')
HmFooter.vue
<template>
<div class="hm-footer">
我是hm-footer
<!-- 直接用 -->
<HmButton></HmButton>
</div>
</template>
<script>
export default {
}
</script>
<style>
.hm-footer{
height: 100px;
line-height: 100px;
text-align: center;
font-size: 30px;
background-color: #6848cf;
}
</style>
兩種注冊(cè)方式總結(jié)
【案例】—— 小兔鮮 組件拆分
組件的三大組成部分
組件的樣式?jīng)_突 scoped
<style scoped>
</style>
data 是一個(gè)函數(shù)
components/BaseButton.vue
<template>
<div class="BaseButton">
<button @click="count--">-</button>
<span>{{ count }}</span>
<button @click="count++">+</button>
</div>
</template>
<script>
export default {
// data必須寫成函數(shù),不能是對(duì)象
data() {
return {
count: 999
}
}
}
</script>
<style>
</style>
App.vue
<template>
<div id="app">
<BaseButton></BaseButton>
<BaseButton></BaseButton>
<BaseButton></BaseButton>
</div>
</template>
<script>
import BaseButton from './components/BaseButton.vue'
export default {
name: 'App',
components: {
BaseButton
}
}
</script>
<style>
</style>
組件通信
父 -> 子 props
components/SonTest.vue
<template>
<div class="son">
{{ title }}
<!-- 渲染使用 -->
</div>
</template>
<script>
export default {
// 2.通過props進(jìn)行接受
// 這里的名字必須和App.vue里的<Son :title="MyTitle"></Son> 相同
props: ['title']
}
</script>
<style scoped>
div {
border: 1px solid black;
width: 100px;
height: 100px;
}
</style>
App.vue
<template>
<div id="app">
我是爸爸
<!-- 1.給組件標(biāo)簽,添加屬性的方式,傳值 -->
<SonTest :title="MyTitle"></SonTest>
</div>
</template>
<script>
import SonTest from './components/SonTest.vue'
export default {
components: {
SonTest
},
data() {
return {
MyTitle: 'slxhhhh'
}
}
}
</script>
<style >
div {
border: 1px solid black;
width: 300px;
height: 200px;
}
</style>
prop概念
components/UserInfo.vue
<template>
<div class="UserInfo">
<h1>個(gè)人信息</h1>
<h3>姓名{{ username }}</h3>
<h3>年齡{{ age }}</h3>
<h3>是否單身{{ isSingle }}</h3>
<h3>座駕{{ car.brand }}</h3>
<h3>興趣愛好{{ hobby.join('、') }}</h3>
</div>
</template>
<script>
export default {
props: ['username', 'age', 'isSingle', 'car', 'hobby']
}
</script>
<style>
</style>
App.vue
<template>
<div id="app">
<UserInfo
:username="username"
:age="age"
:isSingle="isSingle"
:car="car"
:hobby="hobby"
></UserInfo>
</div>
</template>
<script>
import UserInfo from './components/UserInfo.vue'
export default {
components: {
UserInfo
},
data() {
return {
username: 'slx',
age: 20,
isSingle: true,
car: {
brand: 'affrf'
},
hobby: ['aaaa', 'cccc', 'bbbb']
}
}
}
</script>
<style >
div {
border: 1px solid black;
width: 300px;
height: 400px;
}
</style>
props校驗(yàn)
類型校驗(yàn) 基礎(chǔ)寫法+完整寫法
components/BaseProgress.vue
<template>
<div class="BaseProgress">
<div class="progress">
<!-- 在這里面width 不用加{{}} 并且{}里面 是js對(duì)象 ,所以要遵守駝峰命名法 也就是 background-color 要變成 backgroundColor-->
<div
class="inner"
:class="{
low: w < 50,
high: w > 70,
over: w == 100
}"
:style="{ width: w + '%' }"
>
<span>{{ w }}%</span>
</div>
</div>
</div>
</template>
<script>
export default {
// 沒有校驗(yàn)
// props: ['w']
// 1.基礎(chǔ)校驗(yàn)
// props: {
// // 校驗(yàn)的屬性名:類型 Number、String Boolean
// w: Number
// }
// 2.完整寫法(類型、非空、默認(rèn)、自定義校驗(yàn)
props: {
w: {
// 寫成對(duì)象形式
type: Number,
// required: true,
default: 10,
validator(value) {
// return false
console.log(value)
if (value >= 0 && value <= 100) {
return true
}
console.log('傳入的值要是0~100')
return false
}
}
}
}
</script>
<style scoped>
.BaseProgress {
margin: 10px;
}
.progress {
width: 300px;
height: 40px;
margin-bottom: 40px;
background-color: #cdf92c;
border-radius: 25px;
padding: 5px;
}
.inner {
background-color: #0df6c7;
border-radius: 25px;
height: 35px;
margin-top: 3px;
/* width: 20%; */
}
.low {
background-color: #92ee61;
}
.high {
background-color: rgb(141, 179, 216);
}
.over {
background-color: rgb(0, 128, 255);
}
.inner span {
width: 100%;
text-align: right;
display: block;
line-height: 90px;
}
</style>
App.vue
<template>
<div id="app">
<!-- 沒傳值,就用默認(rèn)的10 -->
<BaseProgress></BaseProgress>
<BaseProgress :w="width"></BaseProgress>
</div>
</template>
<script>
import BaseProgress from './components/BaseProgress.vue'
export default {
components: {
BaseProgress
},
data() {
return {
// width: 'sfsd'
width: 20
}
}
}
</script>
<style >
</style>
prop & data 、單向數(shù)據(jù)流
components/BaseProgress.vue
<template>
<div class="BaseProgress">
<div class="progress">
<!-- 在這里面width 不用加{{}} 并且{}里面 是js對(duì)象 ,所以要遵守駝峰命名法 也就是 background-color 要變成 backgroundColor-->
<div
class="inner"
:class="{
low: w < 50,
high: w > 70,
over: w == 100
}"
:style="{ width: w + '%' }"
>
<span>{{ w }}%</span>
</div>
</div>
<button @click="handleFn(75)">設(shè)置75%</button>
<button @click="handleFn(100)">設(shè)置100%</button>
<!-- props傳過來的數(shù)據(jù)(外部數(shù)據(jù)),不能直接改 -->
</div>
</template>
<script>
export default {
props: {
w: {
// 寫成對(duì)象形式
type: Number,
// required: true,
default: 10,
validator(value) {
if (value >= 0 && value <= 100) {
return true
}
console.log('傳入的值要是0~100')
return false
}
}
},
methods: {
handleFn(tt) {
this.$emit('changeWidth', tt)
}
}
// 父組件的props更新,會(huì)單向向下流動(dòng),影響到子組件
}
</script>
<style scoped>
.BaseProgress {
margin: 10px;
}
.progress {
width: 300px;
height: 40px;
margin-bottom: 40px;
background-color: #cdf92c;
border-radius: 25px;
padding: 5px;
}
.inner {
background-color: #0df6c7;
border-radius: 25px;
height: 35px;
margin-top: 3px;
/* width: 20%; */
}
.low {
background-color: #92ee61;
}
.high {
background-color: rgb(141, 179, 216);
}
.over {
background-color: rgb(0, 128, 255);
}
.inner span {
width: 100%;
text-align: right;
display: block;
line-height: 90px;
}
</style>
App.vue
<template>
<div id="app">
<!-- 沒傳值,就用默認(rèn)的10 -->
<!-- <BaseProgress></BaseProgress> -->
<BaseProgress :w="width" @changeWidth="changeFn"></BaseProgress>
</div>
</template>
<script>
import BaseProgress from './components/BaseProgress.vue'
export default {
components: {
BaseProgress
},
data() {
return {
// width: 'sfsd'
width: 20
}
},
methods: {
changeFn(tt) {
this.width = tt
}
}
}
</script>
<style >
</style>
子 -> 父 $emit
components/Son.vue
<template>
<div class="son" style="border: 3px solid #000; margin: 10px">
我是Son組件 {{ title }}
<button @click="changeFn">修改title</button>
</div>
</template>
<script>
export default {
name: 'Son-Child',
props: ['title'],
methods: {
changeFn() {
// 通過this.$emit() 向父組件發(fā)送通知
this.$emit('changTitle','傳智教育')
},
},
}
</script>
<style>
</style>
App.vue
<template>
<div class="app" style="border: 3px solid #000; margin: 10px">
我是APP組件
<!-- 2.父組件對(duì)子組件的消息進(jìn)行監(jiān)聽 -->
<Son :title="myTitle" @changTitle="handleChange"></Son>
</div>
</template>
<script>
import Son from './components/Son.vue'
export default {
name: 'App',
data() {
return {
myTitle: '學(xué)前端,就來黑馬程序員',
}
},
components: {
Son,
},
methods: {
// 3.提供處理函數(shù),提供邏輯
handleChange(newTitle) {
//這里的newTitle就是子組建 this.$emit('changTitle','傳智教育') 傳來的'傳智教育'
this.myTitle = newTitle
},
},
}
</script>
<style>
</style>
通過后面的啟發(fā),父組件還可以直接@事件名=" 父數(shù)據(jù) = $event "
當(dāng)然,組件間也可以傳遞響應(yīng)式數(shù)據(jù)
Son.vue
<template>
<div class="son" style="border: 3px solid #000; margin: 10px">
我是Son組件 title:[{{ title }}]
<input v-model="txt" type="text">
<button @click="changeFn">修改title</button>
</div>
</template>
<script>
export default {
name: 'Son-Child',
props: ['title'],
data(){
return {
txt:''
}
},
methods: {
changeFn() {
// 通過this.$emit() 向父組件發(fā)送通知
this.$emit('changTitle',this.txt)
},
},
}
</script>
<style>
</style>
App.vue
<template>
<div class="app" style="border: 3px solid #000; margin: 10px">
我是APP組件
<!-- 2.父組件對(duì)子組件的消息進(jìn)行監(jiān)聽 -->
<Son :title="myTitle" @changTitle="myTitle = $event"></Son>
</div>
</template>
<script>
import Son from './components/Son.vue'
export default {
name: 'App',
data() {
return {
myTitle: '學(xué)前端,就來黑馬程序員',
}
},
components: {
Son,
},
methods: {
// 3.提供處理函數(shù),提供邏輯
// handleChange(newTitle) {
// console.log(newTitle);
// this.myTitle = newTitle
// },
},
}
</script>
<style>
</style>
非父子通信 event bus事件總線
components/BaseA.vue
<template>
<div class="A">
我是A組件(接收方)
<p>{{ msg }}</p>
</div>
</template>
<script>
import Bus from '../utils/EventBus'
export default {
created() {
// 2.在A組件(接受方)監(jiān)聽Bus的事件(訂閱消息)
// 事件名,回調(diào)
Bus.$on('sendMsg', (msg) => {
console.log(msg)
this.msg = msg
})
},
data() {
return {
msg: ''
}
}
}
</script>
<style>
.A {
width: 200px;
height: 100px;
border: 1px solid black;
}
</style>
在這里,msg就是發(fā)送方BaseB的恐龍扛狼
components/BaseB.vue
<template>
<div class="B">
我是B組件(發(fā)布方)
<button @click="clickSend">發(fā)布通知</button>
</div>
</template>
<script>
import Bus from '../utils/EventBus'
export default {
methods: {
clickSend() {
// 3.B組件(發(fā)送方)觸發(fā)事件的方式傳遞參數(shù)(發(fā)布消息)
Bus.$emit('sendMsg', '恐龍扛狼') // Bus.$on('sendMsg', (msg) => {})和這里的名字同名
}
}
}
</script>
<style>
.B {
width: 200px;
height: 100px;
border: 1px solid black;
}
</style>
App.vue
<template>
<div id="app">
<BaseA></BaseA>
<BaseB></BaseB>
</div>
</template>
<script>
import BaseA from './components/BaseA.vue'
import BaseB from './components/BaseB.vue'
export default {
components: {
BaseA,
BaseB
}
}
</script>
<style>
</style>
utils/EventBus.js
utils通常用來放工具
// 創(chuàng)建一個(gè)都能訪問到的事件總線(空的Vue實(shí)例)
import Vue from 'vue'
const Bus = new Vue()
export default Bus
非父子通信——provide & inject(擴(kuò)展)
總結(jié)
【綜合案例】——小黑記事本— 組件版
components/TodoHeader.vue
<template>
<div class="head">
<h1>小黑記事本</h1>
<input
@keyup.enter="handleAdd"
v-model="todoName"
type="text"
placeholder="請(qǐng)輸入待辦事項(xiàng)"
/>
<button @click="handleAdd">添加任務(wù)</button>
</div>
</template>
<script>
export default {
data() {
return {
todoName: ''
}
},
methods: {
handleAdd() {
if (this.todoName.trim() == '') {
// 輸入許多的空格,無效輸入,不讓添加
alert('請(qǐng)輸入內(nèi)容')
return
}
this.$emit('add', this.todoName)
// 點(diǎn)擊添加之后,輸入框清空
this.todoName = ''
}
}
}
</script>
<style>
.head {
width: 243px;
/* background-color: #584949; */
}
input {
height: 30px;
vertical-align: middle;
}
.head button {
height: 30px;
}
</style>
components/TodoMain.vue
<template>
<section class="body">
<ul>
<li v-for="(item, index) in todoList" :key="item.id">
<span>{{ index + 1 }}</span>
<span class="content">{{ item.name }}</span>
<button @click="handleDel(item.id)">×</button>
</li>
</ul>
</section>
</template>
<script>
export default {
props: {
todoList: Array
},
methods: {
handleDel(tt) {
this.$emit('del', tt)
}
}
}
</script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
list-style: none;
}
.body li {
width: 234px;
height: 50px;
display: flex;
line-height: 50px;
/* justify-content: space-between; */
background-color: #de8282;
border-bottom: black solid 2px;
}
.body li .content {
flex: 1;
}
.body li button {
height: 50%;
align-self: center;
display: none;
}
.body li:hover button {
display: block;
width: 20px;
}
</style>
components/TodoFooter.vue
<template>
<div v-show="todoList.length > 0" class="footer">
<span
>合計(jì):<strong>{{ todoList.length }}</strong></span
>
<button @click="clear()">清空任務(wù)</button>
</div>
</template>
<script>
export default {
props: {
todoList: Array
},
methods: {
clear() {
this.$emit('clear')
}
}
}
</script>
<style>
.footer {
width: 234px;
display: flex;
justify-content: space-between;
}
</style>
components/App.vue
<template>
<div id="app">
<TodoHeader @add="handleAdd"></TodoHeader>
<TodoMain :todoList="todoList" @del="handleDel"></TodoMain>
<TodoFooter @clear="handleClear" :todoList="todoList"></TodoFooter>
</div>
</template>
<script>
import TodoHeader from './components/TodoHeader.vue'
import TodoMain from './components/TodoMain.vue'
import TodoFooter from './components/TodoFooter.vue'
export default {
components: {
TodoHeader,
TodoMain,
TodoFooter
},
// 渲染功能:
// 1.提供數(shù)據(jù):寫在公共的父組件 App.vue
// 2.通過父組件,將數(shù)據(jù)傳遞給TodoMain
// 3.利用v-for 渲染
// 添加功能
// 1.收集表單數(shù)據(jù) v-model
// 2.監(jiān)聽事件(回車+點(diǎn)擊 都要添加
// 3.子傳父 將任務(wù)名稱傳給父App.vue
// 4.進(jìn)行添加 unshift(自己的數(shù)據(jù),自己負(fù)責(zé))
// 刪除功能
// 1.監(jiān)聽事件(刪除的點(diǎn)擊) 攜帶id
// 2.子傳父,將刪除的id傳給父組件App.vue
// 3.父組件刪除 filter(自己的數(shù)據(jù),自己負(fù)責(zé))
// 底部合計(jì):父傳子list 渲染
// 清空功能:子傳父,通知到父組件 父組件清空
// 持久化存儲(chǔ):watch深度監(jiān)視-> 往本地存 -> 進(jìn)入頁面優(yōu)先讀取本地
data() {
return {
todoList: JSON.parse(localStorage.getItem('list')) || [
{ id: 1, name: '吃水果' },
{ id: 2, name: '喝酸奶' }
]
}
},
methods: {
handleAdd(todoName) {
this.todoList.unshift({
id: +new Date(),
name: todoName
})
},
handleDel(tt) {
this.todoList = this.todoList.filter((item) => item.id != tt)
},
handleClear() {
this.todoList = []
}
},
watch: {
todoList: {
deep: true,
handler(newValue) {
localStorage.setItem('list', JSON.stringify(newValue))
}
}
}
}
</script>
<style >
#app {
/* width: 234px;
height: 200px;
display: flex;
flex-direction: column; */
margin: 50px 50px;
}
</style>
v-model原理
$event 解釋一下
用上一個(gè)小黑記事本為例
<TodoMain :todoList=“todoList” @del=“handleDel”>
所以說 :value
是父傳子@input
是子傳父
所以才能實(shí)現(xiàn)雙向綁定
$evevnt也就是e
如果應(yīng)用于復(fù)選框,就是check屬性,和change事件的合寫
表單類組件封裝
e.target.value
也就是 在html里面,就寫成$event ; 在js里面,就寫成e.target.valuecomponents/BaseSelect.vue
子組件不可以用v-model,但是父組件是非常可以用的,因?yàn)槭亲约旱臄?shù)據(jù)自己負(fù)責(zé)
<template>
<div class="BaseSelect">
<!-- <select v-model="cityId">
這樣寫是不行的,因?yàn)楦附M件的數(shù)據(jù),只能父組件改,
而v-model是雙向的,他在嘗試更改父親的數(shù)據(jù),他要造反,要謀權(quán)篡位,所以報(bào)錯(cuò)了
-->
<!--
:value="cityId" 用來父傳子
@change="handleChange" 用來子傳父
-->
<select :value="cityId" @change="handleChange">
<option value="101">北京</option>
<option value="102">深圳</option>
<option value="103">上海</option>
<option value="104">廣州</option>
<option value="105">遼寧</option>
<option value="106">福建</option>
</select>
</div>
</template>
<script>
export default {
props: {
cityId: String
},
methods: {
handleChange(e) {
console.log(e.target.value)
this.$emit('changeId', e.target.value)
}
}
}
</script>
<style>
</style>
App.vue
<<template>
<!-- :cityId="selectId" 用來父傳子
@changeId="selectId = $event" 用來子傳父
-->
<BaseSelect :cityId="selectId" @changeId="selectId = $event"></BaseSelect>
</template>
<script>
import BaseSelect from './components/BaseSelect.vue'
export default {
components: {
BaseSelect
},
data() {
return {
selectId: '106'
}
}
}
</script>
<style>
</style>
@changeId="selectId = $event
" 中的$event是什么
在Vue中,$event是一個(gè)特殊的變量,用于在事件處理函數(shù)中訪問觸發(fā)事件時(shí)傳遞的數(shù)據(jù)。它代表了事件對(duì)象,包含了與事件相關(guān)的信息。
在你提供的代碼中,@changeId="selectId = $event"表示當(dāng) BaseSelect 組件觸發(fā) changeId 事件時(shí),將事件對(duì)象中的數(shù)據(jù)賦值給 selectId。
具體來說,$event 是一個(gè)占位符,用于接收傳遞給事件處理函數(shù)的參數(shù)。在這個(gè)例子中,BaseSelect 組件在觸發(fā) changeId 事件時(shí),會(huì)將選中的城市ID作為參數(shù)傳遞給事件處理函數(shù)。然后,通過 $event 來訪問這個(gè)參數(shù),并將其賦值給 selectId。
例如,如果 BaseSelect 組件在觸發(fā) changeId 事件時(shí)傳遞了一個(gè)值為 123 的參數(shù),那么 selectId 將被賦值為 123。代碼示例中的 @changeId=“selectId = $event” 就是將事件對(duì)象中的數(shù)據(jù)賦值給 selectId 的方式。
請(qǐng)注意,$event 只是一個(gè)約定俗成的命名,你也可以使用其他變量名來接收事件對(duì)象中的數(shù)據(jù)。例如,你可以使用 @changeId=“selectId = value”,其中 value 是你自己定義的變量名,它將接收事件對(duì)象中的數(shù)據(jù)。
總之,$event 是Vue中的一個(gè)特殊變量,用于在事件處理函數(shù)中訪問事件對(duì)象中傳遞的數(shù)據(jù)。它可以幫助你在事件處理函數(shù)中獲取和處理事件的相關(guān)信息。
v-model 簡化代碼
注意是父組件使用v-model,不是子組件
子組建中只能用value接受???
下拉框是@change事件components/BaseSelect.vue
<template>
<div class="BaseSelect">
<!-- :value="value" 父傳子 -->
<select :value="value" @change="handleChange">
<option value="101">北京</option>
<option value="102">深圳</option>
<option value="103">上海</option>
<option value="104">廣州</option>
<option value="105">遼寧</option>
<option value="106">福建</option>
</select>
</div>
</template>
<script>
export default {
// 父傳子
props: {
value: String
},
methods: {
// 子傳父
// 監(jiān)聽
handleChange(e) {
console.log(e.target.value)
this.$emit('input', e.target.value)
}
}
}
</script>
<style>
</style>
App.vue
<template>
<!-- v-model => :value(父傳子) + @input 子傳父 -->
<BaseSelect v-model="selectId"></BaseSelect>
</template>
<script>
import BaseSelect from './components/BaseSelect.vue'
export default {
components: {
BaseSelect
},
data() {
return {
selectId: '106'
}
}
}
</script>
<style>
</style>
發(fā)現(xiàn)父組件的 :屬性名
和@事件名
都是可以自己命名的,但是子組建的不行
小結(jié)
封裝輸入框組件
components/BaseInput.vue
<template>
<div class="BaseInput">
<!--
這里使用@change 和 @input 事件的效果是不一樣的
前者 輸入框失去焦點(diǎn)時(shí)觸發(fā)
后者 輸入框內(nèi)容發(fā)生改變時(shí)就會(huì)觸發(fā)
-->
<input @change="handleChange" :value="value" type="text" />
</div>
</template>
<script>
export default {
props: {
value: String
},
methods: {
handleChange(e) {
console.log(e.target.value)
this.$emit('input', e.target.value)
}
}
}
</script>
<style>
</style>
App.vue
<template>
<div class="app">
父組件顯示text: {{ text }}
<BaseInput v-model="text"></BaseInput>
</div>
</template>
<script>
import BaseInput from './components/BaseInput.vue'
export default {
components: {
BaseInput
},
data() {
return {
text: 'slx'
}
}
}
</script>
<style>
</style>
.sync修飾符 (父子雙向綁定
所以就還是父組件簡單了點(diǎn),子組件還是老樣子在書寫上比較比v-model麻煩,所以,封裝表單還是v-model,其余的用.sync較好
components/SB.vue
發(fā)現(xiàn)這里的update:visable 可以改成其他名字,這個(gè):
有必要加嗎,還是就是起了這個(gè)名字??
<template>
<div v-show="visable" class="base-dialog-wrap">
<div class="base-dialog">
<div class="title">
<h3>溫馨提示:</h3>
<button @click="close" class="close">x</button>
</div>
<div class="content">
<p>你確認(rèn)要退出本系統(tǒng)么?</p>
</div>
<div class="footer">
<button @click="close">確認(rèn)</button>
<button @click="close">取消</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
visable: Boolean
},
methods: {
close() {
this.$emit('update:visable', false)
}
}
}
</script>
<style scoped>
.base-dialog-wrap {
width: 300px;
height: 200px;
box-shadow: 2px 2px 2px 2px #ccc;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 0 10px;
z-index: 9;
background-color: #fff;
}
.base-dialog .title {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 2px solid #000;
}
.base-dialog .content {
margin-top: 38px;
}
.base-dialog .title .close {
width: 20px;
height: 20px;
cursor: pointer;
line-height: 10px;
}
.footer {
display: flex;
justify-content: flex-end;
margin-top: 26px;
}
.footer button {
width: 80px;
height: 40px;
}
.footer button:nth-child(1) {
margin-right: 10px;
cursor: pointer;
}
</style>
App.vue
<template>
<div class="app">
<button @click="isShow = true">退出按鈕</button>
<div :class="{ show_bg: isShow }">
<SB :visable.sync="isShow"></SB>
<!-- :visable.sync => :visable + @update:visable -->
</div>
</div>
</template>
<script>
import SB from './components/SB.vue'
export default {
data() {
return {
isShow: false
}
},
methods: {},
components: {
SB
}
}
</script>
<style>
.show_bg {
position: absolute;
top: 0;
z-index: 2;
width: 100%;
height: 100%;
background-color: #c4bbbba4;
}
</style>
ref 和 $refs (獲取當(dāng)前組件內(nèi)的dom元素
獲取dom元素
<div ref="mychart" class="BaseEchart"></div>
var myChart = echarts.init(this.$refs.mychart)
components/BaseEchart.vue
<template>
<div ref="mychart" class="BaseEchart"></div>
</template>
<script>
import * as echarts from 'echarts'
export default {
mounted() {
// var myChart = echarts.init(document.querySelector('.BaseEchart'))
var myChart = echarts.init(this.$refs.mychart)
var option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'bar'
}
]
}
option && myChart.setOption(option)
}
}
</script>
<!-- <style scoped> -->
<style >
.BaseEchart {
width: 500px;
height: 500px;
}
</style>
App.vue
<template>
<div class="app">
<div class="BaseEchart">搗亂的div</div>
<BaseEchart></BaseEchart>
</div>
</template>
<script>
import BaseEchart from './components/BaseEchart.vue'
export default {
components: {
BaseEchart
}
}
</script>
<style >
</style>
獲取組件實(shí)例
componentes/BaseForm.vue
<template>
<div class="BaseForm">
用戶名: <input v-model="username" type="text" /><br />
密碼: <input v-model="password" type="text" /><br />
</div>
</template>
<script>
export default {
data() {
return {
username: '',
password: ''
}
},
methods: {
getValues() {
return {
username: this.username,
psd: this.password
}
},
clearValues() {
this.username = ''
this.password = ''
}
}
}
</script>
<style>
</style>
App.vue
<template>
<div class="app">
<BaseForm ref="Form"></BaseForm>
<button @click="getDate">獲取數(shù)據(jù)</button>
<button @click="clearDate">清空數(shù)據(jù)</button>
</div>
</template>
<script>
import BaseForm from './components/BaseForm.vue'
export default {
components: {
BaseForm
},
methods: {
getDate() {
console.log('www')
console.log(this.$refs.Form.getValues())
},
clearDate() {
this.$refs.Form.clearValues()
}
}
}
</script>
<style>
</style>
this.$refs['aa']
在項(xiàng)目里看到了另一種寫法,之前沒學(xué)過這種,記錄一下this.$refs['aa']
和 this.$refs.aa
這兩種寫法在大多數(shù)情況下是等效的,可以互相替換使用。
在 Vue 組件中,$refs
是一個(gè)對(duì)象,它包含了通過 ref
屬性引用的子組件或 DOM 元素。當(dāng)你在模板中使用 ref
屬性給元素或組件命名時(shí),可以使用 $refs
來訪問這些引用。
通常情況下,你可以使用點(diǎn) .
運(yùn)算符來訪問 $refs
中的引用,例如 this.$refs.aa
。這種寫法更簡潔直觀,與訪問對(duì)象屬性的方式相似。
而 this.$refs['aa']
使用方括號(hào) []
來訪問 $refs
中的引用。這種寫法可以動(dòng)態(tài)地根據(jù)變量的值來訪問引用,例如 this.$refs[refName]
,其中 refName
是一個(gè)變量。
所以,如果你的引用名稱是一個(gè)靜態(tài)的字符串,那么 this.$refs['aa']
和 this.$refs.aa
是等效的,可以互相替換使用。
需要注意的是,當(dāng)引用名稱包含特殊字符或變量時(shí),使用方括號(hào) []
的寫法更適用。例如,this.$refs['my-component-' + index]
可以動(dòng)態(tài)地根據(jù)索引值來訪問不同的引用。
總結(jié)起來,this.$refs['aa']
和 this.$refs.aa
這兩種寫法在大多數(shù)情況下是相同的,可以互相替換使用。選擇哪種寫法取決于你的需求和引用名稱的特殊性。
Vue 異步更新、$nextTick
App.vue文章來源:http://www.zghlxwxcb.cn/news/detail-631736.html
<template>
<div class="app">
<div v-if="isEdit">
<input ref="inp" v-model="editValue" type="text" />
<button @click="over">ok</button>
</div>
<div v-else>
{{ title }}
<button @click="changeTitle">edit</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
editValue: '',
title: '我是title',
isEdit: false
}
},
methods: {
changeTitle() {
// 1.顯示輸入框(異步dom更新
this.isEdit = true
// 2.讓輸入框獲取焦點(diǎn)
// $nextTick等dom更新完,立刻執(zhí)行函數(shù)體
this.$nextTick(() => {
this.$refs.inp.focus()
})
},
over() {
this.title = this.editValue
this.isEdit = false
}
}
}
</script>
<style>
</style>
這個(gè)光標(biāo)異步的問題 在Day5中也有解決方法——自定義指令文章來源地址http://www.zghlxwxcb.cn/news/detail-631736.html
到了這里,關(guān)于Vue [Day4]的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!