目录结构
总览javascript
-
api 目录用于存放 api 请求,文件名与模型名称基本一致,文件名使用小驼峰,方法名称与后端 restful 控制器一致.php
-
-
enums 目录存放 常量,与后端的常量目录对应css
-
- icons 目录用于存放图标,element-ui 提供的图标实在是太少啦。因此我一般会使用 阿里的 iconfont
- lang 目录存放多语言
- layouts 目录存放布局
-
- 上面展现的是一个后台系统,empty 为一个空布局。用于登陆页面,其余页面则使用 default 布局。布局不须要过多介绍,写过 laravel blade 都很熟悉了。这里的布局须要和 vue-router 配合使用
-
- mixins 相似 php 的 trait, 可是它更强大,完整贴合 vue 组件的生命周期
-
plugins 目录存放插件配置,好比 axios,vue-lazy 等 (这是从 nuxt 中学到的概念)html
-
- router 目录存放与 前端路由相关的配置,整体来讲相似于 laravel 的 api 层
-
store 目录即 vuex 的目录,相似于前端的 model. 其文件与后端 model 相匹配,采用小驼峰命名前端
-
- utils 目录存放辅助函数
- views 为业务视图层,相信后端同窗也很熟悉。其由 vue-router 直接调度
- main.js 为 app 的入口,相似于后端的 index.php
- components 目录,存放组件。一般是一些可复用的组件会单独存放在该目录
整体来讲,已后端的 mvc 思想来看现代的前端项目是很是的天然的。后端的 model 对应前端的 store, 后端的 router 对应前端的 router, 后端的 controller + views 对应前端的 views.vue
基础规范
就目前来讲 vue 项目不多用到 class, 所以 .js 文件一般都是一个 module, 因此文件名使用小驼峰的形式命名。若是有类文件,则类文件使用大驼峰的形式命名.java
.vue 文件 可使用 中划线和大驼峰两种命名方式,参考了 element/iview/nuxt 项目以后,推荐统一使用中划线命名.react
全部的文件夹名称统一使用中划线命名ios
引入 vue 组件时文件时须要转换成大驼峰
import 'TestTest' from '@/components/test-test'
laravel在 template 使用时依旧使用中划线
<test-test />
其余规范如变量命名和使用规范 使用 eslint 的 standard 便可很好的解决.
前端存在不少的事件 如 change/input/upload/sumit 等等,相应的处理推荐使用 handle + 事件名称,如 handleChange
生命周期
vue-router 解析当前用户键入的 url, 而后匹配合适的视图组件加载.
着重介绍一下 我对 views 目录下的视图组件的理解,已修改地址为例
script 部分既控制器部分,其请求数据,而后注入到 view 中,就像后端的 mvc 同样。只不过 vue 将 vc 其写入到了一个文件中。这样理解对于写事后端的同窗显得更加的天然
控制器如何获取数据?
在过去的 vue 项目中,咱们可能会见到这样的写法
// ... views/address/edit.vue created () { axios.get('/addresses/1') .then((response) => { this.list = resposne.data }) } //...
这种写法无异于后端在控制器中写 sql 语句同样,在工程化实践中不推荐这么作,后端经过 model 来获取数据会更加的优雅天然。在 vue 项目中,model 既 vuex, 所以推荐这么作
若是你对我说的东西一脸懵逼,那么你能够看一下 vuex 的文档。我如今作的就是用后端熟悉的概念,来描述前端项目的最佳实践
// ... views/address/edit.vue 控制器+视图 computed: { address: () => this.$store.address.itemBy[1] // 从store模型中取出咱们想要数据 } // ...
// ... store/modules/address.js 数据源 export default { state: { itemBy: {} }, actions: { ... }, mutations: { ... } } // ...
store 中的数据从哪里来?
数据固然是从后端的数据库中获取,咱们不能让前端直接访问咱们的数据库,所以咱们会提供 api 让前端访问.store 中存在一个发起 api 请求的地方,既 action.
// ... store/modules/address.js 模型 export default { state: { itemBy: {} }, actions: { async fetchItem({ commit, state }, { id }) { // 对axios和api进行了简单的封装,使api请求更加语义化 cosnt { data } = await address.show(id) // action只能经过提交commit来修改state,具体缘由请查看vuex文档 (其实我也忘了为啥 (╯﹏╰)) commit('SET_ITEM', data) } }, mutations: { SET_ITEM: (state, item) => { state.itemBy[item.id] = item } } } // ...
这样咱们的模型中就有数据啦
何时去调用 fetchItem
去请求后端呢?
https://router.vuejs.org/zh/guide/advanced/data-fetching.html vue-router 文档的解答
这里不推荐在 vue 原始的生命周期中去调用初始化请求,可能会带来 数据尚未获取到,template 却已经被渲染。会形成一些数据不存在的异常,推荐在 vue-router 的生命周期中去请求数据
// ... views/address/edit.vue async beforeRouteEnter (to, from, next) { // 等待模型数据加载完毕,才继续进行vue组件的生命周期 await store.dispatch('fetchItem', to.params.id) next() } created () { // 不推荐在这里调用 fetchItem } //...
到这里你可能发现,这和你平时写的 vue 有些不同,没有相似 this.data = response.data
这种操做。相似这种操做其实相似赋值操做,或者称为反作用,其引入了时间的概念,使数据的管理变的复杂。直观的体现就是咱们可能会有这种多余的 if(data)
判断.
固然反作用是难以免的,可是咱们能够统一的管理他们。相似上面的代码就是一套我以为还不错的方法。从 view 的角度看,数据是固有存在存在的,其不须要关心是不是否已经被加载完毕,且 store 中的不可被 view 修改,既数据只能单向流动
在 store 中统一管理数据的另一个好处就是方便持久化
view 层如何修改数据源?
上面的描述实际上表达了一种 发布与订阅的模式,从 store 到 view 的数据流是严格单向数据流动.
view 层不容许直接修改 store 中的数据,可是 view 层却能够经过发送 action 来影响数据源.
好比初始化时的 dispatch 的 action, 各类 event 触发的 dispatch. 当数据源发生改变时,做为订阅者的 view 层会很是天然的从新渲染.
这种设计和父子组件相似,vue 中子组件不容许直接修改父组件 props 到子组件的数据,只能经过向父组件 emit event. 在 view 和 store 之间,这种设计依然合理.
这也意味着应用中全部的数据都遵循相同的生命周期,这样可让应用变得更加可预测且容易理解。
上面的图很好的阐述了这种开发模式。引自 https://github.com/sorrycc/blog/issues/1
view 层再深刻
view 层的 script 部分,除了充当了传统的 controller, 起到初始化的做用外,实际上还作了更多的事情.
先从 data 部分提及,在 view 层会有一些状态须要记录,如 菜单的展开或收起,弹窗的弹出与关闭。对于这样的状态的管理,一种作法就是将存储在 data 部分.
也有人将全部的状态 也放在 store 中的 state 维护。既 state 分为状态和数据 两种类型.
view 层的 script 更重要的部分,是其到了一个交互反馈的做用,既相似下面的代码
<template> <button @click="handleSubmit"/> </template> <script> export default { data: {}, methods: { handleSubmit() { } } } </script>
关于 css 部分,因为我的不了解 css, 也不清楚 css 的业界规范及在 vue 上的最佳实践,所以不作过多介绍.
总结一下
在前面的介绍中,store 和 api 目录是和数据挂钩的,当数据库定下来,这一部分也就定了下来.
views/components 和业务 (ui) 挂钩,须要等设计稿肯定后,这一部分才能肯定下来.
PS 设计稿的图层一般就是组件的拆分规范?
使用 vuex 存储数据的另外一个好处就是能够无缝的切换到 ssr 框架 nuxt
views 和 store 之间是一种订阅和发布的模式.
两个问题
store 中的 state 应该如何组织? 对于 api 请求,咱们常常会看到这样的 json 数据
// post { id: 1, title: xxxx, content: xxxx, user: { id: xxx, nickname: xxx, avatar: xxx, }, comments: [ { id: xxx, user_id: xxx, content: xxx, user: { // ... } }, { //.... } ] }
上面的数据结构复杂,嵌套深刻。若是咱们将他们一股脑的存在 post 的 state 中,会形成数据过于集中,冗余. comments 没法独立化更新等等问题。使得前端 scheme/orm, 数据组织的规范化变的迫切须要
可是 vue 在这方面没有很好的规范和最佳实践. react 在这方面比较不错的实践 https://github.com/paularmstrong/normalizr
如何设计良好规范的 compoents?
组件的设计在业务层很是的重要,在下一篇我会介绍一下我总结出的一些实践