Vue3 的新特性經過 Vue 的做者尤雨溪的視頻而來:Vue.js 做者談 Vue 3.0 beta 現狀html
Webpack 是有 Tree-shaking 這個功能的,但 Tree Shaking 前提是 ES Module(import…) 來寫。Vue 3 在瀏覽器中,依然會有一個全局的 Vue對象,可是在用 Webpack 的時候,它就沒有defualt export,你就不能 import vue,把 Vue 當對象自己去操做,全部的 Api 都要 import 進來。這樣使得一些不會被用到的功能不會被引用進來。vue
文件大小變化很明顯,22.5KB ~ 13.5KB,若是使用 Composition 新語法,只有 11.75KBreact
Composition API RFCwebpack
Vue3 在 setup 把一個對象返回,會把對象變成響應式,而後,Vue 在須要的地方去追蹤它所用到的響應式的依賴,當依賴變化的時候從新去渲染。(reactive)git
vue 3 裏暴露一個新的 Api 叫作 watchEffect,Effect 就是反作用和 React 的 useEffect 相似。github
一些原生的數據結構,好比像數字、Boolean 等,這些就須要用一個東西包裝(ref)web
核心:reactive \ ref \ watchEffect \ 其餘是這三者的組合使用。算法
組件再也不須要一個惟一的根節點了vue-cli
Vue 2 模板只有一個單獨的根節點。typescript
Vue 3 文字、多個節點、甚至也能夠是個v-for,都會自動變成碎片,若是使用渲染函數,也能夠直接在渲染函數裏面返回一個數組,自動變成一個碎片。
對標 React Portal
<teleport>
向 Vue 核心添加組件 該組件須要一個目標元素,該元素是經過須要一個 HTMLElement
或 querySelector
字符串的 prop
提供的。
組件將其子代移動到 DOM
選擇器標識的元素 在虛擬 DOM
級別,子級仍然是子代的後代 <teleport>
,所以他們能夠從其祖先那裏得到注入
異步組件
和 React 的 Suspense 相似
<Suspense>
<template #default>
異步的組件
</template>
<template #fallback>
Loading ...(加載狀態的組件)
</template>
</Suspense>
複製代碼
Vue3.0 中支持 自定義渲染器 (Renderer):這個 Api 能夠用來建立自定義的渲染器, (在以往像 weex 和 mpvue,須要經過 fork 源碼的方式進行擴展)
Vue3 提供了一種新工具 vite,它在程序開發階段,採用了 Koa、Node 開啓一個服務和遊覽器對 import 的支持實現了按需請求加載內容,避免了熱更新時長時間的編譯過程,這大大提升了開發效率。
固然發佈生產的時候,仍是經過 Webpack 進行編譯打包流程。
官方 Cli 工具,記得要升級最新版本
npm install -g @vue/cli
vue create 01-vue3-cli
cd 01-vue3-cli
<!--安裝 Vue3 庫-->
vue add vue-next
npm run serve
複製代碼
vue-cli 一開始尚未支持的時候,vue 官網整了一個 webpack 的項目配置,直接 clone 便可
git clone https://github.com/vuejs/vue-next-webpack-preview.git 01-vue3-webpack
cd 01-vue3-webpack
npm install
npm run dev
複製代碼
這是一個 Vue 全新的開發工具,是由 Vue 的做者開發的,目的是之後取代 webpack,原理就是利用遊覽器如今已經支持 ES6 的 import
語法,遇見 import
會發送一個 http 請求去加載文件,vite 攔截這些請求,作一些預編譯,就省去了 webpack 漫長的打包時間,提高開發效率。
npm install -g create-vite-app
create-vite-app 01-vue3-vite
cd 01-vue3-vite
npm install
npm run dev
複製代碼
運行項目後,打開 http://localhost:3000/ 看一下 network 就會發現,全部的文件都是 import
進行了預編譯,而後經過遊覽器發起請求,作到了天生的按需加載,秒開項目。
<template>
<div>
<h1>{{state.count}} * 2={{double}}</h1>
<button @click="add">累加</button>
</div>
</template>
<script>
export default {
data: () => ({
state: {
count: 1
}
}),
computed: {
double() {
return this.state.count * 2;
}
}
methods: {
add() {
this.state.count++;
}
}
}
</script>
複製代碼
從上面的簡單例子能夠看到,在 Vue2 的處理方式就是這樣的。
下面咱們看一下改用 Vue3 的 Composition api 模式
<template>
<h1>{{state.count}} * 2={{double}}</h1>
<button @click="add">累加</button>
</template>
<script>
import {reactive,computed} from 'vue'
export default {
setup(){
const state = reactive({
count:1
})
function add(){
state.count++
}
const double = computed(()=>state.count*2)
return {state,add,double}
}
}
</script>
複製代碼
Composition api 的方式寫,從上面例子中,雖然看不出來有什麼好處,那是由於組件規模還不是很龐大,其實在組件變得愈來愈龐大以後,優點就會愈來愈明顯了,後續會展現出來。
setup
是新的選項,能夠理解是 Composition 的入口,函數內部在 beforeCreate
以前調用,函數返回的內容會做爲模板渲染的上下文
其實和 Vue2 裏的 Vue.observerable
是同樣的,把一個數據變成響應式,這個能力是徹底獨立的。
關於 Vue.observerable
的用法看這裏:cn.vuejs.org/v2/api/#Vue…
其實就是一個計算屬性,和 Vue2 的能力是同樣的
Vue2 裏面的 data
,methods
,computed
都是掛載在 this
之上的,有兩個明顯的缺點
computed
功能,代碼也會被打包Vue3 的按需手動 import
寫法更有利於 Tree-shaking 打包
在 Vue3 中 reactive
負責複雜的數據結構,ref
能夠吧基本的數據結構包裝成響應式的
<template>
<h1>{{state.count}} * 2={{double}}</h1>
<h2>{{num}}</h2>
<button @click="add">累加</button>
</template>
<script>
import {reactive,computed,ref,onMounted} from 'vue'
export default {
setup(){
const state = reactive({
count:1
})
const num = ref(2)
function add(){
state.count++
num.value+=10
}
const double = computed(()=>state.count*2)
onMounted(()=>{
console.log('mouted')
})
return {state,add,double,num}
}
}
</script>
複製代碼
能夠看到,當要包裝獨立基本數據類型的時候,就可使用上 ref
了
<script>
import {reactive,computed,ref,onMounted} from 'vue'
export default {
setup(){
const {state,double} = useCounter(1)
const num = ref(2)
function add(){
state.count++
num.value+=10
}
onMounted(()=>{
console.log('mouted')
})
return {state,add,double,num}
}
}
function useCounter(count,n){
const state = reactive({
count
})
const double = computed(()=>state.count*2)
return {state,double}
}
</script>
複製代碼
這裏能夠看到,這樣寫的話,就很像 React 的 Hook 寫法了,這樣的作法在後續項目愈來愈龐大的時候就能體現出很好的優點出來了。
看了上面的各類寫法後,當咱們在開發一個組件的時候,須要把數據放到 data
,把計算屬性放到 computed
,把方法放到 methods
,這種作法雖說將不一樣的功能對應放到響應的位置,但也帶來了一個問題,當組件十分龐大的時候,會發現對組件的維護變得十分難受,當修改一個功能時須要在組件上下處處去翻騰,先後修改代碼,這也是 大聖老師 所說的上下反覆橫跳的問題。
能夠看看官方的例子:
// 更改成 Vue3 寫法後
export default {
setup () {
// Network
const { networkState } = useNetworkState()
// Folder
const { folders, currentFolderData } = useCurrentFolderData(networkState)
const folderNavigation = useFolderNavigation({ networkState, currentFolderData })
const { favoriteFolders, toggleFavorite } = useFavoriteFolders(currentFolderData)
const { showHiddenFolders } = useHiddenFolders()
const createFolder = useCreateFolder(folderNavigation.openFolder)
// Current working directory
resetCwdOnLeave()
const { updateOnCwdChanged } = useCwdUtils()
// Utils
const { slicePath } = usePathUtils()
return {
networkState,
folders,
currentFolderData,
folderNavigation,
favoriteFolders,
toggleFavorite,
showHiddenFolders,
createFolder,
updateOnCwdChanged,
slicePath
}
}
}
複製代碼
優點就因而可知了。
這個功能呢就是 Vue 組件能夠不用必定要一個根節點了,能夠這樣寫:
<template>
<h1>H1</h1>
<div>Div</div>
</template>
複製代碼
這種寫法在 Vue2 中是會報錯的
下面再來看一個快速排序算法組件小例子:
<template>
<quick :data="left" v-if="left.length"></quick>
<li>{{flag}}</li>
<quick :data="right" v-if="right.length"></quick>
</template>
<script>
export default {
name:'quick',
props:['data'],
setup(props){
let flag = props.data[0]
let left = []
let right = []
props.data.slice(1).forEach(v=>{
v>flag? right.push(v): left.push(v)
})
return {left, right, flag}
}
}
</script>
複製代碼
<ul>
<FragmentDemo :data="[5,3,1,6,9,4,2,8]" />
</ul>
複製代碼
運行後的結果呈現出來是這樣的:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
</ul>
複製代碼
這個組件和 React 的 Portal 組件是相似的,用起來比較簡單,就是能夠渲染 vue 組件的內容,到指定的 dom 節點中。
在作彈窗的時候,比較有用,由於每每,咱們的彈窗都須要渲染到最外層的 body 下面,不然嵌套過多,蒙層可能會被父元素的 transform 影響。
經常使用場景:公共的模態框 Modal
下面來看一下小例子:
<template>
<h1>{{ msg }}</h1>
<div class="confirm-modal">
<button @click="isOpen = true">打開</button>
<!-- 注意這一塊代碼 -->
<Teleport to="#modal-container">
<div class="modal-warp" v-if="isOpen">
<div class="cover"></div>
<div class="content">
<p>我在外部哦</p>
<button @click="isOpen = false">取消</button>
</div>
</div>
</Teleport>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'HelloWorld',
props: {
msg: {
type: String,
default: 'Teleport 使用案例',
},
},
setup() {
const isOpen = ref(false);
return { isOpen };
},
};
</script>
<style>
.modal-warp {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
}
.cover {
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.2);
}
.content {
position: absolute;
padding: 16px 32px;
background-color: white;
border-radius: 4px;
}
</style>
複製代碼
上面的 <Teleport to="#modal-container">
表示,接下來將會把裏面的內容渲染到 id 爲 modal-container
的 dom 節點中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<div id="modal-container"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
複製代碼
運行的效果就是這樣滴:
Suspense 的功能是一個異步組件加載的功能,這個在 React 中也存在一個相似
React
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
複製代碼
Vue3
<Suspense>
<template #default>
異步的組件
</template>
<template #fallback>
加載狀態的組件
</template>
</Suspense>
複製代碼
這裏也將展現一個小例子:
父組件
<template>
<h1>Supense</h1>
<Suspense>
<template #default>
<AsyncComponent :timeout="3000" />
</template>
<template #fallback>
<h1>加載中</h1>
</template>
</Suspense>
</template>
<script>
import AsyncComponent from './AsyncComponent.vue';
export default {
components: {
AsyncComponent,
},
};
</script>
複製代碼
AsyncComponent 組件
<template>
<h1>一個異步小組件</h1>
</template>
<script>
function sleep(timeout) {
return new Promise((resolve) => setTimeout(resolve, timeout));
}
export default {
name: 'AsyncComponent',
props: {
timeout: {
type: Number,
required: true,
},
},
async setup(props) {
await sleep(props.timeout);
},
};
</script>
複製代碼
這樣基本就能體現出來了,運行結果以下:
答:
- beforeCreate
- created
答:
- 使用了 proxy 代替了 Object.defineProperty
- 新增 Composition Api
- 讓 vue 更快的優化
- 更好的 ts 支持,vue3 直接用 ts 重寫
答:
- 更容易維護
- 更多的原生支持
- 更易於開發使用
答:
- Vue3 會兼容以前的寫法,Composition API 也是可選的
- 爲了繼續支持 IE11,Vue3 將發佈一個支持舊觀察者機制和新 Proxy 版本的構建
- Vue3 不只會使用 TypeScript,並且許多軟件包將被解耦,使全部內容更加模塊化
答:
對
答:
頁面首次加載會觸發 beforeCreate、created、beforeMount、mounted、beforeUpdate、updated
正確的是:
beforeCreate、created、beforeMount、mounted
答:
- props
- provide / inject
- listeners
解析:
context 屬於 react 中的一種跨層級傳參方式