鍍金池/ 教程/ HTML/ 構(gòu)建大型應(yīng)用
自定義過濾器
自定義指令
概述
安裝
起步
深入響應(yīng)式原理
Class 與 Style 綁定
混合
列表渲染
方法與事件處理器
表單控件綁定
對(duì)比其它框架
組件
計(jì)算屬性
插件
數(shù)據(jù)綁定語法
構(gòu)建大型應(yīng)用
條件渲染
Vue 實(shí)例
過渡

構(gòu)建大型應(yīng)用

新: 使用腳手架工具 vue-cli 可以快速地構(gòu)建項(xiàng)目:?jiǎn)挝募?Vue 組件,熱加載,保存時(shí)檢查代碼,單元測(cè)試等。

Vue.js 的設(shè)計(jì)思想是專注與靈活——它只是一個(gè)界面庫,不強(qiáng)制使用哪個(gè)架構(gòu)。它能很好地與已有項(xiàng)目整合,不過對(duì)于經(jīng)驗(yàn)欠缺的開發(fā)者,從頭開始構(gòu)建大型應(yīng)用可能是一個(gè)挑戰(zhàn)。

Vue.js 生態(tài)系統(tǒng)提供了一系列的工具與庫,用于構(gòu)建大型單頁應(yīng)用。這些部分會(huì)感覺開始更像一個(gè)『框架』,但是它們本質(zhì)上只是一套推薦的技術(shù)棧而已 - 你依然可以對(duì)各個(gè)部分進(jìn)行選擇和替換。

模塊化

對(duì)于大型項(xiàng)目,為了更好地管理代碼使用模塊構(gòu)建系統(tǒng)非常必要。推薦代碼使用 CommonJS 或 ES6 模塊,然后使用 WebpackBrowserify 打包。

Webpack 和 Browserify 不只是模塊打包器。兩者都提供了源碼轉(zhuǎn)換 API,通過它可以用其它預(yù)處理器轉(zhuǎn)換源碼。例如,借助 babel-loaderbabelify 代碼可以使用 ES2015/2016 語法。

如果你之前沒有用過它們,我強(qiáng)烈推薦你閱讀一些教程,了解模塊打包器,然后使用最新的 ECMAScript 特性寫 JavaScript。

單文件組件

在典型的 Vue.js 項(xiàng)目中,我們會(huì)把界面拆分為多個(gè)小組件,每個(gè)組件在同一地方封裝它的 CSS 樣式,模板和 JavaScript 定義,這么做比較好。如上所述,使用 Webpack 或 Browserify 以及合適的源碼轉(zhuǎn)換器,我們可以這樣寫組件:

http://wiki.jikexueyuan.com/project/vue-js-1.0/images/vue-component.png" alt="img src" />

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

http://wiki.jikexueyuan.com/project/vue-js-1.0/images/vue-component-with-pre-processors.png" alt="img src" />

你可以使用 Webpack + vue-loader 或 Browserify + vueify 構(gòu)建這些單文件 Vue 組件。推薦使用 Webpack,因?yàn)樗募虞d器 API 提供更好的文件依賴追蹤/緩存以及一些 Browserify 沒有的轉(zhuǎn)換功能。

最快的構(gòu)建方式是使用官方出品的腳手架工具 vue-cli。你也可以在 GitHub 上查看官方的構(gòu)建模板:

路由

對(duì)于單頁應(yīng)用,推薦使用官方庫 vue-router。詳細(xì)請(qǐng)查看它的文檔

如果你只需要非常簡(jiǎn)單的路由邏輯,可以這么做,監(jiān)聽 hashchange 事件并使用動(dòng)態(tài)組件:

示例:

<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'

利用這種機(jī)制也可以非常容易地配合其它路由庫,如 Page.jsDirector。

與服務(wù)器通信

Vue 實(shí)例的原始數(shù)據(jù) $data 能直接用 JSON.stringify() 序列化。社區(qū)貢獻(xiàn)了一個(gè)插件 vue-resource,提供一種容易的方式與 RESTful APIs 配合。也可以使用任何自己喜歡的 Ajax 庫,如 $.ajaxSuperAgent。Vue.js 也能很好地與無后端服務(wù)配合,如 Firebase 和 Parse。

狀態(tài)管理

在大型應(yīng)用中,狀態(tài)管理常常變得復(fù)雜,因?yàn)闋顟B(tài)分散在許多組件內(nèi)。常常忽略 Vue.js 應(yīng)用的來源是原生的數(shù)據(jù)對(duì)象—— Vue 實(shí)例代理訪問它。因此,如果一個(gè)狀態(tài)要被多個(gè)實(shí)例共享,應(yīng)避免復(fù)制它:

var sourceOfTruth = {}

var vmA = new Vue({
  data: sourceOfTruth
})

var vmB = new Vue({
  data: sourceOfTruth
})

現(xiàn)在每當(dāng) sourceOfTruth 被修改后,vmAvmB 將自動(dòng)更新它們的視圖。擴(kuò)展這個(gè)思路,我們可以實(shí)現(xiàn) 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 內(nèi),action 修改 store 的狀態(tài)。集中管理狀態(tài)更易于理解狀態(tài)將怎樣變化。組件仍然可以擁有和管理它的私有狀態(tài)。

http://wiki.jikexueyuan.com/project/vue-js-1.0/images/state.png" alt="狀態(tài)管理" />

有一點(diǎn)要注意,不要在 action 中替換原始的狀態(tài)對(duì)象——為了觀察到變化,組件和 store 需要共享這個(gè)對(duì)象。

如果我們約定,組件不可以直接修改 store 的狀態(tài),而應(yīng)當(dāng)派發(fā)事件,通知 store 執(zhí)行 action,那么我們基本上實(shí)現(xiàn)了 Flux 架構(gòu)。此約定的好處是,我們能記錄 store 所有的狀態(tài)變化,并且在此之上實(shí)現(xiàn)高級(jí)的調(diào)試幫助函數(shù),如修改日志,快照,歷史回滾等。

Flux 架構(gòu)常用于 React 應(yīng)用中,但它的核心理念也可以適用于 Vue.js 應(yīng)用。比如 Vuex 就是一個(gè)借鑒于 Flux,但是專門為 Vue.js 所設(shè)計(jì)的狀態(tài)管理方案。React 生態(tài)圈中最流行的 Flux 實(shí)現(xiàn) Redux 也可以通過簡(jiǎn)單的綁定和 Vue 一起使用。

單元測(cè)試

任何支持模塊構(gòu)建系統(tǒng)的單元測(cè)試工具都可以。推薦使用 Karma。它有許多插件,支持 WebpackBrowserify。用法見它們的文檔。

代碼測(cè)試的最佳實(shí)踐是導(dǎo)出組件模塊的選項(xiàng)/函數(shù)。例如:

// my-component.js
module.exports = {
  template: '<span>{{msg}}</span>',
  data: function () {
    return {
      msg: 'hello!'
    }
  }
  created: function () {
    console.log('my-component created!')
  }
}

在入口模塊中使用這個(gè)模塊:

// main.js
var Vue = require('vue')
var app = new Vue({
  el: '#app',
  data: { /* ... */ },
  components: {
    'my-component': require('./my-component')
  }
})

測(cè)試這個(gè)模塊:

// Jasmine 2.0 測(cè)試
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!')
  })
})

Karma 的示例配置:Webpack, Browserify。

因?yàn)?Vue.js 指令是異步更新,如果想在修改數(shù)據(jù)之后修改 DOM ,應(yīng)當(dāng)在 `Vue.nextTick` 的回調(diào)中操作。

生產(chǎn)發(fā)布

為了更小的文件體積,Vue.js 的壓縮版本刪除所有的警告,但是在使用 Browserify 或 Webpack 等工具構(gòu)建 Vue.js 應(yīng)用時(shí),壓縮需要一些配置。

Webpack

使用插件 DefinePlugin 將當(dāng)前環(huán)境指定為生產(chǎn)環(huán)境,警告將在 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 設(shè)置為 "production",然后運(yùn)行打包命令。Vue 會(huì)自動(dòng)應(yīng)用 envify 并讓警告塊不能運(yùn)行。例如:

NODE_ENV=production browserify -e main.js | uglifyjs -c -m > build.js

應(yīng)用示例

Vue.js Hackernews Clone 這個(gè)應(yīng)用示例使用 Webpack + vue-loader 組織代碼,使用 vue-router 作為路由器,HackerNews 官方的 Firebase API 作為后端。它當(dāng)然不是大應(yīng)用,但是它綜合演示了本頁討論的概念。

上一篇:Class 與 Style 綁定下一篇:概述