封面圖來源網上,侵刪javascript
面向將來編程(Future-Oriented Programming),vue-function-api 提供開發者在 Vue2.x 中使用 Vue3 的語法邏輯開發應用。(爲方便如下以 Vue2 表示)html
本文不對文檔 api 對過多說明,僅討論在項目實踐中遇到的問題。比較二者的區別是對 Vue3 寫法最快的瞭解,下面經過對比同一個功能在 Vue2 與 Vue3 的區別。vue
場景是這樣的,一個列表頁,上部分是條件篩選,下部分是 table,table 行內會有刪除,修改(以 dialog 形式展現)功能,template 部分無區別,這裏不作贅述。java
安裝 vue-function-apireact
npm install vue-function-api --save
# 或者
yarn add vue-function-api
複製代碼
在項目中引入 main.jsgit
import Vue from 'vue'
import Element from 'element-ui'
import { plugin } from 'vue-function-api' // <--- 引入 vue-function-api
import App from './App'
import router from './router'
import store from './store'
Vue.use(Element)
Vue.use(plugin) // <--- 這樣就能夠在單文件組件中使用函數式 API了
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app')
複製代碼
準備工做後,就能夠安安靜靜的作比較了。github
在 Vue2 中,首先要定義 data, data 中咱們定義一個 query 對象用於條件篩選,citys 用於條件篩選中城市(動態獲取下拉),statusMap 用於篩選條件中狀態的下拉,list 用於 table。vue-router
export default {
data() {
return {
query: {
city: null,
name: null,
status: null
},
citys: [],
list: [],
statusMap: [{
value: 1,
label: '啓用'
}, {
value: 2,
label: '禁用'
}]
}
}
複製代碼
Vue3 中是這樣定義vuex
import { value } from 'vue-function-api'
export default {
setup() {
const query = value({
city: null,
name: null,
status: null
})
const citys = value([])
const list = value([])
const statusMap = value([{
value: 1,
label: '啓用'
}, {
value: 2,
label: '禁用'
}])
return {
query,
citys,
list,
statusMap
}
}
}
複製代碼
引入 setup, 在其中作 data 作的事情,細心的同窗會發如今 setup 後須要 return 所定義的變量,其中也並不是要將全部的變量都放入 return 中,放入 return 中是爲了可以在 template 中使用,即只需 return template 中依賴的變量shell
上文中說到 table 中有刪除,編輯功能,正常的系統中這類功能是須要有權限的人才能操做,這裏咱們假設已經將 permissions 存入 vuex 中,使用時須要經過 mapGetters 取出 permissions(此處爲舉例這樣實現,正常的權限要比這個複雜)。
在 Vue2 中是這樣寫
import { mapGetters } from 'vuex'
export default {
// 此處先省略 data 部分...
computed: {
...mapGetters(['permissions']),
canUpdate() {
return this.permissions.includes('update')
},
canDelete() {
return this.permissions.includes('delete')
},
}
// ...
}
複製代碼
Vue3:目前 vue-function-api 還不支持 vuex map 的方式 導出 vuex 中的狀態,這裏咱們從 Vue root 中取出 store 中的數據
import { computed } from 'vue-function-api'
export default {
setup(props, ctx) {
const { $store } = ctx.root
// 此處先省略 data 部分...
const permissions = computed(() => $store.getters.permissions)
const canUpdate = () => permissions.includes('update')
const canDelete = () => permissions.includes('delete')
return {
canUpdate,
canDelete // <--- 這裏只導出 template 的依賴
}
}
}
複製代碼
在 template 中, 編輯,刪除的按鈕上 分別加入 v-if="canUpdate"
, v-if="canDelete"
,便可實現對應權限的人才能看到對應的按鈕,瞭解更多 context。
基於上文中的描述,由於是一個列表頁,因此結合 lifecycle 一塊兒作一下對比。
在 Vue2 中
export default {
// ...
methods: {
async fetchCity() {
const response = await fetchCityApi()
this.citys = response.data
},
async fetchList() {
const response = await fetchListApi(this.query)
this.list = response.data
},
async delete(id) {
const response = await deleteItem(id)
const { status, msg } = response.data
if (status !== 'ok') return this.$message.error(msg)
this.$message({
type: 'success',
message: '刪除成功'
})
},
confirm(id) {
this.$confirm(`確認刪除`, '提示', {
confirmButtonText: '肯定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.delete(id)
}).catch(() => {
this.$message({
type: 'info',
message: '已取消'
})
})
},
detail(id) {
this.$router.push({
path: '/detail',
query: { id }
})
}
}
}
created() {
this.fetchCity()
this.fetchList()
}
}
複製代碼
接下來看 Vue3 中實現
import { computed, onCreated } from 'vue-function-api'
export default {
setup(props, ctx) {
const { $message, $router, $confirm, $store } = ctx.root // <--- setup() 中不可使用 this 訪問當前組件實例, 能夠經過 setup 的第二個參數 context 來訪問 vue2.x API 中實例上的屬性。
// 此處先省略 data 部分...
// method
const fetchCity = async () => {
const response = await fetchCityApi()
citys.value = response.data // <--- 劃重點,經過 value() wrap初始化的變量在 setup 內部引用值或者修改值都要加 .value
},
const fetchList = async () => {
const response = await fetchListApi(query.value)
list.value = response.data
},
const delete = async id => {
const response = await deleteItem(id)
const { status, msg } = response.data
if (status !== 'ok') return $message.error(msg)
$message({
type: 'success',
message: '刪除成功'
})
},
confirm(id) {
$confirm(`確認刪除`, '提示', {
confirmButtonText: '肯定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
delete(id)
}).catch(() => {
$message({
type: 'info',
message: '已取消'
})
})
},
detail(id) {
$router.push({
path: '/detail',
query: { id }
})
}
// lifecycle
onCreated(() => {
fetchCity()
fetchList()
})
return {
fetchCity,
fetchList,
confirm,
detail // <--- 這裏只導出 template 的依賴
}
}
}
複製代碼
是否是感受切換到 Vue3 也沒那麼難
watch 的例子在一個列表頁中可能沒有什麼使用場景,上文中說到列表頁上面是一個條件篩選,有城市下拉,狀態下拉,固然正常狀況下,咱們都是經過 select 的 change 事件調用 fetchList,這裏我就爲了舉例而這麼寫,實際項目本身衡量利弊。
在 Vue2 中
export default {
// ...
watch: {
query: {
handler: function(val) {
this.fetchList()
},
{ deep: true }
}
}
// 或者不嫌麻煩這樣寫...
watch: {
'query.city': function(val) {
this.fetchList()
},
'query.name': function(val) {
this.fetchList()
},
'query.status': function(val) {
this.fetchList()
}
}
// ...
}
複製代碼
在 Vue3 中
import { watch } from 'vue-function-api'
export default {
setup() {
// ...
watch(
query,
val => {
fetchList()
},
{ deep: true }
)
// 或者
watch(
() => query.value,
val => {
fetchList()
},
{ deep: true }
)
// ...
}
}
複製代碼
經過以上對比,你應該對 Vue3 寫法有必定理解了,由於是以一個列表頁爲 demo對比,沒有用到 provide、 inject,想了解的同窗能夠到 provide, inject
, 還有 state 至關於 Vue.observable
.
對比完整代碼
Vue2
export default {
data() {
return {
query: {
city: null,
name: null,
status: null
},
citys: [],
list: [],
statusMap: [{
value: 1,
label: '啓用'
}, {
value: 2,
label: '禁用'
}]
}
},
computed: {
...mapGetters(['permissions']),
canUpdate() {
return this.permissions.includes('update')
},
canDelete() {
return this.permissions.includes('delete')
},
},
methods: {
async fetchCity() {
const response = await fetchCityApi()
this.citys = response.data
},
async fetchList() {
const response = await fetchListApi(this.query)
this.list = response.data
},
async delete(id) {
const response = await deleteItem(id)
const { status, msg } = response.data
if (status !== 'ok') return this.$message.error(msg)
this.$message({
type: 'success',
message: '刪除成功'
})
},
confirm(id) {
this.$confirm(`確認刪除`, '提示', {
confirmButtonText: '肯定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.delete(id)
}).catch(() => {
this.$message({
type: 'info',
message: '已取消'
})
})
},
detail(id) {
this.$router.push({
path: '/detail',
query: { id }
})
}
},
created() {
this.fetchCity()
this.fetchList()
},
watch: {
query: {
handler: function(val) {
this.fetchList()
},
{ deep: true }
}
}
複製代碼
Vue3
import { value, computed, onCreated } from 'vue-function-api'
export default {
setup(props, ctx) {
const { $store, $message, $router, $route } = ctx.root
// reactive state
const query = value({
city: null,
name: null,
status: null
})
const citys = value([])
const list = value([])
const statusMap = value([{
value: 1,
label: '啓用'
}, {
value: 2,
label: '禁用'
}])
// computed
const permissions = computed(() => $store.getters.permissions)
const canUpdate = () => permissions.includes('update')
const canDelete = () => permissions.includes('delete')
// method
const fetchCity = async () => {
const response = await fetchCityApi()
citys.value = response.data
},
const fetchList = async () => {
const response = await fetchListApi(query.value)
list.value = response.data
},
const delete = async id => {
const response = await deleteItem(id)
const { status, msg } = response.data
if (status !== 'ok') return $message.error(msg)
$message({
type: 'success',
message: '刪除成功'
})
},
confirm(id) {
$confirm(`確認刪除`, '提示', {
confirmButtonText: '肯定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
delete(id)
}).catch(() => {
$message({
type: 'info',
message: '已取消'
})
})
},
detail(id) {
$router.push({
path: '/detail',
query: { id }
})
}
// watch
watch(
query,
val => {
fetchList()
},
{ deep: true }
)
// lifecycle
onCreated(() => {
fetchCity()
fetchList()
})
return {
query,
citys,
list,
statusMap,
canUpdate,
canDelete,
fetchCity,
fetchList,
confirm,
detail
}
}
}
複製代碼
這裏貼一下 setup 中的第二個參數 context
,對象中的屬性是 2.x 中的 vue 實例屬性的一個子集。完整的屬性列表:
像其餘庫 vuex,vue-router,ElementUI 的一些 $方法能夠從 root 中取得
export default {
setup(props, ctx) {
const { $store, $router, $route, $message, $confirm } = ctx.root
}
}
複製代碼
筆者已用到正式環境,目前來看尚未什麼問題,引用 vue-function-api 官方的說明
vue-function-api 會一直保持與 Vue3.x 的兼容性,當 3.0 發佈時,您能夠無縫替換掉本庫。 vue-function-api 的實現只依賴 Vue2.x 自己,不論 Vue3.x 的發佈與否,都不會影響您正常使用本庫。 因爲 Vue2.x 的公共 API 限制,vue-function-api 沒法避免的會產生一些額外的內存負載。若是您的應用並不工做在極端內存環境下,無需關心此項。