zoukankan      html  css  js  c++  java
  • vue+ts搭建项目

    Tip: 为了避免浪费您的时间,本文符合满足以下条件的同学借鉴参考

    1.本文模版不适用于小型项目,两三个页面的也没必要用vue
    2.对typescriptvue全家桶能够掌握和运用

    此次项目模版主要涉及的技术框架:
    1. vue2.5
    2. vuex3.0
    3. vue-router3.0
    4. axios
    5. typescript3.2

    Tip: 由于vue-cli3.0帮我们简化了webpack的配置,我们只需要在根目录下的vue.config.js文件的chainWebpack进行配置即可。

    接下来进入正题(前提是你已经安装并配置好了node环境)

    一.初始化项目

    安装vue-cli3.0

    如果你之前安装的是vue-cli2.0版本的,那么你需要先卸载2.0版本,再安装3.0版本

    // cnpm 为淘宝镜像
    cnpm uninstall vue-cli -g // 卸载
    cnpm install -g @vue/cli // 安装新版本
    
    创建项目
    vue create vue-cli3-tpl
    

    选择Manually select features回车,按照下图所示选中(空格选中)回车安装插件

     


    然后一路回车,放一下配置图

     
    配置图


    等安装完之后,进入项目并启动项目

    cd vue-cli3-tpl
    cnpm run serve
    

    启动后显示如下,第一步完成。

     
    启动成功显示界面

    二.删除不必要的文件

    1.删除assetscomponentsviews目录下的所有文件。
    2.删除./src/store.ts
    3.删除./src/router.ts

    三.添加并配置文件

    1.添加文件夹并创建文件

    1.在根目录下创建scripts文件夹,并添加template.jscomponent.js
    2.在./src目录下创建api文件夹
    3.在./src目录下创建config文件夹,并添加index.tsrequestConfig.ts
    4.在./src目录下创建router文件夹,并添加index.tsrouter.ts
    5.在./src目录下创建store文件夹,并添加index.tsmodule文件夹
    6.在./src目录下创建types文件夹,并添加index.tscomponents文件夹和views文件夹
    7.在./src目录下创建utils文件夹,并添加common.tsrequest.ts
    8.在./src/assets目录下创建imagesscss两个文件夹,并在scss文件夹下添加common.scssvariables.scss

    2.配置文件

    首先配置一下根目录下tslint.json,编码习惯还是根据团队的要求来,我自己关闭了很多在我看来很鸡肋的东西,下面是个人的配置,仅供参考

    {
      "defaultSeverity": "warning",
      "extends": [
        "tslint:recommended"
      ],
      "linterOptions": {
        "exclude": [
          "node_modules/**"
        ]
      },
      "rules": {
        "quotemark": false, // 字符串文字需要单引号或双引号。
        "indent": false, // 使用制表符或空格强制缩进。
        "member-access": false, // 需要类成员的显式可见性声明。
        "interface-name": false, // 接口名要求大写开头
        "ordered-imports": false, // 要求将import语句按字母顺序排列并进行分组。
        "object-literal-sort-keys": false, // 检查对象文字中键的排序。
        "no-consecutive-blank-lines": false, // 不允许连续出现一个或多个空行。
        "no-shadowed-variable": false, // 不允许隐藏变量声明。
        "no-trailing-whitespace": false, // 不允许在行尾添加尾随空格。
        "semicolon": false, // 是否分号结尾
        "trailing-comma": false, // 是否强象添加逗号
        "eofline": false, // 是否末尾另起一行
        "prefer-conditional-expression": false, // for (... in ...)语句必须用if语句过滤
        "curly": true, //for if do while 要有括号
        "forin": false, //用for in 必须用if进行过滤
        "import-blacklist": true, //允许使用import require导入具体的模块
        "no-arg": true, //不允许使用 argument.callee
        "no-bitwise": true, //不允许使用按位运算符
        "no-console": false, //不能使用console
        "no-construct": true, //不允许使用 String/Number/Boolean的构造函数
        "no-debugger": true, //不允许使用debugger
        "no-duplicate-super": true, //构造函数两次用super会发出警告
        "no-empty": true, //不允许空的块
        "no-eval": true, //不允许使用eval
        "no-floating-promises": false, //必须正确处理promise的返回函数
        "no-for-in-array": false, //不允许使用for in 遍历数组
        "no-implicit-dependencies": false, //不允许在项目的package.json中导入未列为依赖项的模块
        "no-inferred-empty-object-type": false, //不允许在函数和构造函数中使用{}的类型推断
        "no-invalid-template-strings": true, //警告在非模板字符中使用${
        "no-invalid-this": true, //不允许在非class中使用 this关键字
        "no-misused-new": true, //禁止定义构造函数或new class
        "no-null-keyword": false, //不允许使用null关键字
        "no-object-literal-type-assertion": false, //禁止object出现在类型断言表达式中
        "no-return-await": true, //不允许return await
        "arrow-parens": false, //箭头函数定义的参数需要括号
        "adjacent-overload-signatures": false, //  Enforces function overloads to be consecutive.
        "ban-comma-operator": true, //禁止逗号运算符。
        "no-any": false, //不需使用any类型
        "no-empty-interface": true, //禁止空接口 {}
        "no-internal-module": true, //不允许内部模块
        "no-magic-numbers": false, //不允许在变量赋值之外使用常量数值。当没有指定允许值列表时,默认允许-1,0和1
        "no-namespace": [true, "allpw-declarations"], //不允许使用内部modules和命名空间
        "no-non-null-assertion": true, //不允许使用!后缀操作符的非空断言。
        "no-parameter-reassignment": true, //不允许重新分配参数
        "no-reference": true, // 禁止使用/// <reference path=> 导入 ,使用import代替
        "no-unnecessary-type-assertion": false, //如果类型断言没有改变表达式的类型就发出警告
        "no-var-requires": false, //不允许使用var module = require("module"),用 import foo = require('foo')导入
        "prefer-for-of": true, //建议使用for(..of)
        "promise-function-async": false, //要求异步函数返回promise
        "max-classes-per-file": [true, 2], // 一个脚本最多几个申明类
        "variable-name": false,
        "prefer-const": false // 提示可以用const的地方
      }
    }
    

    1.打开./scripts/template.js,并添加以下内容

    /*
     * @Description: 页面快速生成脚本
     * @Date: 2018-12-06 10:28:08
     * @LastEditTime: 2018-12-10 09:43:50
     */
    const fs = require('fs')
    const path = require('path')
    const basePath = path.resolve(__dirname, '../src')
    
    const dirName = process.argv[2]
    const capPirName = dirName.substring(0, 1).toUpperCase() + dirName.substring(1)
    if (!dirName) {
        console.log('文件夹名称不能为空!')
        console.log('示例:npm run tep ${capPirName}')
        process.exit(0)
    }
    
    /**
     * @msg: vue页面模版
     */
    const VueTep = `<template>
      <div class="${dirName}-wrap">
        {{data.pageName}}
      </div>
    </template>
    
    <script lang="ts" src="./${dirName}.ts"></script>
    
    <style lang="scss">
      @import './${dirName}.scss'
    </style>
    
    `
    
    // ts 模版
    const tsTep = `import { Component, Vue } from "vue-property-decorator"
    import { Getter, Action } from "vuex-class"
    import { ${capPirName}Data } from '@/types/views/${dirName}.interface'
    // import {  } from "@/components" // 组件
    
    @Component({})
    export default class About extends Vue {
      // Getter
      // @Getter ${dirName}.author
        
      // Action
      // @Action GET_DATA_ASYN
    
      // data
      data: ${capPirName}Data = {
        pageName: '${dirName}'
      }
    
      created() {
        //
      }
        
      activated() {
        //
      }
    
      mounted() {
        //
      }
    
      // 初始化函数
      init() {
        //
      }
        
    }
    `
    
    // scss 模版
    const scssTep = `@import "@/assets/scss/variables.scss";
    
    .${dirName}-wrap {
       100%;
    }
    `
    
    // interface 模版
    const interfaceTep = `// ${dirName}.Data 参数类型
    export interface ${capPirName}Data {
      pageName: string
    }
    
    // VUEX ${dirName}.State 参数类型
    export interface ${capPirName}State {
      data?: any
    }
    
    // GET_DATA_ASYN 接口参数类型
    // export interface DataOptions {}
    
    `
    
    // vuex 模版
    const vuexTep = `import { ${capPirName}State } from '@/types/views/${dirName}.interface'
    import { GetterTree, MutationTree, ActionTree } from 'vuex'
    import * as ${capPirName}Api from '@/api/${dirName}'
    
    const state: ${capPirName}State = {
      ${dirName}: {
       author: undefined
      }
    }
    
    // 强制使用getter获取state
    const getters: GetterTree<${capPirName}State, any> = {
      author: (state: ${capPirName}State) => state.${dirName}.author
    }
    
    // 更改state
    const mutations: MutationTree<${capPirName}State> = {
      // 更新state都用该方法
      UPDATE_STATE(state: ${capPirName}State, data: ${capPirName}State) {
        for (const key in data) {
          if (!data.hasOwnProperty(key)) { return }
          state[key] = data[key]
        }
      }
    }
    
    const actions: ActionTree<${capPirName}State, any> = {
      UPDATE_STATE_ASYN({ commit, state: ${capPirName}State }, data: ${capPirName}State) {
        commit('UPDATE_STATE', data)
      },
      // GET_DATA_ASYN({ commit, state: LoginState }) {
      //   ${capPirName}.getData()
      // }
    }
    
    export default {
      state,
      getters,
      mutations,
      actions
    }
    
    `
    
    // api 接口模版
    const apiTep = `import Api from '@/utils/request'
    
    export const getData = () => {
      return Api.getData()
    }
    
    `
    
    fs.mkdirSync(`${basePath}/views/${dirName}`) // mkdir
    
    process.chdir(`${basePath}/views/${dirName}`) // cd views
    fs.writeFileSync(`${dirName}.vue`, VueTep) // vue 
    fs.writeFileSync(`${dirName}.ts`, tsTep) // ts
    fs.writeFileSync(`${dirName}.scss`, scssTep) // scss
    
    process.chdir(`${basePath}/types/views`); // cd types
    fs.writeFileSync(`${dirName}.interface.ts`, interfaceTep) // interface
    
    process.chdir(`${basePath}/store/module`); // cd store
    fs.writeFileSync(`${dirName}.ts`, vuexTep) // vuex
    
    process.chdir(`${basePath}/api`); // cd api
    fs.writeFileSync(`${dirName}.ts`, apiTep) // api
    
    process.exit(0)
    

    2.打开./scripts/component.js,并添加以下内容

    /*
     * @Description: 组件快速生成脚本
     * @Date: 2018-12-06 10:26:23
     * @LastEditTime: 2018-12-10 09:44:19
     */
    
    const fs = require('fs')
    const path = require('path')
    const basePath = path.resolve(__dirname, '../src')
    
    const dirName = process.argv[2]
    const capPirName = dirName.substring(0, 1).toUpperCase() + dirName.substring(1)
    if (!dirName) {
      console.log('文件夹名称不能为空!')
      console.log('示例:npm run tep ${capPirName}')
      process.exit(0)
    }
    
    /**
     * @msg: vue页面模版
     */
    const VueTep = `<template>
      <div class="${dirName}-wrap">
        {{data.componentName}}
      </div>
    </template>
    
    <script lang="ts">
      import { Component, Vue, Prop } from "vue-property-decorator"
      import { Getter, Action } from 'vuex-class'
      import { ${capPirName}Data } from '@/types/components/${dirName}.interface'
      // import {  } from "@/components" // 组件
    
      @Component({})
      export default class About extends Vue {
        // prop
        @Prop({
          required: false,
          default: ''
        }) name!: string
    
        // data
        data: ${capPirName}Data = {
          componentName: '${dirName}'
        }
    
        created() {
          //
        }
        
        activated() {
          //
        }
    
        mounted() {
          //
        }
    
      }
    </script>
    
    <style lang="scss">
      @import "@/assets/scss/variables";
    
      .${dirName}-wrap {
         100%;
      }
    </style>
    
    `
    
    // interface 模版
    const interfaceTep = `// ${dirName}.Data 参数类型
    export interface ${capPirName}Data {
      componentName: string
    }
    
    `
    
    fs.mkdirSync(`${basePath}/components/${dirName}`) // mkdir
    
    process.chdir(`${basePath}/components/${dirName}`) // cd views
    fs.writeFileSync(`${dirName}.vue`, VueTep) // vue 
    
    process.chdir(`${basePath}/types/components`) // cd components
    fs.writeFileSync(`${dirName}.interface.ts`, interfaceTep) // interface 
    
    process.exit(0)
    

    3.打开./src/config/index.ts,并添加以下内容

    /** 
     * 线上环境
     */
    export const ONLINEHOST: string = 'https://xxx.com'
    
    /** 
     * 测试环境
     */
    export const QAHOST: string = 'http://xxx.com'
    
    /** 
     * 线上mock
     */
    export const MOCKHOST: string = 'http://xxx.com'
    
    /** 
     * 是否mock
     */
    export const ISMOCK: boolean = true
    
    /**
     * 当前的host  ONLINEHOST | QAHOST | MOCKHOST
     */
    export const MAINHOST: string = ONLINEHOST
    
    /**
     * 请求的公共参数
     */
    export const conmomPrams: any = {}
    
    /**
     * @description token在Cookie中存储的天数,默认1天
     */
    export const cookieExpires: number = 1
    
    

    4.打开./src/config/requestConfig.ts,并添加以下内容

    export default {
      getData: '/mock/5c09ca373601b6783189502a/example/mock', // 随机数据 来自 easy mock
    }
    

    5.打开./src/router/index.ts,并添加以下内容

    import Vue from 'vue'
    import Router from 'vue-router'
    import routes from './router'
    import { getToken } from '@/utils/common'
    
    Vue.use(Router)
    
    const router = new Router({
      routes,
      mode: 'history'
    })
    
    // 登陆页面路由 name
    const LOGIN_PAGE_NAME = 'login'
    
    // 跳转之前
    router.beforeEach((to, from, next) => {
      const token = getToken()
      if (!token && to.name !== LOGIN_PAGE_NAME) {
        // 未登录且要跳转的页面不是登录页
        next({
          name: LOGIN_PAGE_NAME // 跳转到登录页
        })
      } else if (!token && to.name === LOGIN_PAGE_NAME) {
        // 未登陆且要跳转的页面是登录页
        next() // 跳转
      } else if (token && to.name === LOGIN_PAGE_NAME) {
        // 已登录且要跳转的页面是登录页
        next({
          name: 'index' // 跳转到 index 页
        })
      } else {
        if (token) {
          next() // 跳转
        } else {
          next({
            name: LOGIN_PAGE_NAME
          })
        }
      }
    })
    
    
    // 跳转之后
    router.afterEach(to => {
      //
    })
    
    export default router
    

    6.打开./src/router/router.ts,并添加以下内容

    /**
     * meta 可配置参数
     * @param {boolean} icon 页面icon
     * @param {boolean} keepAlive 是否缓存页面
     * @param {string} title 页面标题
     */
    export default [
      {
        path: '/',
        redirect: '/index'
      },
      {
        path: '/login',
        name: 'login',
        component: () => import('@/views/login/login.vue'),
        meta: {
          icon: '',
          keepAlive: true,
          title: 'login'
        }
      },
      {
        path: '/index',
        name: 'index',
        component: () => import('@/views/index/index.vue'),
        meta: {
          icon: '',
          keepAlive: true,
          title: 'index'
        }
      }
    ]
    
    

    7.打开./src/store/index.ts,并添加以下内容

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state: {
        //
      },
      mutations: {
        //
      },
      actions: {
        //
      },
      modules: {
        // 
      }
    })
    
    

    8.打开./src/utils/common.ts,在此之前先下载js-cookie,并添加以下内容

    // 下载js-cookie
    cnpm i js-cookie --S
    cnpm install @types/js-cookie --D
    
    /*
     * @Description: 公共函数
     * @Author: asheng
     * @Date: 2018-12-07 11:36:27
     * @LastEditors: asheng
     * @LastEditTime: 2018-12-12 13:37:30
     */
    
    import Cookies from 'js-cookie'
    import { cookieExpires } from '@/config' // cookie保存的天数
    
    /**
     * @Author: asheng
     * @msg: 存取token
     * @param {string} token
     */
    export const TOKEN_KEY: string = 'token'
    export const setToken = (token: string) => {
      Cookies.set(TOKEN_KEY, token, { expires: cookieExpires || 1 })
    }
    export const getToken = () => {
      const token = Cookies.get(TOKEN_KEY)
      if (token) {
        return token
      } else {
        return false
      }
    }
    
    /**
     * @param {String} url
     * @description 从URL中解析参数
     */
    export const getParams = (url: string) => {
      const keyValueArr = url.split('?')[1].split('&')
      let paramObj: any = {}
      keyValueArr.forEach(item => {
        const keyValue = item.split('=')
        paramObj[keyValue[0]] = keyValue[1]
      })
      return paramObj
    }
    
    /**
     * 判断一个对象是否存在key,如果传入第二个参数key,则是判断这个obj对象是否存在key这个属性
     * 如果没有传入key这个参数,则判断obj对象是否有键值对
     */
    export const hasKey = (obj: any, key: string | number) => {
      if (key) {
        return key in obj
      } else {
        const keysArr = Object.keys(obj)
        return keysArr.length
      }
    }
    
    /**
     * @msg: 获取系统当前时间
     * @param {string} fmt 时间格式 具体看代码
     * @return: string
     */
    export const getDate = (fmt: any) => {
      let time = ''
      const date = new Date()
      const o: any = {
        "M+": date.getMonth() + 1, // 月份 
        "d+": date.getDate(), // 日 
        "H+": date.getHours(), // 小时 
        "m+": date.getMinutes(), // 分 
        "s+": date.getSeconds(), // 秒 
        "q+": Math.floor((date.getMonth() + 3) / 3), // 季度 
        "S": date.getMilliseconds() // 毫秒 
      }
      if (/(y+)/.test(fmt)) {
        time = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length))
      }
      for (const k in o) {
        if (new RegExp("(" + k + ")").test(fmt)) {
          time = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)))
        }
      }
      return time
    }
    
    /**
     * @msg: 获取系统当前时间
     * @param {string} date 时间
     * @param {string} fmt 时间格式
     * @return: string
     */
    export const formatDate = (date: any, fmt: string) => {
      let time = ''
      const o: any = {
        "M+": date.getMonth() + 1, // 月份 
        "d+": date.getDate(), // 日 
        "H+": date.getHours(), // 小时 
        "m+": date.getMinutes(), // 分 
        "s+": date.getSeconds(), // 秒 
        "q+": Math.floor((date.getMonth() + 3) / 3), // 季度 
        "S": date.getMilliseconds() // 毫秒 
      }
      if (/(y+)/.test(fmt)) {
        time = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length))
      }
      for (const k in o) {
        if (new RegExp("(" + k + ")").test(fmt)) {
          time = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)))
        }
      }
      return time
    }
    
    // copy in the 'fx-fuli' utils
    /**
     * 校验手机号是否正确
     * @param phone 手机号
     */
    
    export const verifyPhone = (phone: string | number) => {
      const reg = /^1[34578][0-9]{9}$/
      const _phone = phone.toString().trim()
      let toastStr = _phone === '' ? '手机号不能为空~' : !reg.test(_phone) && '请输入正确手机号~'
      return {
        errMsg: toastStr,
        done: !toastStr,
        value: _phone
      }
    }
    
    export const verifyStr = (str: string | number, text: string) => {
      const _str = str.toString().trim()
      const toastStr = _str.length ? false : `请填写${text}`
      return {
        errMsg: toastStr,
        done: !toastStr,
        value: _str
      }
    }
    
    // 截取字符串
    export const sliceStr = (str: any, sliceLen: number) => {
      if (!str) { return '' }
      let realLength = 0
      const len = str.length
      let charCode = -1
      for (let i = 0; i < len; i++) {
        charCode = str.charCodeAt(i)
        if (charCode >= 0 && charCode <= 128) {
          realLength += 1
        } else {
          realLength += 2
        }
        if (realLength > sliceLen) {
          return `${str.slice(0, i)}...`
        }
      }
    
      return str
    }
    
    
    /**
     * JSON 克隆
     * @param {Object | Json} jsonObj json对象
     * @return {Object | Json} 新的json对象
     */
    export function objClone(jsonObj: any) {
      let buf: any
      if (jsonObj instanceof Array) {
        buf = []
        let i = jsonObj.length
        while (i--) {
          buf[i] = objClone(jsonObj[i])
        }
        return buf
      } else if (jsonObj instanceof Object) {
        buf = {}
        for (let k in jsonObj) {
          buf[k] = objClone(jsonObj[k])
        }
        return buf
      } else {
        return jsonObj
      }
    }
    

    9.打开./src/utils/request.ts,先下载axios,并添加以下内容

    // 下载axios
    cnpm i axios --S
    
    import axios, { AxiosResponse, AxiosRequestConfig } from 'axios'
    import { MAINHOST, ISMOCK, conmomPrams } from '@/config'
    import requestConfig from '@/config/requestConfig'
    import { getToken } from '@/utils/common'
    import router from '@/router'
    
    declare type Methods = "GET" | "OPTIONS" | "HEAD" | "POST" | "PUT" | "DELETE" | "TRACE" | "CONNECT"
    declare interface Datas {
      method?: Methods
      [key: string]: any
    }
    const baseURL = process.env.NODE_ENV === 'production' ? MAINHOST : location.origin
    const token = getToken()
    
    class HttpRequest {
      public queue: any // 请求的url集合
      public constructor() {
        this.queue = {}
      }
      destroy(url: string) {
        delete this.queue[url]
        if (!Object.keys(this.queue).length) {
          // hide loading
        }
      }
      interceptors(instance: any, url?: string) {
        // 请求拦截
        instance.interceptors.request.use((config: AxiosRequestConfig) => {
          // 添加全局的loading...
          if (!Object.keys(this.queue).length) {
            // show loading
          }
          if (url) {
            this.queue[url] = true
          }
          return config
        }, (error: any) => {
          console.error(error)
        })
        // 响应拦截
        instance.interceptors.response.use((res: AxiosResponse) => {
          if (url) {
            this.destroy(url)
          }
          const { data, status } = res
          if (status === 200 && ISMOCK) { return data } // 如果是mock数据,直接返回
          if (status === 200 && data && data.code === 0) { return data } // 请求成功
          return requestFail(res) // 失败回调
        }, (error: any) => {
          if (url) {
            this.destroy(url)
          }
          console.error(error)
        })
      }
      async request(options: AxiosRequestConfig) {
        const instance = axios.create()
        await this.interceptors(instance, options.url)
        return instance(options)
      }
    }
    
    // 请求失败
    const requestFail = (res: AxiosResponse) => {
      let errStr = '网络繁忙!'
      // token失效重新登陆
      if (res.data.code === 1000001) {
        return router.replace({ name: 'login' })
      }
    
      return {
        err: console.error({
          code: res.data.errcode || res.data.code,
          msg: res.data.errmsg || errStr
        })
      }
    }
    
    // 合并axios参数
    const conbineOptions = (_opts: any, data: Datas, method: Methods): AxiosRequestConfig => {
      let opts = _opts
      if (typeof opts === 'string') {
        opts = { url: opts }
      }
      const _data = { ...conmomPrams, ...opts.data, ...data }
      const options = {
        method: opts.method || data.method || method || 'GET',
        url: opts.url,
        header: { 'user-token': token },
        baseURL
      }
      return options.method !== 'GET' ? Object.assign(options, { data: _data }) : Object.assign(options, { params: _data })
    }
    
    const HTTP = new HttpRequest()
    
    /**
     * 抛出整个项目的api方法
     */
    const Api = (() => {
      const apiObj: any = {}
      const requestList: any = requestConfig
      const fun = (opts: AxiosRequestConfig | string) => {
        return async (data = {}, method: Methods = "GET") => {
          if (!token) {
            console.error('No Token')
            return router.replace({ name: 'login' })
          }
          const newOpts = conbineOptions(opts, data, method)
          const res = await HTTP.request(newOpts)
          return res
        }
      }
      Object.keys(requestConfig).forEach((key) => {
        apiObj[key] = fun(requestList[key])
      })
    
      return apiObj
    })()
    
    export default Api as any
    
    

    10.打开./src/main.ts,引用common.scss

    import "@/assets/scss/common.scss"
    

    11.打开./src/App.vue,按照如下修改(未贴代码表示不变)

    <template>
        <div id="app">
            <keep-alive>
                <router-view v-if="$route.meta.keepAlive"/>
            </keep-alive>
            <router-view v-if="!$route.meta.keepAlive"/>
        </div>
    </template>
    

    12.还有非常重要的一步,打开根目录下的vue.config.js,如果没有就自己创建一个,放在根目录下,并添加以下内容

    const path = require('path')
    
    const resolve = dir => {
      return path.join(__dirname, dir)
    }
    
    // 线上打包路径,请根据项目实际线上情况
    const BASE_URL = process.env.NODE_ENV === 'production' ? '/' : '/'
    
    module.exports = {
      baseUrl: BASE_URL,
      outputDir: 'dist', // 打包生成的生产环境构建文件的目录
      assetsDir: '', // 放置生成的静态资源路径,默认在outputDir
      indexPath: 'index.html', // 指定生成的 index.html 输入路径,默认outputDir
      pages: undefined, // 构建多页
      productionSourceMap: false, // 开启 生产环境的 source map?
      chainWebpack: config => {
        // 配置路径别名
        config.resolve.alias
          .set('@', resolve('src'))
          .set('_c', resolve('src/components'))
      },
      css: {
        modules: false, // 启用 CSS modules
        extract: true, // 是否使用css分离插件
        sourceMap: false, // 开启 CSS source maps?
        loaderOptions: {} // css预设器配置项
      },
      devServer: {
        port: 8080, // 端口
        proxy: 'https://www.easy-mock.com' // 设置代理
      }
    }
    

    13.打开根目录下的package.json,在scripts中添加如下代码

    "scripts": {
        ...
        "tep": "node scripts/template",
        "com": "node scripts/component"
    }
    

    四.编写业务代码

    1.编写page页

    运行我们之前添加的脚本命令,创建page,也就是运行之前写的template.js这个脚本,实现快速创建我们所需要的page模版,而不需要一个一个的再创建,大大节省了时间,如果不用用脚本跑也是可以的,分别需要创建以下文件夹:

    • views文件夹下创建login文件夹,再向login文件夹下添加login.vuelogin.tslogin.scss
    • ./src/api下添加login.ts
    • ./src/store/module下添加login.ts
    • ./src/types/views下添加login.interface.ts

    是不是非常繁琐,还可能搞错(不推荐,浪费时间 0.0),而使用脚本只需要在命令行敲一条命令搞定(推荐)如下(根据demo需求,我们创建两个页面index、login):

    cnpm run tep index
    cnpm run tep login
    

    打开./src/views/login/login.ts,发现报错,没有安装模块vuex-class,安装一下就好了

     cnpm i vuex-class --S
    

    再运行创建组件脚本,随意创建一个test组件

    cnpm run com test
    

    ok,这时候发现./src/components目录下创建了test组件,为了引用组件更方便看起来更优雅,我们在./src/components目录下添加一个index.ts,把所有组件都引入到这里,作为一个中转文件,如下:

    import Test from './test/test.vue'
    
    export {
      Test
    }
    
    引用组件

    上面创建好了组件后,打开./src/views/login/login.ts,如下引用:

    import { Test } from "@/components" // 组件
    
    @Component({
        components: {
            Test
        }
    })
    

    ./src/views/login/login.vue中添加组件

    <template>
        <div class="login-wrap">
            {{data.pageName}}
            <div>
                <Test></Test>
            </div>
        </div>
    </template>
    

    这时候页面显示为如下:

     
    组件示意图

    调用http请求示例

    最后再说一下怎么调用http请求吧,在这之前,先重启一遍服务

    cnpm run serve
    

    按照我的步骤来,启动是不会报错的,如果报错,那么可以留言看到会回复,或者重新走一遍。
    没问题的话,我们按照如下步骤:
    1.打开./src/config/requestConfig.ts文件添加接口,由于我们之前的添加过了,那么我们进行下一步。
    2.打开./src/api/login.ts文件添加请求函数,我们之前也写好了,跳过。
    3.进入./src/store/module/login.ts文件,把GET_DATA_ASYN函数的注释打开。
    4.在./src/store/index.ts中的module添加login,如下:

    // modules
    import Login from './module/login'
    import Index from './module/index'
    
    export default new Vuex.Store({
      ...
      modules: {
        Login,
        Index
      }
    }
    

    完成上述动作后就可以在任意页面调用了,我们打开./src/views/index/index.ts,如下调用:

    export default class About extends Vue {
      @Action GET_DATA_ASYN
    
      created() {
        this.GET_DATA_ASYN()
      }
    }
    

    到现在有没有发现不管怎么样路由都到不了index页面,是因为做了路由拦截,我们在cookies添加一个token,如下:

     
    token


    添加完之后,刷新就可以正常跳转到index了,(顺带一句,对于需要登录的项目路由拦截是很有必要的),这时候就可以发现在network里面发现我们的接口请求成功了,如下:

     
    接口请求成功示意图

    篇幅比较长,终于写完了,有问题大家可以提,有更好的优化方法欢迎提出来,共同进步。

    原文链接: https://www.jianshu.com/p/44500385abdd

  • 相关阅读:
    Java邮件开发(JavaMail)
    SQL删除所有表和数据
    Hyper-V由于虚拟机监控程序未运行
    .NET CORE IIS PUT和DELETE请求失败 405
    ReSharper 快捷键调试
    hyper-v提示监控未运行
    c#定义Ilist集合
    WindowsFormsApp1设置开机自启动
    SQL查询总数和分页数
    EF执行SQL语句
  • 原文地址:https://www.cnblogs.com/cczlovexw/p/11551334.html
Copyright © 2011-2022 走看看