我认为规范化的核心:广泛掌握,择优而选
vue安装:
npm install -g @vue/cli
vue create
最好在选择配置时 选择添加路由、vuex等
搭建好环境后的开始是开发公共组件,例如:统一消息提醒模式,统一按钮模式、统一图片上传模式等
项目目录的搭建上,我其实是有一些自己的想法的,因为我刚开始接触的是angular7,而angular7对模块化的拆分对我有很多影响,所以在我个人想法中有很多受其影响的部分,也不一定都是对的。再一点是基于我短浅的见识,曾‘有幸‘见过几个格外随意的vue2项目,所以产生了一些对规范化的执念。
下面的目录中,最主要的部分:(我的理解,并不一定好用)
1、将所有的部分,无论组件还是页面都作为文件夹命名,所有最后一层文件夹下都统一为index.vue,这样我们的所有引入都是引入到文件夹,做到统一
2、views目录基于视图:下面区分模块文件夹,每个模块文件夹中有pages和components两个子文件夹,将视图的主页面部分放入pages中,下面所有页面区分文件夹,文件夹下为Index.vue,而views下的components文件夹下是一个一个的此模块下组件。
3、public文件夹:存放公共组件、公共指令、公共管道、公共混入
4、文件命名:大驼峰,与开发者工具对应
├── README.md 项目介绍
├── index.html 入口页面
├── build 构建脚本目录
│ ├── build-server.js 运行本地构建服务器,可以访问构建后的页面
│ ├── build.js 生产环境构建脚本
│ ├── dev-client.js 开发服务器热重载脚本,主要用来实现开发阶段的页面自动刷新
│ ├── dev-server.js 运行本地开发服务器
│ ├── utils.js 构建相关工具方法
│ ├── webpack.base.conf.js wabpack基础配置
│ ├── webpack.dev.conf.js wabpack开发环境配置
│ └── webpack.prod.conf.js wabpack生产环境配置
├── config 项目配置
│ ├── dev.env.js 开发环境变量
│ ├── index.js 项目配置文件
│ ├── prod.env.js 生产环境变量
│ └── test.env.js 测试环境变量
├── package.json npm包配置文件,里面定义了项目的npm脚本,依赖包等信息
├── src 源码目录
│ ├── main.js 入口js文件
│ ├── app.vue 根组件
│ ├── public 公共组件目录
│ │ ├── components
├── ButtonProvide // 封装公共按钮组件
└── index.js
├── MessageTipProvide // 封装公共提示组件
└── index.js
│ │ ├── directives
└── index.js
│ │ ├── filters
└── index.js
│ │ ├── mixins
└── index.js
│ ├── assets 资源目录,这里的资源会被wabpack构建
│ │ └── images
│ │ └── logo.png
│ ├── routes 前端路由
│ │ └── index.js
│ ├── store 应用级数据(state)
│ │ ├── modules // 模块化
│ └── ModuleA
└──index.js
│ └── ModuleB
└──index.js
│ │ └── getters.js // 公共getters
│ │ └── index.js
│ └── views 页面目录(模块化)
│ ├── ModelA
│ ├── pages
│ ├── HelloAAA
│ └── index.vue
│ ├── HelloBBB
│ └── index.vue
│ └── components
│ ├── HelloCCC
│ └── index.vue
│ ├── HelloDDD
│ └── index.vue
│ ├── HelloEEE
│ └── index.vue
├── static 纯静态资源,不会被wabpack构建。
└── test 测试文件目录(unit&e2e)
└── unit 单元测试
├── index.js 入口脚本
├── karma.conf.js karma配置文件
└── specs 单测case目录
└── Hello.spec.js
${}
第1种:组件中
directives:{
// 函数形式为bind和update的合并写法,缺少了inserted
big(el,binding){
// el: 元素本身
// binding 传入值的集合
}
}
第2种:组件中
directives:{
abc:{
bind(){} // 指令与元素成功绑定时调用
inserted(){} // 指令所在元素插入到页面时
update(){} // 指令所在的模板被重新解析时
componentUpdated(){}// 指令所在组件的 VNode 及其子 VNode 全部更新后调用
unbind(){} // 解绑时调用
}
}
第3种:公共
Vue.directive('abc'(el, binding, vnode)=>{
// el: 元素本身
// binding 传入值的集合
// vnode:vue生成的虚拟节点
})
第4种:公共
Vue.directive('abc'{
bind(){}
inserted(){}
update(){}
})
自定义管道是我特别喜欢写的,但是我很不理解为什么vue3开始将管道剔除了,用computed和methods的使用来替代管道
{{msg | filterName('参数1','参数2')}}
filters:{
filterName(item) {
return item+10;
},
},
在这里插入代码片
Vue.filter('dataTime', (data) => {
if (!data) return '';
return moment(Number(data)).format(
"YYYY-MM-DD HH:mm:ss"
);
});
混入的使用如果不规范,将会使代码的维护变得困难。
父->子:
通常使用第一种
:aaa=”传的值“ props:{aaa:{}} prop中的数据采用结合风格指南中的例子:通常使用第二个。
另外props使用时,组件内时不允许修改值的,虽然检测方式是浅层检测,可以修改对象内的值,但是为了规范,制定不修改props的原则
ref方式 this.$refs.组件内定义好的属性
props: {
status: {
type: String,
required: true,
validator: function (value) {
return ['syncing'].indexOf(value) !== -1
}
default(){
return ""
}
}
}
props: {
status: {
type: String,
required: true,
default:""
}
}
子->父:
通常我们使用第二种方式:自定义事件
VueComponent.prototype.__proto__ ==Vue.prototype
将全局事件绑定到一个固定属性上( b u s ) , 在 所 有 组 件 中 就 都 可 以 找 到 bus),在所有组件中就都可以找到 bus),在所有组件中就都可以找到bus了,注意:销毁是必要的new Vue({
el:"#app",
render:h=>h(app),
// vm要在new Vue 创建后才有,但是创建完成后意味着代码执行完成,所以要放到new Vue() 的beforeCreate() 声明周期中,刚创建完成时调用
beforeCreate(){
Vue.prototype.$bus = this // 安装全局事件总线 $bus就是当前应用的vm
}
})`
this.$bus.$on() // 监听
this.$bus.$emit() // 调用
beforeDestroy(){ // 销毁绑定的事件
this.$bus.$off("名称")
}
消息的订阅与发布:需要安装插件 推荐:pubsub-js 使用publish
subscribe发布与订阅消息(当项目中很多处简单的传递时可以使用,如果少有需要的功能没有必要使用),另外:也需要组件销毁时解除绑定pubsub.unsubscribe(this.publd)
vuex (vue风格指南推荐,优先级优于全局事件总线/消息订阅与发布,但不优于路由传参)是否是多于两处需要使用此数据,并且此数据为全局的关键数据,存储的意义大不大
css3提供了优秀的动画写法,vue也提供了动画的标签与解决方案,说实话我也没搞懂两者到底使用哪一个会比较好?所以仅供参考,留待之后的学习(也像echart和v-chart到底选择哪一个,原因是什么我还没有理解到位)
对于toC端的项目,对样式的要求严格,并且需求量大,我们统一采用成型的第三方动画库: animate.style
npm install animate.css
// 基本使用:
name="库中的name"
enter-active-class=""
leave-active-class=""
对于toB端的项目,如果对样式的要求停留在能看就行,但是最好有:
插槽的选择:组件的封装过程中,需要不同的组件内部展现形式,而且这种展现是随机的。(类似于elementui中使用来重新定义组件内容),我们没办法在组件内通过v-if列举所有可能,并且这也是不好的,所以与其列举不如放开组件内的一个位置,让外部传入的内容添加到此处。更灵活,更易用。
三种插槽方式:建议统一使用具名插槽,当需要子传父值的时候选择作用域插槽
标签体内直接写内容:<hello><img>hello>
hello组件中:<slot>等待数据加载...slot>定义插入位置,slot标签体中的内容放的为默认值
样式:可以放在slot里也可以写在slot外,这样也增加了维护的难度,所以不建议使用默认插槽
放的位置增加name:
<slot name="img">slot>
使用插槽增加slot:
<hello>
<img slot="img">
hello>
建议使用<template>包裹:(vue2.6后可使用v-slot:img代替slot="img",只用于template标签)
<hello>
<template slot="img">
<img/>
template>
hello>
在这里插入代码片
放的位置:通过games将数据传递
<slot :games="games">slot>
使用插槽:通过scope获取到数据 或者写成slot-scope
<hello>
<template scope="data">
{{data.games}} // data:{games:""}
template>
hello>
route:路由 每个路由的详情与参数
router:路由器,整个应该只有一个路由器
// 使用导航
<router-link to="/aaa" active-class="active">router-link>
// 展示位置
<router-view>router-view>
export default new VueRouter({
mode:'history',// 通常web应用我们都会使用history模式而不是hash模式,让后端处理识别路由
routes:[
{
name:"about",
path:"/about",
meta:{isAuth:false}, // 元信息,可以配置标志位用于路由守卫中做权限处理等问题
// 统一按照vue-router官网建议,使用懒加载方式配置
component:()=>import('路径'),
children:[
{
path:'child1',
component:()=>import('')
}
]
}
]
})
// 接收三个参数:to 去哪的路由对象,from来自哪的路由对象,next() 放行
router.beforeEach((to,from,next)=>{
next() //必须通过next() 才能跳转到下一步
})
router.afterEach((to,from)=>{
document.title = to.meta.title || "首页" //并且需要把配置中的title也修改
})
beforeEnter((to,from,next)=>{})
通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next){}
刷新路由:
beforeRouteUpdate(to,from,next){}
离开组件时调用:
beforeRouteLeave(to,form,next){}
vuex风格指南:
应该优先通过 Vuex 管理全局状态,而不是通过 this. r o o t 或 一 个 全 局 事 件 总 线 。 通 过 t h i s . root 或一个全局事件总线。 通过 this. root或一个全局事件总线。通过this.root 和/或全局事件总线管理状态在很多简单的情况下都是很方便的,但是并不适用于绝大多数的应用。
- 是否是多于两处需要使用此数据,并且此数据为全局的关键数据
- 路由传参、全局事件总线传递是否优于vuex
store中的属性命名:mutations方法中的参数统一使用全大写字母命名,其他方法中的方法名、属性名统一按照小驼峰命名
1、当项目很小,没有模块化vuex时:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 存放字段(使用小驼峰命名)
state = {
userName: ''
},
mutations = {
// 全大写命名,接收两个参数(state对象,改变的值)
SET_USER_NAME: (state, value) => {
state.userName = value;
}
},
// 重复使用的API调用
actions = {
// context,上下文中是一个简化的store,里面包含着commit的上下文方法,所需的数据
// context.commit("方法",value),利用commit调用mutations里面的方法,如果不需要其他方法可以结构赋值 {commit}
setUserName(context, value) {
context.commit("SET_USER_NAME", value)
}
},
// vuex的计算属性,可以调用里面的方法二次更新state数据(state,其他的getters)
getters = {
setUserData(state, getters) {
return state.userName + "很帅";
}
}
})
调用时:
// 获取数据
this.$store.state.userName
// 直接修改数据
this.$store.conmmit("SET_USER_NAME",value)
// 通过action修改数据
this.$store.dispatch("setUserName",value)
// 通过getter获取修改后的数据
this.$store.getters.getData();
2、当项目很大时,使用模块化的vuex存储数据
// 模块化 配置store的index.js
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import system from './modules/system' // system模块
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
system, // 模块化命名
},
getters // 公共getters
})
export default store
modules目录下每个模块vuex文件中:
const state = {};
const getters = {};
const mutations = {};
const actions = {};
export default {
namespaced: true, // 开启命名空间
state,
mutations,
actions,
getters
};
调用时:
直接的方式
// 获取数据
this.$store.state.模块名.userName
// 直接修改数据
this.$store.conmmit("模块名/SET_USER_NAME",value)
// 通过action修改数据
this.$store.dispatch("模块名/setUserName",value)
// 通过getter获取修改后的数据
this.$store.getters["模块名/getData"];
统一化的方式:
// 数据获取: 放在computed中 this.a调用
...mapState('a', ['a','b','c']) 模块名,[字段名]形式
...mapGetters('b',['a','b'])
// 数据提交:放在methods, this.a(传参) 调用
...mapMutations('a',['a','b'])
...mapActions('b',['a','b'])