在新项目中使用 Vue3 使用总结
一、使用背景
最近公司需要搭建一個新項目,用于做官網。因為作為官網,首先項目不算大,一共只有十來個頁面,并且想要用戶體驗感很好,所以最終選定以 Vue 作為技術棧。
雖然 Vue3 (中文官網)剛問世不久,但是如果采用VUE2 搭建項目,以后可能會面臨 Vue 版本升級問題,從 Vue2 跨度到 Vue3 有很多不兼容的變更,并且時代在發展,使用新技術會更加具有 可維護可擴展性。
二、項目搭建
這里我使用的是 vue cli4 進行項目搭建
三、devtools
Vue.js devtools 沒辦法調試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()創建Vue實例,并沒有app的概念,但是在Vue3中,引入了createApp。所以axios的y引用方式有所差異。具體內容參照:Vue3 安裝axios使用報錯:Uncaught TypeError: Cannot read property ‘use‘ of undefined。
如何使用axios:
// 方式一:全局掛載 此方式不合適于 將項目里的不同模塊的請求都放在相應文件里, // 因為我們在發請求時可能無法獲取到當前實例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鉤子函數供使用。
在 setup里使用 router
五、Vue3新特性
1、組合式API
什么是組合式API?
簡單來說就是 將之前散布在vue文件里 的各個功能函數 組合 在一個函數里
這樣會使我們的代碼更加類聚,如果使用過react 函數式組件的同學應該會發現,他們及其類似。
(1)Setup
setup作為組合式api的入口點,所有的函數都可以被放置在setup里進行書寫,需要被setup外部使用的參數,通過 return 暴露出去
setup接收兩個參數:props、context。
props接受父組件傳遞而來的參數,當然,這些參數也是響應式的。值得注意的是,在setup里使用 props時,必須在props里定義的變量 才可被setup的props所接收到,在setup里獲取props的變量時,不可使用es6的解構方法,因為它會消除 prop 的響應性,如果需要解構 prop并保留響應式,可以使用 setup 函數中的 toRefs 函數。
context 暴露三個組件的 property: attrs(Attribute)、slots(卡槽)、emit(觸發事件),context是一個普通的 JavaScript 對象,不具有響應式,可以使用es6解構語法。
注意??
setup是在解析其它組件選項之前被調用的,所以在setup里無法準確的訪問到當前組件實例:this。
想要在setup里獲取當前組件實例,官方提供了一個函數:getCurrentInstance,但是getCurrentInstance只能在setup和生命鉤子函數里使用
// getCurrentInstance代表全局上下文,ctx相當于Vue2的this, // 但是特別注意ctx代替this只適用于開發階段,等你放到服務器上運行就會出錯, // 后來查閱資料說的得用proxy替代ctx,才能在你項目正式上線版本正常運行const { ctx, proxy } = getCurrentInstance()(2)生命周期鉤子
(3)Provide / Inject:父子組件跨層級傳輸數據
// 父組件 setup () {// readonly包裹后可以在組件內引用時不被改變值。// 否則在組件內可以直接通過applyPositionId.value=***將值改變provide('applyPositionId', readonly(applyPositionId))// 函數也可傳遞provide('hideApplyModal', hideApplyModal) }//子組件接收 setup() {const applyPositionId = inject('applyPositionId')const hideApplyModal = inject('hideApplyModal')// 函數調用hideApplyModal() }2、響應性 API
在Vue2里,我們定義在data里的數據,會被 Object.defineProperty() 數據劫持,通過觀察者模式 最終成為響應式數據。
在Vue3里,實現響應式的 方式改為了 es6 新增的語法 proxy,通過對象攔截方式實現響應式,解決了vue2中的一些漏區。
關于這兩點的相關知識有很多,我會新寫一篇文章做相應的說明。這里主要介紹Vue3的一些新特性的使用。
(1)reactive
reactive用于為對象創建響應式狀態,其作用相當于Vue 2.x 中的 Vue.observable()。為對象創建響應式狀態后,就不會存在 Vue2中 修改 某個對象在data定義時沒有被定義到的屬性,不會存在響應式狀態 的問題。
setup() {const obj = { count: 0}const state = reactive(obj)console.log(state.count) // 0 }官網有一句話:
響應式轉換是“深層”的——它影響所有嵌套 property。這是什么意思呢?
當我們使用 proxy (不了解 proxy 的可以閱讀:阮一峰 es6入門)做 對象攔截 時,他會劫持此對象的所有property 屬性。
vue2使用 Vue.observable() 使一個對象可響應,被傳入的對象會直接被 Vue.observable 變更為響應式。而 reactive 是為傳入對象創建一個響應式的副本,不改變原始對象,當我們直接改變原始對象時,原始對象不會是響應式的。
(2)readonly
readonly獲取一個對象 (響應式或純對象) 或 ref 并返回原始代理的只讀代理const state = reactive({ count: 0 }) const copy = readonly(state)// error copy.count++(3)ref
此ref非彼ref,在Vue和react里,都有ref的概念。當其作用域HTML文件時,ref是React/Vue提供的用來操縱React/Vue組件實例或者DOM元素的接口。
而 響應性 API 里 ref 是 用來創建一個具有響應式且可變的 ref 對象,ref 對象具有指向 其所創建變量的.value屬性。在setup里被return 出去時,會自動綁定其value值。
setup() {const count = ref(0)console.log(count.value) // 0return {count} }mounted() {console.log(count) // 0 }官網有這么一句話:
看了半天我也不是特別明白它是什么意思。將 ref 與 reactive 使用下來,發現他們兩除了在使用方式上有所不同以外,沒有什么太大的區別。高度響應式 的感念還是沒有體會到。通過源碼我可以看見,在使用ref為 對象創建 響應式數據時,其內部調用的也是 reactive 方法。
如果大家有什么理解,可以評論告訴我。🙏🙏
(4)computed
computed的使用很簡單,本質上和vue2沒有什么太大的區別
(5)watch
等同于vue2的this.$watch,可監聽一個或多個源。當 被依賴的值 發生改變時,執行。
// 偵聽一個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) })// 監聽多個源,使用數組形式 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響應式地跟蹤其依賴項時立即運行一個函數,并在更改依賴項時重新運行它:意思就是頁面初始化時會被立即執行一次,并在依賴項發生改變時,再次執行。熟悉React Hook 的同學會非常熟悉,他與useEffect 所實現的功能是一樣的,唯一不同的是,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節點時,v-for 的優先級 高于 v-if 。
在Vue3中,v-if 與 v-for 同時作用于同一個DOM節點時,v-if 的優先級 高于 v-for 。
且 v-if/v-else/v-else-if 的分支中繼續使用 key attribute,因為沒有為條件分支提供 key 時,也會自動生成唯一的 key。
3、filters
Vue3中filters已被徹底移除,建議使用computed代替。
4、template
在Vue2中,template 里只支持一個根結點,否則會報錯。
在Vue3中,template 里只支持多個根結點。
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')}// 定義初始狀態const state = reactive({data: null,loading: false,})// 定義查詢函數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})}// 返回狀態refs和查詢函數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初始狀態const state = reactive({items: [],loading: false,isLastPage: false,})const { pageSize = 10, ...otherParams } = params || {}// 定義查詢函數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})}// 返回狀態refs和查詢函數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時一些相對變化比較重要的地方。
總結
以上是生活随笔為你收集整理的在新项目中使用 Vue3 使用总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Intellij IDEA中Mybati
- 下一篇: Windows 环境 Jenkins集成