vue3.0 beta已出,來快速實踐一下吧
本節源碼立即前往
前段時間尤大在嗶哩嗶哩直播了vue3的預覽,來簡單實踐一下吧
vue3的改變
- 更小更快
- 支持自定義渲染器
- 響應式修改為基於Proxy的偵測
- 深度結合typescript
- 基於treeshaking優化
- ...
創建項目😄
引入文件
克隆一個官方倉庫
git clone https://github.com/vuejs/vue-next.git
打開這個項目,下載一下依賴,然后編譯
npm i
npm run dev
編譯文件在vue-next\packages\vue\dist,可以把它復制出來放到我們創建的項目文件夾
在vue-next\packages\vue\examples目錄下有官方的示例
腳手架創建項目
首先確保你的@vue/cli為最新版本 vue -V

然后使用 vue create vue3demo創建一個vue項目,創建的時候勾選 vuex router
然后進入項目文件夾,命令行安裝插件vue add vue-next將項目升級到vue3,打開package.json可以發現都升級到了最新版本

初始化你的第一個vue3程序⭐
實例化
為了體驗整個api的變化,我們先使用直接引入的方式體驗效果,新建一個index.html 引入vue.global.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.global.js"></script>
</head>
<body>
</body>
</html>
然后我們新建一個盒子用來掛載vue實例,新建一個模板當做根節點
<body>
<div id='app'>
</div>
<template id="root">
<div>
</div>
</template>
</body>
實例化一個vue3,vue3是按需引入,使用一個api之前需要先引入,通過createApp來實例化
<script>
const {
createApp
} = Vue;
const root = {
template: '#root',
}
createApp(root).mount('#app');
</script>
setup
setup函數是一個新的組件選項。作為在組件內使用 Composition API 的入口點。
vue3將更多的方法和數據在 setup函數中定義,這就是大家都覺得像react hook 的原因,其實實現的原理是不同的,只是寫法有點像;這樣做的目的就是為了更好的實現邏輯復用,具體會在后面舉例
與 2.x 版本生命周期相對應的組合式 API
beforeCreate-> 使用setup()created-> 使用setup()beforeMount->onBeforeMountmounted->onMountedbeforeUpdate->onBeforeUpdateupdated->onUpdatedbeforeDestroy->onBeforeUnmountdestroyed->onUnmountederrorCaptured->onErrorCaptured
setup最后返回所有的對象和方法,注意這里的msg不是響應式對象
const root = {
template: '#root',
// beforeCreate created data和methods
setup() {
console.log('vue init');
const msg = "vue init";
return {
msg
};
},
}
<template id="root">
<div>
{{msg}}
</div>
</template>

ref和reactive
ref和reactive都是用來創建響應式對象的,接下來的案例都使用腳手架創建項目來進行測試
打開views/Home.vue文件,刪除掉無用的東西,首先引入ref, reactive
import { ref, reactive } from "vue";
export default {
name: "Home",
setup() {
// 定義一個ref響應式對象
const count = ref(0);
// 如果要定義多個可以使用reactive
const state = reactive({
size: 36,
color: "red"
});
// 定義一個方法
const increment = () => {
count.value++;
};
return {
count,
increment,
state
};
}
};
在頁面中的使用和vue2是一樣的
<template>
<div>
<div>{{count}}</div>
<button @click="increment">increment</button>
<div :style="{fontSize:state.size+'px',color:state.color}">reactive</div>
</div>
</template>

toRefs
上面reactive的導出和使用,必須state.key,如果想像vue2中的data一樣定義直接使用,可以使用 toRefs
引入 import { ref, reactive, toRefs } from "vue";,使用 toRefs返回展開對象,如果你不使用toRefs而直接返回對象,原對象的響應式將丟失。
return {
count,
increment,
...toRefs(state)
};
頁面中使用
<div :style="{fontSize:size+'px',color:color}">reactive</div>

計算屬性和監聽器
我們來到about.vue中進行代碼編寫,首先引入需要使用的
import { reactive, watch, computed, toRefs, watchEffect } from "vue";
watchEffect 傳入一個函數並立即執行,如果函數里面使用了上面定義好的響應式對象,當對象發生變化時,會再次觸發這個函數
定義計算屬性和監聽器
export default {
setup() {
const state = reactive({
num1: 1,
//定義一個計算屬性
num2: computed(() => state.num1 * 2)
});
// 如果響應性的屬性有變更,就會觸發這個函數,但他是惰性的
watchEffect(() => {
console.log(`effect 觸發了!${state.num1}`);
});
// 定義一個監聽器
const stop = watch(state, (val, oldVal) => {
console.log("watch ", oldVal.num1);
});
//數值增加方法
const increment = () => state.num1++;
// 停止監聽
const stopwatch = () => stop();
return {
...toRefs(state),
stopwatch,
increment
};
}
};
查看效果

dom操作
在components文件夾下新建一個DomRef.vue文件,定義一個空的ref響應數據refdemo並返回,頁面中使用ref標簽來綁定這個數據,然后就可以通過操作這個響應數據來操作dom
<template>
<div>
<div ref="refdemo">domref</div>
<input type="range" v-model="size" />
</div>
</template>
<script>
import { ref, watch, onMounted } from "vue";
export default {
setup() {
// 定義空白ref作為Template ref操作 dom
const refdemo = ref(null);
const size = ref(24);
// 監聽拖動
watch(size, (val, oldVal) => {
refdemo.value.style.fontSize = size.value + "px";
});
return {
refdemo,
size
};
}
};
</script>
在home組件中使用這個組件查看效果
<DomRef></DomRef>
...
import DomRef from "../components/DomRef";
...
components: {
HelloWorld,
DomRef,
},
看看效果

組件通信
和vue2的通信大同小異,新建ComDemo.vue,setup函數接受一個 props 和 context 上下文
父組件>>子組件
- 父組件觸發子組件事件
- 父組件得到子組件屬性
- props傳值
<template>
<div>{{name}}</div>
</template>
<script>
import { inject } from "vue";
export default {
props: {
name: String
},
setup(props, context) {
// setup中使用 prop
console.log("props.name:" + props.name);
// 供父組件測試
const str = "我是子組件的str屬性";
const talk = () => {
console.log("我被父組件觸發了");
};
return {
str,
talk
};
}
};
</script>
來的Home.vue中使用這個組件,並給這個子組件綁定一個響應式的ref屬性
<ComDemo :name="'我是父組件傳值'" @talk="talk" ref="comdemo"></ComDemo>
import ComDemo from "../components/ComDemo";
...
components: {
HelloWorld,
DomRef,
},
...
setup() {
// 定義一個ref響應式對象
const comdemo = ref(null);
onMounted(() => {
//得到子組件的值
console.log(comdemo.value.str);
// 觸發子組件事件
comdemo.value.talk();
});
return {
...
comdemo
};
}
看看效果

子組件>>父組件
綁定一個方法給子組件
<ComDemo :name="'我是父組件傳值'" @talk="talk" ref="comdemo"></ComDemo>
...
setup() {
const talk = e => {
console.log(e);
};
...
return {
...
talk,
comdemo
};
...
ComDemo.vue
setup(props, context) {
...
// setup中觸發父組件事件
context.emit("talk", "我是子組件 我觸發你了");
...
return {
str,
talk
};
}
效果

inject和provide
這個和vue2使用一樣
父組件定義
import { provide } from "vue";
...
provide("injectmsg", "provide talk");
后代組件使用
import { inject } from "vue";
...
const injectmsg = inject("injectmsg");
console.log("injectmsg :>> ", injectmsg);
組合式api示例❤️
前面說到 Composition API的好處,這來舉一個例
放一個尤大神的demo,這是一段鼠標操作的功能代碼,我們自己還需要編寫一個功能來實現結合
const useMouse = () => {
const state = reactive({
x: 0,
y: 0
});
const update = e => {
state.x = e.pageX;
state.y = e.pageY;
};
onMounted(() => {
window.addEventListener("mousemove", update);
});
onUnmounted(() => {
window.removeEventListener("mousemove", update);
});
return toRefs(state);
};
我們再寫一個鍵盤方法,記錄每次用戶按下鍵盤
// 監控鍵盤事件
const useKeyBoard = () => {
const status = ref(false);
const update = () => {
status.value = !status.value;
};
onMounted(() => {
window.addEventListener("keypress", update);
});
onUnmounted(() => {
window.removeEventListener("onkeydown", update);
});
return {
status
};
};
我們來到 HelloWorld.vue組件中使用這兩個方法
<template>
<div>{{status?`${x}-${y}`:`${y}-${x}`}}</div>
</template>
<script>
import { reactive, onMounted, onUnmounted, toRefs, ref } from "vue";
// 返回鼠標位置
const useMouse = () => {
const state = reactive({
x: 0,
y: 0
});
const update = e => {
state.x = e.pageX;
state.y = e.pageY;
};
onMounted(() => {
window.addEventListener("mousemove", update);
});
onUnmounted(() => {
window.removeEventListener("mousemove", update);
});
return toRefs(state);
};
// 監控鍵盤事件
const useKeyBoard = () => {
const status = ref(false);
const update = () => {
status.value = !status.value;
};
onMounted(() => {
window.addEventListener("keypress", update);
});
onUnmounted(() => {
window.removeEventListener("onkeydown", update);
});
return {
status
};
};
export default {
name: "HelloWorld",
setup() {
return {
...useMouse(),
...useKeyBoard()
};
}
};
</script>
我們在頁面中顯示鼠標方位,當我們按下任何按鍵的時候,這個顯示顛倒
看下效果

vuex和vuerouter💥
vuex和vuerouter在實例化方式上有了一點區別,分別使用 createStore和createRouter進行實例化
使用vuex
打開vue3demo\src\store\index.js文件
import Vuex from 'vuex'
export default Vuex.createStore({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
console.log('當前count:',state.count);
}
},
actions: {},
modules: {}
});
新建一個 RouterAndVuex.vue組件
<template>
<div>
<button @click="increment">increment</button>
</div>
</template>
<script>
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();
const count = store.state.count;
console.log("vuex >>", count);
const increment = () => {
// mutations
store.commit("increment");
};
return {
increment,
};
}
};
</script>
看看效果

使用vuerouter
<template>
<div>
<button @click="increment">increment</button>
<button @click="gotoAbout">about</button>
</div>
</template>
<script>
import { useStore } from "vuex";
import { useRouter } from "vue-router";
export default {
setup() {
// 使用vuex
const store = useStore();
const count = store.state.count;
console.log("vuex >>", count);
const increment = () => {
// mutations
store.commit("increment");
};
// 使用vue-router
const router = useRouter();
const gotoAbout = () => {
router.push("About");
};
return {
increment,
gotoAbout
};
}
};
</script>
看看效果

