• 开发实战[CLI-3.x]


    开发实战[CLI-3.x]

    安装配置

    下面已vuetao 为项目名称进行说明

    1. 安装脚手架

    npm install -g @vue/cli
    # OR
    yarn global add @vue/cli

    2. 创建项目:vue create vuetao 

    从官方拿过来一张图,为啥呢(他的好看),上下方向键移动,空格键选中,看自己需要了,一般 BabelRouter、Vuex、Css Pre-processors、Linter / Formatter 这几个为必选的了,下面的两个测试用的自己选择。

    3. 项目配置- vue.config.js

    • 编译相关配置
    // 基础路径:按自己的项目修改 线上: './.' 本地: '/'
    let publicPath = process.env.NODE_ENV === 'development' ? '/' : './.'
    module.exports = {
        publicPath,
      // https://cli.vuejs.org/zh/config/#lintonsave
      lintOnSave: true,
      outputDir: 'dist',// 编译后文件的目录名称
      assetsDir: 'static', // 编译后资源文件的目录
      //生产环境的 source map,可以看到打包前的源码,一般在线上有问题需要排查的时候需要
      productionSourceMap: false, 
      // 代理配置
      devServer: {
        disableHostCheck: true,// 这个不用配置,我这边内网穿透的时候加的
        port: 8085,
        publicPath,
        proxy: {
          '/api': {
            target: process.env.VUE_APP_BASE_URL,// 这个是你域名地址我加在env文件中调用了
            ws: true,
            changeOrigin: true,
            pathRewrite: {
              '^/api': '/api/'
            }
          }
        }
      },
    }
    View Code
    • 全局css 配置
    module.exports = {
      css: {
        loaderOptions: {
          // 设置 scss 公用变量文件
          sass: {
            prependData: `@import '~@/assets/style/public.scss';`
          }
        }
      },
    }
    View Code
    • 删除prefetch 降低带宽压力
    module.exports = {
      chainWebpack: config => {
         config.plugins
          .delete('prefetch')
          .delete('preload')
      }
    }
    /**
     * 删除懒加载模块的 prefetch preload,降低带宽压力
     * https://cli.vuejs.org/zh/guide/html-and-static-assets.html#prefetch
     * https://cli.vuejs.org/zh/guide/html-and-static-assets.html#preload
     * 而且预渲染时生成的 prefetch 标签是 modern 版本的,低版本浏览器是不需要的
     */
    View Code
    • alias 设置
    module.exports = {
      chainWebpack: config => {
          config.resolve.alias
          .set('@', resolve('src'))
          .set('@v', resolve('src/views'))
          .set('~', resolve('public'))
      }
    }
    View Code

    上面单个示例合起来就是咱们需要配置的文件

    使用CDN优化

        打包编译速度减少项目体积 | 此处配置参考d2Admin 作者

     1. 根目录创建cdn 依赖文件 

    // dependencies-cdn.js
    
    module.exports = [{
      name: 'vue',
      library: 'Vue',
      js: 'https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js',
      css: ''
    },
    {
      name: 'vue-router',
      library: 'VueRouter',
      js: 'https://cdn.jsdelivr.net/npm/vue-router@3.1.3/dist/vue-router.js',
      css: ''
    },
    {
      name: 'vuex',
      library: 'Vuex',
      js: 'https://cdn.jsdelivr.net/npm/vuex@3.1.2/dist/vuex.js',
      css: ''
    },
    {
      name: 'axios',
      library: 'axios',
      js: 'https://cdn.jsdelivr.net/npm/axios@0.19.0/dist/axios.min.js',
      css: ''
    },
    {
      name: 'better-scroll',
      library: 'BScroll',
      js: 'https://cdn.jsdelivr.net/npm/better-scroll@1.15.2/dist/bscroll.min.js',
      css: ''
    },
    {
      name: 'element-ui',
      library: 'ELEMENT',
      js: 'https://cdn.jsdelivr.net/npm/element-ui@2.13.0/lib/index.js',
      css: 'https://cdn.jsdelivr.net/npm/element-ui@2.13.0/lib/theme-chalk/index.css'
    },
    {
      name: 'nprogress',
      library: 'NProgress',
      js: 'https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js',
      css: 'https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.css'
    }
    ]
    View Code

     2. vue.config.js 配置

    const cdnDependencies = require('./dependencies-cdn') // cdn 依赖文件
    // 设置不参与构建的库
    let externals = {
      'vue': 'Vue',
      'vue-router': 'VueRouter',
      'vuex': 'Vuex',
      'axios': 'axios',
      'element-ui': 'ELEMENT'
    }
    cdnDependencies.forEach(packages => {
      externals[packages.name] = packages.library
    })
    // 引入文件的 cdn 链接
    const cdn = {
      css: cdnDependencies.map(e => e.css).filter(e => e),
      js: cdnDependencies.map(e => e.js).filter(e => e)
    }
    module.exports = {
       chainWebpack: config => {
         //添加 CDN 参数到 htmlWebpackPlugin 配置中
         config.plugin('html').tap(args => {
          if (process.env.NODE_ENV === 'production') {
            args[0].cdn = cdn
          } else {
            args[0].cdn = []
          }
          return args
        })
       }
    }
    View Code

     3. html 中引入cdn 链接

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
        <!-- 使用 CDN 加速的 CSS 文件,配置在 vue.config.js 下 -->
      <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
      <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style">
      <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
      <% } %>
      <!-- 使用 CDN 加速的 JS 文件,配置在 vue.config.js 下 -->
      <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
      <link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script">
      <% } %>
    </head>
    <body>
     <noscript>
        <strong>
          很抱歉,没有 JavaScript 支持。
        </strong>
     </noscript>
     <div id="app">
        xxx
     </div>
      <!-- 使用 CDN 加速的 JS 文件,配置在 vue.config.js 下 -->
      <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
      <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
      <% } %>
    </body>
    </html>
    View Code

    开启GzipGzip 压缩

    本来这些都是在项目使用的时候遇到需要优化的时候才会去寻求解决的,都是需要配置的我就当放一起一次性写出来了,以后查看也方便。

    1. 安装插件 compression-webpack-plugin

    npm install compression-webpack-plugin -D

    2. vue.config.js 配置

     文件开启Gzip,也可以通过服务端(如:nginx)(https://github.com/webpack-contrib/compression-webpack-plugin)

    const CompressionPlugin = require('compression-webpack-plugin') // Gzip
    
    module.exports = {
      configureWebpack: config => {
        let pluginsPro = [
          new CompressionPlugin({ 
            filename: '[path].gz[query]',
            algorithm: 'gzip',
            test: new RegExp('\.(' + ['js', 'css'].join('|') + ')$'),
            threshold: 10240,
            deleteOriginalAssets: false, // 不删除源文件
            minRatio: 0.8
          })
        ]
        const configNew = {}
        // 非开发环境
        if (process.env.NODE_ENV !== 'development') {
           config.plugins = [ ...pluginsPro]
        }
        return configNew
      }
    }
    View Code

    顺带再加个webpack 去掉console和简要的文档说明,和这个压缩在同一个地方,我就直接在上面的配置中加了

    const CompressionPlugin = require('compression-webpack-plugin') // Gzip
    
    module.exports = {
      configureWebpack: config => {
        let pluginsPro = [
          new CompressionPlugin({ 
            filename: '[path].gz[query]',
            algorithm: 'gzip',
            test: new RegExp('\.(' + ['js', 'css'].join('|') + ')$'),
            threshold: 10240,
            deleteOriginalAssets: false, // 不删除源文件
            minRatio: 0.8
          })
        ]
        const configNew = {}
        // 非开发环境
        if (process.env.NODE_ENV !== 'development') {
          configNew.externals = externals
          config.optimization.minimizer[0].options.terserOptions.compress.warnings = false
          config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
          config.optimization.minimizer[0].options.terserOptions.compress.drop_debugger = true
          config.optimization.minimizer[0].options.terserOptions.compress.pure_funcs = ['console.log']
          config
            .optimization.splitChunks = {
              chunks: 'all',
              cacheGroups: {
                libs: {
                  name: 'chunk-libs',
                  test: /[\/]node_modules[\/]/,
                  priority: 10,
                  chunks: 'initial' // only package third parties that are initially dependent
                },
                elementUI: {
                  name: 'chunk-elementUI', // split elementUI into a single package
                  priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
                  test: /[\/]node_modules[\/]_?element-ui(.*)/ // in order to adapt to cnpm
                },
                commons: {
                  name: 'chunk-commons',
                  test: resolve('src/components'), // can customize your rules
                  minChunks: 2, //  minimum common number
                  priority: 5,
                  reuseExistingChunk: true
                }
              }
            }
          config.optimization.runtimeChunk = 'single'
          config.plugins = [...config.plugins, ...pluginsPro]
        }
        return configNew
      }
    }
    View Code

    路由配置

    • 路由钩子配置(路由拦截权限判断处理)
    • 路由文件配置
    • 公共路由文件配置(404,重定向,路由刷新配置等)
    • 动态路由配置 (一般是权限控制后端控制菜单路由,其他动态匹配了)

    路由钩子配置

    // src/router/index.js
    
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    // 进度条-需要安装插件,不用的话需要删除
    import NProgress from 'nprogress'
    import 'nprogress/nprogress.css'
    
    // 路由数据
    import routes from './routes' // 静态路由文件,包括公共路由,404 路由
    if (process.env.NODE_ENV === 'development') {
      Vue.use(VueRouter)// 开发环境不用cdn,不配置cdn的话直接使用不用判断
    }
    // 路由白名单
    let WhiteList = routes.map(item=>item.path)
    
    // 导出路由 在 main.js 里使用
    const router = new VueRouter({
      routes
    })
    // 路由钩子,访问路由
    router.beforeEach((to, from, next) => {
      // 开始进度条
      NProgress.start()
      // 这地方可对权限什么的进行判断
      if(!WhiteList.includes(to.path)) {// 白名单内的不需要校验权限相关直接next
        //登陆判断 暂时以是否存在token作为示例
         const token = util.cookies.get('token') 
        // token这边存在cokkies 具体封装cokkies下面专门说明
         if(token) {
           // 这边就可以对通过登陆的用户进行动态路由权限的校验处理
           // 主要使用 router.addRoutes(array) 权限路由设计处理有点多,这地方不做说明了下面专门写
            next()
         }else {
           if (to.path === '/login') {
            // 如果是登录页面路径,就直接next()
            next()
           }else {
            // 不然就跳转到登录;
            next({
              name: 'login',
              query: {
                redirect: to.fullPath
              }
            })
            NProgress.done()
           }
         }
      }else {
       next()
      }
    })
    // 路由钩子,访问之后
    router.afterEach(to => {
       // 结束进度条
      NProgress.done()
    })
    export default router
    View Code

     公共路由元配置

    // src/router/routes.js   这个js 中会设计到相关的一些组件文件,我就不写出来了
    const frameIn = [
      {
          path: '/',
            redirect: {
                name: 'index'
            },
            component: () => import(/* webpackChunkName: 'layout' */ '@/layout'),// 主体布局组件
         children: [
            {
                path:'index',
                name:'index',
                meta: {
                    auth: true
                },
                component:()=>import('@/system/index')
            },
            // 刷新页面 
            {
            path: 'refresh',
            name: 'refresh',
            hidden: true,
            component: {
                beforeRouteEnter(to, from, next) {
                next(vm => vm.$router.replace(from.fullPath))
                },
                render: h => h()
            }
            },
            // 页面重定向
            {
            path: 'redirect/:route*',
            name: 'redirect',
            hidden: true,
            component: {
                beforeRouteEnter(to, from, next) {
                next(vm => vm.$router.replace(JSON.parse(from.params.route)))
                },
                render: h => h()
            }
            }
        ]
      }
    ]
    
    const frameOut = [
      {
        path:'/login',
        name:'login',
        component: () => import('@/system/login')
      }
    ]
    const errorPage = [
      {
        path:'*',
        name:'404',
        component: () => import('@/system/error/404')
      }
    ]
    
    export default = [ ...frameIn, ...frameOut, ...errorPage ]
    View Code

    路由文件定义,主要是为了后端权限控制返回的时候好匹配,也为了防止组件多了不易查找

    // src/router/routerMap.js
    const demo = {
      // 为了防止名称重复,这个地方命名可以以:内容形式+后端控制器+方法名去命名,如下
      list_demo_page1: () => import(/* webpackChunkName: 'demo' */ '@/views/demo/page1'),
      list_demo_page2: () => import(/* webpackChunkName: 'demo' */ '@/views/demo/page2'),
      list_demo_page3: () => import(/* webpackChunkName: 'demo' */ '@/views/demo/page3')
    }
    export default file => {
      return {
        ...demo,// 上面定义几个这地方引入几个,一般一个大模块定义一个
      }[file] || null
    }
    View Code

    路由跳转

    <template>
        <div>
                <!-- 页面中-大体相当于a标签 -->
                <router-link to ="/home">返回首页</router-link> 
                <!-- 事件操作跳转 -->
                <button @click="handleClickReturn">返回首页</button>
        </div>
    </template>
    
    <script>
    export default {
        methods: {
        handleClickReturn() {
            this.$router.push('/home') // 跳转路由
          this.$router.replace('/home') // 路由替换导航后不会留下 history 记录
        }
      } 
    }
    </script>
    View Code

    路由传参于接收

    总的来说有两种,一种是 query另一种是params 效果类型于 get和post 的区别,url 中看到于看不到传递的参数

    <!-- 页面中-传参 命名的路由 -->
    <router-link :to="{ name: 'user', params: { id: 123 }}">User</router-link>
    
    <!-- 带查询参数,下面的结果为 /register?plan=private -->
    <router-link :to="{ path: 'register', query: { id: '122' }}">Register</router-link>
    
    
    <!--事件操作-传参 -->
    <button @click="handleClickReturn">返回首页</button>
    <script>
    export default {
        methods: {
        handleClickReturn() {
            this.$router.push('/home') // 跳转路由
          this.$router.replace('/home') // 路由替换
        }
      } 
    }
    </script>
    // 接收页面- 需要注意的是使用 route 没有r
    this.$route.query.id
    this.$route.params.id
    View Code

     

    动态匹配

    // 定义路由 router.js 
     {
         path: "/navigate/:name",
         name: "navigate",
         component: () => import(/* webpackChunkName: 'navigate' */ "@/views/navigate")
     }
    实现如:
    navigate/page1
    navigate/page2
    navigate/page3
    ...
    不同路由跳转到同一个文件,实现同一文件不同数据的渲染
    
    // 路由跳转
    this.$router.push({
      name: "navigate",
      params: {
        name: 'page1'
      }
    });
    
    // 接收页面 两种方法
    // 第一种: 路由导航钩子
     beforeRouteEnter(to, from, next) {
        next(vm => {
          to.params.name = vm.name;
        });
     }
    
    // 第二种: 监听路由变化执行函数
    created () {
        // 组件创建完后获取数据,
        // 此时 data 已经被 observed 了
        this.fetchData()
    },
    watch: {
          // 如果路由有变化,会再次执行该方法
        '$route': 'fetchData'
    },
    methods: {
     fetchData(e) {
        console.log(e) 
     }
    }
    View Code

    组件

    全局组件

    1. 组件定义
    创建公共全局组件文件 /src/components/index.js 
    
    // 异步加载组件
    const vueFiles = require.context('./tao-component', true, /component.vue$/)
    vueFiles.keys().forEach(key => {
      const component = vueFiles(key).default
      Vue.component(component.name, component)
    })
    
    const jsFiles = require.context('./tao-component', true, /component.js$/)
    jsFiles.keys().forEach(key => {
      const component = jsFiles(key).default
      Vue.component(component.name, component)
    })
    
    // 遍历 tao-component 目录下的所有 compoent.js 或 component.vue 文件注册为组件
    // 需要注意的一点是组件中的name一定要定义并与文件夹名保持一致作为组件的名称
    View Code
    1. 示例 tao-hello 组件
    创建文件 src/components/tao-component/tao-hello/component.vue
    
    <template>
        <div>Hello Tao</div>
    </template>
    <script>
    export default {
        name:'tao-hello' 
    }
    </script>
    View Code
    1. 全局注册
    main.js 中引入刚才创建的文件夹
    // 组件
    import '@/components'
    View Code
    1. 使用
    <tao-hello />

    组件传值及插槽

    1. 父组件传值 子组件接收
    //父组件 传递值不做限制,以子组件接收的类型为基准
    
    <tao-hello :title="父组件传递" />
    // 或者
    <tao-hello v-bind="父组件传递" />
    
    <!-- 子组件 文件 src/components/tao-component/tao-hello/component.vue -->
    
    <template>
        <div>
                <h1>Hello Tao</h1>
                <small>{{title}}</small>
        </div>
    </template>
    <script>
    export default {
        name:'tao-hello' ,
      props: { // 可以用数据如: props:['title']
        title: {
            type: String,
          default:null
        }
      }
    }
    </script>
    View Code
    1. 子组件给父组件传值与父组件接收
    // 子组件传递 
    <template>
        <div>
                <h1>Hello Tao</h1>
                <small>{{title}}</small>
                <button @click="handleClick">累加</button>
        </div>
    </template>
    <script>
    export default {
        name:'tao-hello' ,
      props: { // 可以用数据如: props:['title']
        title: {
            type: String,
          default:null
        }
      },
      data() {
        return  {
            count: 0 
        }
      },
      methods: {
        handleClick() {
            this.count++
          this.$emit('addEvent', this.count)
          // 子组件通过$emit 触发事件传递数据
        }
    }
    </script>
      
    // 父组件接收-通过事件监听接收
      <tao-hello :title="父组件传递" @addEvent="handleClickChi"/>
      <script>
    export default {
        name:'tao-hello-parent' ,
      methods: {
        handleClickChi(e) {
          console.log(e)
        }
    }
    </script>
    View Code
    1. 插槽 https://cn.vuejs.org/v2/guide/components-slots.html
    // 子组件
    <template>
        <div>
                <h1>Hello Tao</h1>
                <small>{{title}}</small>
                <button @click="handleClick">累加</button>
                <slot name="header"></slot>
          <slot></slot>         
        </div>
    </template>
    <script>
    export default {
        name:'tao-hello' ,
      props: { // 可以用数据如: props:['title']
        title: {
            type: String,
          default:null
        }
      },
      data() {
        return  {
            count: 0 
        }
      },
      methods: {
        handleClick() {
            this.count++
          this.$emit('addEvent', this.count)
          // 子组件通过$emit 触发事件传递数据
        }
    }
    </script>
      
    <!-- 注意 v-slot 只能添加在 <template> 上 -->
    
    <tao-hello :title="父组件传递" @addEvent="handleClickChi">
        <template v-slot:header>
        <p>这里是具名插槽-header</p>
      </template>  
      <template>
        <p>默认插槽</p>
      </template>
    </tao-hello>
      
    <!-- 另一种写法 可以使用子组件中的数据 -->
    <tao-hello :title="父组件传递" @addEvent="handleClickChi" v-slot="slotProps">
      <p>{{slotProps}}</p>
      <!-- 注意默认插槽的缩写语法不能和具名插槽混用 -->
    </tao-hello>
    <script>
    export default {
        name:'tao-hello-parent' ,
      methods: {
        handleClickChi(e) {
          console.log(e)
        }
    }
    </script>
    View Code

    深层组件传值与接收值

    场景:a,b,c 三个文件 传值 a->b->c(c->b->a)

    <!--page-a -->
    <page-a :title="title" @_setData="handleSetData"/>
    <script>
    data() {
     return {
      title: 'xx'
     }
    },
    methods: {
     handleSetData(e) {
       this.$emit('c页面传递', e)
     }
    }
    </script>
    
    <!--page-b -->
    
    <page-b v-bind="$attrs" v-on="$listeners"/>
    <script>
    created() {
        console.info(this.$attrs, this.$listeners)
    }
    </script>
    
    <!-- page-c -->
    <div>
      <button @click="handleClick">add</button>
    </div>
    <script>
    created() {
        console.log(this.$attrs)
    },
    methods: {
     handleClick() {
       this.$emit('_setData', 'xx')
     }
    }
    </script>
    View Code

    内置组件妙用

    介绍:使用 is 去绑定不同的组件,可根据js 动态切换组件

    <component :is="which_to_show" />
    <script>
    components: {
      demoPageOne:() => import('@/views/demo/page1')
    },
    data() {
        return {
            which_to_show:'demoPageOne'
      }
    }
    </script>
    View Code

     keep-alive

    比如当前列表页面需要详情,编辑,新增... 等每个都是一个组件,可能会打开很多次,这个时候就可以用keep-alive

    <!--路由缓存 -->
    <keep-alive>
      <component :is="which_to_show" ref="ruleFomOperate" />
    </keep-alive>
    <button @click="handleClickOpe('add')">add</button>
    <button @click="handleClickOpe('detail')">detail</button>
    <script>
    components: {
      demoPageOne:() => import('@/views/demo/page1'),
      demoPageTwo:() => import('@/views/demo/page2')
    },
    data() {
        return {
            which_to_show:'demoPageOne'
      }
    },
    methods: {
     // 根据操作类型的不同加载不同的组件
     handleClickOpe(type) {
       if(type === 'add') {
          this.which_to_show = 'demoPageOne'
       }else {
         this.which_to_show = 'demoPageTwo'
       }
     }
    }
    </script>
    View Code

    还有应用场景如:动态表单构建,动态首页配置等等

    状态管理

    • state -- 辅助函数 --  mapState -- 状态树
    • getter -- 辅助函数 --  mapGetters -- 从状态数中获得状态
    • action -- 辅助函数 -- mapActions -- 
    • mutations

    请求响应-axios

    封装

    // src/api/service.js
    
    import axios from 'axios'
    import {
      Message
    } from 'element-ui'
    // 记录和显示错误
    function errorLog(err) {
      // 打印到控制台
      if (process.env.NODE_ENV === 'development') {
        console.log(err)
      }
      // 显示提示
      Message({
        message: err.message,
        type: 'error',
        duration: 5 * 1000
      })
    }
    
    const service = axios.create()
    // 不用可省略此处
    service.interceptors.request.use(config => {
      // 可在此设置加载等待loading 当然不用loading可以不写这部分
    });
    service.interceptors.response.use(
      async response => {
        // 加了loading可在此数据响应后关闭
        if (response.status === 200) {
          const dataAxios = response.data
          if (dataAxios.code === 200) {
            // 正常返回数据
            return dataAxios
          } else {
            errorLog(dataAxios)
            return dataAxios
          }
        } else {
          return Promise.reject(response.msg)
        }
      },
      error => {
        if (error && error.response) {
          // 对常用的http 状态码进行判断
          switch (error.response.status) {
            case 400:
              error.message = '请求错误'
              break
            case 401:
              error.message = '未授权,请登录'
              break
            case 403:
              error.message = '拒绝访问'
              break
            case 404:
              error.message = `请求地址出错: ${error.response.config.url}`
              break
            case 408:
              error.message = '请求超时'
              break
            case 500:
              error.message = '服务器内部错误'
              break
            case 501:
              error.message = '服务未实现'
              break
            case 502:
              error.message = '网关错误'
              break
            case 503:
              error.message = '服务不可用'
              break
            case 504:
              error.message = '网关超时'
              break
            case 505:
              error.message = 'HTTP版本不受支持'
              break
            default:
              break
          }
        }else if(error.message.includes('timeout')) {
            // 请求超时状态
          error.message = '请求超时,请检查你的网络是否正常!'
        }
        errorLog(error)
        return Promise.reject(error)
      }
    )
    
    export function request(config) {
      let configDefault = {
        headers: {
          token: 'you-token',
          from: 'web',
          'Content-Type': 'application/json'
        },
        timeout: 5000,
    baseURL: process.env.VUE_APP_API,// 基础请求路径
    data: {}
      }
    return service(Object.assign(configDefault, config))
    }
    View Code

    API请求响应

    // 自动匹配modules 下的所有.js 文件
    src/api/index.js
    
    import { request } from './service'
    const files = require.context('./modules', false, /.js$/)
    const apiGenerators = files.keys().map(key => files(key).default)
    
    let api = {}
    apiGenerators.forEach(generator => {
      const apiInstance = generator({
        request
      })
      for (const apiName in apiInstance) {
        if (apiInstance.hasOwnProperty(apiName)) {
          api[apiName] = apiInstance[apiName]
        }
      }
    })
    
    export default api
    // modules 下的接口文件示例,modules 下的接口文件名称最好和你的页面文件夹名称一致,便于管理
    src/api/modules/test.js
    ...
    export default ({
      request
    }) => ({
      USER_LOGIN(data) {
        return request({
          url: '/login/cellphone',
          method: 'post',
          data
        })
      }
    })
    View Code

    注册插件

    由于api 文件整个系统中会多地方调用,比较常用的可以使用 Vue.use() 注册为插件全局使用,需要注意的是必须提供 install方法

    //src/utils/api.js
    
    import api from '@/api'
    
    export default {
      install(Vue, options) {
        Vue.prototype.$api = api
      }
    }
    // main.js
    import pluginApi from '@/utils/api'
    Vue.use(pluginApi)
    View Code

    UI 框架 Element-ui 学习总结

    安装配置 文档地址

    自定义主题

    直接通过浏览器中修改

    image.png

    image.png

    调整好你的主题色配置后下载

    本地项目中新建文件 element-variables.scss

    image.png

    需要注意的是 字体路径一定是需要的

    最后引入刚才新增的文件,原先的css 样式文件就可以去掉了

    image.png

     推荐学习框架 d2-admin: 地址: d2.pub

  • 相关阅读:
    Java Web总结十Jsp
    当前结果
    QFontMetrics的一个问题
    设想的用户交互流程
    多视图工作
    改进函数、变量的表示
    接口测试Session/Cookie笔记(二)
    接口测试笔记(一)
    创业公司心力交瘁
    禅道BUG管理工具使用链接存储
  • 原文地址:https://www.cnblogs.com/bigtreegrowth/p/12540201.html
Copyright © 2020-2023  润新知