实习后对vue项目实战经验汇总

序言

vue作为前端主流的3大框架之一,目前在国内有着非常广泛的应用,由于其轻量和自底向上的渐进式设计思想,对于PC端,移动端,桌面软件(electronjs)等也有广泛的应用。

优秀的开源框架比如elementUI,iView, ant-design-vue等也极大的降低了开发者的开发成本,并极大的提高了开发效率。

饿了么的Mint UI,Element UI。

美团的mpVue。

京东的Nut UI,IView UI。

都是上手非常舒服的好的UI框架。

贼好玩!!!极有成就感!!

区别一下

移动端UI框架:vant ,cube UI ,Mint UI。

pc端UI框架:mpVue ,element UI ,IView UI,nut UI 。

微信小程序UI框架详情可以看本博客之前写的UI框架整理:微信小程序ui组件库

最初接触vue时是使用的elementUI,iview框架,亲自体会之后确实非常易用且强大。

之前有了一段时间的vue项目上手经验,基于vue做过PC端项目,当然以后工作还是会积累很多vue相关的最佳实践和做一些基于vue的开源项目,所以说总结vue的项目经验我觉得是最好的成长,也希望给未来的自己一些阶段性的经验和思考。

总结一些vue使用踩过的一些坑和项目经验,更多的是使用框架(vue/react)过程中的方法论和组件的设计思路,最后还会有一些个人对vue工程化的一些总结。

特别提醒未来的自己:对javascript, css, html基础是要有相当一定的了解,但是,注意啊!!!千万不要钻牛角尖!

因为会用框架不一定能很好的实现业务需求和功能,要想实现不同场景下不同复杂度的需求,所有一定要对web基础有充足的了解。

常用到的技术点(不要本本主义):

javascript:

  • 数组常用方法的使用,比如遍历有forEach,map,filter,every, some,reduce,操作方法有splice,slice, join,push,shift, pop,sort等
  • 基本数据结构,引用类型(对象,数组)
  • 基本逻辑运算if else, switch,三目运算:?,for/while循环等
  • 字符串常见api(如replace,slice, substr,indexOf)
  • 基本正则使用
  • 变量作用域,作用域链,变量提升,函数声明提升
  • 对象基本用法,面向对象编程

css:

  • 基本盒模型(border/content/padding等)
  • 4种常用定位(static/absolute/relative/fixed)
  • 常用布局方式(浮动布局/弹性布局flex/自适应布局/网格布局grid)
  • css3基本样式与动画(transition,animation)

html:

  • 新标签基本用法和使用
  • head标签作用与用法(主要是meta属性的用法)

1. vue框架使用注意事项和个人最佳体验

vue学习最快的方式就是实践,然后不停地总结开发vue项目中的一些实践经验,才是正确道路,不宜求快,反而极大丧失对前端的兴趣,以至于心不定,容易怀疑自己从而转行,乐趣才是这一行最重要的东西。

沉下去,好好玩与学,还远着呢……..

1.1 谈谈vue生命周期

vue生命周期

vue官网上的生命周期的方法: 大致划分一下分为创建前/后,挂载前/后,更新前/后,销毁前/后这四个阶段。

各个阶段的状态总结如下:

1.1.1 创建前/后:
  • beforeCreate:在beforeCreate生命周期执行时,data和methods中的数据还未初始化,所以此时不能使用data中的数据和methods中的方法。

  • create:data 和 methods初始化完毕,此时可以使用methods 中的方法和data 中的数据。

    1.1.2 挂载前/后
  • beforeMount:template模版已经编译好,但还未挂载到页面,此时页面还是上一个状态。

  • mounted:此时Vue实例初始化完成了,DOM挂载完毕,可以直接操作dom或者使用第三方dom库。

1.1.3 更新前/后
  • beforeUpdate: 此时data已更新,但还未同步页面。
  • updated:data和页面都已经更新完成。
1.1.4 销毁前/后
  • beforeDestory:Vue实例进入销毁阶段,但所有的 data 和 methods , 指令, 过滤器等都处于可用状态。
  • destroyed: 此时组件已经被销毁,data,methods等都不可用。

根据以上介绍,页面第一次加载时会执行 beforeCreate, created, beforeMount, mounted这四个生命周期。

所以:

我们一般在 created 阶段处理http请求获取数据或者对data做一定的处理。

我们会在 mounted 阶段操作dom,比如使用jquery,或者其他第三方dom库。

其次,根据以上不同周期下数据和页面状态的不同,我们还可以做其他更多操作,所以说每个生命周期的发展状态非常重要,一定要理解,这样才能对vue有更多的控制权。

1.2 vue常用的指令以及动态指令的使用

指令 (Directives) 是带有 v- 前缀的特殊属性。

1.2.1 vue常用的指令:
  • v-bind 用于响应式地更新 HTML属性
  • v-if 根据表达式的值的真假来决定是否插入/移除元素
  • v-on 用于监听 DOM 事件
  • v-show 用于决定是否展示该元素,底层通过display:none实现
  • v-html 在dom内插入html内容
  • v-for 循环
  • v-text 渲染指定dom的内容文本
  • v-cloak 和CSS规则如 [v-cloak] { display: none } 一起用,可以隐藏未编译的 Mustache 标签直到实例准备完毕

以上是比较常用的指令,其中v-cloak主要是用来避免页面加载时出现闪烁的问题,可以结合css的[v-cloak] { display: none } 方式解决这一问题。关于指令的动态参数,使用也很简单,方法很灵活,具体使用如下:

1
2
<a v-on:[eventName]="doSomething"> ... </a>
复制代码

可以根据具体情况动态切换事件名,从而绑定同一个函数。

1.3 vue常用修饰符及作用

  1. 事件修饰符
  • .stop 阻止事件冒泡
  • .prevent 阻止事件默认行为
  • .self 事件绑定的元素本身触发时才触发回调
  • .once 事件只能触发一次,第二次就不会触发了
  • .native 将一个vue组件变成一个普通的html,使其可以监听click等原生事件 具体使用如下:
1
2
<Tag @click.native="handleClick">ok</Tag>
复制代码
2. 表单修饰符
  • .lazy 在输入框输入完内容,光标离开时才更新视图
  • .trim 过滤首尾空格
  • .number 如果先输入数字,那它就会限制你输入的只能是数字;如果先输入字符串,那就相当于没有加.number

用法如下:

1
2
<input type="text" v-model.trim="value">
复制代码

还有很多修饰符比如键盘,鼠标等修饰符等等。。。

1.4 组件间-父子组件间的通信方案

组件之间的通信方案:

  • 通过事件总线(bus),即通过发布订阅的方式
  • vuex 状态管理器

父子组件:

  • 父组件通过prop向自组件传递数据
  • 子组件绑定自定义事件,通过this.$emit(event,params) 来调用自定义事件
  • 使用vue提供的 children & $refs方法来通信

1.5 vue实现按需加载组件

组件的按需加载是项目性能优化的一个环节,也可以降低首屏渲染时间,笔者在项目中用到的组件按需加载的方式如下:

  1. 使用() => import(), 具体代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>
<ComponentA />
<ComponentB />
</div>
</template>
<script>
const ComponentA = () => import('./ComponentA')
const ComponentB = () => import('./ComponentB')
export default {
// ...
components: {
ComponentA,
ComponentB
},
// ...
}
</script>
复制代码
  1. 使用resolve => require([‘./ComponentA’], resolve),使用方法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>
<ComponentA />
</div>
</template>
<script>
const ComponentA = resolve => require(['./ComponentA'], resolve)
export default {
// ...
components: {
ComponentA
},
// ...
}
</script>
复制代码

1.6 vuex的几种属性和作用,以及使用vuex的基本模式

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。

它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

vuex的基本工作模式如下图所示:

vuex的基本工作模式

state的改变完全由mutations控制, 也没必要任何项目都使用vuex,对于中大型复杂项目而言,需要共享的状态很多时,使用vuex才是最佳的选择。

vuex各api模式的概念和作用:
  • state 单一状态树,用一个对象就包含了全部的应用层级状态,并且作为一个唯一数据源而存在

  • getters 就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算,可以实现实时监测变化。

  • Mutation 更改 Vuex 的 store 中的状态的唯一方法,使用案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const store = new Vuex.Store({
state: {
num: 1
},
mutations: {
add (state) {
// 变更状态
state.num++
}
}
})
// 在项目中使用mutation
store.commit('add')
// 添加额外参数
store.commit('add', 10)
  • Action 提交的是mutation,而不是直接变更状态,可以包含任意异步操作,具体用法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const store = new Vuex.Store({
state: {
num:
},
mutations: {
add (state) {
state.num++
}
},
actions: {
add (context) {
context.commit('add')
},
asyncAdd ({ commit }) {
setTimeout(() => {
commit('add')
}
}
})
// 分发action
store.dispatch('add')
// 异步action
store.dispatch('asyncAdd')
// 异步传参
store.dispatch('asyncAdd', { num: 10 })
复制代码
  • Module 将store分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块

标准使用模式如下:

store目录是用来组织vuex代码用的,将action,mutation,state分文件管理,这样项目大了之后很容易管理和查询。

摘来的一个很好模板和列子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// type.ts
// 用来定义state等的类型文件
export interface State {
name: string;
isLogin: boolean;
config: Config;
[propName: string]: any; // 用来定义可选的额外属性
}

export interface Config {
header: HeaderType,
banner: Banner,
bannerSider: BannerSider,
supportPay: SupportPay
}

export interface Response {
[propName: string]: any;
}

// state.ts
// 定义全局状态
import { State } from './type'
export const state: State = {
name: '',
isLogin: false,
curScreen: '0', // 0为pc, 1为移动
config: {
header: {
columns: ['首页', '产品', '技术', '运营', '商业'],
height: '50',
backgroundColor: '#000000',
logo: ''
}
},
// ...
articleDetail: null
};

// mutation.ts
import {
State,
Config,
HeaderType,
Banner,
BannerSider,
SupportPay
} from './type'

export default {
// 预览模式
setScreen(state: State, payload: string) {
state.curScreen = payload;
},

// 删除banner图
delBanner(state: State, payload: number) {
state.config.banner.bannerList.splice(payload, 1);
},

// 添加banner图
addBanner(state: State, payload: object) {
state.config.banner.bannerList.push(payload);
},

// ...
};

// action.ts
import {
HeaderType,
Response
} from './type'
import http from '../utils/http'
import { uuid, formatTime } from '../utils/common'
import { message } from 'ant-design-vue'

export default {
/**配置 */
setConfig(context: any, paylod: HeaderType) {
http.get('/config/all').then((res:Response) => {
context.commit('setConfig', res.data)
}).catch((err:any) => {
message.error(err.data)
})
},
/**header */
saveHeader(context: any, paylod: HeaderType) {
http.post('/config/setHeader', paylod).then((res:Response) => {
message.success(res.data)
context.commit('saveHeader', paylod)
}).catch((err:any) => {
message.error(err.data)
})
},
// ...
};

// index.ts
import Vue from 'vue';
import Vuex from 'vuex';
import { state } from './state';
import mutations from './mutation';
import actions from './action';

Vue.use(Vuex);

export default new Vuex.Store({
state,
mutations,
actions
});

// main.ts
// 最后挂载到入口文件的vue实例上
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store/';
import './component-class-hooks';
import './registerServiceWorker';

Vue.config.productionTip = false;

new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app');

在实际项目中都可以使用这种方式组织管理vuex相关的代码

1.7 vue-router基本使用模式和导航钩子的用法及作用

从前辈那摘来的一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// router.ts
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/admin/Home.vue';

Vue.use(Router);

const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
component: Home,
beforeEnter: (to, from, next) => {
next();
},
children: [
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: '',
name: 'header',
component: () => import(/* webpackChunkName: "header" */ './views/admin/subpage/Header.vue'),
},

{
path: '/banner',
name: 'banner',
component: () => import(/* webpackChunkName: "banner" */ './views/admin/subpage/Banner.vue'),
},
{
path: '/admin',
name: 'admin',
component: () => import(/* webpackChunkName: "admin" */ './views/admin/Admin.vue'),
},
],
},
{
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: "login" */ './views/Login.vue'),
meta:{
keepAlive:false //不需要被缓存的组件
}
},
{
path: '*',
name: '404',
component: () => import(/* webpackChunkName: "404" */ './views/404.vue'),
},
],
});

// 路由导航钩子的用法
router.beforeEach((to, from, next) => {
if(from.path.indexOf('/preview') < ) {
sessionStorage.setItem('prevToPreviewPath', from.path);
}
next();
})

export default router

以上案例是很典型的静态路由配置和导航钩子的用法(如何加载路由组件,动态加载路由组件,404页面路由配置,路由导航钩子使用)。

如果做后台系统,往往会涉及到权限系统,所以一般会采用动态配置路由,通过前后端约定的路由方式,路由配置文件更具不同用户的权限由后端处理后返。

由于设计细节比较繁琐,涉及到前后端协定,所以这里浅谈方法思想,随机应变。

1.8 vue中检测变化的注意点

受现代 JavaScript 的限制,Vue 无法检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。还有一种情况是,vue无法检测到data属性值为数组或对象的修改,所以我们需要用原对象与要混合进去的对象的属性一起创建一个新的对象。可以使用this.$set或者对象的深拷贝,如果是数组则可以使用splice,扩展运算符等方法来更新。

1.9 对指定页面使用keep-alive内置组件路由缓存

keep-alive是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。我们可以使用以下方式设置某些页面是否被缓存:

  1. 通过路由配置文件和router-view设置:路由视图配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// routes 配置
export default [
{
path: '/A',
name: 'A',
component: A,
meta: {
keepAlive: true // 需要被缓存
}
}, {
path: '/B',
name: 'B',
component: B,
meta: {
keepAlive: false // 不需要被缓存
}
}
]

路由视图配置:

1
2
3
4
5
6
7
8
9
10
// 路由设置
<keep-alive>
<router-view v-if="$route.meta.keepAlive">
<!-- 会被缓存的视图组件-->
</router-view>
</keep-alive>

<router-view v-if="!$route.meta.keepAlive">
<!-- 不需要缓存的视图组件-->
</router-view>
  1. 通过router-view的key属性 具体方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<div id="app">
<keep-alive>
<router-view :key="key" />
</keep-alive>
</div>
</template>
<script lang="ts">
import { Vue } from 'vue-property-decorator';
import Component from 'vue-class-component';
@Component
export default class App extends Vue {
get key() {
// 缓存除预览和登陆页面之外的其他页面
console.log(this.$route.path)
if(this.$route.path.indexOf('/preview') > -1) {
return '0'
}else if(this.$route.path === '/login') {
return '1'
}else {
return '2'
}
}
}
</script>

2. vue项目配置经验总结

​ 需要知道从0开始搭建项目的步骤,以及通过项目实际情况,自己配置一个符合的项目框架,比如有些公司会采用vue+element+vue+less搭建,有些公司采用vue+iview+vue+sass,或者其他更多的技术栈,所以要有把控能力,我们需要熟悉webpack或者vue-cli3脚手架的配置,之前有过一些简单的webpack和vue-cli3搭建经历,还要系统性学习。

接下去就是求职,去工作了,在工作中积累学习了,祝我好运。