# 構建大型應用
Vue.js 的設計思想是專注與靈活——它只是一個界面庫,不強制使用哪個架構。它能很好地與已有項目整合,不過對于經驗欠缺的開發者,從頭開始構建大型應用可能是一個挑戰。
Vue.js 生態系統提供了一系列的工具與庫,用于構建單頁應用。但是它們只是推薦而已。
## 模塊化
對于大型項目,為了更好地管理代碼使用模塊構建系統非常必要。推薦代碼使用 CommonJS 或 ES6 模塊,然后使用 [Webpack](http://webpack.github.io/) 或 [Browserify](http://browserify.org/) 打包。
Webpack 和 Browserify 不只是模塊打包器。兩者都提供了源碼轉換 API,通過它可以用其它預處理器轉換源碼。例如,借助 [babel-loader](https://github.com/babel/babel-loader) 或 [babelify](https://github.com/babel/babelify) 代碼可以使用 ES2015/2016 語法。
如果你之前沒有用過它們,我強烈推薦你閱讀一些教程,了解模塊打包器,然后使用最新的 ECMAScript 特性寫 JavaScript。
## 單文件組件
在典型的 Vue.js 項目中,我們會把界面拆分為多個小組件,每個組件在同一地方封裝它的 CSS 樣式,模板和 JavaScript 定義,這么做比較好。如上所述,使用 Webpack 或 Browserify 以及合適的源碼轉換器,我們可以這樣寫組件:

如果你喜歡預處理器,甚至可以這么做:

你可以使用 Webpack + [vue-loader](https://github.com/vuejs/vue-loader) 或 Browserify + [vueify](https://github.com/vuejs/vueify) 構建這些單文件 Vue 組件。推薦使用 Webpack,因為它的加載器 API 提供更好的文件依賴追蹤/緩存以及一些 Browserify 沒有的轉換功能。
在 GitHub 上有一些構建示例:
* [Webpack + vue-loader](https://github.com/vuejs/vue-loader-example)
* [Browserify + vueify](https://github.com/vuejs/vueify-example)
## 路由
對于單頁應用,推薦使用[官方庫 vue-router](https://github.com/vuejs/vue-router)。詳細請查看它的[文檔](http://vuejs.github.io/vue-router/)。
如果你只需要非常簡單的路由邏輯,可以這么做,監聽 `hashchange` 事件并使用動態組件:
**示例:**
```
<div id="app">
<component :is="currentView"></component>
</div>
```
```
Vue.component('home', { /* ... */ })
Vue.component('page1', { /* ... */ })
var app = new Vue({
el: '#app',
data: {
currentView: 'home'
}
})
// 在路由處理器中切換頁面
app.currentView = 'page1'
```
利用這種機制也可以非常容易地配合其它路由庫,如 [Page.js](https://github.com/visionmedia/page.js) 或 [Director](https://github.com/flatiron/director)。
## 與服務器通信
Vue 實例的原始數據 `$data` 能直接用 `JSON.stringify()` 序列化。社區貢獻了一個插件 [vue-resource](https://github.com/vuejs/vue-resource),提供一種容易的方式與 RESTful APIs 配合。也可以使用任何自己喜歡的 Ajax 庫,如 `$.ajax` 或 [SuperAgent](https://github.com/visionmedia/superagent)。Vue.js 也能很好地與無后端服務配合,如 Firebase 和 Parse。
## 狀態管理
在大型應用中,狀態管理常常變得復雜,因為狀態分散在許多組件內。常常忽略 Vue.js 應用的來源是原生的數據對象—— Vue 實例代理訪問它。因此,如果一個狀態要被多個實例共享,應避免復制它:
```
var sourceOfTruth = {}
var vmA = new Vue({
data: sourceOfTruth
})
var vmB = new Vue({
data: sourceOfTruth
})
```
現在每當 `sourceOfTruth` 被修改后,`vmA` 與 `vmB` 將自動更新它們的視圖。擴展這個思路,我們可以實現 **store 模式**:
```
var store = {
state: {
message: 'Hello!'
},
actionA: function () {
this.state.message = 'action A triggered'
},
actionB: function () {
this.state.message = 'action B triggered'
}
}
var vmA = new Vue({
data: {
privateState: {},
sharedState: store.state
}
})
var vmB = new Vue({
data: {
privateState: {},
sharedState: store.state
}
})
```
我們把所有的 action 放在 store 內,action 修改 store 的狀態。集中管理狀態更易于理解狀態將怎樣變化。組件仍然可以擁有和管理它的私有狀態。

有一點要注意,不要在 action 中替換原始的狀態對象——為了觀察到變化,組件和 store 需要共享這個對象。
如果我們約定,組件不可以直接修改 store 的狀態,而應當派發事件,通知 store 執行 action,那么我們基本上實現了 [Flux](https://facebook.github.io/flux/) 架構。此約定的好處是,我們能記錄 store 所有的狀態變化,并且在此之上實現高級的調試幫助函數,如修改日志,快照,歷史回滾等。
Flux 架構常用于 React 應用中。借助于響應系統,Flux 的核心思想能非常容易地用 Vue.js 實現。注意我們這里的演示只是為了介紹概念,簡單的情況完全不需要這么做,應根據應用的需求調整這個模式。
## 單元測試
任何支持模塊構建系統的單元測試工具都可以。推薦使用 [Karma](http://karma-runner.github.io/0.12/index.html)。它有許多插件,支持 [Webpack](https://github.com/webpack/karma-webpack) 和 [Browserify](https://github.com/Nikku/karma-browserify)。用法見它們的文檔。
代碼測試的最佳實踐是導出組件模塊的選項/函數。例如:
```
// my-component.js
module.exports = {
template: '<span>{{msg}}</span>',
data: function () {
return {
msg: 'hello!'
}
}
created: function () {
console.log('my-component created!')
}
}
```
在入口模塊中使用這個模塊:
```
// main.js
var Vue = require('vue')
var app = new Vue({
el: '#app',
data: { /* ... */ },
components: {
'my-component': require('./my-component')
}
})
```
測試這個模塊:
```
// Jasmine 2.0 測試
describe('my-component', function () {
// require source module
var myComponent = require('../src/my-component')
it('should have a created hook', function () {
expect(typeof myComponent.created).toBe('function')
})
it('should set correct default data', function () {
expect(typeof myComponent.data).toBe('function')
var defaultData = myComponent.data()
expect(defaultData.msg).toBe('hello!')
})
})
```
因為 Vue.js 指令是異步更新,如果想在修改數據之后修改 DOM ,應當在 `Vue.nextTick` 的回調中操作。
## 生產發布
為了更小的文件體積,Vue.js 的壓縮版本刪除所有的警告,但是在使用 Browserify 或 Webpack 等工具構建 Vue.js 應用時,壓縮需要一些配置。
### Webpack
使用插件 [DefinePlugin](http://webpack.github.io/docs/list-of-plugins.html#defineplugin) 將當前環境指定為生產環境,警告將在 UglifyJS 壓縮代碼過程中被刪除。配置示例:
```
var webpack = require('webpack')
module.exports = {
// ...
plugins: [
// ...
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
}
```
### Browserify
將 NODE_ENV 設置為 “production”,然后運行打包命令。Vue 會自動應用 [envify](https://github.com/hughsk/envify) 并讓警告塊不能運行。例如:
```
NODE_ENV=production browserify -e main.js | uglifyjs -c -m > build.js
```
## 應用示例
[Vue.js Hackernews Clone](https://github.com/vuejs/vue-hackernews) 這個應用示例使用 Webpack + vue-loader 組織代碼,使用 vue-router 作為路由器,HackerNews 官方的 Firebase API 作為后端。它當然不是大應用,但是它綜合演示了本頁討論的概念。
- 教程
- 起步
- 概述
- Vue 實例
- 數據綁定語法
- 計算屬性
- Class 與 Style 綁定
- 條件渲染
- 列表渲染
- 方法與事件處理器
- 表單控件綁定
- 過渡
- 組件
- 深入響應式原理
- 自定義指令
- 自定義過濾器
- 混合
- 插件
- 構建大型應用
- 對比其它框架
- API
- 示例
- Markdown 編輯器 Example
- GitHub 提交 Example
- Firebase + 驗證 Example
- 表格組件 Example
- 樹狀視圖 Example
- SVG 圖形 Example
- 模態組件 Example
- Elastic Header Example
- 自定義指令 Example
- TodoMVC Example
- HackerNews 克隆 Example