總結vue知識體系之實用技巧
vue 作為目前前端三大框架之一,對于前端開發者可以說是必備技能。那么怎么系統地學習和掌握 vue 呢?為此,我做了簡單的知識體系體系總結,不足之處請各位大佬多多包涵和指正,如果喜歡的可以點個小贊!本文主要講述一些vue開發中的實用技巧。
監聽組件的生命周期
比如有父組件 Parent 和子組件 Child,如果父組件監聽到子組件掛載 mounted 就做一些邏輯處理,常規的寫法可能如下:
- // Parent.vue
- <Child @mounted="doSomething"/>
- // Child.vue
- mounted() {
- this.$emit("mounted");
- }
此外,還有一種特別簡單的方式,子組件不需要任何處理,只需要在父組件引用的時候通過@hook 來監聽即可,代碼如下:
- <Child @hook:mounted="doSomething" />
- <Child @hook:updated="doSomething" />
當然這里不僅僅是可以監聽 mounted,其它的生命周期事件,例如:created,updated 等都可以。
watch 的初始立即執行
觀察和響應 Vue 實例上的數據變動。類似于某些數據的監聽回調 ,每當監聽的數據變化時都會執行回調進行后續操作。
但是當 watch 一個變量的時候,初始化時并不會執行,如下面的例子,你需要在 created 的時候手動調用一次。
- created() {
- this.getList();
- },
- watch: {
- keyWord: 'getList',
- }
上面這樣的做法可以使用,但很麻煩,我們可以添加 immediate 屬性,這樣初始化的時候就會自動觸發(不用再寫 created 去調用了),然后上面的代碼就能簡化為:
- watch: {
- keyWord: {
- handler: 'getList',
- immediate: true
- }
- }
watch 有三個參數:
- handler:其值是一個回調函數。即監聽到變化時應該執行的函數
- deep:其值是 true 或 false;確認是否深入監聽。
- immediate:其值是 true 或 false,確認是否以當前的初始值執行 handler 的函數
路由參數變化組件不更新
同一path的頁面跳轉時路由參數變化,但是組件沒有對應的更新。
原因:主要是因為獲取參數寫在了created或者mounted路由鉤子函數中,路由參數變化的時候,這個生命周期不會重新執行。
解決方案1:watch監聽路由
- watch: {
- // 方法1 //監聽路由是否變化
- '$route' (to, from) {
- if(to.query.id !== from.query.id){
- this.id = to.query.id;
- this.init();//重新加載數據
- }
- }
- }
- //方法 2 設置路徑變化時的處理函數
- watch: {
- '$route': {
- handler: 'init',
- immediate: true
- }
- }
解決方案2 :為了實現這樣的效果可以給router-view添加一個不同的key,這樣即使是公用組件,只要url變化了,就一定會重新創建這個組件。
- <router-view :key="$route.fullpath"></router-view>
路由懶加載
Vue 項目中實現路由按需加載(路由懶加載)的 3 中方式:
- // 1、Vue異步組件技術:
- {
- path: '/home',
- name: 'Home',
- component: resolve => reqire(['path路徑'], resolve)
- }
- // 2、es6提案的import()
- const Home = () => import('path路徑')
- // 3、webpack提供的require.ensure()
- {
- path: '/home',
- name: 'Home',
- component: r => require.ensure([],() => r(require('path路徑')), 'demo')
- }
require.context()
require.context(directory,useSubdirectories,regExp)
- directory:說明需要檢索的目錄
- useSubdirectories:是否檢索子目錄
- regExp:匹配文件的正則表達式,一般是文件名
場景:如頁面需要導入多個組件,原始寫法:
- import titleCom from '@/components/home/titleCom'
- import bannerCom from '@/components/home/bannerCom'
- import cellCom from '@/components/home/cellCom'
- components: {
- titleCom, bannerCom, cellCom
- }
這樣就寫了大量重復的代碼,利用 require.context 可以寫成
- const path = require('path')
- const files = require.context('@/components/home', false, /\.vue$/)
- const modules = {}
- files.keys().forEach(key => {
- const name = path.basename(key, '.vue')
- modules[name] = files(key).default || files(key)
- })
- components: modules
遞歸組件
遞歸組件: 組件在它的模板內可以遞歸的調用自己,只要給組件設置 name 組件就可以了。
不過需要注意的是,必須給一個條件來限制數量,否則會拋出錯誤: max stack size exceeded
組件遞歸用來開發一些具體有未知層級關系的獨立組件。比如:聯級選擇器和樹形控件
- <template>
- <div v-for="(item,index) in treeArr"> {{index}} <br/>
- <tree :item="item.arr" v-if="item.flag"></tree>
- </div>
- </template>
- <script>
- export default {
- // 必須定義name,組件內部才能遞歸調用
- name: 'tree',
- data(){
- return {}
- },
- // 接收外部傳入的值
- props: {
- item: {
- type:Array,
- default: ()=>[]
- }
- }
- }
- </script>
清除定時器或者事件監聽
由于項目中有些頁面難免會碰到需要定時器或者事件監聽。但是在離開當前頁面的時候,定時器如果不及時合理地清除,會造成業務邏輯混亂甚至應用卡死的情況,這個時就需要清除定時器事件監聽,即在頁面卸載(關閉)的生命周期函數里,清除定時器。
- methods:{
- resizeFun () {
- this.tableHeight = window.innerHeight - document.getElementById('table').offsetTop - 128
- },
- setTimer() {
- this.timer = setInterval(() => { })
- },
- clearTimer() {//清除定時器
- clearInterval(this.timer)
- this.timer = null
- }
- },
- mounted() {
- this.setTimer()
- window.addEventListener('resize', this.resizeFun)
- },
- beforeDestroy() {
- window.removeEventListener('resize', this.resizeFun)
- this.clearTimer()
- }
自定義路徑別名
我們也可以在基礎配置文件中添加自己的路徑別名
- resolve: {
- extensions: ['.js', '.vue', '.json'],
- alias: {
- 'vue$': 'vue/dist/vue.esm.js',
- '@': resolve('src'),
- 'assets': resolve('src/assets')
- }
- }
然后我們導入組件的時候就可以這樣寫:
- // import YourComponent from '/src/assets/YourComponent'
- import YourComponent from 'assets/YourComponent'
這樣既解決了路徑過長的麻煩,又解決了相對路徑的煩惱。
動態給修改dom的樣式
原因:因為我們在寫.vue文件中的樣式都會追加scoped。這樣針對模板dom中的樣式就可以生效,但其生效后的最終樣式并不是我們寫的樣式名,而是編碼后的。比如:
- <template>
- <div class="box">dom</div>
- </template>
- <style lang="scss" scoped>
- .box {
- background: red;
- }
- </style>
vue 將代碼轉譯成如下,所以我們在js中拼接上的dom結構樣式并不會生效。
- .box[data-v-11c6864c]{ background:red; }
- <template>
- <div class="box" data-v-11c6864c>dom</div>
- </template>
解決方法:將要改變的樣式寫在非scoped樣式標簽中。
長列表性能優化
我們應該都知道 vue 會通過 object.defineProperty 對數據進行劫持,來實現視圖響應數據的變化,然而有些時候我們的組件就是純粹的數據展示,不會有任何改變,我們就不需要 vue 來劫持我們的數據,在大量數據展示的情況下,這能夠很明顯的減少組件初始化的時間。
所以,我們可以通過 object.freeze 方法來凍結一個對象,這個對象一旦被凍結,vue就不會對數據進行劫持了。
- export default {
- data: () => ({
- list: []
- }),
- async created() {
- const list = await axios.get('xxxx')
- this.list = Object.freeze(list)
- },
- methods: {
- // 此處做的操作都不能改變list的值
- }
- }
另外需要說明的是,這里只是凍結了 list 的值,引用不會被凍結,當我們需要 reactive 數據的時候,我們可以重新給 list 賦值。
內容分發(slot)
插槽 slot,也是組件的一塊 HTML 模板,這一塊模板顯示不顯示、以及怎樣顯示由父組件來決定。實際上,一個 slot 最核心的兩個問題在這里就點出來了,是顯示不顯示和怎樣顯示。
默認插槽
又名單個插槽、匿名插槽,這類插槽沒有具體名字,一個組件只能有一個該類插槽。
- <!-- 父組件 parent.vue -->
- <template>
- <div class="parent">
- <h1>父容器</h1>
- <child>
- <div class="tmpl">
- <span>菜單1</span>
- </div>
- </child>
- </div>
- </template>
- <!-- 子組件 child.vue -->
- <template>
- <div class="child">
- <h1>子組件</h1>
- <slot></slot>
- </div>
- </template>
具名插槽
匿名插槽沒有 name 屬性,所以叫匿名插槽。那么,插槽加了 name 屬性,就變成了具名插槽。具名插槽可以在一個組件中出現 N 次,出現在不同的位置,只需要使用不同的 name 屬性區分即可。
- <!-- 父組件 parent.vue -->
- <template>
- <div class="parent">
- <h1>父容器</h1>
- <child>
- <div class="tmpl" slot="up">
- <span>菜單up-1</span>
- </div>
- <div class="tmpl" slot="down">
- <span>菜單down-1</span>
- </div>
- <div class="tmpl">
- <span>菜單->1</span>
- </div>
- </child>
- </div>
- </template>
- <!-- 子組件 child.vue -->
- <template>
- <div class="child">
- <!-- 具名插槽 -->
- <slot name="up"></slot>
- <h3>這里是子組件</h3>
- <!-- 具名插槽 -->
- <slot name="down"></slot>
- <!-- 匿名插槽 -->
- <slot></slot>
- </div>
- </template>
作用域插槽
作用域插槽可以是默認插槽,也可以是具名插槽,不一樣的地方是,作用域插槽可以為 slot 標簽綁定數據,讓其父組件可以獲取到子組件的數據。
- <!-- parent.vue -->
- <template>
- <div class="parent">
- <h1>這是父組件</h1>
- <child
- >>
- <template slot="default" slot-scope="slotProps">
- {{ slotProps.user.name }}
- </template> </child
- >>
- </div>
- </template>
- <!-- 子組件 child.vue -->
- <template>
- <div class="child">
- <h1>這是子組件</h1>
- <slot :user="user"></slot>
- </div>
- </template>
- <script>
- export default {
- data() {
- return {
- user: {
- name: '小趙'
- }
- }
- }
- }
- </script>


























