5招讓你成為Vue.js大師

來源 |?https://segmentfault.com/a/1190000014085613
第一招:化繁為簡的Watchers
created(){this.fetchPostList()},watch: {searchInputValue(){this.fetchPostList()}}
組件創(chuàng)建的時(shí)候我們獲取一次列表,同時(shí)監(jiān)聽input框,每當(dāng)發(fā)生變化的時(shí)候重新獲取一次篩選后的列表這個(gè)場景很常見,有沒有辦法優(yōu)化一下呢?
招式解析:
首先,在watchers中,可以直接使用函數(shù)的字面量名稱;其次,聲明immediate:true表示創(chuàng)建組件時(shí)立馬執(zhí)行一次。
watch: {searchInputValue:{handler: 'fetchPostList',immediate: true}}
第二招:一勞永逸的組件注冊
import BaseButton from './baseButton'import BaseIcon from './baseIcon'import BaseInput from './baseInput'export default {components: {BaseButton,BaseIcon,BaseInput}}
<BaseInputv-model="searchText"@keydown.enter="search"/><BaseButton @click="search"><BaseIcon name="search"/>BaseButton>
我們寫了一堆基礎(chǔ)UI組件,然后每次我們需要使用這些組件的時(shí)候,都得先import,然后聲明components,很繁瑣!秉持能偷懶就偷懶的原則,我們要想辦法優(yōu)化!
招式解析:
我們需要借助一下神器webpack,使用?require.context()?方法來創(chuàng)建自己的(模塊)上下文,從而實(shí)現(xiàn)自動(dòng)動(dòng)態(tài)require組件。這個(gè)方法需要3個(gè)參數(shù):要搜索的文件夾目錄,是否還應(yīng)該搜索它的子目錄,以及一個(gè)匹配文件的正則表達(dá)式。
我們在components文件夾添加一個(gè)叫g(shù)lobal.js的文件,在這個(gè)文件里借助webpack動(dòng)態(tài)將需要的基礎(chǔ)組件統(tǒng)統(tǒng)打包進(jìn)來。
import Vue from 'vue'function capitalizeFirstLetter(string) {return string.charAt(0).toUpperCase() + string.slice(1)}const requireComponent = require.context('.', false, /\.vue$///找到components文件夾下以.vue命名的文件)requireComponent.keys().forEach(fileName => {const componentConfig = requireComponent(fileName)const componentName = capitalizeFirstLetter(fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')//因?yàn)榈玫降膄ilename格式是: './baseButton.vue', 所以這里我們?nèi)サ纛^和尾,只保留真正的文件名)Vue.component(componentName, componentConfig.default || componentConfig)})
第三招:釜底抽薪的router key
下面這個(gè)場景真的是傷透了很多程序員的心...先默認(rèn)大家用的是Vue-router來實(shí)現(xiàn)路由的控制。
data() {return {loading: false,error: null,post: null}},watch: {'$route': {handler: 'resetData',immediate: true}},methods: {resetData() {this.loading = falsethis.error = nullthis.post = nullthis.getPost(this.$route.params.id)},getPost(id){}}
bug是解決了,可每次這么寫也太不優(yōu)雅了吧?秉持著能偷懶則偷懶的原則,我們希望代碼這樣寫:
data() {return {loading: false,error: null,post: null}},created () {this.getPost(this.$route.params.id)},methods () {getPost(postId) {// ...}}
招式解析:
那要怎么樣才能實(shí)現(xiàn)這樣的效果呢,答案是給router-view添加一個(gè)unique的key,這樣即使是公用組件,只要url變化了,就一定會重新創(chuàng)建這個(gè)組件。(雖然損失了一丟丟性能,但避免了無限的bug)。
同時(shí),注意我將key直接設(shè)置為路由的完整路徑,一舉兩得。
:key="$route.fullpath"> 第四招: 無所不能的render函數(shù)
場景還原:
vue要求每一個(gè)組件都只能有一個(gè)根元素,當(dāng)你有多個(gè)根元素時(shí),vue就會給你報(bào)錯(cuò)。
<liv-for="route in routes":key="route.name"><router-link :to="route">{{ route.title }}router-link>li>template>ERROR - Component template should contain exactly one root element.If you are using v-if on multiple elements, use v-else-ifto chain them instead.
那有沒有辦法化解呢,答案是有的,只不過這時(shí)候我們需要使用render()函數(shù)來創(chuàng)建HTML,而不是template。
functional: true,render(h, { props }) {return props.routes.map(route =><router-link to={route}>{route.title}router-link>li>)}
第五招:無招勝有招的高階組件
當(dāng)我們寫組件的時(shí)候,通常我們都需要從父組件傳遞一系列的props到子組件,同時(shí)父組件監(jiān)聽子組件emit過來的一系列事件。舉例子:
//父組件:value="value"label="密碼"placeholder="請?zhí)顚懨艽a"@input="handleInput"@focus="handleFocus>//子組件{{ label }}:value="value":placeholder="placeholder"@focus=$emit('focus', $event)"@input="$emit('input', $event.target.value)"></label>>
:value="value"v-bind="$attrs"@input="$emit('input', $event.target.value)"????>
$attrs包含了父作用域中不作為 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外)。當(dāng)一個(gè)組件沒有聲明任何 prop 時(shí),這里會包含所有父作用域的綁定,并且可以通過 v-bind="$attrs" 傳入內(nèi)部組件——在創(chuàng)建更高層次的組件時(shí)非常有用。
2、注意到子組件的@focus=$emit('focus', $event)"其實(shí)什么都沒做,只是把event傳回給父組件而已,那其實(shí)和上面類似,我完全沒必要顯式地申明:
:value="value"v-bind="$attrs"v-on="listeners">computed: {listeners() {return {...this.$listeners,input: event =>this.$emit('input', event.target.value)}}}
結(jié)尾
陸續(xù)可能還會更新一些別的招數(shù),敬請期待。

