1. 程式人生 > >Vue 開發技巧總結

Vue 開發技巧總結

​部落格地址:[https://ainyi.com/95](https://ainyi.com/95) 本人玩了 Vue 兩年多,在此總結一下開發時的一些技巧和方法 ## 自定義元件 v-model v-model 是 Vue 提供的一個語法糖,它本質上是由 value 屬性 + input 事件組成的(都是原生的預設屬性) 自定義元件中,可以通過傳遞 value 屬性並監聽 input 事件來實現資料的雙向繫結 **自定義元件** ```html ``` **父元件呼叫** ```html ``` ## 函式式元件 簡單說一下函式式元件 函式式元件就是函式是元件。使用過 React 的同學,應該不會對函式式元件感到陌生 函式式元件,我們可以理解為==沒有內部狀態==,==沒有生命週期鉤子函式==,==沒有 this==(不需要例項化的元件) 由於它像函式一樣輕巧,沒有例項引用,所以渲染效能提高了不少 在日常開發中,經常會開發一些純展示性的業務元件,比如一些詳情頁面,列表介面等,它們有一個共同的特點是隻需要將外部傳入的資料進行展現,不需要有內部狀態,不需要在生命週期鉤子函式裡面做處理,這時候你就可以考慮使用函式式元件 ```js export default { // 通過配置 functional 屬性指定元件為函式式元件 functional: true, // 元件接收的外部屬性,也可無需顯式宣告 props props: { avatar: { type: String } }, /** * 渲染函式 * @param {*} h * @param {*} context 函式式元件沒有 this, props, slots 等,都在 context 上面掛著 */ render(h, context) { const { props } = context if (props.avatar) { return } return } } ``` **使用函式式元件的原因:** 1. 最主要最關鍵的原因是函式式元件不需要例項化,無狀態,沒有生命週期,所以渲染效能要好於普通元件 2. 函式式元件結構比較簡單,程式碼結構更清晰 **函式式元件與普通元件的區別** 1. 函式式元件需要在元件上宣告==functional== 2. 函式式元件不需要例項化,所以沒有 this,==this==通過==render==函式的第二個引數來代替 3. 函式式元件沒有生命週期鉤子函式,不能使用計算屬性、watch 等等 4. 函式式元件不能通過 $emit 對外暴露事件,呼叫事件只能通過==context.listeners.click==的方式呼叫外部傳入的事件 5. 因為函式式元件是沒有例項化的,所以在外部通過==ref==去引用元件時,實際引用的是 HTMLElement 6. 函式式元件的==props==可以不用顯式宣告,所以沒有在==props==裡面宣告的屬性都會被自動隱式解析為 prop,而普通元件所有未宣告的屬性都被解析到 $attrs 裡面,並自動掛載到元件根元素上面(可以通過 inheritAttrs 屬性禁止) **模板語法宣告函式式元件** 在 Vue2.5 之前,使用函式式元件只能通過 JSX 的方式,在之後可以通過模板語法來宣告函式式元件 ```html ``` ## 事件引數 $event $event 是事件物件的一個特殊變數。它在某些場景下為複雜的功能提供了更多的可選引數 ```html ``` ## EventBus 宣告一個全域性 Vue 例項變數 EventBus,把所有的通訊資料、事件監聽都儲存到這個變數上 類似於 Vuex,但這種方式一般適用於小的專案 原理就是利用 on、emit 並例項化一個全域性 vue 實現資料共享 可以實現平級、巢狀元件傳值;但是對應的事件名 eventTarget 必須是全域性唯一的 ```js // 在 main.js Vue.prototype.$eventBus = new Vue() // 傳值元件 this.$eventBus.$emit('eventTarget','這是eventTarget傳過來的值') // 接收元件 this.$eventBus.$on('eventTarget', v => { console.log('eventTarget', v) }) ``` 也可以新建一個 bus.js 檔案 ```js import Vue from 'vue' export default new Vue() ``` 在要通訊的元件匯入此檔案,進行監聽或傳送 ```js import Bus from '@/bus' // 元件1 Bus.$emit('operateMusic', id) // 元件2 Bus.$on('operateMusic', id => {}) ``` ## Mixin 混入 一般在 src 定義一個 mixins 資料夾,裡面存放每個 mixin,用 index.js 檔案彙總匯出 index.js ```js import serviceMixinsModule from './service-mixins' import tableListMixinsModule from './tableList-mixins' export const serviceMixins = serviceMixinsModule export const tableListMixins = tableListMixinsModule // 元件中使用 // import { serviceMixins, tableListMixins } from '@/mixins' // export default { // mixins: [serviceMixins, tableListMixins], // } ``` **主要說說衝突問題** 當元件和混入物件含有同名選項時,這些選項將以恰當的方式進行“合併” 比如,資料物件在內部會進行遞迴合併,並在發生衝突時以元件資料優先 ```js let mixin = { data: function () { return { message: 'hello', foo: 'abc' } } } new Vue({ mixins: [mixin], data: function () { return { message: 'goodbye', bar: 'def' } }, created: function () { console.log(this.$data) // => { message: "goodbye", foo: "abc", bar: "def" } } }) ``` 同名鉤子函式將合併為一個數組,因此都將被呼叫。另外,混入物件的鉤子將在元件自身鉤子之前呼叫 ```js let mixin = { created: function () { console.log('混入物件的鉤子被呼叫') } } new Vue({ mixins: [mixin], created: function () { console.log('元件鉤子被呼叫') } }) // => "混入物件的鉤子被呼叫" // => "元件鉤子被呼叫" ``` 值為物件的選項,例如 methods、components 和 directives,將被合併為同一個物件。兩個物件鍵名衝突時,取元件物件的鍵值對 ```js let mixin = { methods: { foo: function () { console.log('foo') }, conflicting: function () { console.log('from mixin') } } } let vm = new Vue({ mixins: [mixin], methods: { bar: function () { console.log('bar') }, conflicting: function () { console.log('from self') } } }) vm.foo() // => "foo" vm.bar() // => "bar" vm.conflicting() // => "from self" ``` 注意:Vue.extend() 也使用同樣的策略進行合併 ## 路由引數解耦 相信這是大多數人處理元件中路由引數的方式: ```js export default { computed: { paramsId() { return this.$route.params.id } } } ``` 在元件內部使用 $route 會對某個URL產生強耦合,這限制了元件的靈活性 正確的解決方案是向路由器新增 props ```js const router = new VueRouter({ routes: [{ path: '/:id', component: Component, props: true }] }) ``` 這樣,元件可以直接從 props 獲取 params ```js export default { props: ['id'], computed: { paramsId() { return this.id } } } ``` 此外,你還可以傳入函式以返回自定義 props ```js const router = new VueRouter({ routes: [{ path: '/:id', component: Component, props: router => ({ id: route.query.id }) }] }) ``` ## hook 妙用 如果在頁面掛載時增加一個定時器,但銷燬時需要清除定時器 一般想法是在 beforeDestroy 中使用 clearInterval(this.timer) 來清除 ```js export default { data () { return { timer: null } }, mounted () { this.timer = setInterval(() => { console.log(Date.now()) }, 1000) }, beforeDestroy () { clearInterval(this.timer) } } ``` 還有種更方便的方法,使用 $once 監聽 hook 函式 ```js export default { mounted () { let timer = null timer = setInterval(() => { console.log(Date.now()) }, 1000) this.$once('hook:beforeDestroy', () => { clearInterval(timer) }) } } ```
**監聽子元件生命週期 Hook** 通常,可以像這樣監聽子元件的生命週期(例如 mounted) ```html ``` 還有另一種簡單的解決方案,可以改用 @hook:mounted 在父元件直接監聽 ```html ``` ## 掛載全域性變數 ```js Vue.prototype.$lang = Lang // 可以在任何一個元件使用 this.$lang ``` ## Watcher 技巧 watch 的深度監聽==deep: true== 和 立即觸發==immediate: true== 就不多說了 需要注意的是深度監聽==deep: true== 只能監聽原有屬性的變化,不能監聽新增、刪除的屬性 還有一個有趣的特性,隨時監聽,隨時取消,$watch ```js const unwatch = this.$watch('say', curVal => { console.log('資料發生了變化', curVal) }, { deep: true, immediate: true // 是否第一次觸發 }) setTimeout(() => { unwatch() }, 3000) ``` this.$watch 的返回值 unwatch 是個方法,執行後就可以取消監聽 ## 傳送門 [Vue 自定義指令](https://ainyi.com/94) [Vue3 為何使用 Proxy 實現資料監聽](https://ainyi.com/93) [Vue JSX、自定義 v-model](https://ainyi.com/92) [Vue.nextTick 的應用解析](https://ainyi.com/86) [Vue 路由模組化配置](https://ainyi.com/77) [關於 Vue 不能 watch 陣列 和 物件變化的解決方案](https://ainyi.com/51) [Vue.mixin](https://ainyi.com/40) [Vuex 相關理解](https://ainyi.com/5) [Vue 知識總結](https://ainyi.com/4) ​部落格地址:[https://ainyi.com/95](https://ainyi.com/95)