2016-11-29 Vue2.0+Mint UI+Vue-router+Vux构建移动端APP
** 本该拼搏的年龄,却想太多,做太少!~~~~~~ **
刚开始接触的时候,Vue还是1.0的版本,学习的还是一些很简单的东西,
还不知道什么vuex,vue-router,前端发展的真的很快,几个月的时间,
angular已经更新到2.0,Vue也已经更新2.0了;不管是angular还是react,还是Vue,
他们都是前端MVVM(Model–View–ViewModel)框架;
先到官网学习了Vue2.0的教程,知道Vue的安装、常用的语法、构造器、属性和方法、
生命周期以及渲染、事件、表单控件和组件;
看了尤雨溪写的学习vue2.0的顺序
- 起步
扎实的
JavaScript / HTML / CSS
基本功。这是前置条件。通读官方教程
(guide)
的基础篇。不要用任何构建工具,就只用最简单的<script>
,把教程里的例子模仿一遍,理解用法。不推荐上来就直接用 vue-cli 构建项目,尤其是如果没有 Node/Webpack 基础。
照着官网上的示例,自己想一些类似的例子,模仿着实现来练手,加深理解。
阅读官方教程进阶篇的前半部分,到『自定义指令 (Custom Directive) 』为止。
着重理解 Vue 的响应式机制和组件生命周期。『渲染函数
(Render Function)
』如果理解吃力可以先跳过。阅读教程里关于路由和状态管理的章节,然后根据需要学习 vue-router 和 vuex。
同样的,先不要管构建工具,以跟着文档里的例子理解用法为主。
走完基础文档后,如果你对于基于 Node 的前端工程化不熟悉,就需要补课了。
下面这些严格来说并不是 Vue 本身的内容,也不涵盖所有的前端工程化知识,
但对于大型的 Vue 工程是前置条件,也是合格的『前端工程师』应当具备的知识。
- 前端生态/工程化
了解 JavaScript 背后的规范,ECMAScript 的历史和目前的规范制定方式。
学习
ES2015/16
的新特性,理解 ES2015 modules,适当关注还未成为标准的提案。学习命令行的使用。建议用 Mac。
学习 Node.js 基础。建议使用 nvm 这样的工具来管理机器上的 Node 版本,
并且将 npm 的 registry 注册表配置为淘宝的镜像源。
至少要了解 npm 的常用命令,npm scripts 如何使用,语义化版本号规则,
CommonJS 模块规范(了解它和 ES2015 Modules 的异同),Node 包的解析规则,
以及 Node 的常用 API。应当做到可以自己写一些基本的命令行程序。
注意最新版本的
Node (6+)
已经支持绝大部分 ES2015 的特性,可以借此巩固 ES2015。了解如何使用 / 配置 Babel 来将 ES2015 编译到 ES5 用于浏览器环境。
学习 Webpack。Webpack 是一个极其强大同时也复杂的工具,
作为起步,理解它的『一切皆模块』的思想,并基本了解其常用配置选项和 loader 的概念/使用方法即可,
比如如何搭配 Webpack 使用 Babel。学习 Webpack 的一个挑战在于其本身文档的混乱,建议多搜索搜索,
应该还是有质量不错的第三方教程的。英文好的建议阅读 Webpack 2.0 的文档,比起 1.0 有极大的改善,
但需要注意和 1.0 的不兼容之处。
- Vue 进阶
有了
Node
和Webpack
的基础,可以通过vue-cli
来搭建基于Webpack
,并且支持单文件组件的项目了。建议用
webpack-simple
这个模板开始,并阅读官方教程进阶篇剩余的内容以及
vue-loader
的文档,了解一些进阶配置。有兴趣的可以自己亲手从零开始搭一个项目加深理解。
根据例子尝试在
Webpack
模板基础上整合vue-router
和vuex
深入理解
Virtual DOM
和『渲染函数 (Render Functions
)』这一章节(可选择性使用JSX
),理解模板和渲染函数之间的对应关系,了解其使用方法和适用场景。
(可选)根据需求,了解服务端渲染的使用(需要配合 Node 服务器开发的知识)。
其实更重要的是理解它所解决的问题并搞清楚你是否需要它。
阅读开源的 Vue 应用、组件、插件源码,自己尝试编写开源的 Vue 组件、插件。
作者:尤雨溪
链接:https://zhuanlan.zhihu.com/p/23134551
做了一个小demo
是在node环境中的,所以就用构建工具安装Vue-cli、mint-ui、vue-router、Vuex
;
全局安装 vue-cli
$ npm install --global vue-cli
创建一个基于 webpack 模板的新项目
$ vue init webpack my-project
安装依赖
$ cd my-project
$ npm install
$ npm run dev
这样一个很简单的Vue小项目已经完成。
默认服务器的端口是8080,如果需要改变端口号,
可以到src/config/index.js文件中port端口号就可以;
dev: {
env: require('./dev.env'),
port: 8080,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
cssSourceMap: false
}
2
3
4
5
6
7
8
- 安装Mint UI命令:
文档:http://mint-ui.github.io/docs/#!/zh-cn2
// install# for Vue 1.x
npm install mint-ui@1 -S
### for Vue 2.0
npm install mint-ui -S
// import all components
import Vue from 'vue';import Mint from 'mint-ui';
Vue.use(Mint);
2
3
4
5
6
7
8
9
- 安装Vue-router命令:
文档:http://router.vuejs.org/zh-cn/
npm install vue-router如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
2
3
4
5
- 安装Vuex命令:Vuex文档:http://vuex.vuejs.org/zh-cn/
npm install vuex在一个模块化的打包系统中,您必须显式地通过 Vue.use() 来安装
2
Vuex:状态管理模式
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex)
1
2
3Vuex是Vue的状态管理模式,每一个 Vuex 应用的核心就是 store(仓库)。
"store" 基本上就是一个容器,它包含着你的应用中大部分的状态(即 state);State:单一状态树
从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态;
初始 state 对象:
// 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)const store = new Vuex.Store({ state: { count: 0 } })
1
2
3
4
5当一个组件需要获取多个状态时候,使用 mapState 辅助函数帮助我们生成计算属性;
computed: mapState({ // 箭头函数可使代码更简练 count: state => state.count, // 传字符串参数 'count' 等同于 `state => state.count` countAlias: 'count' })
1
2
3
4
5
6
7当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给
mapState
传一个字符串数组。如:computed: { ...mapState(['headNav']) }
1
2
3Getters
Vuex 允许我们在 store 中定义
『getters』
(可以认为是 store 的计算属性)。Getters 接受 state 作为其第一个参数:
const store = new Vuex.Store({ state: { audio: { songUrl: '', imgUrl: 'http://m.kugou.com/v3/static/images/index/logo_kugou.png', title: '', singer: '', currentLength: 0, songLength: 0, currentFlag: false }, head: { toggle: false, title: '', style: {'background': 'rgba(43,162,251,0)'} }, headNav: 'head-nav1', audioLoadding: false, detailPlayerFlag: false, showPlayer: false, listenCount: 0, isPlay: true }, getters: { audio: state=>state.audio, head: state=>state.head, audioLoadding: state=>state.audioLoadding, detailPlayerFlag: state=>state.detailPlayerFlag, showPlayer: state=>state.showPlayer, isPlay: state=>state.isPlay } })
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
32mapGetters 辅助函数仅仅是将 store 中的 getters 映射到局部计算属性
Mutations:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
Vuex 中的 mutations 非常类似于事件:
每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数;
const store = new Vuex.Store({ state: { audio: { songUrl: '', imgUrl: 'http://m.kugou.com/v3/static/images/index/logo_kugou.png', title: '', singer: '', currentLength: 0, songLength: 0, currentFlag: false }, head: { toggle: false, title: '', style: {'background': 'rgba(43,162,251,0)'} }, headNav: 'head-nav1', audioLoadding: false, detailPlayerFlag: false, showPlayer: false, listenCount: 0, isPlay: true }, getters: { audio: state=>state.audio, head: state=>state.head, audioLoadding: state=>state.audioLoadding, detailPlayerFlag: state=>state.detailPlayerFlag, showPlayer: state=>state.showPlayer, isPlay: state=>state.isPlay }, mutations: { setAudio(state, audio){ if (!state.listenCount) { state.showPlayer = true; //首次进入应用时不可打开播放详情 } state.listenCount++; state.audio = {...(state.audio), ...audio}; }, setAudioTime(state, time){ state.audio.currentLength = time; }, setCurrent(state, flag){ state.audio.currentFlag = flag; }, showHead(state, flag){ state.head.toggle = flag; }, setHeadTitle(state, title){ state.head.title = title; }, setHeadStyle(state, style){ state.head.style = style; }, resetHeadStyle: state=> { state.head.style = {'background': 'rgba(43,162,251,0)'}; }, toggleAudioLoadding: (state, flag)=> { state.audioLoadding = flag; }, setHeadNav: (state, index)=> { state.headNav = 'head-nav' + index; }, showDetailPlayer: (state, flag)=> { state.detailPlayerFlag = flag; }, showPlayer: (state, flag)=> { state.showPlayer = flag; }, isPlay: (state, flag)=> { state.isPlay = flag; }, setLrc: (state, lrc)=> { state.audio = {...(state.audio), lrc} } } });
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可以在组件中使用
this.$store.commit('xxx')
提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为
store.commit
调用(需要在根节点注入 store)。import { mapMutations } from 'vuex'export default { // ... methods: { ...mapMutations([ 'increment' // 映射 this.increment() 为 this.$store.commit('increment') ]), ...mapMutations({ add: 'increment' // 映射 this.add() 为 this.$store.commit('increment') }) }}
1
2
3
4
5
6
7
8
9
10Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
Action 通过 store.dispatch 方法触发:
const store = new Vuex.Store({ state: { audio: { songUrl: '', imgUrl: 'http://m.kugou.com/v3/static/images/index/logo_kugou.png', title: '', singer: '', currentLength: 0, songLength: 0, currentFlag: false }, head: { toggle: false, title: '', style: {'background': 'rgba(43,162,251,0)'} }, headNav: 'head-nav1', audioLoadding: false, detailPlayerFlag: false, showPlayer: false, listenCount: 0, isPlay: true }, getters: { audio: state=>state.audio, head: state=>state.head, audioLoadding: state=>state.audioLoadding, detailPlayerFlag: state=>state.detailPlayerFlag, showPlayer: state=>state.showPlayer, isPlay: state=>state.isPlay }, mutations: { setAudio(state, audio){ if (!state.listenCount) { state.showPlayer = true; //首次进入应用时不可打开播放详情 } state.listenCount++; state.audio = {...(state.audio), ...audio}; }, setAudioTime(state, time){ state.audio.currentLength = time; }, setCurrent(state, flag){ state.audio.currentFlag = flag; }, showHead(state, flag){ state.head.toggle = flag; }, setHeadTitle(state, title){ state.head.title = title; }, setHeadStyle(state, style){ state.head.style = style; }, resetHeadStyle: state=> { state.head.style = {'background': 'rgba(43,162,251,0)'}; }, toggleAudioLoadding: (state, flag)=> { state.audioLoadding = flag; }, setHeadNav: (state, index)=> { state.headNav = 'head-nav' + index; }, showDetailPlayer: (state, flag)=> { state.detailPlayerFlag = flag; }, showPlayer: (state, flag)=> { state.showPlayer = flag; }, isPlay: (state, flag)=> { state.isPlay = flag; }, setLrc: (state, lrc)=> { state.audio = {...(state.audio), lrc} } }, actions: { getSong({commit,state}, hash){ commit('toggleAudioLoadding', true); Vue.http.get('http://lavyun.applinzi.com/apis/getKugouSong.php?hash=' + hash).then(res=> { var json_obj = JSON.parse(res.data); var songUrl = json_obj.url, imgUrl = json_obj.imgUrl.split('{size}').join('100'), title = json_obj.songName, singer = json_obj.choricSinger, songLength = json_obj.timeLength, currentLength = 0, audio = {songUrl, imgUrl, title, singer, songLength, currentLength}; commit('setAudio', audio); commit('toggleAudioLoadding', false); }); }, getLrc({commit,state}, hash){ Vue.http.get('http://lavyun.applinzi.com/apis/getLrc.php?hash=' + hash).then(res=> { commit('setLrc', res.data); }) } } });
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
99modules:
Vuex 允许我们将 store 分割到模块(module)。 每个模块拥有自己的 state、mutation、action、getters、甚至是嵌套子模块——从上至下进行类似的分割:
const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... }}const moduleB = { state: { ... }, mutations: { ... }, actions: { ... }}const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB }}) store.state.a // -> moduleA 的状态 store.state.b // -> moduleB 的状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15这几个state,getters, mutations,actions,modules是Vuex的接个核心概念;
还有一个需要安装的插件,Vue-resource:主要用来发送http请求的时候用的;
安装命令:
npm install vue-resource
这些组件安装后,开始编写demo,前面已经安装了vue-cli,创建了基于webpack模板的新项目,
一般的单页面应用,入口文件就是main.js,以后都是用模板组件来构建项目页面的;
先在App.vue中添加组件,分别创建各个组件对应的模板文件,
<template> <div id="app"> <!--<img src="./assets/logo.png">--> <headlogo></headlogo> <hello></hello> <head-nav></head-nav> <div class="main"> <keep-alive> <router-view></router-view> </keep-alive> </div> <!--<conlist></conlist> 原直接加载组件,后改为路由--> </div> </template> <script> import Hello from './components/Hello' import Headlogo from './components/Headlogo' import HeadNav from './components/HeadNav' import Conlist from './components/Conlist' export default { name: 'app', components: { Hello,Headlogo,HeadNav,Conlist } } </script> <style> body{ margin:0px; padding:0px; } #app { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: justify; color: #2c3e50; margin-top:40px; } .main{ margin-top: 5px; } </style>
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
因为用到了路由,所以main.js中需要引入路由,引入前面安装的东西;
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import VueResource from 'vue-resource'
import Mint from 'mint-ui'
import 'mint-ui/lib/style.css'
import '../static/style.css'
Vue.use(Mint)
Vue.use(VueResource)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
src目录下创建router目录,新建index.js路由文件,
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
routes:[{
path:'/index',component:require('../views/index')
},{
path:'/rank',component:require('../views/rank')
},{
path:'/ringtone',component:require('../views/ringtone')
},{
path:'/plist',component:require('../views/plist')
},{
path:'/singer',component:require('../views/singer')
},{
path:'/search',component:require('../views/search')
},{
path:'*',redirect:'/index'
}]
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
src目录下新建views目录,创建各个路由的模板文件;
src目录下新建store目录,创建index.js状态管理文件;
import Vue from 'vue'
import Vuex from 'vuex'
import VueResource from 'vue-resource'
Vue.use(Vuex);
Vue.use(VueResource)
const store = new Vuex.Store({
state: {
audio: {
songUrl: '',
imgUrl: 'http://m.kugou.com/v3/static/images/index/logo_kugou.png',
title: '',
singer: '',
currentLength: 0,
songLength: 0,
currentFlag: false
},
head: {
toggle: false,
title: '',
style: {'background': 'rgba(43,162,251,0)'}
},
headNav: 'head-nav1',
audioLoadding: false,
detailPlayerFlag: false,
showPlayer: false,
listenCount: 0,
isPlay: true
},
getters: {
audio: state=>state.audio,
head: state=>state.head,
audioLoadding: state=>state.audioLoadding,
detailPlayerFlag: state=>state.detailPlayerFlag,
showPlayer: state=>state.showPlayer,
isPlay: state=>state.isPlay
},
mutations: {
setAudio(state, audio){
if (!state.listenCount) {
state.showPlayer = true; //首次进入应用时不可打开播放详情
}
state.listenCount++;
state.audio = {...(state.audio), ...audio};
},
setAudioTime(state, time){
state.audio.currentLength = time;
},
setCurrent(state, flag){
state.audio.currentFlag = flag;
},
showHead(state, flag){
state.head.toggle = flag;
},
setHeadTitle(state, title){
state.head.title = title;
},
setHeadStyle(state, style){
state.head.style = style;
},
resetHeadStyle: state=> {
state.head.style = {'background': 'rgba(43,162,251,0)'};
},
toggleAudioLoadding: (state, flag)=> {
state.audioLoadding = flag;
},
setHeadNav: (state, index)=> {
state.headNav = 'head-nav' + index;
},
showDetailPlayer: (state, flag)=> {
state.detailPlayerFlag = flag;
},
showPlayer: (state, flag)=> {
state.showPlayer = flag;
},
isPlay: (state, flag)=> {
state.isPlay = flag;
},
setLrc: (state, lrc)=> {
state.audio = {...(state.audio), lrc}
}
},
actions: {
getSong({commit,state}, hash){
commit('toggleAudioLoadding', true);
Vue.http.get('http://lavyun.applinzi.com/apis/getKugouSong.php?hash=' + hash).then(res=> {
var json_obj = JSON.parse(res.data);
var songUrl = json_obj.url,
imgUrl = json_obj.imgUrl.split('{size}').join('100'),
title = json_obj.songName,
singer = json_obj.choricSinger,
songLength = json_obj.timeLength,
currentLength = 0,
audio = {songUrl, imgUrl, title, singer, songLength, currentLength};
commit('setAudio', audio);
commit('toggleAudioLoadding', false);
});
},
getLrc({commit,state}, hash){
Vue.http.get('http://lavyun.applinzi.com/apis/getLrc.php?hash=' + hash).then(res=> {
commit('setLrc', res.data);
})
}
}
});
export default store
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
src目录下新建jsons目录, 创建数据listindex.js和listplist文件;
demo参考一个酷狗app写的,主要是熟悉使用构建工具搭建Vue应用,
练习使用Vue-router、Vuex、以及Mint UI,
come on!