Bootstrap

前端项目配置ts,axios,router,vuex

1.使用tslint.json,如下配置仅供参考
{
    "rules": {
        // TS特性
        "member-access": true, // 设置成员对象的访问权限(public,private,protect)
        "member-ordering": [// 设置修饰符顺序
            true,
            {
                "order": [ 
                    "public-static-field",
                    "public-static-method",
                    "protected-static-field",
                    "protected-static-method",
                    "private-static-field",
                    "private-static-method",
                    "public-instance-field",
                    "protected-instance-field",
                    "private-instance-field",
                    "public-constructor",
                    "protected-constructor",
                    "private-constructor",
                    "public-instance-method",
                    "protected-instance-method",
                    "private-instance-method"
                ]
            }
        ],
        "no-empty-interface":true,// 不允许空接口
        "no-parameter-reassignment":true,// 不允许修改方法输入参数
        "prefer-for-of":true,// 如果for循环中没有使用索引,建议是使用for-of

        // 功能特性
        "await-promise":true,// 不允许没有Promise的情况下使用await
        "curly":true,// if/for/do/while强制使用大括号
        "forin":true,// 使用for in语句时,强制进行hasOwnProperty检查
        "no-arg":true,// 不允许使用arguments.callee
        // "no-bitwise":true, // 不允许使用特殊运算符 &, &=, |, |=, ^, ^=, <<, <<=, >>, >>=, >>>, >>>=, ~
        "no-conditional-assignment":true,// do while/for/if/while 语句中将会对例如if(a=b)进行检查
        // "no-console":true,// 不允许使用console对象
        "no-debugger":true,// 不允许使用debugger
        "no-duplicate-super":true,// 不允许super() 两次使用在构造函数中
        "no-empty":true,// 函数体不允许空
        "no-eval":true,// 不允许使用eval
        "no-for-in-array":true,// 不允许对Array使用for-in
        "no-invalid-template-strings":true,// 只允许在模板字符串中使用${
        "no-invalid-this":true,// 不允许在class之外使用this
        "no-null-keyword":true,// 不允许使用null,使用undefined代替null,指代空指针对象
        "no-sparse-arrays":true,// 不允许array中有空元素
        "no-string-throw":true,// 不允许throw一个字符串
        "no-switch-case-fall-through":true,// 不允许case段落中在没有使用breack的情况下,在新启一段case逻辑
        "no-unsafe-finally":true,// 不允许在finally语句中使用return/continue/break/throw
        "no-unused-expression":true,// 不允许使用未使用的表达式
        "no-use-before-declare":true,// 在使用前必须声明
        "no-var-keyword":true,// 不允许使用var
        "radix":true,// parseInt时,必须输入radix精度参数
        "restrict-plus-operands":true,// 不允许自动类型转换,如果已设置不允许使用关键字var该设置无效
        "triple-equals":true,// 必须使用恒等号,进行等于比较
        "use-isnan":true,// 只允许使用isNaN方法检查数字是否有效

        // 维护性功能
        "indent":[true, "spaces", 4],// 每行开始以4个空格符开始
        "linebreak-style":[true,"CR/LF"],// 换行符格式 CR/LF可以通用使用在windows和osx
        "max-classes-per-file":[true,1],// 每个文件中可定义类的个数
        "max-file-line-count":[true,500],// 定义每个文件代码行数
        "max-line-length":[true,120],// 定义每行代码数
        "no-default-export":true,// 禁止使用export default关键字,因为当export对象名称发生变化时,需要修改import中的对象名。https://github.com/palantir/tslint/issues/1182#issue-151780453
        "no-duplicate-imports":true,// 禁止在一个文件内,多次引用同一module

        // 格式
        "align":[true,"parameters","arguments","statements","members","elements"],// 定义对齐风格
        "array-type":[true,"array"],// 建议使用T[]方式声明一个数组对象
        "class-name":true,// 类名以大驼峰格式命名
        "comment-format":[true, "check-space"],// 定义注释格式
        "encoding":true,// 定义编码格式默认utf-8
        "import-spacing":true,// import关键字后加空格
        "interface-name":[true,"always-prefix"],// interface必须以I开头
        "jsdoc-format":true,// 注释基于jsdoc风格
        "new-parens":true,// 调用构造函数时需要用括号
        "no-consecutive-blank-lines":[true,2],// 不允许有空行
        "no-trailing-whitespace": [// 不允许空格结尾
            true,
            "ignore-comments",
            "ignore-jsdoc"
        ],
        "no-unnecessary-initializer":true,// 不允许没有必要的初始化
        "variable-name":[true,"check-format",// 定义变量命名规则
            "allow-leading-underscore",
            "allow-trailing-underscore",
            "ban-keywords"]
    }
}

{
    "defaultSeverity": "warning",
    "extends": [
        "tslint:recommended"
    ],
    "linterOptions": {
        "exclude": [
            "node_modules/**",
        ]
    },
    "rules": {
        "quotemark": [
            true,
            "single"
        ],
        "indent": [
            true,
            "spaces",
            4
        ],
        "interface-name": false,
        "ordered-imports": false,
        "object-literal-sort-keys": false,
        "no-consecutive-blank-lines": false,
        "no-console": [
            false
        ],
        "only-arrow-functions": [
            false
        ],
        "member-access": [
            true,
            "no-public"
        ],
        "no-unused-expression": [
            true,
            "allow-fast-null-checks",
            "allow-new",
            "allow-tagged-template"
        ],
        "arrow-parens": [
            "as-needed"
        ],
        "max-line-length": [
            true,
            {
                "limit": 180,
                "ignore-pattern": "^import |^export {(.*?)} |import(.*?)"
            }
        ],
        "no-empty": [
            true,
            "allow-empty-catch",
            "allow-empty-functions"
        ],
        "space-before-function-paren": {
            "severity": "off"
        },
        "eofline": false,
        "no-shadowed-variable": false
    }
}
2.使用axios
import axios from 'axios';
import Qs from 'qs';

import { hideLoading, showLoading } from '../units/index'
const instance: any = axios.create({
    baseURL: process.env.VUE_APP_BASE_URL,
    timeout: 1000,
    validateStatus: function (status) {
        return status >= 200 && status < 300;
    },
    transformRequest: [function (data) {
        // 对 data 进行任意转换处理
        return Qs.stringify(data)
    }],
});

export enum ApiRequestMethod {
    get = 'get',
    post = 'post',
    delete = 'delete',
    put = 'put',
}
export interface ApiRequestParam {
    url: string;
    method?: ApiRequestMethod;
    data?: object;
    params?: object;
    headers?: object;
    timeout?: number;
    responseType?: string;
    [propName: string]: any;
}

// 添加请求拦截器
instance.interceptors.request.use((config: any) => {
    let { use } = config;
    // 1,加载loading
    // 2.配置Token
    // 在发送请求之前做些什么
    // config.headers.Authorization = Token;
    console.log(config);
    if (use.autoLoading) {
        showLoading();
    }
    return config;
}, (error: any) => {
    // 对请求错误做些什么
    return Promise.reject(error);
});

instance.interceptors.response.use((response: any) => {
    let { data, config } = response;
    // 对响应数据做点什么
    console.log(response, 'succ');
    hideLoading()
    // 是否请求成功
    if (data.statusCode === '000000') {
        return Promise.resolve(response);
    } else {
        data.desc ? alert(data.desc) : config.use.autoTipError ? alert(config.use.autoTipError) : '';
        return Promise.reject(response);
    }
    
}, (error: any) => {
    // 1.关闭loading
    // 2.全局处理报错信息
    const { response, status } = error
    if (response) {
        // 3.服务器的状态码处理(validateStatus配置相关):401 500 
        switch (status) {
            case 401: // 未登录

                break;

            case 403: // 拒绝访问

                break;
            case 404: // 请求失败

                break;
        }
    } else {
        // 是否未连接网络
        if (!window.navigator.onLine) {
            alert('请检查网络');
        }
    }
    return Promise.reject(error);
});


export const apiRequest = (
    params: ApiRequestParam,
    autoLoading: boolean = true,
    autoTipError: string = '',
    ) => {
    console.log(params, autoLoading, autoTipError, Qs.stringify({a:'9p掘金'}));
    instance.defaults.use = {
        autoLoading,
        autoTipError
    }
    return instance(params)
}

使用封装好的接口

// xx.ts
import { ApiRequestMethod, apiRequest } from '../axios';
const index = (params: any) => {
    return apiRequest({
        params,
        method: ApiRequestMethod.get,
        url: '/common/weather/get15DaysWeatherByArea?apiKey=ZZGPUJJ6eaae98f6de9f6019aab2909ef47612004bf3cd0',
    }, false, 'errrrrr')
};

export default {
    index,
};

// xxx.vue使用封装好的接口
import {getToken} from 'xx'
getToken({}).then().catch((err) =>{})
3.使用router
  {
    path: '/vip',
    name: 'vip',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  },

2.嵌套路由

  {
    path: '/test',
    name: 'test',
    component: () => import(/* webpackChunkName: "test" */ '../views/test.vue')
    children: [
      {
        path: '', // 第一个展示为空
        name: '',
        component: '',
      }
    ]
  },
    
  test.vue

    

我是test.vue组件

// 渲染子组件里面的内容

3.动态路由匹配

  {
    path: '/vip/:id',
    name: 'vip',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  },

// xx.vue
    this.$router.push({
      name: 'vip', // 路由名称,唯一
      params: { id: '123' } // 动态匹配id: http://localhost:8080/#/vip/123
    })

4.动态传参query

// 带查询参数,变成 /register?plan=private
this.$router.push({ name: 'vip', query: { plan: 'private' }})

5.操作路由的几种方式

this.$router.push() // 入栈
this.$router.replace({}) // 替换当前路由
this.$router.router.go(n) // 前进后退步数,更换指针位置,与 window.history.go(n)相似

6.路由组件传参

布尔模式:用于动态路由,模板渲染
对象模式:静态的时候有用
函数模式:
    {
      path: '/search',
      component: SearchUser,
      props: route => {
        return {
          	
          // 返回参数
        }
			}
    }

7.导航守卫

路由守卫: 写在routes配置里面
	beforeEnter:路由只独享这一个钩子,在rutes里配置;用的少

组件内的守卫: 注意:这类路由钩子是写在组件内部:
  beforeRouteEnter(to, from, next) 进入路由前,此时实例还没创建,无法获取到zhis
  beforeRouteUpdate (to, from, next) 路由更新时触发的钩子函数
  beforeRouteLeave(to, from, next) 离开当前路由,此时可以用来保存数据,或数据初始化,或关闭定时器等等

全局守卫:
router.beforeEach 全局前置守卫 // 这个用的多
router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用.
router.afterEach 全局后置钩子 进入路由之后 注意:不支持next(),只能写成这种形式router.afterEach((to, from) => {});

8.路由元信息:meta:any

9.滚动行为

// 这个功能只在支持 history.pushState 的浏览器中可用  
scrollBehavior (to, from, savedPosition) {
    // 浏览器的前进后退才会触发
    if (savedPosition) {
      return savedPosition;
    } else if (to.hash) { // 跳到hash的位置
      return {
        selector: to.hash,
        behavior: 'smooth',
      }
    } else {
      return {
        x: 0,
        y: 0,
      }
    }
    // return 期望滚动到哪个的位置
  }

10.使用vue-class-component提供的Component.registerHooks来注册钩子

import Component from 'vue-class-component'

Component.registerHooks([
  'beforeRouteEnter',
  'beforeRouteLeave',
  'beforeRouteUpdate' // for vue-router 2.2+
])

xx.vue
beforeRouteEnter(to: unknown, from: unknown, next: any) {
  console.log('beforeRouteEnter',to)
  next()
  // 在渲染该组件的对应路由被 confirm 前调用
  // 不!能!获取组件实例 `this`
  // 因为当守卫执行前,组件实例还没被创建
}
4.使用vuex
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  getters(){
    name(state){
      return val;
		}
  },
  mutations: {
    increment (state, val) {
      state.count++
    }
  },
  // 异步操作
  actions: {
    name(ctx, obj){}
  }
})

xx.vue使用辅助函数 js
import { mapState, mapMutations, mapGetters  } from 'vuex'
computed: {
  ...mapState([
    // 映射 this.count 为 store.state.count
    'count'
  ])
}
methods: {
	...mapMutations([name]),
  ...mapGetters([name]) // 类似于计算属性,组合,全局数据
}

2.命名空间

const moduleA = {
	namespaced: true,
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  namespaced: true,
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

xx.vue使用
computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions('some/nested/module', [
    'foo', // -> this.foo()
    'bar' // -> this.bar()
  ])
}

3.在ts中使用vuex-class

import Vue from 'vue'
import Component from 'vue-class-component'
import {
  State,
  Getter,
  Action,
  Mutation,
  namespace
} from 'vuex-class'
 
const someModule = namespace('path/to/module')
 
@Component
export class MyComp extends Vue {
  @State('foo') stateFoo
  @State(state => state.bar) stateBar
  @Getter('foo') getterFoo
  @Action('foo') actionFoo
  @Mutation('foo') mutationFoo
  @someModule.Getter('foo') moduleGetterFoo
 
  // If the argument is omitted, use the property name
  // for each state/getter/action/mutation type
  @State foo
  @Getter bar
  @Action baz
  @Mutation qux
 
  created () {
    this.stateFoo // -> store.state.foo
    this.stateBar // -> store.state.bar
    this.getterFoo // -> store.getters.foo
    this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
    this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true })
    this.moduleGetterFoo // -> store.getters['path/to/module/foo']
  }
}