Giter Site home page Giter Site logo

zhanghaibin0712 / vue-demo Goto Github PK

View Code? Open in Web Editor NEW

This project forked from kenberkeley/vue-demo

0.0 2.0 0.0 460 KB

Vue.js 示例项目 · 简易留言板。本项目拥有完善的文档说明与注释,让您快速上手 Vue.js 开发 SPA。Webpack / ES6 + Babel / Vue Router / Vue Resource / (Vue Validator ?) / (Vuex ?) —— An Excellent Vue Starter with Best Practice / 最佳实践

JavaScript 47.21% Vue 43.08% HTML 9.71%

vue-demo's Introduction

Vue 示例项目 · 简易留言板

###写在前面
这年头的 Demo,不是 Todo 就是 Counter,其余基本就是要多复杂有多复杂
本 Demo 文档完善,注释丰满(咦?),架构精良,更重要的是简单有木有!!!
如果您把玩几下后发现还行,希望您可以点颗 star
如果您有任何意见或建议,请 issue,我会尽快解决与完善 ^.^

###更新 2016/8/6   新增表单验证分支,详情请切换到 vue-validator 分支查看:git checkout vue-validator
2016/8/7   将服务挂载到顶级变量中实现即可实现可视化追踪
2016/8/15   使用 definePlugin 引入 __DEV____PROD__ 全局环境变量
2016/8/18   引入 copyWebpackPluginstatic/ 复制到 dist/
2016/8/28   引入 cross-env 解决跨平台问题,新增优化项 DedupePlugin
2016/9/10   将全局布局从 src/index.html 移到根组件

应用截图

目录


详情可参阅 package.json

  • Vue 1.0.18
  • Vue Router
  • Ajax 请求库(Vue Resource / Superagent / jQuery-Ajax / ...)
  • Webpack
  • ES6 + Babel
  • jQuery + BootStrap (UI)

在开始前,希望您已通读如下资料

同时您还需要熟悉 ES6,详情参考阮老师的教程

推荐升级到 node 5.x + npm 3.x 环境
推荐使用 cnpm 或手动切换到淘宝 npm 源
npm set registry https://registry.npm.taobao.org/

本示例项目需要结合 简易留言板 RESTful API
模拟前后端分离开发(还为了与 React Demo 共用)
请分别 git clone,打开两个命令窗口( Windows 下推荐使用 Cygwin分别切换到两者的目录下
分别敲下 npm install 安装依赖(为避免 Windows 下的 npm 软链接问题,可加上 --no-bin-link 完全解构所有依赖)

虽然我们已经切换到了淘宝 npm 源,但安装 [email protected] 的时候还是很有可能卡住
因为它的安装需要从 Github 的 AWS 服务器拉取二进制文件,因此您可以为它指定源:
npm i [email protected] --registry=https://registry.npm.taobao.org

如果您想简单粗暴一点,这里还提供了 node_modules.zip,直接解压即可

最后需要全局安装跨平台环境配置器 npm i cross-env -g

先后在 msg-board-apivue-demo 的命令窗口下,敲下 npm start
如无意外,默认浏览器就会自动打开 localhost:8080,您立即可以看到效果
若浏览器没有自动弹出,则请自行手动访问

开发过程中,通过 Webpack 处理的静态资源都由基于内存的 webpack-dev-server 提供
P.S. 如果您还不清楚如何安装与启动,请看这个 issue


.
├── build/               # Webpack 配置目录
├── dist/                # build 生成的生产环境下的项目
├── src/                 # 源码目录(开发都在这里进行)
│   ├── assets/            # 放置需要经由 Webpack 处理的静态文件
│   ├── components/        # 组件(COMPONENT)
│   ├── directives/        # 指令
│   ├── filters/           # 过滤器
│   ├── routes/            # 路由
│   ├── services/          # 服务(SERVICE,用于统一管理 XHR 请求)
│   ├── views/             # 路由视图基页(VIEW)
│   ├── app.js             # 启动文件
│   ├── index.html         # 静态基页
├── static/              # 放置无需经由 Webpack 处理的静态文件
├── .babelrc             # Babel 转码配置
├── .eslintignore        # (配置)ESLint 检查中需忽略的文件(夹)
├── .eslintrc            # ESLint 配置
├── .gitignore           # (配置)需被 Git 忽略的文件(夹)
├── package.json         # (这个就不用多解释了吧)

您可以根据业务需求改动目录结构。最常见的例子就是在 src/ 新建一个 utils/ 用于存放工具函数
(若 utils/ 目录使用频繁,建议配置 路径别名

  • 本示例项目秉承最佳实践,高度洁癖地实现代码分离/复用
  • 优化目录结构,更好的模块分离
  • 引入服务层(详细内容在下面的深入设计·引入服务层
  • 引入 路径别名(也就是上面的 COMPONENT / SERVICE / VIEW

Vue 参照 Flux / Redux,实现出 Vuex,专注于应用层级的状态管理。 本示例项目并没有引入 Vuex ,主要是基于以下考量:

如果您看过文档中的简易教程 · 计数器,您应该会觉得 Vuex 把简单问题复杂化了(的确如此)。为了实现全局状态管理,把原本简单纯粹的双向绑定以及操作方法都剥离开来,抽象成 Vuex 的 state / mutation / action。对于绝大部分中小型项目而言,这显然是过度设计,牛刀杀鸡。

对于这个问题,作者尤雨溪有如下评述

   当你的应用还很简单的时候,你多半并不需要 Vuex。也不建议过早地使用 Vuex。但如果你正在构建一个中型以上规模的 SPA,你很有可能已经需要思考应该如何更好地归纳 Vue 之外,应用的其他组成部分。这就是 Vuex 要大显身手的时刻。
   我们在单独使用 Vue.js 的时候,通常会把状态储存在组件的内部。也就是说,每一个组件都拥有当前应用状态的一部分,整个应用的状态是分散在各个角落的。然而我们经常会需要把状态的一部分共享给多个组件。一个常见的解决策略为:使用定制的事件系统,让一个组件把一些状态“发送”到其他组件中。这种模式的问题在于,大型组件树中的事件流会很快变得非常繁杂,并且调试时很难去找出究竟哪错了。
   为了更好的解决在大型应用中状态的共用问题,我们需要对组件的 组件本地状态(component local state) 和 应用层级状态(application level state) 进行区分。应用级的状态不属于任何特定的组件,但每一个组件仍然可以监视(Observe)其变化从而响应式地更新 DOM。通过汇总应用的状态管理于一处,我们就不必到处传递事件。因为任何牵扯到一个以上组件的逻辑,都应该写在这里。此外,这样做也能让我们更容易地记录并观察状态的变更(Mutation,原意为突变),甚至可以实现出华丽如时光旅行一般的调试效果。

React 作为一个 View 层,不具备数据的双向绑定能力,其数据流是单向的。既然是单向数据流,那么将整个应用状态汇于一处集中管理(这就是传统意义上的 Model 层,只是改名为 Store 层罢了),抽离出操作方法等(Controller 层,在此为 Action 与 Reducer 层),也是自然而然的。这是基于大型项目协作开发中,前人踩坑后的最佳经验总结,同时也是当前前端 MVC 的最佳实践。

但 Vue 乃轻量级的 MVVM 框架,若完全照搬相对抽象的 React + Flux / Redux 架构,未免有点舍本逐末了。私认为,对于一个使用 MVVM 模式构建的单页应用而言,需要置于 store 层的,仅限于全局通用状态持久的数据(例如用户认证信息)。若把所有应用数据(尤指一些实时性较高的数据以及非共享的数据)都糅合在一处,那就像是把所有变量都挂载到全局。

结合作者尤雨溪的说法,Vue 状态管理的最佳实践应为:组件自包含状态数据(组件本地状态),全局持久性通用数据(应用层级状态)集中管理

可是,全局通用状态持久的数据占极少数,若为此而引入 Vuex,实在是太不值得了。这个时候,就需要借鉴 AngularJS 的一些经验实践来实现 Vuex 的功能。

您无需有 Angular 的开发经验,因为 Vue 本身就是 Angular 的优雅简化版

在 Angular 中,组件间的数据传递一般是使用服务(Service),有时也会使用事件传递。若是全局通用(包括模板中)需要用到的状态数据,就挂载到 $rootScope 上。参照上述实践,我们让 Vue 的根组件 App(位于 src/components/App.vue)充当 $rootScope,直接把全局通用的数据挂载到根组件的 data 属性上。这样一来,在子组件中直接使用 this.$root 即可访问。同样地,全局单例的 Service 也可存储数据,也是直接挂载到其 data 属性即可。

例如,本示例中,根组件 AppuserService.data 均存储着用户的 session
(二者是手动同步的,详情请看 src/components/Navbar/ 下的 LoginForm.vue / LogoutDropDown.vue
App 的仅能在组件内部通过 this.$root.userData 访问
userService 的则可以在任何地方访问(见 src/routes/index.js
仅需要 import userService from 'SERVICE/userService' 即可

虽说 Service 可以存储数据,但这并不是它主要的功能。皆因开发过程中,使用 Vue DevTools 是无法直接查看 Service 中挂载的数据,亦即 Service 中的数据具有一定的不可追踪性。因此,对于那些状态毋须持久的数据(例如表单项),请直接存储在组件内部(data)。 在组件间的传递这些数据,也可以直接使用事件传递 dispatch 或父子组件间的 props 即可。

实际上可以在根组件 App 中的 data 属性实现追踪,举例如下:
data: { userData: null, userService }
(但本项目中 this.$root.userDatauserService.data 一直保持手动同步,故上述操作非必要)

Service 在 Angular 中还有一个很重要的作用,就是封装 Ajax 请求。

例如,小明和小刚分工合作一个项目,分别负责 Foo 与 Bar 模块
在 Foo 的控制器中,需要请求用户 session,于是小明自己在控制器中引入 $http 请求
在 Bar 的控制器中,又需要请求用户 session,于是小刚又自己写了一遍
这其实有点像在后端开发中,将 M 层的代码直接写在 C 层,导致代码冗余揉杂,复用率低
因此在 Angular 中,在控制器中实现 Ajax 请求是反模式,应当把这部分封装成 Service 以便复用

在本示例项目中,Service 层的主要功能是:封装好与后端 API 一一对应的函数。

例如,后端用于用户登录的API为 /login
那么前端 services/userService.js 这个服务类中就对应存在一个名为 login 的函数
只需要调用 userService.login({ 用户帐号密码 }) 即可实现请求

引入服务层的主要作用就是为了轻量化组件,统一管理 XHR 请求,提高代码复用,方便 mock,避免在组件中分别实现请求而导致管理上的混乱(对日后的重构也不友好)。而且,前端的服务与后端的 API 一一对应,在理解上也会变得更加容易。

更重要的是,服务层与 Vue 技术栈并没有太大关系(前提是您没有用 Vue Resource)。
以后前端改用其他技术栈(React 等)时,服务层可直接复制过去,毋须改动任何代码。

React Demo 就是直接复制本示例项目的 services/ 目录

您可能会觉得,这是要把 Vue 当 Angular 使的节奏,的确如此。不管黑猫白猫,能捉到老鼠的就是好猫。
同样,Angular 虽被喷复杂,但其经验确实有助于大型项目的开发与维护。故汲取其精髓,何乐而不为?

本示例项目封装出统一的 xhr 函数提供 Ajax 请求:

// 详见 src/services/xhr/
const xhr = ({ url, body = null, method = 'get' }) => {
  return <then>
}

服务层直接引入该 xhr 函数即可
毋须理会底层封装的是什么库

例如,一开始用 jQuery 的 $.ajax
之后项目升级,已经不需要 jQuery 了,改用 Superagent
那么也就是用 Superagent 重新实现 xhr 函数即可
服务层不需要更改任何代码
可以把 xhr 理解成是一个 接口


由于已经拥有相对成熟的 Webpack 配置,因此在一定程度上您可以不求甚解,但了解其配置会更能把握整体开发

  • 前端开发服务器为 localhost:8080,可在 build/webpack.config.dev.js 中找到

后端 RESTful API 基地址写在了 src/services/xhr/config.js 中,请根据实际自行修改

  • 框架 / 类库 须单独打包。若您还需要引入别的 Package,则将其添加到 build/webpack.base.conf.js 中的 vendor

实际上该步骤可通过读取 package.jsondependencies 字段实现自动化,但其灵活度不够高,必要性也不大
P.S. 安装包时勿忘添加 --save

  • 路径别名 的定义位于 build/webpack.base.conf.js,好处就是引入与重构都很方便

例如,在某组件中,引入 userService 需要 import userService from '../../../services/userService'
但有了路径别名后,只需要 import userService from 'SERVICE/userService'
相比于 AngularJS 中的依赖注入,这种方式依赖于构建工具,显得更为简单

您可能会说,Webpack 只需要设定了 root属性为 src/
就可以 import userService from 'services/userService'
但在这里其实是会引起歧义的(不过这属于强迫症的范畴。。。)
例如,import createBrowserHistory from 'history/lib/createBrowserHistory'
您可能会觉得这是 src/history/lib/createBrowserHistory.js
但实际上 history 是一个 npm package
同样地,您又怎么知道 services 不是一个 npm package?
而且重构之后,文件夹的变动会导致相对路径的变化,services/ 目录未必仍在 src/
因此,路径别名相当有必要。其常量的形式,让人一看就知道不是一个 npm package

  • 开发全局变量,由 webpack.DefinePlugin 提供(详见 build/webpack.base.conf.js

默认有 __DEV____PROD__ 两个全局变量
若要继续添加,则还需要在 .eslintrcglobals 同步写入
由此即可根据当前运行环境执行对应的代码(例子见 src/app.js 中有关 Devtools 的配置)

在此需要提醒,在 package.json 中设置 NODE_ENV 要注意末尾空格的问题
最好就是使用前 trim 一下:process.env.NODE_ENV.trim()

拓展阅读:解读 UglifyJS
看看生产环境下编译 if (__PROD__) { ... } => if (true) { ... }UglifyJS 会如何处理

开发规范请参考 最佳实践
本示例项目的代码极尽详细地添加了注释,其中不乏最佳实践提示

vue-devtools Github 地址
注意:Vue 1.0.8+ 需要显式设置 Vue.config.devtools = true


请自行选择测试工具


vue-demo 的命令窗口下,敲下 npm run build,将会在项目根目录下生成 dist/

您可以使用命令行静态资源服务器 serve ( npm i serve -g ),敲下 serve -p [端口] dist 来快速查看 build 后的项目
还可以 cd dist 后,python -m SimpleHTTPServer [端口]php -S localhost:[端口] 快速便捷地实现静态资源服务器

关于生产环境下的部署与优化,已超出本文档的论述范围,请自行查阅相关资料


vue-demo's People

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.