Vue 2.0 已经正式发布好长时间了。想找一 个 Vue.js、Vue Router 和 Vuex 三件套的实战项目来练手,上 gitHub 搜索了一下搜到一大堆,不过基本上都不是使用单文件组件开发的,更不用说基于 Vue.js 全家桶了。自己动手,丰衣足食。既然做了好几年电商项目,做个电商 App 吧。本项目基于 vue-cli
脚手架搭建,响应式页面布局,真实 的 restful web api 后端数据:就是一个 Vue 全栈开发项目。
这是最终的效果图:
一、后端服务
采用的 是 Express + mLab + Mongoose 的技术栈。http
模块加上 Express 自带路由利用中间件思想可以很方便地基于 Node 开发提 供 restful web api 的 Web 应用。考虑到方便在线演示部署,数据库选用了 MongoDB 的云服务 Atla。当然,提交数据到数据库时一般要选用便捷操作的对象模型工具(ORM)。在 node.js 和 MongoDB 数据库环境下,Mongoose 作为就是不二之选了。具体实现请看 shop-api。另外,我将 node.js 代码免费托管到 了Render(免费的,够用就好)。直接通过浏览器访问商品列表服务地址 就可以返回商品列表所需要的数据了,当然更好的选择是使用 Postman 之类的 HTTP 请求调试工具。
如果我们不了解后端 和 mLab,只要在 ations.js
手动将 axios
的 baseUR
改为 在 Render 托管的服务地址就可以了。或者对 Atla 数据 API 足够了解,也可以不写 Node.js 应用的。为了方便大家全面学习测试,建议大家最好访问本地的 restful 应用。
访问不同端口的本地服务或不同域名的远程服务都有跨域问题,使用 CORS
可以很好解决这个问题。
app.all('/*', function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-type,Accept,X-Access-Token,X-Key');
if (req.method == 'OPTIONS') {
res.status(200).end();
} else {
next();
}
});
好了,接下来集中精力开发前端。
二、前端
- 运 行 vue-cli + webpack template 就得到了一 个 Vue.js、Vuex 和 vue-route 全家桶脚手架项目。我们要完成两个部分:前台展示和后台管理。
- 前台展示有两个功能:展示商品列表和详情的功能、添加商品到购物车的功能。商品列表和详情页面都能添加商品到购物车,商品列表页只能通过点击按钮一次添加一件商品,但详情页面可以通过
+/-
按钮或文本框输入来改变商品数量(不能大于库存),再通过点击添加按钮一次性添加多件商品。购物车列表页也可以可以通过+/-
按钮或文本框输入来改变商品数量,与详情页面操作不一样的是购物车列表页改变商品数量时会同步保存数据,即使重新刷新页面或关闭浏览器,都会显示已保存的数据。而但详情页面如果改变数量后没有按添加按钮就不会添加商品到购物车。 - 后台管理有的功能就是实现商品及品牌的 CRUD 操作。在新增修改商品时,会通过下拉框选择品牌,而下拉框用品牌数据来填充的。因此商品数据是依赖品牌数据的,因此后台管理时先应该添加商品数据。
- 我们来说一说具体实现。单页面应用(SPA)一般都有路由,复杂 Vue.js 应用页面之间的导航都要用 到 vue-route。该插件使用 JavaScript 动态初始化配置全局对象,将组件(
components
)映射到路由(routes
),然后告诉 vue-route 在哪里渲染它们。通过注入路由器,我们还可以在任何组件内通 过this.$router
访问路由器,也可以通 过this.$route
访问当前路由。比如说当添加或修改商品成功后我们就是通 过this.$router
回到商品列表页的。模板中的组件让用户在具有路由功能的应用中(点击)导航,其中to
表示目标路由的链接,这个值可以是一个字符串或者是描述目标位置的对象,对应路由配置对象的path
属性值或name
对象。比如说App
模板中的组件to
属性设置为{name: 'Home'}
,通过路由映射会导航到“Home 组件所在的页面。建议设置to
的属性值为name
对象,因为这个对象一般很少改动,而首页链接设置成 /,同样是因为这个值很少改动。另外首页链接还得添加exact
属性来精确匹配链接,大家都知道,其它path
值肯定包含/
,不设置该属性会造成导航链接样式都添加激活链接类名。 - 如下面的文件路径树所示,本项目使用了大量的嵌套单文件组件,下面的项目
components
和pages
文件夹包含了大量的单文件组件。嵌套单文件组件提高了代码复用性,让分模块团队开发变得容易,这对于开发大型 Vue.js 应用尤为重要。不过 Vue 组件使用单向数据流向下进行组件数据通信,大量的会让代码难以维护,此时我们就应该考虑用 Vuex 来实现多个组件共享状态。
如下面的文件路径树所示,本项目 Vuex 代码集中 store
文件夹中。Vuex 应用的核 心 store(仓库),包含应用中大部分的状态,如商品、品牌、购物车数组, 我们是在 index.js
中定义的; Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,可以包含任意异步操作,如对后台数据访问商品和品牌数据的异步操作,我们是在 action.js
中定义的;Vuex 中的 Mutation 非常类似于事件:每个 Mutation 都有一个字符串的事件类型(type
) 和一个回调函数(handler
)。如对异步回调函数和添加商品到购物车时的同步函数,我们是在 mutations.js
中定义的;Vuex 允许定义 getter
从 store
中的 state 中派生出一些状态(计算属性),如从商品和品牌数组状态中派生出根据 id
查询到的状态,我们是在 getters.js
中定义的;另外,我们的 mutation-types.js
中使用常量替代 Mutation 事件类型以方便多人协作。
─store
│ actions.js
│ getters.js
│ index.js
│ mutation-types.js
│ mutations.js
在任意组件中访问 store 的计算属性时可使用 mapState
和 mapGetters
辅助函数,分 发 Action、Mutation 时使用 mapAction
、 mapMutation
辅助函数。它们本质是都是语法糖,将 store
中 的 state、getter
映射到局部计算属性,或将 mutations
和 actions
映射到局部方法,从而避免使用 this.$store
的方式来访问相应的函数。当然,并不是所有情况下都能使用辅助函数,如 created
生命周期钩子函数中或条件语句中。
注意:使用 Vuex 并不意味着我们需要将所有的状态放 入 Vuex。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。我们应该根据应用开发需要进行权衡和确定。如CartControl
组件中的count
状态和每个cart
组件绑定不能单例化,因此不需要用放入 Vuex 来管理。
- 如下面的文件路径树所示,本项目
plugins
文件夹中有两个插件。插件会为 Vue.js 添加全局功能,本质上就是封装更好,复用性更强的组件,Vuex 和 vue-route 就是这样两个优秀的插件。如果我们想在任意组件中弹出模态框和信息提示、控制加载动画等,怎么办?每个组件都加一个布尔控制变量肯定导致代码冗余难以维护。用 Vuex 集中在根组件中处理也好不到哪里去,因为要写很多代码来判断事件类型。所以我们得自己开发插件,感兴趣的话可参考vue-dialog
和vue-toast
的代码自行开发一个 CSS Loading 插件,那样就可以在任意组件中控制加载动画了。
─plugins
│─vue-dialog
│ index.js
│ VueDialog.vue
│─themes
│ default.css
│ ios.css
│─vue-toast
│ index.js
├─components
│ Notification.vue
│ VueToast.vue
- 当我们 build 好代码,在 IE 或手机自带的 Android 浏览器上运行时,发现添加商品到购物车的功能用不了。
报错:Vuex requires a Promise polyfill in this browser。
原来 Vuex 依 赖 Promise。如果浏览器并没有实现 Promise,那么可以使用一个 polyfill 的库,例如 babel-polyfill
。
第一步: 安装
npm install --save babel-polyfill
第二步:在 webpack.base.conf.js
文件中,entry
的配置如下:
entry: {
app: ['babel-polyfill', './src/main.js']
}
或者将下列代码添加到使用 Vuex 之前的一个地方:
import 'babel-polyfill'
三、有待实现和改进功能
- 登录注册模块
- 会员模块
- 支付模块
- 列表搜索排序功能
- 图片列表的懒加载
- 轮播图效果
- 加入购物车动画
- 页面切换动画
Vue.js devtools 是一个很好的测试 Vue.js 应用的工具,对 Vuex 也支持得很好,值得拥有。
评论 (0)