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的顺序

  • 起步
  1. 扎实的 JavaScript / HTML / CSS 基本功。这是前置条件。

  2. 通读官方教程 (guide) 的基础篇。不要用任何构建工具,就只用最简单的 <script>

    把教程里的例子模仿一遍,理解用法。不推荐上来就直接用 vue-cli 构建项目,尤其是如果没有 Node/Webpack 基础。

  3. 照着官网上的示例,自己想一些类似的例子,模仿着实现来练手,加深理解。

  4. 阅读官方教程进阶篇的前半部分,到『自定义指令 (Custom Directive) 』为止。

    着重理解 Vue 的响应式机制和组件生命周期。『渲染函数(Render Function)』如果理解吃力可以先跳过。

  5. 阅读教程里关于路由和状态管理的章节,然后根据需要学习 vue-router 和 vuex。

    同样的,先不要管构建工具,以跟着文档里的例子理解用法为主。

  6. 走完基础文档后,如果你对于基于 Node 的前端工程化不熟悉,就需要补课了。

    下面这些严格来说并不是 Vue 本身的内容,也不涵盖所有的前端工程化知识,

    但对于大型的 Vue 工程是前置条件,也是合格的『前端工程师』应当具备的知识。

  • 前端生态/工程化
  1. 了解 JavaScript 背后的规范,ECMAScript 的历史和目前的规范制定方式。

    学习 ES2015/16 的新特性,理解 ES2015 modules,适当关注还未成为标准的提案。

  2. 学习命令行的使用。建议用 Mac。

  3. 学习 Node.js 基础。建议使用 nvm 这样的工具来管理机器上的 Node 版本,

    并且将 npm 的 registry 注册表配置为淘宝的镜像源。

    至少要了解 npm 的常用命令,npm scripts 如何使用,语义化版本号规则,

    CommonJS 模块规范(了解它和 ES2015 Modules 的异同),Node 包的解析规则,

    以及 Node 的常用 API。应当做到可以自己写一些基本的命令行程序。

    注意最新版本的 Node (6+) 已经支持绝大部分 ES2015 的特性,可以借此巩固 ES2015。

  4. 了解如何使用 / 配置 Babel 来将 ES2015 编译到 ES5 用于浏览器环境。

  5. 学习 Webpack。Webpack 是一个极其强大同时也复杂的工具,

    作为起步,理解它的『一切皆模块』的思想,并基本了解其常用配置选项和 loader 的概念/使用方法即可,

比如如何搭配 Webpack 使用 Babel。学习 Webpack 的一个挑战在于其本身文档的混乱,建议多搜索搜索,

应该还是有质量不错的第三方教程的。英文好的建议阅读 Webpack 2.0 的文档,比起 1.0 有极大的改善,

但需要注意和 1.0 的不兼容之处。

  • Vue 进阶
  1. 有了 NodeWebpack 的基础,可以通过 vue-cli 来搭建基于 Webpack

    并且支持单文件组件的项目了。建议用 webpack-simple 这个模板开始,

    并阅读官方教程进阶篇剩余的内容以及 vue-loader 的文档,了解一些进阶配置。

    有兴趣的可以自己亲手从零开始搭一个项目加深理解。

  2. 根据例子尝试在 Webpack 模板基础上整合 vue-routervuex

  3. 深入理解 Virtual DOM 和『渲染函数 (Render Functions)』这一章节(可选择性使用 JSX),

    理解模板和渲染函数之间的对应关系,了解其使用方法和适用场景。

  4. (可选)根据需求,了解服务端渲染的使用(需要配合 Node 服务器开发的知识)。

    其实更重要的是理解它所解决的问题并搞清楚你是否需要它。

  5. 阅读开源的 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
  }
1
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);  
1
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)  
1
2
3
4
5
  • 安装Vuex命令:Vuex文档:http://vuex.vuejs.org/zh-cn/
npm install vuex在一个模块化的打包系统中,您必须显式地通过 Vue.use() 来安装

1
2
  • Vuex:状态管理模式

    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    
    1
    2
    3

    Vuex是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
      3
    • 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
        }
      })
      
      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

      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}
          }
        }
      });
      
      
      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
      10
    • 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);
            })
          }
        }
      });
      
      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
    • 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 的状态
      
      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 }
})
1
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'
  }]
})
1
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
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

src目录下新建jsons目录, 创建数据listindex.js和listplist文件;

demo参考一个酷狗app写的,主要是熟悉使用构建工具搭建Vue应用,

练习使用Vue-router、Vuex、以及Mint UI,

demo地址:

come on!