vue.js应用开发笔记
看vue.js有几天了,之前也零零散散的瞅过,不过一直没有动手去写过demo,这几天后台事比较少,一直在讨论各种需求(其实公司对需求还是比较重视与严谨的,一个项目需求讨论就差不多一周了,这要搁之前,天哪。。。),于是就琢磨着把vue简单的过下,如下所讲只是个人一些理解,不到的地方还望园友指正,涉及到的东西有vue、vue-router、vuex、axios以及nodejs一些后台东西,废话不说了直接上菜吧。
一、vue.js
1、项目搭建使用vue-cli脚手架,首先必须安装vue、vue-cli:cnpm i vue vue-cli -g,全局安装完成之后我们便可以使用vue-cli脚手架进行项目结构搭建,具体如下:
vue init webpack frontend,如下:
这里我们环境部分选择“运行环境+编译环境”,这样,后面直接可以使用webpack进行编译后部署,十分方便,再继续:
一般每个团队都有自己的代码规范,这时候打开eslint,配置自己团队的代码规范就显得尤为重要,下面说下vscode编辑器下如何配置eslint,在之前项目基础上我们配置下eslint,首先是vscode安装eslint插件:
然后在用户设置栏配置具体的eslint规则:
依次打开vscode的“文件”->“首选项”->“用户设置”,在右侧编辑区输入我们定义好的eslint规则:
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
|
{ "editor.fontSize" : 17, "editor.tabSize" : 2, "editor.formatOnSave" : false , "files.associations" : { "*.vue" : "vue" }, "eslint.validate" : [ "javascript" , "javascriptreact" , "html" , "vue" , { "language" : "html" , "autoFix" : true } ], "emmet.syntaxProfiles" : { "vue-html" : "html" , "vue" : [ "css" , "html" , "less" ] }, "editor.fontFamily" : "Source Code Pro, 'Courier New', monospace" , "files.autoSave" : "off" , "workbench.iconTheme" : "vscode-icons" } |
这里我们设置字体大小为17,tab缩进为2个空格,eslint的校验适用于js、html、vue,设置vscode图标为vscode-icons,其它规则可以参考下eslint官方说明。
2、项目结构
前面通过vue-cli生成的项目结构如下:
主要是src文件夹,我们对其进行了一些拓展,其中api文件夹用来存放前端各种请求api模块,components文件夹如下:
用来存放各种页面组件,其中base为公共组件,比如一些页头、页尾、分页组件等等,Home为主界面,该页面通过router路由来整合其它组件,Login为登录组件。mock文件夹用来进行mock.js配置,这样前端可以独立于后端接口开发,使用虚拟数据不依赖后端从而更加高效。router文件夹用来配置vue-router下的各种前端路由,vuex文件夹主要用来配置vuex状态管理相关:
其中modules文件夹用来存放各个vuex的module,分模块配置的话有个好处就是项目比较庞大的时候便于数据查看与管理,mutation.types.js用来存放各种vuex的mutation类型常量,关于vuex后面继续介绍。
3、关于vue.js一些知识点
1)、组件概念
组件这个比较好解释,简单理解就是一个个通过vue自己的方式注册的页面(可以是公有页面也可以是单个页面),组件化开发时通过template包裹的一系列部分功能页面都可以称作组件,比如我们的App.vue:
当然,每个组件都有属于自己作用域,组件下的各个数据及相关操作均写在当前组件下的script标签中,如下:
可以看到如上为App.vue组件的数据结构,这里通过ES6的export default导出当前vue实例,组件下包含很多东西,比如data(data是一个function,通过return一个数据对象来表示当前组件都有哪些数据实例)、components(组件是可以被其它组件导入使用的,components就是用来声明当前组件导入的子组件)、computed(计算属性,和data类似,本质返回的是一个数据对象)、watch(监听,主要是监听数据对象的变化,有变化则执行对应的function)、mounted(钩子函数,组件初始化时调用)、methods(用得最多,当前组件下的私有方法,可通过this.methodName调用)、events(事件,当前组件下的事件函数),这里还有很多,具体可以参考官网。
当然,每个组件都有属于自己的style样式,使用如下定义:
这里的scoped表示下方样式仅仅当前组件有效,否则全局有效,通过@import我们可以导入外来样式文件(这里公共资源我们一般定义在src的assets文件夹下,不用写在static文件夹下,因为assets被打包时会自动打包,为了统一还是放在assets下面)
2)、组件间的数据交换
首先是父子组件间的数据交换:
父组件好比我们这里的App.vue组件,子组件就好比我们import进来的那些组件,vue.js提供了我们很便捷的方式进行跨组件间的通信,对于父子组件那便是:子组件dispatch事件到父组件,父组件broadcast事件到下面的所有子组件,默认事件传输为冒泡传输。什么意思呢,就是说在App.vue里面我们可以直接通过dispatch方法向上派发事件,前提是父组件需要在methods或者events那里配置好改事件(只有配置了该事件才能接收到,推荐写在events里面),当然派发时可以传递一些数据,同理父组件也是通过broadcast广播事件到子组件。二是我们可以通过props属性进行,子组件在script标签中写明需要prop的哪些属性,父组件在调用子组件的地方直接写上该prop(如果添加v-bind:prop则为动态prop),那么该数据便直接从父组件传递到了子组件中三是我们可以通过全局的$emit进行事件通信,这个可以参考官网文档。
其次是任意无关系的组件如何通信:
这个时候需要使用中间组件进行数据传输,相当于搭建起一个中央数据总线,比如A组件需要和C组件进行通信,那么我们可以在定义一个空的组件为B,那么在A组件中导入B组件,同时向B组件派发事件,同时在C组件中导入B组件,并在此进行事件接收即可。当然了解vuex的话,使用vuex可以轻松解决任意组件间的通信问题,这个后面说。
3)、vue.js实例属性
一个是.parent和.parent和.children,这两个一个是获取当前组件的父实例,一个是获取当前组件的所有子组件,获取到组件后便可以方便访问实例的方法、数据资源等了。.refs是在父组件调用子组件的地方,为了区分各个子组件,可以为组件指定不同的ref属性,然后在通过this..refs是在父组件调用子组件的地方,为了区分各个子组件,可以为组件指定不同的ref属性,然后在通过this.refs.xxx获得该子组件实例,然后可以进行各种实例操作。
4、运行效果
刚刚说了那么多,我们的项目差不多搭起来了,通过命令行:npm run dev,然后直接在浏览器中可以查看效果:
默认启动的是8080端口,访问下:
到这里,一切都ok了,简单的vue.js脚手架项目就搭建完毕了。
二、vue-router
看到router,顾名思义“路由”的意思,vue-router赋予SPA应用前端路由的权利,从而实现自定义页面跳转而不请求服务端,主要记录如下:
1、配置vue-router
在前面的脚手架项目里面我们已经生成了自带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
|
import Vue from 'vue' import Router from 'vue-router' // import store from '../vuex' // import App from '../App.vue' // import _ from 'lodash' Vue.use(Router) export const router = new Router({ mode: 'history' , routes: [ // 组件懒加载,这样可防止组件太多时首屏打开慢的问题 { path: '/user' , component: resolve => require([ '../components/user/User.vue' ], resolve) }, { path: '*' , component: null } ] }) router.beforeEach((to, from, next) => { // to and from are Route Object,next() must be called to resolve the hook // if (!store.getters.logined) { // } else { // next() // } next() }) |
router必须使用vue.use进行全局注册方可使用,mode参数用来配置router的模式,默认为带“#”的hash模式,当然配置为history则使用H5的history模式,路由更像是普通url。routes用来配置各种具体路由信息,这里routes是一个对象数组,每一个对象都是一个路由对象,其中包括path(路由路径,支持正则匹配)、component(该路由对应的组件实例),注意的是一般我们会对路由组件做懒加载处理,以便加快首屏渲染速度。
2、在组件中配置使用
router-view是用来做路由视图的显示的,第一步配置了每个路由对应的路由,那么一旦某个页面使用router-view进行视图显示的话,恰好该路由匹配了上面path,那么path对应的组件将会展示在router-view的位置,相当于路由组件的填位吧。当然一个页面或组件可以配置多个router-view,那么相应的就需要引入多个组件实例了。
router-link是一个路由跳转标签,可以理解为我们html中的a标签,router-link标签内部包含属性to,同样可以路由配置。
关于路由方法:
router.push()、router.replace():路由跳转
router.back():路由回退
router.forward():forward跳转
其它参考官网说明,应用较多的是beforeEach方法,所有路由调用之前均执行该方法,这里我们一般可以做一些权限判断、登录判断之类的操作:
三、vuex状态管理
vuex主要用来进行全局状态管理,可以理解为全局的数据管理,vuex主要由几部分组成:action、mutation、getters、state组成,一般的使用流程是:组件中可以直接调用上面四个部分,比如调用action,可以使用:this.$store.actionName,mutation也是一样,不同的是action支持异步调用,mutation下的操作完全同步,也就是说,action下可以调用各种api调用(api方法一般都是异步的,返回promise对象)。组件访问getters:this.store.getters.gettersName,组件中调用state:this.store.getters.gettersName,组件中调用state:this.store.moduleName.stateName(模块化配置vuex时这样访问)。action一般commit事件到mutation,然后再在mutation中操作state中的数据,最后通过getters暴露state中的数据给组件使用,如果不涉及到异步操作的话,可以直接在组件中dispatch到相应的mutation而跳过action直接操作state。
1、配置vuex
首先是module下的user模块内容:
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
|
import * as types from '../mutation.types' import * as http from '../../api/http' // import _ from 'lodash' const state = { userinfo: JSON.stringify(localStorage.getItem( 'userinfo' ) || {}) } const actions = { setUserInfo ({ commit }, userinfo) { localStorage.setItem( 'userinfo' , userinfo) commit(types.SET_USERINFO, userinfo) }, login ({dispatch, commit, getters}, plyload) { return http.get( '/user/caiya' , {}) } } const mutations = { [types.SET_USERINFO] (state, userinfo) { state.userinfo = userinfo }, [types.LOGOUT] (state) { localStorage.setItem( 'userinfo' , '' ) state.userinfo = '' } } const getters = { logined (state) { return state.userinfo !== '' && state.userinfo !== '{}' }, userinfo (state) { if (state.userinfo !== '' && state.userinfo !== '{}' && typeof state.userinfo === 'string' ) { return JSON.parse(state.userinfo) } else if ( typeof state.userinfo === 'object' ) { return state.userinfo } return null } } export default { state, actions, mutations, getters } |
每个module都有相应的四个组成部分,分别定义如上,然后再在index.js中配置出该模块:
1
2
3
4
5
6
7
8
9
10
11
12
|
import Vue from 'vue' import Vuex from 'vuex' import user from './modules/user' Vue.use(Vuex) export default new Vuex.Store({ modules: { user } }) |
最后再将该store配置到全局的vue实例中:
2、actions
actions接收键值对函数,该函数第一个参数为vuex的context对象,其中包括dispatch、commit、getters对象函数,通过ES6结构的方式可以直接取到:
然后可以直接进行相关操作,也可以在此将payload数据commit到mutation中在此进行处理。
3、mutation
mutation一般用来对state中的数据进行修改操作,其中第一个参数为state对象,后面参数不定,均为action传过来的参数或者页面通过dispatch直接传过来的参数
4、getters
可以看到getters其实就是用来过滤处理state中的数据的,每个getters函数第一个参数为当前module下的state对象,定义好getters后组件中直接获取getters即可获取到state中的过滤后的数据:
5、最后看下要操作的state
其实state就是json对象,用来保存任意状态的:
6、组件辅助函数
辅助函数是干嘛的呢,比如之前我们定义好的action、mutation、getters、state后,在组件中想使用他们要怎么做呢?必须使用this.store.getters.xxx来获取gettersxxx,要调用action必须使用this.store.getters.xxx来获取gettersxxx,要调用action必须使用this.store.actionName来调用,mutation同理,获取state要使用this.$store.state.moduleName.stateName获取指定moduleName下的stateName这个state数据,这样调用比较麻烦,所以辅助函数就出现了。
比如我们Login.vue组件需要调用actions中的Login方法,如下:
同样地方式我们可以这样直接调用mutation:
再来看个getters的:
四、axios
axios是一个http请求包,类似于vue-resource(该包已停止维护),vue官网推荐使用axios进行http调用,因为axios压缩后体积更小,支持restful方法调用,关于axios的使用看如下代码,其中有详尽介绍:
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
|
import axios from 'axios' axios.defaults.baseURL = 'https://cnodejs.org/api/v1' // axios.defaults.headers.common['Authorization'] = AUTH_TOKEN axios.defaults.headers.post[ 'Content-Type' ] = 'application/x-www-form-urlencoded' const ACCESS_TOKEN = 'accesstoken=ed20aac8-9fd8-45bf-8112-62fd2425b4a5' // 添加请求拦截器 axios.interceptors.request.use(config => { // 在发送请求之前做些什么 config.url = `${config.url}?${ACCESS_TOKEN}` return config }, error => { // 对请求错误做些什么 return Promise.reject(error) }) // 添加响应拦截器 axios.interceptors.response.use(response => { // 对响应数据做点什么 return response }, error => { // 对响应错误做点什么 return Promise.reject(error) }) export function fetch (url, params) { return new Promise((resolve, reject) => { axios.post(url, params).then(res => { resolve(res) }). catch (err => { reject(err) }) }) } export function get (url, params) { return new Promise((resolve, reject) => { axios.get(url, { params: params }).then(res => { resolve(res) }). catch (err => { reject(err) }) }) } |
这样将该http.js文件导出后,可以直接在action中调用,不用全局vue配置,十分方便。axios.interceptors.request方法主要用来给所有的请求添加拦截器,这里可以进行各种请求权限校验、方法重写等操作,axios.interceptors.response主要是对响应做拦截处理,比如说我们服务端返回状态码为403,表示无权限信息,那么这里可以直接进行相关错误信息展示,或者直接跳转至登录或其它页面。
五、一个简单应用
闲暇时间做了个简单的登录及后台管理系统(ps:刚开始做),简单效果如下,github地址:https://github.com/caiya/frontend:
六、最后想多说几句
1、任何技术,想学就得多动手去做,实践出真知啊,光看不做经不起实战的考验
2、多写博客还是不错的哈哈哈,不光是练文笔,陶冶情操也是有的,更别提那些大无畏的分享精神了