1.初始化开发环境

1.1 初始化项目

npm install @vue/cli -g
vue create vue-ketang
1
2

CLI CHANGELOG

 (*) Choose Vue version
 (*) Babel
 ( ) TypeScript
 (*) Progressive Web App (PWA) Support
 (*) Router
 (*) Vuex
 (*) CSS Pre-processors
1
2
3
4
5
6
7

dart-sass 性能更好,后续sass新的特性会优先支持,也解决了node-sass不稳定问题

1.2 安装依赖

vue add style-resources-loader
1
const path = require('path');
module.exports = {
    pluginOptions: {
        'style-resources-loader': {
            preProcessor: 'scss',
            patterns: [path.resolve(__dirname, 'src/assets/common.scss')]
        }
    }
}
1
2
3
4
5
6
7
8
9

增加全局scss变量

npm i postcss-plugin-px2rem lib-flexible
1
import "lib-flexible"; // main.js
module.exports = {
    css: {
        loaderOptions: {
            postcss: {
                plugins: [
                    require("postcss-plugin-px2rem")({
                        rootValue: 75,
                        exclude: /node_module/,
                    })
                ]
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

增加px2rem插件

npm i vant axios -D
1
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
1
2
3

1.3 配置目录

src
    │  App.vue     # 根组件
    │  main.js     # 入口文件
    ├─api          # 存放接口
    ├─assets       # 存放资源
    ├─components   # 组件
    ├─config       # 存放配置文件
    ├─router       # 存放路由配置
    ├─store        # 存放vuex配置
    ├─utils        # 存放工具方法
    └─views        # 存放Vue页面
1
2
3
4
5
6
7
8
9
10
11

2.项目路由搭建

2.1 配置router.js

const routes = [{
        path: '/',
        name: 'home',
        component: Home
    },
    {
        path: '/lesson',
        name: 'lesson',
        component: () => import('@/views/lesson/index.vue')
    },
    {
        path: '/profile',
        name: 'profile',
        component: () => import('@/views/profile/index.vue')
    }
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

2.2 使用vant ui组件

<div id="app">
  <router-view></router-view>
  <van-tabbar route>
    <van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item>
    <van-tabbar-item icon="shop-o" to="/lesson">我的课程</van-tabbar-item>
    <van-tabbar-item icon="friends-o" to="/profile">个人中心</van-tabbar-item>
  </van-tabbar>
</div>
1
2
3
4
5
6
7
8

2.3 增加路由loading效果

import Loading from '@/components/loading';
const loadable = (asyncFUnction) => {
    const component = () => ({
        component: asyncFUnction(),
        loading:Loading
    })
    return {
        render(h) {
            return h(component)
        }
    }
}
export default loadable
1
2
3
4
5
6
7
8
9
10
11
12
13

3.首页头部导航搭建

3.1 头部绘制

<template>
    <div class="home-header">
        <img src="@/assets/logo.png" />
        <van-dropdown-menu>
            <van-dropdown-item :value="category" :options="categories" @change="change" />
        </van-dropdown-menu>
    </div>
</template>
<script>
export default {
    data() {
        return {
            category: 0,
            categories: [
                { text: '全部课程', value: 0 },
                { text: 'vue课程', value: 1 },
                { text: 'react课程', value: 2 },
            ]
        }
    },
    methods: {
        change(newVal) {
            this.category = newVal
        }
    }
}
</script>
<style lang="scss">
.home-header {
    background: $background;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 2.5%;
    position: fixed;
    top: 0;
    left: 0;
    width: 95%;
    img {
        height: 50px;
    }
    .van-dropdown-menu__title {
        color: #fff;
    }
    .van-dropdown-menu__bar {
        background: $background;
    }
}
</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
45
46
47
48
49

3.2 同步数据

<template>
    <HomeHeader v-model="currentCategory"></HomeHeader>
</template>
<script>
import HomeHeader from './home-header'
export default {
    data(){
        return {currentCategory:0}
    },
    components:{
        HomeHeader
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
    <div class="home-header">
        <img src="@/assets/logo.png" />
        <van-dropdown-menu>
            <van-dropdown-item :value="value" :options="categories" @change="change" />
        </van-dropdown-menu>
    </div>
</template>
<script>
export default {
    props:{
        value:Number  
    },
    data() {
        return {
            categories: [
                { text: '全部课程', value: 0 },
                { text: 'vue课程', value: 1 },
                { text: 'react课程', value: 2 },
            ]
        }
    },
    methods: {
        change(newVal) {
            this.$emit('input',newVal);
        }
    }
}
</script>
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

4.vuex流程搭建

4.1 vuex实现模块化

const files = require.context('.',true,/\.js$/);
const modules = {}
files.keys().forEach(key=>{
    const path = key.replace(/(\.\/|\.js)/g, '');
    if(path === 'index') return;
    const [namespace,type] = path.split('/');
    if(!modules[namespace]){
        modules[namespace] = {
            namespaced :true
        }
    }
    modules[namespace][type] = files(key).default;
})
export default modules
1
2
3
4
5
6
7
8
9
10
11
12
13
14
─home
    |-- state.js
    |-- actions.js
    |-- mutations.js
─profile
    |-- state.js
    |-- actions.js
    |-- mutations.js
─user
    |-- state.js
    |-- actions.js
    |-- mutations.js
1
2
3
4
5
6
7
8
9
10
11
12
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
  ...modules
})
1
2
3
4
5
6
7

4.2 设置分类

  • state.js 设置vuex中的默认状态
const state = {
    category:0
}
export default state;
1
2
3
4
  • action-types.js 设置action类型
export const SET_CATEGORIES = 'SET_CATEGORIES'; // 设置分类
1
  • mutations.js 增加修改状态方法
import * as Types from '@/store/action-types'
const mutations = {
    [Types.SET_CATEGORIES](state,payload){
        state.category = payload
    }
}
export default mutations
1
2
3
4
5
6
7
<template>
    <HomeHeader v-model="currentCategory"></HomeHeader>
</template>
<script>
import HomeHeader from './home-header';
import { createNamespacedHelpers } from 'vuex';
import * as Types from '@/store/action-types.js'
const { mapState,mapMutations } = createNamespacedHelpers('home');
export default {
    methods:{
        ...mapMutations([Types.SET_CATEGORIES]),
    },
    computed:{
        ...mapState(['category']),
        currentCategory:{
            get(){
                return this.category
            },
            set(val){
                this[Types.SET_CATEGORIES](val);
            }
        }
    },
    components: {
        HomeHeader
    }
}
</script>
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

5.轮播图实现

5.1 扩展axios

import axios from 'axios';
class HTTP {
    constructor() {
        this.baseURL = process.env.NODE_ENV !== 'production' ? 'http://localhost:7001' :
            '/';
        this.timeout = 3000;
        this.queue = {};
    }
    setInterceptor(instance, url) {
        instance.interceptors.request.use((config) => { // 请求拦截
            this.queue[url] = url;
            return config;
        }, err => {
            return Promise.reject(err);
        });
        instance.interceptors.response.use((res) => {
            delete this.queue[url];
            if (res.data.err === 0) {
                return res.data.data
            } else {
                return Promise.reject(res.data)
            }
        }, err => {
            delete this.queue[url];
            return Promise.reject(err);
        })
    }
    request(options) {
        let instance = axios.create();
        let config = {
            ...options,
            baseURL: this.baseURL,
            timeout: this.timeout
        }
        this.setInterceptor(instance, options.url);
        return instance(config);
    }
    post(url, data) {
        return this.request({
            method: 'post',
            url,
            data
        })
    }
    get(url, config = {}) {
        return this.request({
            method: 'get',
            url: url,
            ...config
        })
    }
}
export default new HTTP
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

5.2 接口封装

import axios from "../utils/axios"
// 获取轮播图 
export const fetchSlides = () => axios.get('/api/slider');
1
2
3

5.3 vuex中同步接口数据

const state = {
    category:0,
    slides: []
}
export default state;
1
2
3
4
5
export const SET_SLIDES = 'SET_SLIDES' // 设置轮播图数据
1
import * as Types from '@/store/action-types'
import { fetchSlides } from '@/api/home.js'
const actions = {
    async [Types.SET_SLIDES]({ commit }) {
        let slides = await fetchSlides();
        commit(Types.SET_SLIDES, slides);
    }
}
export default actions
1
2
3
4
5
6
7
8
9
import * as Types from '@/store/action-types'
const mutations = {
    [Types.SET_SLIDES](state, slides) {
        state.slides = slides
      }
}
export default mutations
1
2
3
4
5
6
7
<van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
    <van-swipe-item v-for="(slide,index) in slides" :key="index">
        <img :src="slide.url"></van-swipe-item>
</van-swipe>
<script>
 mounted() {
     if (this.slides.length == 0) {
         this[Types.SET_SLIDES]();
     }
 }
</script>
1
2
3
4
5
6
7
8
9
10
11