在新项目中使用 Vue3 使用总结
一、使用背景
最近公司需要搭建一個新項目,用于做官網(wǎng)。因為作為官網(wǎng),首先項目不算大,一共只有十來個頁面,并且想要用戶體驗感很好,所以最終選定以 Vue 作為技術(shù)棧。
雖然 Vue3 (中文官網(wǎng))剛問世不久,但是如果采用VUE2 搭建項目,以后可能會面臨 Vue 版本升級問題,從 Vue2 跨度到 Vue3 有很多不兼容的變更,并且時代在發(fā)展,使用新技術(shù)會更加具有 可維護(hù)可擴(kuò)展性。
二、項目搭建
這里我使用的是 vue cli4 進(jìn)行項目搭建
三、devtools
Vue.js devtools 沒辦法調(diào)試vue3的項目,官方也提供了升級版的devtools:vue devtools beta。
四、安裝依賴
安裝一些常用依賴,值得注意的是Vue3的某些插件與Vue2并不相同。
特別說明:
1、element-plus
這是基于 Vue 3.0 的桌面端組件庫。
如何全局使用element-plus:
// plugins/element.js import ElementPlus from 'element-plus' import 'element-plus/lib/theme-chalk/index.css' import 'dayjs/locale/zh-cn' import locale from 'element-plus/lib/locale/lang/zh-cn'export default (app) => {app.use(ElementPlus, { locale }) }// main.js import installElementPlus from './plugins/element'const app = createApp(App) installElementPlus(app)// 使用 <template><el-button>按鈕</el-button> </template>2、 axios
值得注意的是,在Vue2中通過 new Vue()創(chuàng)建Vue實例,并沒有app的概念,但是在Vue3中,引入了createApp。所以axios的y引用方式有所差異。具體內(nèi)容參照:Vue3 安裝axios使用報錯:Uncaught TypeError: Cannot read property ‘use‘ of undefined。
如何使用axios:
// 方式一:全局掛載 此方式不合適于 將項目里的不同模塊的請求都放在相應(yīng)文件里, // 因為我們在發(fā)請求時可能無法獲取到當(dāng)前實例this // plugins/axios.js export const plugin = {install: function (app, options) {console.log(options)// 添加全局的方法app.config.globalProperties.axios = _axios;} }// main.js import { plugin as axios } from './plugins/axios' const app = createApp(App) app.use(axios)//使用: this.$axios.post('api/Login',{card:111}).then(res => console.log(res))//方式二:直接使用 // plugins/axios.js const _axios = axios.create(config) export default _axios// 使用: import axios from '@/plugins/axios'// 獲取崗位類別 export function getJobCategories() {return axios.post('api/Login',{card:111}) }3、Vue Router
Vue router4 也提供了hook鉤子函數(shù)供使用。
在 setup里使用 router
五、Vue3新特性
1、組合式API
什么是組合式API?
簡單來說就是 將之前散布在vue文件里 的各個功能函數(shù) 組合 在一個函數(shù)里
這樣會使我們的代碼更加類聚,如果使用過react 函數(shù)式組件的同學(xué)應(yīng)該會發(fā)現(xiàn),他們及其類似。
(1)Setup
setup作為組合式api的入口點,所有的函數(shù)都可以被放置在setup里進(jìn)行書寫,需要被setup外部使用的參數(shù),通過 return 暴露出去
setup接收兩個參數(shù):props、context。
props接受父組件傳遞而來的參數(shù),當(dāng)然,這些參數(shù)也是響應(yīng)式的。值得注意的是,在setup里使用 props時,必須在props里定義的變量 才可被setup的props所接收到,在setup里獲取props的變量時,不可使用es6的解構(gòu)方法,因為它會消除 prop 的響應(yīng)性,如果需要解構(gòu) prop并保留響應(yīng)式,可以使用 setup 函數(shù)中的 toRefs 函數(shù)。
context 暴露三個組件的 property: attrs(Attribute)、slots(卡槽)、emit(觸發(fā)事件),context是一個普通的 JavaScript 對象,不具有響應(yīng)式,可以使用es6解構(gòu)語法。
注意??
setup是在解析其它組件選項之前被調(diào)用的,所以在setup里無法準(zhǔn)確的訪問到當(dāng)前組件實例:this。
想要在setup里獲取當(dāng)前組件實例,官方提供了一個函數(shù):getCurrentInstance,但是getCurrentInstance只能在setup和生命鉤子函數(shù)里使用
// getCurrentInstance代表全局上下文,ctx相當(dāng)于Vue2的this, // 但是特別注意ctx代替this只適用于開發(fā)階段,等你放到服務(wù)器上運行就會出錯, // 后來查閱資料說的得用proxy替代ctx,才能在你項目正式上線版本正常運行const { ctx, proxy } = getCurrentInstance()(2)生命周期鉤子
(3)Provide / Inject:父子組件跨層級傳輸數(shù)據(jù)
// 父組件 setup () {// readonly包裹后可以在組件內(nèi)引用時不被改變值。// 否則在組件內(nèi)可以直接通過applyPositionId.value=***將值改變provide('applyPositionId', readonly(applyPositionId))// 函數(shù)也可傳遞provide('hideApplyModal', hideApplyModal) }//子組件接收 setup() {const applyPositionId = inject('applyPositionId')const hideApplyModal = inject('hideApplyModal')// 函數(shù)調(diào)用hideApplyModal() }2、響應(yīng)性 API
在Vue2里,我們定義在data里的數(shù)據(jù),會被 Object.defineProperty() 數(shù)據(jù)劫持,通過觀察者模式 最終成為響應(yīng)式數(shù)據(jù)。
在Vue3里,實現(xiàn)響應(yīng)式的 方式改為了 es6 新增的語法 proxy,通過對象攔截方式實現(xiàn)響應(yīng)式,解決了vue2中的一些漏區(qū)。
關(guān)于這兩點的相關(guān)知識有很多,我會新寫一篇文章做相應(yīng)的說明。這里主要介紹Vue3的一些新特性的使用。
(1)reactive
reactive用于為對象創(chuàng)建響應(yīng)式狀態(tài),其作用相當(dāng)于Vue 2.x 中的 Vue.observable()。為對象創(chuàng)建響應(yīng)式狀態(tài)后,就不會存在 Vue2中 修改 某個對象在data定義時沒有被定義到的屬性,不會存在響應(yīng)式狀態(tài) 的問題。
setup() {const obj = { count: 0}const state = reactive(obj)console.log(state.count) // 0 }官網(wǎng)有一句話:
響應(yīng)式轉(zhuǎn)換是“深層”的——它影響所有嵌套 property。這是什么意思呢?
當(dāng)我們使用 proxy (不了解 proxy 的可以閱讀:阮一峰 es6入門)做 對象攔截 時,他會劫持此對象的所有property 屬性。
vue2使用 Vue.observable() 使一個對象可響應(yīng),被傳入的對象會直接被 Vue.observable 變更為響應(yīng)式。而 reactive 是為傳入對象創(chuàng)建一個響應(yīng)式的副本,不改變原始對象,當(dāng)我們直接改變原始對象時,原始對象不會是響應(yīng)式的。
(2)readonly
readonly獲取一個對象 (響應(yīng)式或純對象) 或 ref 并返回原始代理的只讀代理const state = reactive({ count: 0 }) const copy = readonly(state)// error copy.count++(3)ref
此ref非彼ref,在Vue和react里,都有ref的概念。當(dāng)其作用域HTML文件時,ref是React/Vue提供的用來操縱React/Vue組件實例或者DOM元素的接口。
而 響應(yīng)性 API 里 ref 是 用來創(chuàng)建一個具有響應(yīng)式且可變的 ref 對象,ref 對象具有指向 其所創(chuàng)建變量的.value屬性。在setup里被return 出去時,會自動綁定其value值。
setup() {const count = ref(0)console.log(count.value) // 0return {count} }mounted() {console.log(count) // 0 }官網(wǎng)有這么一句話:
看了半天我也不是特別明白它是什么意思。將 ref 與 reactive 使用下來,發(fā)現(xiàn)他們兩除了在使用方式上有所不同以外,沒有什么太大的區(qū)別。高度響應(yīng)式 的感念還是沒有體會到。通過源碼我可以看見,在使用ref為 對象創(chuàng)建 響應(yīng)式數(shù)據(jù)時,其內(nèi)部調(diào)用的也是 reactive 方法。
如果大家有什么理解,可以評論告訴我。🙏🙏
(4)computed
computed的使用很簡單,本質(zhì)上和vue2沒有什么太大的區(qū)別
(5)watch
等同于vue2的this.$watch,可監(jiān)聽一個或多個源。當(dāng) 被依賴的值 發(fā)生改變時,執(zhí)行。
// 偵聽一個getter const state = reactive({ count: 0 }) watch(() => state.count,(newcount, prevCount) => {console.log('新值:', newcount)console.log('舊值:', prevCount)} )// 直接偵聽一個ref const count = ref(0) watch(count, (newcount, prevCount) => {console.log('新值:', newcount)console.log('舊值:', prevCount) })// 監(jiān)聽多個源,使用數(shù)組形式 const fooRef = ref(0) const barRef = ref(1) watch([fooRef, barRef], ([newFoo, newBar], [prevFoo, prevBar]) => {console.log('新Foo值:', newFoo)console.log('舊Foo值:', prevFoo)console.log('新Bar值:', newBar)console.log('舊Bar值:', prevBar)})(6)watchEffect
watchEffect響應(yīng)式地跟蹤其依賴項時立即運行一個函數(shù),并在更改依賴項時重新運行它:意思就是頁面初始化時會被立即執(zhí)行一次,并在依賴項發(fā)生改變時,再次執(zhí)行。熟悉React Hook 的同學(xué)會非常熟悉,他與useEffect 所實現(xiàn)的功能是一樣的,唯一不同的是,useEffect需要我們手動傳入依賴,而 watchEffect 會自動收集依賴。
const count = ref(0)watchEffect(() => console.log('value:', count.value)) // value:0setTimeout(() => {count.value++// value:1 }, 100)六、非兼容變更
Vue3 在 Vue2上,有許多的非兼容變更,這里列舉一些比較常用的屬性。
1、v-model
在Vue2中的 v-model 等同于:value + input 的語法糖。
在Vue3中的 v-model 等同于:modelValue + update:modelValue。
在Vue2中,我們想要改變v-model傳下來的值,需要model選項去改變。
// parentComponent <ChildComponent v-model="pageTitle" />// ChildComponent export default {model: {prop: 'title',event: 'change'},props: {// 這將允許 `value` 屬性用于其他用途value: String,// 使用 `title` 代替 `value` 作為 model 的 proptitle: {type: String,default: 'Default title'}} }// 或者 <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" /> // 或者 <ChildComponent :title.sync="pageTitle" />// 子組件 this.$emit('update:title', '新title')在Vue3中,移除了v-bind .sync 修飾符,以上功能簡寫為:
<ChildComponent v-model:title="pageTitle" /> // 等同于 <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />// 子組件: setup(props, context) {const { emit } = context || {}const onClose =() => {emit('update:pageTitle', '新title')}return {onClose,} }2、v-if 與 v-for
在Vue2中,v-if 與 v-for 同時作用于同一個DOM節(jié)點時,v-for 的優(yōu)先級 高于 v-if 。
在Vue3中,v-if 與 v-for 同時作用于同一個DOM節(jié)點時,v-if 的優(yōu)先級 高于 v-for 。
且 v-if/v-else/v-else-if 的分支中繼續(xù)使用 key attribute,因為沒有為條件分支提供 key 時,也會自動生成唯一的 key。
3、filters
Vue3中filters已被徹底移除,建議使用computed代替。
4、template
在Vue2中,template 里只支持一個根結(jié)點,否則會報錯。
在Vue3中,template 里只支持多個根結(jié)點。
5、prop
在Vue2中,我們需要在 prop 里訪問data里的字段,可以使用 this 。
在Vue3中,prop 里不再支持this。
七、自定義Hook
1、useFetch
import { reactive, toRefs } from 'vue'export default function useFetch(api, params, transformer) {if (typeof api !== 'function') {throw new TypeError('api should be type of fuction')}// 定義初始狀態(tài)const state = reactive({data: null,loading: false,})// 定義查詢函數(shù)const doFetch = (otherParams) => {const finalParams = {...(params || {}),...(otherParams || {})}state.loading = truereturn api(finalParams).then((data) => {state.data = typeof transformer === 'function' ? transformer(data) : datastate.loading = falsereturn data}).catch((err) => {console.log(err && err.message)state.loading = false})}// 返回狀態(tài)refs和查詢函數(shù)return [toRefs(state), doFetch] }// 使用 import useListFetch from '@/hooks/useListFetch'setup() {const getdataFunc = (params) => {const { id } = params || {}return axios.get(`/xxx?id=${id}`)}const [{ data, loading: isLoading},getData,] = useListFetch(getdataFunc, {id: 10,})getData() }2、useListFetch
import { reactive, toRefs } from 'vue'export default function useListFetch(api, params, transformer) {if (typeof api !== 'function') {throw new TypeError('api should be type of fuction')}// 定義list初始狀態(tài)const state = reactive({items: [],loading: false,isLastPage: false,})const { pageSize = 10, ...otherParams } = params || {}// 定義查詢函數(shù)const doFetch = () => {const preSize = state.items.lengthconst finalParams = {...otherParams,offset: preSize,limit: pageSize,}state.loading = truereturn api(finalParams).then((data) => {if (data && Array.isArray(data)) {const newData = typeof transformer === 'function' ? transformer(data) : dataconst newItems = [...state.items, ...newData]state.items = newItemsif (newItems.length !== preSize + pageSize) {state.isLastPage = true}}state.loading = falsereturn data}).catch((err) => {console.log(err && err.message)state.loading = false})}// 返回狀態(tài)refs和查詢函數(shù)return [toRefs(state), doFetch] }// 使用 import useListFetch from '@/hooks/useListFetch'setup() {const getListFunc = (params) => {const { offset = 0, limit = 10 } = params || {}return axios.get(`/xxx?offset=${offset}&limit=${limit}`)}const [{ items: videoList, loading: isLoading, isLastPage: isLastPage },getList,] = useListFetch(getListFunc, {pageSize: 10,})getList() }八、項目打包
參考文檔:https://cli.vuejs.org/zh/guide/
以上就是本人在使用Vue3時一些相對變化比較重要的地方。
總結(jié)
以上是生活随笔為你收集整理的在新项目中使用 Vue3 使用总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Intellij IDEA中Mybati
- 下一篇: Vue - 去除控制台“你正在开发模式下