前端框架Vue.js構(gòu)建大型應用淺析
真正的模塊化
前端模塊化很早就開始了,無論是 require.js,browserify 進行模塊化打包, 還是 Angular 進行依賴注入,我們都可以把JS代碼分成一個個小的模塊并組裝起來。然后我們還會通過 less 或者 sass 來把CSS文件也拆成一個個小的模塊來寫,甚至我們在CSS代碼中感受到了 封裝,繼承,多態(tài) 等面向?qū)ο蟮奶匦浴?/p>
然而,在 webpack 出來之前,我們所謂的模塊化根本不能算作模塊化。為什么這么講,因為我們存在一個重要的問題沒有解決,就是JS模塊對CSS模塊的依賴。
比如我們有一個JS模塊 modal 那么我們直接導入并調(diào)用它就能彈出一個對話框嗎?如下圖所示可以嗎?
理論上應該是這樣的,但實際上這個 modal其實還依賴一個對應的CSS模塊 modal.less ,如果不導入這個模塊我們是無法彈出一個正常的對話框的,而且,導入這個CSS模塊竟然不是和導入JS模塊寫在同一個地方,而是寫在另一個CSS文件中。也就是說,其實依賴關系是這樣的:

為了使用一個模塊,我們需要在兩個文件中分別做一次引入操作。這其實是一件非常奇怪不合理的事!我們?yōu)槭裁匆K化?就是為了封裝一個模塊,可以做到導入它就能使用,而它是如何實現(xiàn)的,它有什么依賴關系完全是這個模塊自己處理的,也就是上圖中對 modal.css 的依賴應該是 modal.js 自己處理的。
但是我們寫了N年的前端卻一直這樣寫模塊,不是因為他對,而是因為我們習慣了這種錯誤的方式。現(xiàn)在用Vue我們可以完全封裝一個模塊的全部依賴,無論是模板、CSS還是JS,我們都不需要再去關心,只要引入這個模塊就可以使用,而模塊的依賴是它自己進行處理的。
那么我們的依賴關系就變成了:

其中 modal.vue 包含了全部所需要的依賴,那么我們就不在需要自己去處理對應的 CSS 甚至 模板了。這才是模塊化應該達到的效果。
創(chuàng)建 Vue 項目
Vue 提供了一個工具 vue-cli 可以創(chuàng)建一個項目模板: https://github.com/vuejs/vue-cli
這里我先嘗試了一下另一個模板項目:https://github.com/vuejs-templates/webpack
然后我們就可以不用 純JS來寫模塊了,而是借助 webpack 來把一個模塊相關的所有內(nèi)容全部寫到一個文件中。以之前的 todo list 為例,其實上一章講的只是component的用法所以那樣寫了。我們改成一個更好的寫法如下:
List.vue:
<template>
<ul>
<li v-for='todo in list'>
<label v-bind:class="{ done : todo.done }" >
<input type="checkbox" v-model="todo.done"/>
{{todo.title}}
</label>
</li>
</ul>
</template>
<script>
export default {
props: {
initList: {
type: Array
}
},
data () {
return {
list: []
}
},
events: {
add (input) {
if (!input) return false
this.list.unshift({
title: input,
done: false
})
}
}
}
</script>
<style lang="less" scoped>
ul {
margin-left: 2rem;
padding: 0;
.done {
text-decoration: line-through;
}
}
</style>
Form.vue:
<template>
<h1>{{username}}'s Todo List</h1>
<form v-on:submit="add" v-on:submit.prevent>
<input type="text" v-model="input"/>
<input type="submit" value='add' />
</form>
</template>
<script>
export default {
props: {
username: {
type: String,
default: 'Unnamed'
}
},
data () {
return {
input: ''
}
},
methods: {
add () {
this.$dispatch('add', this.input)
this.input = ''
}
}
}
</script>
Todo.vue:
<template>
<div id="todo">
<todo-form username='Lily'></todo-form>
<todo-list></todo-list>
</div>
</template>
<script>
import Form from './Form.vue'
import List from './List.vue'
export default {
components: {
'todo-form': Form,
'todo-list': List
},
events: {
add (input) {
this.$broadcast('add', input)
}
}
}
</script>
<style>
</style>
App.vue:
<template>
<todo></todo>
</template>
<script>
import Todo from './components/Todo.vue'
export default {
components: {
'todo': Todo
}
}
</script>
<style>
</style>
這樣我們就把之前的 Todo List 按照 模塊化 重寫了一遍。模塊化是構(gòu)建大型應用的基礎之一,但是這一點還不夠,我們還需要做到:
•更好的狀態(tài)管理,把不同組件共享的 State 獨立出來管理
•自動化測試
•路由等
這里我們只做其中一個,就是把 State 獨立成一個單獨模塊。很顯然,對一個 Todo List 應用來說,保存 todo list 的數(shù)據(jù)結(jié)構(gòu)就是不同組件共享的 State。
之前我們?yōu)槭裁葱枰M行事件廣播,就是因為不同組件之間要操作的數(shù)據(jù)就保存在 List.vue 中,所以在 Form.vue 中想增加一條數(shù)據(jù)的時候需要通過事件的方式去通知 List.vue 來添加。
也就是其實這個數(shù)據(jù)不是 List.vue 私有的,應該至少是這兩個組件公有的,現(xiàn)在被 List.vue 據(jù)為己有之后,F(xiàn)orm.vue 沒法修改它只好通過事件進行通知。
雖然事件的方式很優(yōu)雅,但其實我們可以做的更好,就是把數(shù)據(jù)獨立出來,這樣 Form.vue 和 List.vue 都可以直接修改數(shù)據(jù),而不用那么麻煩發(fā)通知。
這里我們增加一個 Store.js 文件:
export default {
list: [
],
add (title) {
if (!title) return
this.list.unshift({
title: title,
done: false
})
}
}
然后 我們可以把 List.vue 改成這樣,這里只貼出JS部分的代碼:
import Store from '../Store.js'
export default {
props: {
initList: {
type: Array
}
},
data () {
return Store
}
}
Form.vue 也不需要廣播了,直接調(diào)用 Store.add 方法既可以添加:
import Store from '../Store.js'
export default {
props: {
username: {
type: String,
default: 'Unnamed'
}
},
data () {
return {
input: ''
}
},
methods: {
add () {
Store.add(this.input)
this.input = ''
}
}
}
這樣一改之后,整個邏輯會清晰很多,并且應用越是復雜,越是應該抽出公有的 Store ,不然會出現(xiàn)廣播事件滿天飛的情況。
另外用這個項目模板之后,hot-reload 爽的不要不要的,刷新操作都省了。
上述的源碼在這里:https://github.com/lihongxun945/vue-webpack-todo-list
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
淺析webpack-bundle-analyzer在vue-cli3中的使用
這篇文章主要介紹了webpack-bundle-analyzer在vue-cli3中的使用,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-10-10
Vue源碼學習之關于對Array的數(shù)據(jù)偵聽實現(xiàn)
這篇文章主要介紹了Vue源碼學習之關于對Array的數(shù)據(jù)偵聽實現(xiàn),Vue使用了一個方式來實現(xiàn)Array類型的監(jiān)測就是攔截器,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-04-04
vue-router實現(xiàn)組件間的跳轉(zhuǎn)(參數(shù)傳遞)
這篇文章主要為大家詳細介紹了vue-router實現(xiàn)組件間的跳轉(zhuǎn),參數(shù)傳遞方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11
VUE Error: getaddrinfo ENOTFOUND localhost
這篇文章主要介紹了VUE Error: getaddrinfo ENOTFOUND localhost,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
VUE搭建分布式醫(yī)療掛號系統(tǒng)的前臺預約掛號步驟詳情
這篇文章主要介紹了VUE搭建分布式醫(yī)療掛號系統(tǒng)的前臺預約掛號步驟詳情,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-04-04
Vite結(jié)合whistle實現(xiàn)一勞永逸開發(fā)環(huán)境代理方案
這篇文章主要為大家介紹了Vite結(jié)合whistle實現(xiàn)一勞永逸開發(fā)環(huán)境代理方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07
解決vue3報錯:Unexpected?mutation?of?“xxx“?prop.(eslintvue/no
這篇文章主要給大家介紹了關于如何解決vue3報錯:Unexpected?mutation?of?“xxx“?prop.(eslintvue/no-mutating-props)的相關資料,文中通過代碼將解決辦法介紹的非常詳細,需要的朋友可以參考下2023-12-12

