第一章:vue3.0基礎
1,認識vue3.0
vue3.0發布時間為2020-9-18,從項目體驗上,vue3.0比起vue2.0有以下優勢:
打包大小減少41%
初次渲染塊55%,更新渲染塊133%
內存占比少54%
2,新增內容
源碼重構,新增特性,重寫架構。
第二章:使用vue3.0
創建vue3.0有兩種方式:使用vue-cli和使用vite
i.使用cli:
vue create vue-name 選擇vue3.0項目
2,使用vite
安裝vite
npm install -g create-vite-app
創建項目
create-vite-app vue3_vite
進入項目目錄后:
npm i
運行
npm run dev
第三章:從vue3.0項目中學習(cli項目)
1.配置文件
vue3.0的配置文件還是和vue2.0的一樣:vue.config.js。配置內容和vue2.0的一樣,也就是webpack配置。
2,main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
就只有三段代碼
createApp :是一個工廠函數
將三段代碼拆開:
import { createApp } from 'vue'
import App from './App.vue'
let app=createApp(App)
app.mount('#app')
變量app是一個實例,和vm類似,但是比vm更加輕盈。
3,app.vue
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
在vue文件中不需要使用根標簽。
目前為止。vue3.0項目上和vue2.0的不同主要集中在兩個上,后面再慢慢學習。
第四章:組合式API
1,setup配置項
vue3.0使用setup作為vue的配置項,數據、方法等都配置在setup里面,也就是說vue2.0中全部的配置都配置在setup里面。
setup是一個函數,放回值為一個對象,該對象內容可以直接被使用
<template>
<span>name:{{ name }}</span>
<span>age:{{ age }}</span>
<button @click="change">點我</button>
</template>
<script>
export default {
name: 'App',
setup() {
let name = '小智';
let age = 12;
function change() {
alert('點我成功');
}
return {
name,
age,
change
};
}
};
</script>
注意:vue2.0和vue3.0不要混合使用,要不然vue3.0使用vue2.0的內容會包underfined。
2,ref函數實現數據響應式
ref函數會將數據封裝成一個引用對象,通過這個引用對象可以實現數據的響應式。
ref對基本數據類型的處理
<template>
<span>name:{{ name }}</span>
<span>age:{{ age }}</span>
<button @click="change">點我</button>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'App',
setup() {
let name = ref('小智');
let age = ref(12);
function change() {
name.value = '你大爺';
name.age = 190;
}
return {
name,
age,
change
};
}
};
</script>
先打印name和age變量
這里使用了get/set方法,通過ref將數據封住成一個引用對象,在這個改變這個引用對象的value值實現數據的雙向綁定。所以這里的name和age是一個引用對象。
ref對對象的處理:這里說一般對象和數組對象吧
<template>
<span>name:{{ name }}</span
><br />
<span>age:{{ age }}</span
><br />
<span>學號:{{ student.no }}</span
><br />
<span>班級:{{ student.class }}</span
><br />
<span>數組1{{ array[0] }}</span
><br />
<span>數組2:{{ array[1] }}</span
><br />
<button @click="change">點我</button>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'App',
setup() {
let name = ref('小智');
let age = ref(12);
let student = ref({
no: 12,
class: '一班'
});
let array = ref(['哈哈', 'hahah']);
console.log('基本數據類型', name);
console.log('對象', student);
console.log('數組', array);
function change() {
name.value = '你大爺';
age.value = 190;
student.value.no = 10000;
student.value.class = '100班級';
array.value[0] = 'biaiabi';
array.value[1] = 'biaiabi';
}
return {
name,
age,
student,
array,
change
};
}
};
</script>
打印結果:
我們可以看到,ref將對象封裝成一個proxy,而proxy則是vue3.0響應式的另一種方案——代理。
測試設置值得時候,只需要通過:
student.value.class = '100班級';
array.value[0] = 'biaiabi';
的方式設置即可,其中student.value不再是一個屬性值而是一個proxy代理對象實現響應,而基本數據類型設置值只需要name.value設置值,是使用defineProperty實現響應
注意:單層的get/set放在原型對象中。對象的層次也是使用defineProperty,這里的對象單層是指其引用標識,這個引用標識相當一個基本數據類型
可以得出結論,在vue3.0對響應式的處理有兩個部分
基本數據類型:使用 Object.defineProperty
引用類型:使用的是window.Proxy
ref對set,map的處理
對map:
<script>
import { ref } from 'vue';
export default {
name: 'App',
setup() {
let map = ref(new Map());
map.value.set('key', '12');
console.log('取值', map.value.get('key'));
console.log('map', map.value);
}
};
</script>
對set處理
<script>
import { ref } from 'vue';
export default {
name: 'App',
setup() {
let set = ref(new Set());
set.value.add('12');
set.value.add('13');
console.log('set', set);
console.log('value', set.value);
for (let item of set.value) {
console.log('取值', item);
}
}
};
</script>
ref函數封裝的數據,ref.value是數據的本身
3.reactive函數
我們知道的一點:對於ref來說,ref可以實現基本數據類型和引用類型的響應式。
基本數據類型:使用 Object.defineProperty的get/set實現響應式
引用數據類型:使用reactive函數實現響應式(封裝window.Proxy)
vue3.0提供了一個函數來實現對象類型的響應數據——reactive
reactive函數接收一個對象,返回一個代理對象,該代理對象可實現“深層次”的代理(聯想拷貝),數據有多層也可以實現響應式。
<template>
<div>姓名:{{ student.name }}</div>
<br />
<div>年齡:{{ student.age }}</div>
<br />
<div>班級:{{ student.classNO }}</div>
<br />
<button @click="change">改變</button>
</template>
<script>
import { reactive } from 'vue';
export default {
name: 'App',
setup() {
let student = reactive({
name: '小智',
age: 18,
classNO: 123
});
console.log("reactive",student);
function change() {
student.name = '小白';
student.age = 12;
student.classNO = 12345;
}
return {
student,
change
};
}
};
</script>
reactive函數可以處理任意引用類型,例如數組、map、set等。
4,vue3.0實現響應式原理
vue2.0響應式的弊端主要有:更新和刪除屬性界面不會更新,通過數據下標更新數組,界面不會更新。
在vue2.0解決上述問題,提供了許多API,例如更新$set,刪除$delete等。對數組則是封裝了數組的方法。
vue3.0實現響應式的原理是封裝了window.Proxy實現數據響應式,對數據進行更新刪除操作沒有這個問題。
實現響應式原理:
<script>
let person={
name:'小智',
age:18,
}
let p=new Proxy(person,{})
</script>
這個就可以實現數據增刪改查的響應情況,如果需要響應到視圖,需要在第二個參數進行相關處理捕獲到更改信息並響應到視圖上。
<script>
let person = {
name: '小智',
age: 18,
}
let p = new Proxy(person, {
get(target, propName) {
console.log("讀取了", target, propName, target[propName]);
//return target[propName]
return Reflect.get(target,propName)
},
set(target, propName, value) {
//target[propName]=value
Reflect.set(target,propName,value)
},
deleteProperty(target, propName) {
console.log("刪除", target, propName,target[propName]);
//delete target[propName]
return Reflect.defineProperty(target,propName)
}
})
</script>
get(target, propName):target原對象,propName讀取的屬性名
set(target, propName, value):target原對象,propName修改的屬性名,value修改成的屬性值,target[propName]原屬性值
deleteProperty(target, propName):target原對象,propName刪除的屬性名,
vue3.0使用反射對數據進行增刪改,(很多Object的方法都逐漸移到Reflect上,主要是如果同名的時候,反射會有個返回值告訴是否操作成功)
只要掌握vue2.0的響應式原理,vue3.0的更好理解。
注意:數據是可以追加屬性的,也是響應式的
5,setup的參數(setup兩個注意點)
i,setup會在beforeCreate之前調用一次,此時this為underfined
ii.setup參數:
setup(props,context)
props:值為一個對象,包含外部傳入得props且組件內部聲明接收了屬性。組件使用props接收后,該參數包含接收到的屬性
context:上下文對象,包括了
attrs:相當this.$attrs
slots:相當this.$slots
emit:相當this.$emit
a,props
App.vue
<template>
<Dome :name="student.name" @change="change" />
</template>
<script>
import { reactive } from 'vue';
import Dome from './components/dome.vue';
export default {
name: 'App',
components: {
Dome
},
setup() {
let student = reactive({
name: '小智'
});
return {
student
};
}
};
</script>
<style scoped></style>
<template>
<div>{{ data.name }}</div>
</template>
<script>
import { reactive } from 'vue';
export default {
name: 'dome',
props: ['name'],
emits: ['change'],
components: {},
setup(props) {
let data = reactive({
name: props.name
});
console.log(props);
return {
data
};
}
};
</script>
<style scoped></style>
我們可以看到dome使用props接收屬性之后,setup的prop參數含有代理的屬性。
b,context:上下文對象
在父組件中:
<Dome :name="student.name" :age="student.age" @change="change">
子組件中:
<template>
<div>{{ data.name }}</div>
<slot></slot>
<button @click="test">點我</button>
</template>
<script>
import { reactive } from 'vue';
export default {
name: 'dome',
props: ['name'],
emits: ['change'],
components: {},
setup(props, context) {
let data = reactive({
name: props.name
});
console.log('props', props);
console.log('emit', context.emit);
console.log('attrs', context.attrs);
console.log('slots', context.slots);
function test() {
context.emit('change', '大白貓');
}
return {
data,
test
};
}
};
</script>
<style scoped></style>
標注的部分注意:如果子組件中沒有使用props來接收屬性,這個屬性就會放到attrs中,接收了就不放進去。
警告:如果向字組件傳入一個方法,需要使用一下定義:
emits: ['change'],
要不然vue3.0會在控制台給出警告,特別煩人礙眼!!!
6,計算屬性computed
用法:和ref或reactive類似
簡寫:
<template>
<div>{{ data.num1 }}+{{ data.num2 }}={{ num }}</div>
<button @click="change1">加法</button>
<button @click="change2">減法</button>
</template>
<script>
import { reactive, computed } from 'vue';
export default {
name: 'App',
setup() {
let data = reactive({
num1: 12,
num2: 1
});
let num = computed(() => {
return data.num1 + data.num2;
});
function change1() {
data.num1 = data.num1 + 1;
}
function change2() {
data.num2 = data.num2 + 100;
}
return {
num,
data,
change1,
change2
};
}
};
</script>
<style scoped></style>
get/set方式寫法
let num = computed({
get() {
console.log('調用了');
return data.num1 + data.num2;
},
set(newValue, oldValue) {
console.log("修改調用");
}
});
7,監視watch
a,監視ref單個值
<template>
<div>{{ mun }}+{{ age }}}</div>
<br />
<button @click="change1">加法</button><br />
<button @click="change2">減法</button>
</template>
<script>
import { ref, watch } from 'vue';
export default {
name: 'App',
setup() {
let mun = ref('xiaozhi');
let age = ref(12);
watch(mun, (oldValue, newValue) => {
console.log('改變了', oldValue, newValue);
});
watch(age, (oldValue, newValue) => {
console.log('改變了', oldValue, newValue);
});
function change1() {
mun.value = 'hahah';
}
function change2() {
age.value = 233;
}
return {
mun,
age,
change1,
change2
};
}
};
</script>
b,監視多個ref值
<template>
<div>{{ mun }}+{{ age }}</div>
<br />
<button @click="change1">加法</button><br />
<button @click="change2">減法</button>
</template>
<script>
import { ref, watch } from 'vue';
export default {
name: 'App',
setup() {
let mun = ref('xiaozhi');
let age = ref(12);
watch([age, mun], (newValue, oldValue) => {
console.log('改變了', newValue, oldValue);
});
function change1() {
mun.value = 'hahah';
}
function change2() {
age.value = 233;
}
return {
mun,
age,
change1,
change2
};
}
};
</script>
<style scoped></style>
監視多個值的時候,oldValue和newValue是一個數組,
監視單個值不能用.value.
監視ref值(值為對象)
watch(data.value, (newValue, oldValue) => {
console.log('改變了', newValue, oldValue);
});
或者
watch(data, (newValue, oldValue) => {
console.log('改變了', newValue, oldValue);
},{deep:true});
c.監視reactive
<template>
<div>{{ data.name }}:{{ data.age }}</div>
<br />
<button @click="change1">加法</button><br />
<button @click="change2">減法</button>
</template>
<script>
import { reactive, watch } from 'vue';
export default {
name: 'App',
setup() {
let data = reactive({
name: 'xiaozhi',
age: 12
});
watch(data, (newValue, oldValue) => {
console.log('改變了', newValue, oldValue);
});
function change1() {
data.name = 'hahah';
}
function change2() {
data.age = 233;
}
return {
data,
change1,
change2
};
}
};
</script>
監視一個reactive數據的時候(不包括其屬性),無法獲取到oldValue(現價段無法獲取reactive數據的oldValue),解決方式,將其需要獲取改變狀態的數據提出來單獨監視。
例如我們需要監視age值得時候,建age提出來單獨使用ref監視
let age=ref(12)
d.監視多個reactive
<template>
<div>{{ data.name }}:{{ data.age }}___{{ data1.name }}+{{ data1.age }}</div>
<br />
<button @click="change1">加法</button><br />
<button @click="change2">減法</button>
</template>
<script>
import { reactive, watch } from 'vue';
export default {
name: 'App',
setup() {
let data = reactive({
name: 'xiaozhi',
age: 12
});
let data1 = reactive({
name: 'wowowo',
age: 134
});
watch([data, data1], (newValue, oldValue) => {
console.log('改變了', newValue, oldValue);
});
function change1() {
data.name = 'hahah';
}
function change2() {
data.age = 233;
}
return {
data,
data1,
change1,
change2
};
}
};
</script>
監視兩個的時候和ref一樣,oldValue和newValue是一個數組,
注意:監視reactive數據的時候,默認開啟深度監視,而且無法關閉
watch(data, (newValue, oldValue) => {
console.log('改變了', newValue, oldValue);
},{deep:false});
不生效
監視reactive數據的屬性(對象或基本類型)的時候,深度監視默認打開,而且可以關閉。
watch(data.job, (newValue, oldValue) => {
console.log('改變了', newValue, oldValue);
},{deep:false});
deep生效
e.監視reactive數據的屬性(基本數據類型的時候),寫成函數並返回。
<template>
<div>{{ data.name }}:{{ data.age }}</div>
<br />
<button @click="change1">加法</button><br />
<button @click="change2">減法</button>
</template>
<script>
import { reactive, watch } from 'vue';
export default {
name: 'App',
setup() {
let data = reactive({
name: 'xiaozhi',
age: 12,
job: {
zhiye: '學生',
zhize: '學習'
}
});
watch(
() => {
return data.name;
},
(newValue, oldValue) => {
console.log('改變了', newValue, oldValue);
},
{ deep: true }
);
function change1() {
data.name = 'hahah';
}
function change2() {
data.age = 233;
}
return {
data,
change1,
change2
};
}
};
</script>
f,監視reactive的屬性為對象的方式和與函數的方式
let data = reactive({
name: 'xiaozhi',
age: 12,
job: {
j1: {
zhiye: '學生',
zhize: '學習'
}
}
});
為對象:
watch(data.job, (newValue, oldValue) => {
console.log('改變了', newValue, oldValue);
});
為函數:
watch(
() => data.job,
(newValue, oldValue) => {
console.log('改變了', newValue, oldValue);
}
);
無法監視到其變化,加入深度監視之后:
watch(
() => data.job,
(newValue, oldValue) => {
console.log('改變了', newValue, oldValue);
},
{ deep: true }
);
因此:監視對象的時候,如果之間監視reactive數據中的對象,默認開啟深度監視而且無法關閉,監視reactive數據中的對象以函數的形式必須開啟深度監視才能監視到下一層的數據。
8,watchEffect:監視
用法:
watchEffect(()=>{}):無參數,返回值,用於在回調函數內部使用到的數據,初始化調用一次
<template>
<div>{{ data.name }}:{{ data.age }}:{{ data.job.j1.zhiye }}</div>
<br />
<button @click="change1">姓名</button><br />
<button @click="change2">職業</button>
</template>
<script>
import { reactive, watch, watchEffect } from 'vue';
export default {
name: 'App',
setup() {
let data = reactive({
name: 'xiaozhi',
age: 12,
job: {
j1: {
zhiye: '學生',
zhize: '學習'
}
}
});
watchEffect(() => {
let x1 = data.name;
let x2 = data.job;
console.log('監視', x1, x2);
});
function change1() {
data.name = '小虎';
}
function change2() {
data.job.j1.zhiye = '老師';
}
return {
data,
change1,
change2
};
}
};
</script>
<style scoped></style>
初始化調用一次:
點擊姓名:
點擊職業:
可以看到必須要使用data中的數據才能觸發該監視函數
再換一種寫法:修改x2
let x2 = data.job.j1.zhiye;
點擊職業:
9.vue3的生命周期
五.組合api2
1.自定義hook
hooks和mixin有點像,都是為了復用代碼,只是hooks是一系列函數並放回相關數據。
自定義一個js文件hooks.js
import { reactive, onMounted } from 'vue';
const getPoint = function() {
let data = reactive({
pageX: 0,
pageY: 0
});
onMounted(() => {
window.addEventListener('click', function(event) {
data.pageX = event.pageX;
data.pageY = event.pageY;
});
});
return data;
};
export { getPoint };
導入hooks
<template>
<div>{{ data.pageX }}:{{ data.pageY }}</div>
<br />
<button @click="change1">姓名</button><br />
<button @click="change2">職業</button>
</template>
<script>
import { getPoint } from './hooks/usePoint';
export default {
name: 'App',
setup() {
let data = getPoint();
return { data };
}
};
</script>
<style scoped></style>
2.toRef和toRefs
用於創建一個ref對象(必須)使其value指向另一個ref的屬性
作用:
創建一個toRef對象(實質是一個ref對象)的value指向另一個ref對象的屬性。
將響應式對象的某個屬性向外提供使用。
<template>
<div>{{ name }}:{{ data.age }}:{{ pq }}</div>
<br />
<button @click="change2">職業</button>
</template>
<script>
import { reactive, toRef } from '@vue/reactivity';
export default {
name: 'App',
setup() {
let data = reactive({
name: 'xiaozhi',
age: 23,
job: {
p: {
zhiye: '打工人'
}
}
});
function change2() {
data.name = 'xiaobai';
data.age = 34;
data.job.p.zhiye = 'hh';
}
return {
data,
name: toRef(data, 'name'),
pq: toRef(data.job.p, 'zhiye'),
change2
};
}
};
</script>
<style scoped></style>
toRefs:批量處理
<template>
<div>{{ name }}:{{ data.age }}:{{ job.p.zhiye }}</div>
<br />
<button @click="change2">職業</button>
</template>
<script>
import { reactive, toRefs } from '@vue/reactivity';
export default {
name: 'App',
setup() {
let data = reactive({
name: 'xiaozhi',
age: 23,
job: {
p: {
zhiye: '打工人'
}
}
});
function change2() {
data.name = 'xiaobai';
data.age = 34;
data.job.p.zhiye = 'hh';
}
return {
data,
...toRefs(data),
change2
};
}
};
</script>
<style scoped></style>
批量處理只能處理一層數據,深層的單獨取出,如上html代碼。或者如下
...toRefs(data.job.p),
六,其他組合API
1,shallowReactive和shallowRef
shallowReactive:只考慮第一層的數據響應
shallowRef:只考慮基本數據類型的響應式不考慮引用類型的響應
2,readonly和shallowReadonly
readonly:數據只讀(深只讀)
let data = reactive({
name: 'xiaozhi',
age: 23,
});
data=readonly(data)
shallowReadonly:數據只讀(淺只讀,一層)
3,toRow和markRaw
toRow:將一個響應式數據傳成普通對象(ref數據不可用)。
markRaw:標記一個數據,使其永遠不會變成響應式數據。
比如第三方數據,如果不使其凍結,添加到另一個對象的時候會注意查詢其屬性使其變成響應式,這回導致效率問題。
let data=reative{...}
let code={....}
data.code=markRow(code)
4,自定義ref:customRef
參數:
track:通知vue追蹤讀取的數據
trigger:通知vue重新渲染模板
setup() {
let data = myRef('xx');
function myRef(value) {
return customRef((track, trigger) => {
return {
get() {
track(); //通知vue追蹤讀取的數據
return value;
},
set(newValue, oldValue) {
console.log(newValue, oldValue);
value = newValue;
trigger(); //通知vue重新渲染模板
}
};
});
}
return {
data
};
}
set方法沒有oldValue。
5,provide和inject
用於祖先后代元素之間的通信
祖先
setup(props, context) {
let data = reactive({
name: '給噎死',
age: 100
});
provide('parentData', data);
return {
...toRefs(data)
};
}
后代:
setup(props, context) {
let parentData = inject('parentData');
let data = reactive(parentData);
return {
...toRefs(data)
};
}
由provide向后代組件傳數據,后代使用inject接收數據
注意:祖先元素的數據和后代的數據是具有雙向相應式的,同時修改一方的數據,另一方的數據也會跟着變。
7,響應數據類型的判斷
七.vue3.0組件
1.fragment組件
vue3.0模板中無需一個根標簽的原因是vue3.0自動添加添加一個虛擬組件fragment作為模板的更標簽。
2.teleport組件
作用:將包含的內容標簽移動到特定html標簽內。
例如我想將ul標簽全部移動到body內和div標簽同級,就可以使用eleport組件
用法:
<teleport to="body">......</teleport>
例如設計一個彈框就可以使用這個組件。
app.vue
<div class="app">
<h1>祖先</h1>
<son />
</div>
son.vue
<div class="son">
<h2>后代組件</h2>
<child />
</div>
child.vue
<template>
<div class="child">
<h4>后代元素</h4>
<button @click="show = true">打開</button>
<teleport to="body">
<div class="shallow" v-if="show">
<div class="glob">
<p>這是一個彈窗</p>
<button @click="show = false">關閉</button>
</div>
</div>
</teleport>
</div>
</template>
<script>
import { reactive, inject, toRefs } from 'vue';
export default {
name: 'child',
props: [],
components: {},
setup(props, context) {
let data = reactive({
show: false
});
return {
...toRefs(data)
};
}
};
</script>
<style scoped>
.child {
width: 200px;
background-color: yellowgreen;
}
.glob {
height: 300px;
width: 300px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: violet;
}
.shallow {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: lightslategrey;
}
</style>
在child.vue里面設計彈框,將彈框內容移動到body里面去。
3.Suspense組件
異步組件,官方還在試驗階段。。。。
八.vue3.0其他改動內容
截圖了,都是比較好理解的內容。
來源:尚硅谷