一、創(chuàng)建3.0項目
1.
2. main.js頁面
//引入createApp工廠函數(shù)玛荞,用于創(chuàng)建Vue實例對象
import { createApp } from 'vue';
//引入項目的 主vue頁面
import App from '@/App.vue';
//創(chuàng)建vue實例對象app,并將實例對象放到App.vue頁面里(實例對象上的路由組件也會在那里顯示)
const app = createApp(App);
//將app實例對象掛載到id選擇器為app的元素上,該元素在index.js文件里
app.mount('#app');
注意:vue3.0不再支持2.0中引入浙踢、掛載Vue的方式。
vue3.0的頁面不再需要頁面根元素包裹所有HTML內(nèi)容灿渴。
3.vue開發(fā)者工具
vue3.0不能使用vue2.0的開發(fā)者工具洛波。
去Chrome應(yīng)用商店搜索新版本的開發(fā)者工具。
https://www.gugeapps.net/webstore/search?key=vue
二骚露、setUp()函數(shù)
1.什么是setUp
(1)setup()是vue3.0中的一個新的配置項蹬挤,值是一個函數(shù)。
(2)setUp()是所有 組合式API的 “舞臺”荸百。
2.setUp函數(shù)的作用
- 組件中所用到的:數(shù)據(jù)闻伶、方法、計算屬性够话、生命周期函數(shù)蓝翰、監(jiān)聽器光绕、nextTick函數(shù)......等內(nèi)容都要配置在setUp()中。
- 用到的配置項都要從vue引入畜份。
- setUp()函數(shù)應(yīng)有返回值诞帐,兩種返回值類型:
(1)若返回一個 對象,則對象的屬性和方法在模板中都可以直接使用爆雹。(重點關(guān)注)
(2)若返回一個渲染函數(shù)停蕉,則可以自定義渲染內(nèi)容。(了解钙态。通常是vue2.0模式或結(jié)合jsx)
(3)vue3.0推薦使用 渲染函數(shù)render() 進行搭建HTML頁面慧起。(結(jié)合jsx)
3.參數(shù)
setUp()函數(shù)接收兩個參數(shù):
(1)props,子組件內(nèi)册倒,要先在setUp函數(shù)外 聲明props屬性蚓挤,然后通過參數(shù),將父組件傳遞的數(shù)據(jù)傳到setUp()內(nèi)使用驻子。(如果在HTML中直接使用props數(shù)據(jù)灿意,則無需在setup()中聲明、返回)
(2)context崇呵,該參數(shù)是一個對象缤剧,包含 attrs、emits域慷、slots荒辕。
- attrs:值為對象,包含:組件外部傳來芒粹,且沒有用props接收的屬性兄纺,相當(dāng)于this.$attrs。
- emits:自定義事件化漆,子組件內(nèi)估脆,要先在setUp函數(shù)外 聲明emits屬性,然后在setUp()內(nèi)觸發(fā)自定義事件座云。相當(dāng)于this.$eimt疙赠。
- slots:收到的插槽內(nèi)容,相當(dāng)于this.$slots朦拖。
<template>
<!-- arr.id是將props數(shù)據(jù)解構(gòu)到本頁面使用圃阳;childValue.name則是直接使用的props數(shù)據(jù) -->
<div class="child-container">
{{arr.id}}
{{childValue.name}}
<el-button type="primary" @click="changeValue">點擊改變父組件傳遞的數(shù)據(jù)</el-button>
</div>
</template>
<script>
import { defineComponent, reactive } from 'vue';
export default defineComponent({
// 子組件要接收props數(shù)據(jù)
props: {
childValue: {
type: Object,
default: () => {},
},
},
// 子組件要聲明emits事件
emits: ['childClick'],
setup(props, { emit }) {
// 通過props參數(shù)在setup中拿到父組件傳遞的props數(shù)據(jù)
const arr = reactive({
id: props.childValue.arr[1],
});
// 子組件觸發(fā)父組件的自定義事件并傳遞數(shù)據(jù)
const changeValue = () => {
emit('childClick', '我是修改后的數(shù)據(jù)');
};
return {
changeValue,
arr,
};
},
});
</script>
4.注意
(1)setUp()在beforCreate()之前執(zhí)行,所有函數(shù)內(nèi)的 this值為undefined璧帝。getCurrentInstance()方法可以獲取到組件實例捍岳。
先引入:import {getCurrentInstance} from 'vue';
proxy相當(dāng)于this:const { proxy } = getCurrentInstance();
(2)setUp() 不能被 async 修飾,一旦使用,則返回值會被Promise包裹锣夹。
(3)vue2.0配置不要和3.0配置混用页徐,因為:
- vue3.0中的方法不能訪問到vue2.0中定義的變量和方法。
- 混用時银萍,變量或方法重名变勇,以vue3.0為準(zhǔn)。
5.小結(jié)
(1) 組件通信props贴唇、事件觸發(fā)emits搀绣、組件聲明component 都寫在 export 之中,setup()之前戳气。
(2) 外部引用 的.js文件链患,無需在setup()中聲明,但是要在 return 中返回瓶您。
(5)vue3.0中锣险,setup()內(nèi)部使用數(shù)據(jù),和模板中一樣览闰,都不用加 this或proxy。
三巷折、ref()函數(shù)
1.作用
不加任何修飾的 setUp()函數(shù)中 定義的變量压鉴,不具有響應(yīng)式。ref函數(shù)可以讓數(shù)據(jù)具有響應(yīng)式锻拘。
2.語法
先引入:import {ref} from 'vue'
const 變量名 = ref(變量值)
(1)ref將變量放到了 引用對象 中油吭。(RefImpl:引用實現(xiàn)的實例對象)
(2)JS中操作變量:變量名.value;在模板中 直接使用 ref定義的變量署拟⊥裨祝【重要!M魄睢心包!】
(3)如果用ref轉(zhuǎn)換對象,則在js中應(yīng)使用變量名.value.屬性名
3.注意:
(1)ref可以接收基本數(shù)據(jù)類型馒铃,或者對象類型蟹腾。
(2)基本數(shù)據(jù)類型依靠 defineProperty()的getter、setter實現(xiàn)響應(yīng)式区宇。
(3)對象類型數(shù)據(jù)依靠 reactive()方法實現(xiàn)響應(yīng)式娃殖。
四、reactive()函數(shù)
1.作用
將 對象(或數(shù)組)類型的數(shù)據(jù) 變?yōu)轫憫?yīng)式议谷。
2.語法
先引入:import {reactive} form 'vue'
let 變量名 = reactive(變量值)
(1)內(nèi)部通過ES6的Proxy實現(xiàn)炉爆。返回的是一個Proxy代理對象。
(2)vue3.0中可以直接修改對象屬性和數(shù)組元素,也有響應(yīng)式
3.修改對象的屬性
(1)如果修改對象中多個屬性值并保留響應(yīng)式芬首,下面的情況是不生效的:
let obj = reactive({
name: '張三',
age: 5,
});
const changeInfo = () => {
obj = {
name: '李四',
age: 66,
};
};
(2)可以使用 .屬性名 的方式逐個修改對象的屬性
(3)也可以在定義數(shù)據(jù)時再包裹一層對象
const obj = reactive({
info: {
name: '張三',
age: 55,
},
});
const changeInfo = () => {
obj.info = {
name: '李四',
age: 66,
};
};
4.應(yīng)用
(1)reactive()函數(shù)只能接受 "對象"赴捞。
(2)實際開發(fā)中,用一個對象變量state衩辟,包裹所有數(shù)據(jù)螟炫,reactive()接收state即可,return返回state即可艺晴。
setup() {
//數(shù)據(jù)
const state = reactive({});
//方法昼钻、計算屬性、監(jiān)視器......
......
}
5.為什么不用ref代替reactive
const person = ref({name:'李四'})
如果用ref包裹對象封寞,則在js中獲取數(shù)據(jù)時:person.value.name然评。
五、toRef()
1.作用
(1)將對象中的某個屬性 單獨拿出來狈究,轉(zhuǎn)換成一個單獨的 ref響應(yīng)式數(shù)據(jù)碗淌,并且與原對象(的屬性)是 同一個數(shù)據(jù)。
(2)這樣拆解抖锥,是為了方便在HTML中使用亿眠,不用再用對象.屬性這么麻煩。
語法:const newKey = toRef(對象磅废,屬性名)
2.使用ref()和toRef()轉(zhuǎn)換響應(yīng)式數(shù)據(jù)的區(qū)別
ref()將任意轉(zhuǎn)換為響應(yīng)式數(shù)據(jù)纳像;
toRef()將 對象 中的 某個屬性 拿出來,轉(zhuǎn)換為ref響應(yīng)式數(shù)據(jù)拯勉。
ref()是對原數(shù)據(jù)的 復(fù)制竟趾,返回值是一個 新的響應(yīng)式數(shù)據(jù)。
toRef()是對原數(shù)據(jù)的 引用宫峦,返回值與原數(shù)據(jù)(的屬性值)是同一個數(shù)據(jù)岔帽。
const person = ref({ name: '馬冬梅' }); // 原數(shù)據(jù)
const refValue = ref(person.name); // 用ref轉(zhuǎn)換的響應(yīng)式數(shù)據(jù)
const toRefValue = toRef(person,'name'); // 用toRef()轉(zhuǎn)換的響應(yīng)式數(shù)據(jù)
const changeRef = () => {
refValue .value = '王祖賢'; // 修改的只是refValue這個數(shù)據(jù),原數(shù)據(jù)person沒變
toRefValue .value = '王祖賢'; // toRefValue和person同時被修改导绷,因為他們是同一個數(shù)據(jù)
};
3.toRefs()
可以將一個對象中的 所有屬性 轉(zhuǎn)換為 ref響應(yīng)式數(shù)據(jù)犀勒。返回值是一個對象。
語法:const newKey = toRefs(對象)
六妥曲、ref組件實例
1.vue3.0中通過ref獲取組件實例
(1)給組件添加 ref屬性账蓉,屬性值為自定義變量(refName)。
<Child ref="refName" />
(2)在setup()中 聲明自定義變量
const refName = ref(null)
(3)在js中通過訪問 變量名.value 拿到組件實例逾一。
console.log(refName.value)
<Child ref="childRef"></Child>
const childRef = ref(null);
onMounted(() => {
console.log(childRef.value.arr);
});
注意: 如果使用的是<script setup></script>形式铸本,則子組件要用 defineExpose 暴露屬性和方法。
2.vue2.0中通過ref獲取組件實例
(1)給組件添加 ref屬性遵堵,屬性值為自定義變量箱玷。
(2)在js中通過 this.$refs.變量名 獲取到組件實例怨规。
3.循環(huán)動態(tài)生成的表單添加ref
(1)通過方法的形式給ref屬性賦值。:ref="(el)=>getRefs(el,index)"
(2)使用時無需調(diào)用this.$refs锡足,因為自定義變量中存儲的就是 表單對象ref本身波丰。
<div v-for="(item, index) in formArr" :key="item.id">
<a-form :model="item.data" :rules="formRules" :ref="(el) => getRefs(el, index)">
<a-form-item name="age" label="年齡">
<a-input v-model:value="item.data.age" />
</a-form-item>
</a-form>
</div>
// 數(shù)據(jù)部分
const state = reactive({
formArr: [
{
id: 0,
data: { age: '222' },
rules: { age: [{ required: true, trigger: 'change', message: '空' }] },
},
],
refs: [],
})
// 方法
const getRefs = (el, index) => {
state.refs[index] = el;
};
// 更新前清除校驗,否則表單雖然刪除了舶得,但是檢驗規(guī)則被下一個表單 繼承了
onBeforeUpdate(() => {
state.refs = state.refs.filter((item) => item !== null);
state.refs.forEach((item) => {
item.clearValidate();
});
});
七掰烟、計算屬性
1.先引入computedAPI(組合式API)
import { defineComponent, reactive, toRefs, computed } from 'vue';
2.計算屬性的兩種寫法
// 簡寫版,只讀
let js1 = computed(() => { return xxx })
// 完整版沐批,可讀也可以對計算屬性的值進行修改
let js2 = computed(() => { return {
get() {
return xxx
},
set(value) {
}}
})
八纫骑、watch監(jiān)視器
import { watch } from 'vue'
watch(監(jiān)視的值,(newValue,oldValue)=>{ },{ deep: true })
1.監(jiān)聽 ref 定義的響應(yīng)式數(shù)據(jù)(簡單數(shù)據(jù))
(1)如果要監(jiān)聽多個數(shù)據(jù)九孩,可以寫多個watch()函數(shù)進行監(jiān)聽先馆。
watch(name, (newValue, oldValue) => {
console.log(newValue, oldValue);
}, { immediate: true, deep: true });
(2)或者監(jiān)聽一個數(shù)組,此時newValue和oldValue也是數(shù)組
watch([name, age], (newValue, oldValue) => {
console.log(newValue, oldValue);
}, { immediate: true });
2.監(jiān)聽 reactive 定義的響應(yīng)式數(shù)據(jù)(對象)
(1)監(jiān)聽reactive定義的響應(yīng)式數(shù)據(jù)的 全部屬性(對象本身)
- oldValue的值有誤躺彬,與newValue值一樣
- 強制開啟深度監(jiān)聽(deep屬性無效)
// (1)oldValue的值有誤煤墙,與newValue值一樣
// (2)強制開啟深度監(jiān)聽(deep屬性無效)
watch(person, (newValue, oldValue) => {
console.log(newValue, oldValue);
});
(2)監(jiān)聽reactive定義的響應(yīng)式數(shù)據(jù)的 某個屬性(簡單數(shù)據(jù)類型)
- 應(yīng)當(dāng)用函數(shù)返回值的形式
// 應(yīng)當(dāng)用函數(shù)返回值的形式
watch(() => person.name, (newValue, oldValue) => {
console.log(newValue, oldValue);
});
(3)監(jiān)聽reactive定義的響應(yīng)式數(shù)據(jù)的 多個屬性(簡單數(shù)據(jù)類型)
- 用數(shù)組包裹,數(shù)組元素依然是函數(shù)形式
// 用數(shù)組包裹宪拥,數(shù)組元素依然是函數(shù)形式
watch([() => person.name, () => person.age], (newValue, oldValue) => {
console.log(newValue, oldValue);
});
(4)監(jiān)聽reactive定義的響應(yīng)式數(shù)據(jù)中的一個對象屬性
- oldValue值有誤
- 深度監(jiān)聽deep的值有效
// (1)oldValue值有誤
// (2)深度監(jiān)聽deep的值有效
watch(() => person.job, (newValue, oldValue) => {
console.log(newValue, oldValue);
}, { deep: true });
3. 監(jiān)聽ref定義的 對象
(1)當(dāng)ref定義的是簡單數(shù)據(jù)類型時仿野,watch應(yīng)當(dāng) 直接監(jiān)聽該數(shù)據(jù),因為ref將數(shù)據(jù)變成了一個 RefImpl對象她君。(如果監(jiān)聽了 "變量.value"设预,相當(dāng)于監(jiān)聽的是一個數(shù)字或字符串。)
(2)當(dāng)ref定義的是對象犁河,watch應(yīng)當(dāng)監(jiān)聽 "變量.value" ,因為 "變量.value" 是有reactive定義的對象魄梯。
(3)或者開啟深度監(jiān)聽桨螺,因為 "變量.value" 值是一個對象,只有當(dāng)對象的地址被覆蓋時才能被監(jiān)聽到酿秸。
九灭翔、watchEffect
watch(()=>{ } )
watchEffect類似于 計算屬性:
(1)不直接指明監(jiān)視的數(shù)據(jù);而是回調(diào)函數(shù)中用到的任意一個數(shù)據(jù)發(fā)生改變時辣苏,會重新執(zhí)行回調(diào)函數(shù)肝箱。
(2)計算屬性注重的是結(jié)果,所以必須有返回值稀蟋;watchEffect注重的是回調(diào)函數(shù)的執(zhí)行過程煌张。
十、生命周期
1.beforDestoryed和destroyed鉤子函數(shù)替換為 beforUnmounted 和 unmounted
2.可以和vue2.0一樣退客,通過配置項的形式寫生命周期函數(shù)骏融,與setup()同級別链嘀。
3.也可以在setup()中寫生命周期函數(shù)。
4.setup()中沒有beforeCreate档玻、created對應(yīng)的鉤子函數(shù)怀泊。
5.其他六個鉤子函數(shù)都要在前面加on。
6.語法:onMounted(()=>{})
十一误趴、父子通信
1.普通父子傳值
跟vue2.0用法一致霹琼,變化的是要提前聲明props和emits。
2.v-model
(1)父組件給子組件傳遞數(shù)據(jù)時凉当,用v-model代替v-bind綁定數(shù)據(jù)
<Child v-model:自定義名稱="數(shù)據(jù)值" />
(2)子組件用props 接收后可以使用數(shù)據(jù)枣申。
(3)父組件無需綁定自定義事件,子組件也無需聲明emits纤怒,可以直接在子組件中更新這個數(shù)據(jù)糯而。
emit('update:自定義名稱',最新數(shù)據(jù));
// 父組件給子組件傳值
<Kid v-model:doubleaction="doubleaction"></Kid>
// 子組件直接修改傳遞的數(shù)據(jù)
emit('update:doubleaction', '我是修改后的雙向綁定數(shù)據(jù)');
(4)一個組件上可以 多次 使用v-model雙向傳遞數(shù)據(jù)泊窘。
3.provide和inject
先在頁面 引入 provide或inject熄驼;然后在setup()中寫入下面的代碼:
provide('自定義變量名',傳遞的數(shù)據(jù))
inject('同名變量名'烘豹,默認(rèn)值)
// 祖先傳值
const people = ref({
name: '李四',
});
provide('people', people);
// 后代接收
const peopleFormInject = inject('people', {});
const peopleFormInject = ref(inject('people', {}))
注意:
(1)這種傳值方式也是 單向數(shù)據(jù)流瓜贾。如果后代組件想要修改數(shù)據(jù),可以由父組件傳遞方法携悯。
(2)如果provide 一個 響應(yīng)式數(shù)據(jù)祭芦,且該數(shù)據(jù)被對象包裹,則應(yīng)該傳遞 完整的對象憔鬼,才不會破壞其響應(yīng)式龟劲。
參考:http://www.reibang.com/p/e97612cd437d
4. 子組件修改父組件數(shù)據(jù)注意
子組件可以直接修改父組件傳遞的 對象中的屬性、數(shù)組元素轴或,但是昌跌,該屬性必須是由 父組件傳遞而來,不能是在子組件中添加的照雁,否則數(shù)據(jù)能更新蚕愤,視圖不更新。
要在父組件中處理好數(shù)據(jù)饺蚊,如下圖:
在子組件中添加的數(shù)據(jù)萍诱,能通過emit修改,但視圖不更新:
5. 子組件使用父組件數(shù)據(jù)
1.由于生命周期污呼,子組件先OnMounted后父組件才OnMounted裕坊,所以父組件通過接口獲取的數(shù)據(jù),子組件在OnBeforeUpdate時才能獲取到燕酷。
2.父子傳值時可以不用props傳遞碍庵,而是在子組件內(nèi)部聲明方法映企,方法的形參就是父組件傳遞的數(shù)據(jù),方法內(nèi)部使用數(shù)據(jù)静浴。
3.父組件通過refs調(diào)用子組件的方法堰氓,通過實參傳遞數(shù)據(jù)。
(1)父組件按鈕點擊時的事件處理函數(shù):調(diào)用子組件方法并傳遞數(shù)據(jù)
handleOpenModal(flag, row) {
const { handleOpen } = this.$refs.DataModal;
handleOpen(flag, row);
}
},
(2)子組件聲明方法苹享,并使用數(shù)據(jù)
// 彈窗打開
const handleOpen = (flag, row) => {
const { id, name } = row;
MyOrgApi.subOrgList(id).then((res) => {
const { code, data, message } = res.data || {};
if (code === 200) {
});
};
十二双絮、vue3.0響應(yīng)式原理
vue2.0和vue3.0響應(yīng)式原理 重點參考146:
https://www.bilibili.com/video/BV1Zy4y1K7SH?p=145