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) 的基础篇。不要用任何构建工具,就只用最简单的
把教程里的例子模仿一遍,理解用法。不推荐上来就直接用 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命令:
全局安装 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 }
安装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);
安装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)
安装Vuex命令:Vuex文档:http://vuex.vuejs.org/zh-cn/
npm install vuex在一个模块化的打包系统中,您必须显式地通过 Vue.use() 来安装
Vuex:状态管理模式
import Vue from ‘vue’ import Vuex from ‘vuex’ Vue.use(Vuex)
Vuex是Vue的状态管理模式,每一个 Vuex 应用的核心就是 store(仓库)。
“store” 基本上就是一个容器,它包含着你的应用中大部分的状态(即 state);
State:单一状态树
从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态;
初始 state 对象:
// 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)
const store = new Vuex.Store({ state: { count: 0 } })
当一个组件需要获取多个状态时候,使用 mapState 辅助函数帮助我们生成计算属性;
computed: mapState({ // 箭头函数可使代码更简练 count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count' })
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。如:
computed: { …mapState([‘headNav’]) } Getters
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 } })
mapGetters 辅助函数仅仅是将 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}
}
}
});
可以在组件中使用 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')
})
}}
Action 类似于 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);
})
}
}
});
modules:
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 的状态
这几个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>
因为用到了路由,所以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 }
})
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'
}]
})
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
src目录下新建jsons目录, 创建数据listindex.js和listplist文件;
demo参考一个酷狗app写的,主要是熟悉使用构建工具搭建Vue应用,
练习使用Vue-router、Vuex、以及Mint UI,
come on!