新: 使用脚手架工具 vue-cli 可以快速地构建项目:单文件 Vue 组件,热加载,保存时检查代码,单元测试等。
Vue.js 的设计思想是专注与灵活——它只是一个界面库,不强制使用哪个架构。它能很好地与已有项目整合,不过对于经验欠缺的开发者,从头开始构建大型应用可能是一个挑战。
Vue.js 生态系统提供了一系列的工具与库,用于构建大型单页应用。这些部分会感觉开始更像一个『框架』,但是它们本质上只是一套推荐的技术栈而已 - 你依然可以对各个部分进行选择和替换。
模块化
对于大型项目,为了更好地管理代码使用模块构建系统非常必要。推荐代码使用 CommonJS 或 ES6 模块,然后使用 Webpack 或 Browserify 打包。
Webpack 和 Browserify 不只是模块打包器。两者都提供了源码转换 API,通过它可以用其它预处理器转换源码。例如,借助 babel-loader 或 babelify 代码可以使用 ES2015/2016 语法。
如果你之前没有用过它们,我强烈推荐你阅读一些教程,了解模块打包器,然后使用最新的 ECMAScript 特性写 JavaScript。
单文件组件
在典型的 Vue.js 项目中,我们会把界面拆分为多个小组件,每个组件在同一地方封装它的 CSS 样式,模板和 JavaScript 定义,这么做比较好。如上所述,使用 Webpack 或 Browserify 以及合适的源码转换器,我们可以这样写组件:
如果你喜欢预处理器,甚至可以这么做:
你可以使用 Webpack + vue-loader 或 Browserify + vueify 构建这些单文件 Vue 组件。推荐使用 Webpack,因为它的加载器 API 提供更好的文件依赖追踪/缓存以及一些 Browserify 没有的转换功能。
最快的构建方式是使用官方出品的脚手架工具 vue-cli。你也可以在 GitHub 上查看官方的构建模板:
- Webpack + vue-loader
- Browserify + vueify
路由
对于单页应用,推荐使用官方库 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 或 Director。
与服务器通信
Vue 实例的原始数据 $data
能直接用 JSON.stringify()
序列化。社区贡献了一个插件 vue-resource,提供一种容易的方式与 RESTful APIs 配合。也可以使用任何自己喜欢的 Ajax 库,如 $.ajax
或 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 架构。此约定的好处是,我们能记录 store 所有的状态变化,并且在此之上实现高级的调试帮助函数,如修改日志,快照,历史回滚等。
Flux 架构常用于 React 应用中,但它的核心理念也可以适用于 Vue.js 应用。比如 Vuex 就是一个借鉴于 Flux,但是专门为 Vue.js 所设计的状态管理方案。React 生态圈中最流行的 Flux 实现 Redux 也可以通过简单的绑定和 Vue 一起使用。
单元测试
任何支持模块构建系统的单元测试工具都可以。推荐使用 Karma。它有许多插件,支持 Webpack 和 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!')
})
})
Karma 的示例配置:Webpack, Browserify。
因为 Vue.js 指令是异步更新,如果想在修改数据之后修改 DOM ,应当在 `Vue.nextTick` 的回调中操作。