初始化vue-cli4.0项目

vue-cli4.0项目创建

vue-cli4.0全局安装

1
2
3
4
5
6
7
8
npm install -g @vue/cli     // 目前cli最新版本为5.0
npm install -g @vue/cli@4.0 // 安装4.0

// 升级cli
npm update -g @vue/cli

// 检查安装是否成功
vue --version

创建vue项目

1
2
3
4
5
6
7
8
vue create pro-name     // 创建项目

vue add router // 添加router

vue add vuex // 添加vuex

vue ui // 开启可视化项目管理


安装常用依赖

避坑指南

  • less、sass安装;
    1
    2
    3
    4
    5
    // 两种loader都有版本过高问题,less-loader可用5.0,sass-loader可用7.0
    npm i less less-loader@5 -D
    npm i sass sass-loader@7 -D

    // sass用于代码中是应写为scss
  • element-ui
    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
    // 安装
    npm i element-ui -S

    // cdn引入

    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>

    cdn用unpkg.com建议控制版本号(element-ui@2.15.7)
    <script src="https://unpkg.com/element-ui@2.15.7/lib/index.js"></script>

    // 引入
    全局引入
    -- main.js
    import ElementUI from 'element-ui';
    import 'element-ui/lib/theme-chalk/index.css';
    Vue.use(ElementUI);

    Vue.use(ElementUI, { size: 'small', zIndex: 3000 }) // 全局配置,目前支持 size 与 zIndex 字段

    按需引入
    import { Button, Select } from 'element-ui'
    Vue.component(Button.name, Button);
    Vue.component(Select.name, Select);
    /* 或写为
    * Vue.use(Button)
    * Vue.use(Select)
    */

    无需配置.babelrc
  • 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
    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
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    // request.js
    /**
    * 1.配置axios
    * 1. baseURL、timeOut
    * 2. 请求拦截:配置headers(token)
    * 3. 响应拦截:token过期退出登录、状态码处理、错误信息提示
    * 2.loading配置
    * */

    // process.env

    import axios from "axios";

    // 引入store,控制token、登出
    import store from "../store";

    // 引入router控制退出跳转login
    import router from "../router";

    // 引入message提示组件
    import { Message, Loading } from "element-ui";

    // loading控制
    let requestCount = 0
    let loading = null

    function addRequestCount() {
    requestCount++
    loading = Loading.service({
    background: "rgba(0,0,0,0.3)",
    spinner: "my-loading-spinner",
    customClass: "my-loading", // 可新建css文件,在main.js引入,覆盖默认样式
    text: "加载中..."
    })
    }

    function subRequestCount() {
    requestCount--
    if (requestCount < 0) requestCount = 0
    if (requestCount == 0) {
    loading.close()
    }
    }

    // process.env
    const ins = axios.create({
    baseURL: '',
    timeout: 5000
    })
    // ins.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'

    // 请求拦截
    ins.interceptors.request.use(config => {
    addRequestCount()
    console.log(store.getters.token)
    const token = store.getters.token || ''
    token && (config.headers.Authorization = token)
    return config
    }, error => Promise.error(error))

    // 响应拦截
    ins.interceptors.response.use(res => {
    subRequestCount()
    return res.status === 200 ? Promise.resolve(res.data) : Promise.reject(res)
    }, error => {
    subRequestCount()
    const { response } = error
    if (response) {
    // 请求已发出,但是不在2xx的范围
    errorHandle(response.status, response.data.message);
    return Promise.reject(response);
    } else {
    // 处理断网的情况
    // eg:请求超时或断网时,更新state的network状态
    // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
    // 关于断网组件中的刷新重新获取数据,会在断网组件中说明
    store.commit('changeNetwork', false);
    }
    })



    /**
    * 跳转登录页
    * 携带当前页面路由,以期在登录页面完成登录后返回当前页面
    */
    const toLogin = () => {
    router.replace({
    path: '/login',
    query: {
    redirect: router.currentRoute.fullPath
    }
    });
    }

    /**
    * 请求失败后的错误统一处理
    * @param {Number} status 请求失败的状态码
    */
    const errorHandle = (status, other) => {
    // 状态码判断
    switch (status) {
    // 401: 未登录状态,跳转登录页
    case 401:
    toLogin();
    break;
    // 403 token过期
    // 清除token并跳转登录页
    case 403:
    Message({
    message: '登录过期,请重新登录',
    type: 'error'
    })
    localStorage.removeItem('token');
    store.commit('loginOut');
    setTimeout(() => {
    toLogin();
    }, 1000);
    break;
    // 404请求不存在
    case 404:
    Message({
    message: '请求的资源不存在',
    type: 'error'
    })
    break;
    default:
    console.log(other);
    }
    }

    export default ins
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // api.js
    import request from '../utils/request'

    export const getList = () => {
    return request({
    method: 'get',
    url: '/web-info.php'
    })
    }
    export const getToken = () => {
    return request({
    method: 'get',
    url: '/web-info.php'
    })
    }
  • FileManagerPlugin 自动打包插件
    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
    // 安装:npm i filemanager-webpack-plugin -D
    // 配置:vue.config.js
    const FileManagerPlugin = require('filemanager-webpack-plugin')
    const envName = process.env.VUE_APP_CURRENTMODE
    const dirName = envName === 'test' ? 'dist-test' : 'dist-pro' // dirName区分环境设置对应打包目录
    configureWebpack: config => { //webpack的相关配置在这里
    if (process.env.NODE_ENV !== 'development') { // 判断非开发环境才打包,否则serve也会打包
    config.plugins.push(
    new FileManagerPlugin(
    {
    events: {
    onEnd: {
    delete: ['./' + dirName + '.zip'], //首先需要删除项目根目录下的dirName.zip
    archive: [{ //然后我们选择dist文件夹将之打包成dirName.zip并放在根目录
    source: './' + dirName,
    destination: './' + dirName + '.zip'
    }]
    }
    }
    }
    )
    )
    }
    }
    // configureWebpack的原始写法
    configureWebpack: { //webpack的相关配置在这里
    plugins: [
    new FileManagerPlugin({ //初始化 filemanager-webpack-plugin 插件实例
    events: {
    onEnd: {
    mkdir: ['./' + dirName],
    delete: [ //首先需要删除项目根目录下的dist.zip
    './' + dirName + '.zip',
    ],
    archive: [ //然后我们选择dist文件夹将之打包成dist.zip并放在根目录
    { source: './' + dirName, destination: './' + dirName + '.zip' },
    ]
    }
    }
    })
    ]
    }
  • terser-webpack-plugin 去除console、debugger
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // npm i terser-webpack-plugin -D
    configureWebpack: config => { //webpack的相关配置在这里
    if (process.env.NODE_ENV !== 'development') { // 判断非开发环境才打包,否则serve也会打包
    config.plugins.push(
    new TerserPlugin({
    terserOptions: {
    ecma: undefined,
    warnings: false,
    parse: {},
    compress: {
    drop_console: true, // 去除console
    drop_debugger: true, // 去除debugger
    pure_funcs: ['console.log'] // 移除console
    }
    },
    })
    )
    }
    }

  • vue.config.js
  • .env
    • .env.development
    • .env.production
    • .env.test
      1
      2
      3
      4
      5
      6
      7
      8
      9
      // 默认参数
      BASE_URL = ""
      NODE_ENV = "development" //表明这是开发环境

      // 可配置参数
      以VUE_APP_ 开头
      VUE_APP_TITLE = "自定义标题"
      VUE_APP_BASE_URL = 'http://www.baidu.com'
      VUE_APP_CURRENTMODE = 'development'

      process.env获取环境变量

  • package.json
    1
    2
    3
    4
    5
    6
    7
    // 根据.env.development;.env.production,.env.test环境文件
    "scripts": {
    "serve": "vue-cli-service serve", // 开发环境本地服务
    "build": "vue-cli-service build --mode production", // 生产环境打包
    "build:test": "vue-cli-service build --mode test", // 测试环境打包
    "lint": "vue-cli-service lint"
    }

    打包不同环境到不同文件夹,配置vue.config.js:outputDir: envName === ‘test’ ? ‘dist-test’ : ‘dist-pro’,

  • 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
    27
    28
    29
    30
    31
    32
    33
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import HomeView from '../views/HomeView.vue'
    import PageRouter from './page/'
    import ViewsRouter from './views/'

    Vue.use(VueRouter)

    const routes = [
    {
    path: '/',
    name: 'home',
    component: HomeView
    },
    {
    path: '/about',
    name: 'about',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
    }
    ]

    const router = new VueRouter({
    routes: [...routes, ...PageRouter, ...ViewsRouter]
    })

    Router.beforeEach((ro, from, next) => {
    next()
    })

    export default router
  • vuex
    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
    // index.js
    import Vue from 'vue'
    import Vuex from 'vuex'

    import userStore from './user'

    import getters from './getters'

    Vue.use(Vuex)

    export default new Vuex.Store({
    state: {
    },
    getters,
    mutations: {
    },
    actions: {
    },
    modules: {
    userStore
    }
    })
    -----------------------------------------
    // user.js
    import { getToken } from '@/api/index'

    const userStore = {
    state: {
    token: 'null'
    },
    mutations: {
    SAVETOKEN(state, token){
    state.token = token
    localStorage.setItem('token', token)
    }
    },
    actions: {
    getToken({ commit }, data) {
    return new Promise((resolve, reject) => {
    getToken(data).then(res => {
    commit('SAVETOKEN', data)
    resolve(res)
    }).catch(err => {reject(err)})
    })
    }
    }
    }

    export default userStore
    ----------------------------------------------
    // getters.js
    export default {
    token: state => state.userStore.token
    }
    ----------------------------------------------=
    // 页面中应用
    import { mapActions, mapGetters, mapMutations, mapState } from 'vuex'
    computed: {
    ...mapGetters(['token']),
    ...mapState({
    token: state => state.userStore.token
    })
    }
    methods: {
    ...mapActions(['getToken']),
    ...mapMutations(['SAVETOKEN']),
    handleGetList () {
    getList().then(res => {
    console.log(res)
    })
    },
    handleGetToken () {
    this.getToken('token-123123').then(res => {
    console.log(res)
    this.SAVETOKEN('token-3737')
    })
    }
    }