.vue文件的分類
在工程化項目里面,默認會設置views和components這兩個文件夾,而兩個文件夾里面放的又都是.vue文件,既然是同類型的文件,為啥要設置兩個文件夾呢?因為用途有點區別。
按照使用方式,.vue文件可以分為頁面、組件和控件三類。
頁面
一般存放在views目錄里面,一開始我還以為這里的.vue文件不需要屬性(props)呢,因為沒有誰能給他傳遞屬性值。
后來學習路由知識后發現我錯了,路由不僅可以加載組件,而且還能給組件傳遞屬性。
這樣.vue文件不管在哪,都可以有屬性(props),那么從代碼角度,就沒有明確的區分了。
所以只能從使用方式來區分一下。
頁面可以看做是路由直接加載的組件,作為一個“容器”,存放多個組件,確定其位置,負責各個組件之間的連系。
組件
導航、header、footer這類多個頁面共用的部分,或者功能比較獨立的部分。
從頁面里面分離出來,可以讓代碼更清晰易讀,擴展性和維護性也會更好。
比如我要改個導航,直接去找導航組件就好,不用怕誤改了其他代碼。
其他組件里面也不會看到共用組件里的代碼,指定位置就好。可以把重點放在自己的邏輯上面來。
控件
簡單的說,就是UI庫提供的各種控件,比如表單里的文本框、選擇列表框、日期選擇控件等,還有表單控件、列表控件、分頁控件等。
雖然在vue里面,這些都是組件的形式出現,但是還是可以細分、明確一下,這樣不容易產生誤解。
本系列主要說的是控件這一類的組件。
我們先用一個簡單的文本框來舉個例子。
定義組件
<template>
<input
:value="modelValue"
@input="myInput"
><br>
組件內部值:{{modelValue}}
</template>
<script>
import { toRef, toRefs, watch } from 'vue'
export default {
name: 'mytext',
props: {
modelValue: String,
meta: Object
},
emits: ['input'],
setup (props, context) {
// 向父組件提交修改屬性
const myInput = (e) => {
console.log(e)
context.emit('update:modelValue', e.target.value)
}
return {
myInput
}
}
}
</script>
- template:模板部分,寫html代碼和vue的綁定語句。
- script:js腳本部分。
- style:可以寫css。
- export default:返回一個對象,用于創建組件。
- name :組件名稱,可以不寫這個屬性,但是如果要寫的話,就不要重名。
- props:定義組件的屬性。
- emits:定義組件的事件。
- setup:vue3的新寫法。雖然vue3也兼容vue2的方式,但是我們都用上vue3了,那么盡量用新的方式。
父組件里面使用組件
<template>
<div class="home">
父組件的值:{{value}}<br>
自己的組件:<mytext v-model="value"/><br>
原生文本框:<input type="text" v-model="value"/>
</div>
</template>
<script>
import { ref } from 'vue'
import mytext from '@/components/nf-html/input-text2.vue'
export default {
name: 'eleBase',
components: {
mytext
},
setup () {
const value = ref('1')
return {
value
}
}
}
</script>
import
可以用 import 的方式引入組件。components
如果組件沒有做全局注冊的話,那么需要使用 components 在父組件里面注冊。
setup 的參數
setup提供兩個參數,一個是props,傳遞組件的屬性;一個是context(上下文),提供emit這類的方法。
props
在 setup 里面可以通過 props 訪問組件的屬性,但是請注意:原則上是不允許在組件內部直接修改屬性值的。所以一般可以使用 watch 的方式來監控屬性的變化。
但是如果我就是想訪問屬性呢,結果會如何?
我們可以設計幾個方式來看看效果:
const myProps1 = props // 獲取屬性
const myProps = toRefs(props) // toRefs的方式獲取屬性
const myValue = toRef(props, 'modelValue') // toRef獲取指定的屬性
console.log('原生屬性:', props)
console.log('toRefs的屬性:', myProps)
console.log('toRef 的屬性:', myValue)
console.log('myProps.modelValue:', myProps.modelValue)
// 直接修改,會報錯(ESLint)
// props.modelValue = '直接改屬性'
// 嘗試修改,雖然只有警告不會報錯,但是修改不會成功。
myProps1.modelValue = '內部直接修改'
// 嘗試修改,雖然只有警告不會報錯,但是修改不會成功。
myValue.value = '內部修改屬性'
// 監控屬性變化
watch(() => props.modelValue, (v1, v2) => {
console.log('原值:', v1)
console.log('新值:', v2)
})
-
屬性的打印結果
002setup-propsKind.png
從打印結果可以看出來,組件的屬性被封裝成了reactive的形式,然后又在外面套上了readonly,所以實現了無法直接賦值的效果。
注意:不能修改只是對簡單類型有效,如果屬性是對象類型的話,還是可以直接修改屬性值的。
-
嘗試直接賦值。
以前嘗試直接賦值的時候會報錯,現在雖然不報錯了,但是無法修改,而且會給出警告,如圖:
002setup-props1.png -
toRefs 和 toRef
003setup-propsTorefs.png
toRefs可以把屬性拆分成多個ref的形式,嘗試了一下,如果屬性是簡單類型,還是不可以直接修改值的。但是如果屬性類型是對象的話,又可以直接修改值了。
toRef也是一樣的效果。
context
我們來看一看context的打印結果:
比較常用的就是emit,相當于vue2的this.emit。
我們可以寫一個向父組件提交屬性的函數
// 向父組件提交修改屬性
const myInput = (e) => {
console.log(e)
context.emit('update:modelValue', e.target.value)
}
- update:modelValue
這是修改父組件的屬性的一種方法,后面是要修改的值。
v-model
vue3支持多個v-model了。父組件可以這樣寫:
v-model:foo="foo" v-model:fo="fo"
如果只需要一個屬性的話,也可以簡寫
v-model="fooo"
只是組件內部的屬性名稱要寫成固定的“modelValue”,如果是其他屬性名稱的話,必須用帶冒號的方式。
小結
這個只是一個開篇,介紹基礎知識,后面還有更精彩的內容。