构建大型应用程序

最新: 使用 vue-cli 在几分钟内启动并运行单文件 Vue 组件、热重载、保存时 lint 和单元测试!

Vue.js 核心库旨在专注且灵活 - 它只是一个视图层库,不强制执行任何应用程序级架构。虽然这对于与现有项目集成非常有用,但对于那些没有经验的人来说,从头开始构建更大规模的应用程序可能是一个挑战。

Vue.js 生态系统提供了一套工具和库,用于使用 Vue 构建大型 SPA。这部分是我们开始变得有点“框架化”的地方,但它实际上只是一份有见地的推荐列表;你仍然可以选择在堆栈的每个部分使用什么。

模块化

对于大型项目,有必要使用模块化的构建系统来更好地组织代码。推荐的做法是在 CommonJS 或 ES6 模块中编写源代码,并使用 WebpackBrowserify 将它们捆绑在一起。

Webpack 和 Browserify 不仅仅是模块捆绑器。它们都提供源代码转换 API,允许你使用其他预处理器转换源代码。例如,你可以使用 babel-loaderbabelify 使用未来的 ES2015/2016 语法编写代码。

如果你以前从未使用过它们,我强烈建议你学习一些教程,熟悉模块捆绑器的概念,并开始使用最新的 ECMAScript 功能编写 JavaScript。

单文件组件

在一个典型的 Vue.js 项目中,我们将把我们的界面划分为许多小的组件,并且最好让每个组件将它的 CSS 样式、模板和 JavaScript 定义封装在同一个地方。如上所述,当使用 Webpack 或 Browserify 时,通过适当的源代码转换,我们可以这样编写组件

如果你喜欢预处理器,你甚至可以这样做

你可以使用 Webpack + vue-loader 或 Browserify + vueify 构建这些单文件 Vue 组件。你也可以在 Webpackbin.com 上在线试用它!

选择哪个构建工具很大程度上取决于你的经验和需求。基于 Webpack 的设置提供了更强大的功能,例如代码拆分、将静态资产处理为模块依赖项以及将组件 CSS 提取到单独的文件中,但配置起来可能稍微复杂一些。在不需要 Webpack 提供的更高级功能的情况下,Browserify 的设置可能更容易。

使用官方的 vue-cli 是启动并运行预配置构建设置的最快方法。你也可以在 GitHub 上找到官方的脚手架模板

路由

对于单页应用程序,建议使用 官方的 vue-router 库,该库目前处于技术预览阶段。有关更多详细信息,请参阅 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'
}
})
// Switching pages in your route handler:
app.currentView = 'page1'

使用这种机制,也很容易利用外部路由库,例如 Page.jsDirector

与服务器通信

所有 Vue 实例都可以使用 JSON.stringify() 直接序列化其原始 $data,无需任何额外操作。社区贡献了 vue-resource 插件,它提供了一种使用 RESTful API 的简便方法。你也可以使用任何你喜欢的 Ajax 库,例如 $.ajaxSuperAgent。Vue.js 也能很好地与无后端服务(如 Firebase、Parse 和 Hoodie)配合使用。

状态管理

在大型应用程序中,状态管理通常会变得很复杂,因为多个状态片段分散在许多组件中,以及它们之间的交互。通常会忽略的是,Vue.js 应用程序中的真相来源是原始数据对象 - Vue 实例只是代理对其的访问。因此,如果你有一个应该由多个实例共享的状态片段,你应该避免重复它。相反,通过身份共享它

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

现在,每当 sourceOfTruth 被修改时,vmAvmB 都会自动更新它们的视图。进一步扩展这个想法,我们将得到 存储模式

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
}
})

请注意,我们将所有修改存储状态的操作都放在存储本身中。这种集中式状态管理使它更容易理解可能发生在状态上的哪种类型的修改,以及它们是如何触发的。每个组件仍然可以拥有和管理其私有状态。

State Management

需要注意的一点是,你永远不应该在操作中替换原始状态对象 - 组件和存储需要共享对同一个对象的引用,以便观察到修改。

如果我们强制执行一个约定,即组件永远不允许直接修改属于存储的状态,而应该改为分派事件来通知存储执行操作,那么我们实际上就得到了 Flux 架构。这种约定的好处是,我们可以记录发生在存储上的所有状态修改,最重要的是,我们可以实现高级调试助手,例如修改日志、快照、历史记录重播等。

Flux 架构通常用于 React 应用程序,但它也可以应用于 Vue.js 应用程序。例如,Vuex 是一种受 Flux 启发的应用程序架构,专门设计用于管理大型 Vue.js 应用程序中的状态。Redux 是 React 最流行的 Flux 实现,它是视图层无关的,也可以通过一些 简单的绑定 很容易与 Vue 配合使用。

单元测试

任何与基于模块的构建系统兼容的东西都可以。建议使用 Karma 测试运行器。它有很多社区插件,包括对 WebpackBrowserify 的支持。有关详细设置,请参阅每个项目的相应文档。

在测试的代码结构方面,最佳实践是在组件模块中导出原始选项/函数。考虑这个例子

// 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')
}
})

你可以像这样测试该模块

// Some Jasmine 2.0 tests
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!')
})
})

WebpackBrowserify 的示例 Karma 配置。

由于 Vue.js 指令异步执行更新,因此当你断言更改数据后的 DOM 状态时,你必须在 Vue.nextTick 回调中执行此操作。

部署到生产环境

Vue.js 的最小化独立版本已经为你剥离了所有警告,以减小文件大小,但是当你使用 Browserify 或 Webpack 等工具构建 Vue.js 应用程序时,你需要一些额外的配置来实现这一点。

Webpack

使用 Webpack 的 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 转换应用于自身,并使警告块无法访问。例如

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

一个应用程序示例

Vue.js Hackernews 克隆 是一个示例应用程序,它使用 Webpack + vue-loader 进行代码组织,使用 vue-router 进行路由,并使用 HackerNews 的官方 Firebase API 作为后端。它绝不是一个大型应用程序,但它演示了本页讨论的概念的组合使用。