記錄一些在使用Vue
開發(fā)中常見的坑婴氮。
先聲明本人菜鳥一只竿裂,一些描述可能會(huì)有偏頗纵苛,大佬輕錘剿涮,歡迎拍磚。
小葵花課堂開課啦攻人!
插槽相關(guān):slot
與slot-scope
單個(gè)插槽 | 匿名插槽
以上兩種叫法也就說明了它的含義:
- 一個(gè)組件中只能有一個(gè)該類插槽
- 該類插槽不設(shè)置
name
屬性
<!-- 父組件 -->
<template>
<div class="father">
<h3>父組件</h3>
<child>
<div class="tmpl">
<span>test1</span>
<span>test2</span>
<span>test3</span>
</div>
</child>
</div>
</template>
<!-- 子組件 -->
<template>
<div class="child">
<h3>子組件</h3>
<slot></slot>
</div>
</template>
子組件中定義的slot
將被父組件中child
內(nèi)包裹的html
模板所替代取试,即渲染后成:
<div class="father">
<h3>父組件</h3>
<div class="child">
<h3>子組件</h3>
<div class="tmpl">
<span>test1</span>
<span>test2</span>
<span>test3</span>
</div>
</div>
</div>
具名插槽
該插槽name
屬性不為空,且可以在一個(gè)組件中出現(xiàn)多次怀吻。
<!-- 父組件 -->
<template>
<div class="father">
<h3>父組件</h3>
<child>
<div class="tmpl" slot="first">
<span>test1</span>
<span>test2</span>
<span>test3</span>
</div>
<div class="tmpl" slot="second">
<span>test4</span>
<span>test5</span>
<span>test6</span>
</div>
<div class="tmpl">
<span>test7</span>
<span>test8</span>
<span>test9</span>
</div>
</child>
</div>
</template>
<!-- 子組件 -->
<template>
<div class="child">
<h3>子組件</h3>
<!-- 具名插槽 -->
<slot name="first"></slot>
<!-- 具名插槽 -->
<slot name="second"></slot>
<!-- 匿名插槽 -->
<slot></slot>
</div>
</template>
組件中匿名插槽可與具名插槽同時(shí)使用瞬浓,并且:
- 聲明了
name
的具名插槽將在父組件中找到與其對(duì)應(yīng)名稱的slot
屬性,并將html
模版替換到其中 - 未聲明
name
的將找到對(duì)應(yīng)的未聲明slot
屬性的html
模版做替換。
作用域插槽
觀察可以發(fā)現(xiàn)烙博,以上兩種插槽的html
模版的內(nèi)容都是由父組件指定的瑟蜈,那如果我想展示來自子組件的內(nèi)容該怎么辦呢?
所以就有了第三種插槽渣窜,我們稱之為作用域插槽铺根,坊間也叫攜帶數(shù)據(jù)的插槽,顧名思義乔宿,這種插槽可以攜帶數(shù)據(jù)給到父組件中的html
模板位迂。
<!-- 父組件 -->
<template>
<div class="father">
<h3>父組件</h3>
<child>
<template slot-scope='scope'>
<div class="tmpl">
<span v-for='u in scope.data'>test1</span>
</div>
</template>
</child>
</div>
</template>
<!-- 子組件 -->
<template>
<div class="child">
<h3>子組件</h3>
<!-- 作用域插槽 -->
<slot :data="arr"></slot>
</div>
</template>
<script>
export default {
data () {
return {
arr: ['test1','test2','test3']
}
}
}
</script>
子組件中定義了一個(gè)數(shù)組data
傳給父組件展示,父組件以${slotScopeName}.data
的格式去接收這個(gè)來自子組件的數(shù)組加以展示详瑞。
想給第三方UI庫Event加自定義參數(shù)時(shí)
<el-date-picker v-model="editForm[item.name]"
placeholder="選擇日期"
:editable="false" style="width:100%"
@change="(value) => changeHandler(value, item.name)">
</el-date-picker>
如上掂林,使用箭頭函數(shù),return
一個(gè)新的自定義函數(shù)坝橡,在自定義函數(shù)changeHandler
中加入你想要的參數(shù)泻帮。
同路由不同參數(shù)時(shí)
如果由 app/12345
跳轉(zhuǎn)到 app/23456
,兩個(gè)路由可能都是app/:id
计寇,用的同一個(gè)組件锣杂,所以vue
并不會(huì)重新走一遍生命周期,導(dǎo)致一些可能的數(shù)據(jù)沒更新的bug
番宁。
所以元莫,一個(gè)比較好的解決方案是監(jiān)控路由的變化來刷新一個(gè)必要的參數(shù)。
watch: {
'$route.params': function (newValue) {
this.init()
}
}
watch
如上是我們常用的 watch
蝶押,但可能你不知道watch
還有兩個(gè)比較實(shí)用的配置:deep
與immediate
踱蠢。
deep
deep
,默認(rèn)值是 false
棋电。顧名思義即表示深入觀察茎截,watch
會(huì)將obj
一層層往下遍歷,給對(duì)象的所有屬性都添加了監(jiān)聽离陶〖诨ⅲ可想而知,這一做法的性能耗費(fèi)比較大招刨。
watch: {
obj: {
handler (newVal, oldVal) {
console.log('obj的屬性變化了')
},
deep: true
},
}
// 建議使用
watch: {
'obj.key': {
handler (newVal, oldVal) {
console.log('obj的屬性key變化了')
}
},
}
immediate
immediate
霎俩,默認(rèn)值為 false
。顧名思義即表示立即監(jiān)聽沉眶,在定義了 handler
方法后將立即執(zhí)行一次打却。
watch: {
firstName: {
handler (newval, oldVal) {
this.fullName = `${newval} ${this.lastName}`
},
immediate: true
}
}
定時(shí)器
很多時(shí)候我們可能會(huì)有寫定時(shí)器的需求(比如有個(gè)輪詢?nèi)蝿?wù)),當(dāng)我們離開這個(gè)頁面時(shí)谎倔,你會(huì)發(fā)現(xiàn)定時(shí)任務(wù)還在跑柳击,性能炸了。
于是我們可以這么干:
data() {
return {
timer: null
}
}
methods: {
timing() {
this.timer = setInterval(() => {
// dosomething
}, 100)
}
}
beforeDestroy() {
clearInterval(this.timer) // 在組件即將銷毀時(shí)清掉定時(shí)任務(wù)
this.timer = null // 變量釋放
}
關(guān)鍵就在當(dāng)我們離開這個(gè)頁面(組件銷毀前)時(shí)清掉定時(shí)任務(wù)片习。
路由懶加載
Vue
的首屏渲染慢飽受詬病捌肴。
究其原因蹬叭,在于webpack打出來的包太大,導(dǎo)致在進(jìn)入首頁的時(shí)候需要加載的文件大且多状知。
那么我們就可以使用懶加載將頁面切分(vue-router
支持webpack
內(nèi)置的異步模塊加載系統(tǒng)秽五,使用較少的路由組件不再打包到bundles
中,只在路由被訪問到時(shí)按需加載)饥悴,隨用隨載坦喘,減少首頁加載的負(fù)擔(dān)。
常規(guī)非懶加載路由配置
import Vue from 'vue'
import Router from 'vue-router'
import Login from '@/components/Login'
import Home from '@/components/Home'
import Profile from '@/components/Profile'
Vue.use(Router)
export default new Router({
routes: [{
path: '/login',
name: 'Login',
compontent: Login
}, {
path: '/home',
name: 'Home',
compontent: Home
}, {
path: '/profile',
name: 'Profile',
compontent: Profile
}]
})
懶加載路由配置
以下列了常用的三種寫法西设,webpack
都支持瓣铣。
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [{
path: '/login',
name: 'Login',
compontent: resolve => require(['@/component/Login'], resolve)
}, {
path: '/home',
name: 'Home',
compontent: () => import(/* webpackChunkName: "home" */ '@/component/Home')
}, {
path: '/profile',
name: 'Profile',
compontent: r => require.ensure([], () => r(require('@/component/Profile')), 'profile')
}]
})
Computed
先來看一段代碼:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Computed</title>
</head>
<body>
<div id="app">
<label for="">FirstName:<input v-model="firstName"/></label><br>
<label for="">LastName:<input v-model="lastName"/></label><br>
<label for="">NickName<input v-model="nickName"/></label><br>
<p>名稱:{{name}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
new Vue({
el: '#app',
data: function() {
return {
firstName: '',
lastName: '',
nickName: ''
}
},
computed: {
name: function() {
console.log('觸發(fā)computed了')
if (this.nickName) {
return this.nickName
} else {
return this.firstName + '.' + this.lastName
}
}
}
})
</script>
</body>
</html>
天真時(shí)候的我們可能會(huì)以為每當(dāng)nickName
、firstName
或lastName
三者任一變化都將重新觸發(fā)name
的變更贷揽。
其實(shí)不然棠笑,當(dāng)nickName
有值(不為空)后,重新觸發(fā)依賴收集禽绪,此后將移除對(duì)firstName
與lastName
的依賴腐晾;而當(dāng)nickName
再次為空值時(shí),又將會(huì)收集到對(duì)firstName
與lastName
等的依賴丐一。
也就是說藻糖,依賴收集是動(dòng)態(tài)的。
re-render相關(guān)
還是看一段代碼:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Re-render</title>
</head>
<body>
<div id="app">
<div v-for="(item, index) in items" :key="index">
<div v-if="index === 0">
<input v-model="formData[item.key]">
</div>
<div v-else>{{item.key}}:{{Date.now()}}</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
new Vue({
el: '#app',
data: function() {
return {
items: [{
key: 'input'
}, {
key: 'Re-render'
}],
formData: {
input: ''
}
}
}
})
</script>
</body>
</html>
以上可能是一種常見的場(chǎng)景库车。我們會(huì)驚奇的發(fā)現(xiàn)巨柒,當(dāng)input
框中值發(fā)生變化,頁面re-render
了一個(gè)新的時(shí)間戳柠衍。不難看出問題出在Date.now()
洋满,這是一個(gè)方法(雖然沒定義在實(shí)例的methods
中),故而每當(dāng)template
重新渲染時(shí)會(huì)再次觸發(fā)一次method
珍坊,進(jìn)而引發(fā)意料之外的事故牺勾。
<div id="app">
<div>
<label>input:<input v-model="model"/></label>
</div>
<div>
<child :items="makeItems()"></child>
</div>
</div>
以上可能更能說明問題,一些場(chǎng)景下我們可能沒深考慮或下意識(shí)地直接使用一個(gè)method
做一些props
傳給子組件阵漏。那么當(dāng)輸入值model
變化時(shí)觸發(fā)父組件模板重新渲染驻民,child
的items
是從method
來的所以會(huì)被重新做一遍,產(chǎn)生不必要的麻煩履怯。
所以回还,一些場(chǎng)景下為了避免這種不必要的麻煩,還是推薦使用computed
叹洲,因?yàn)?strong>計(jì)算屬性是基于它們的依賴進(jìn)行緩存的柠硕,而method
在觸發(fā)重新渲染時(shí)總會(huì)再次執(zhí)行函數(shù),不做緩存运提。
...
未完待續(xù)蝗柔。闻葵。。