项目模版封装优化及管理(vant,element,antdV,小程序)

 

1.模板管理

1.1 yeoman-environment插件

 

基于yeoman-generator 、yeoman-environment插件,将各类基础模板(vant、antdV、element、eapp)等做成基础模版,并可将该模版上传至npm进行管理。

参考链接:https://www.npmjs.com/package/generator-yo-template

 

项目模版封装优化及管理(vant,element,antdV,小程序)

 

2.模板生成

2.1 平台创建项目(前置条件--未完成)

通过平台,录入项目信息,通过接口在git创建对应的git项目,并初始化生成init.js文件

 

????init.js

 

项目模版封装优化及管理(vant,element,antdV,小程序)

 

 

2.2创建模版

执行指令

node init.js

 

 

 

2.3自动生成模板

 

项目模版封装优化及管理(vant,element,antdV,小程序)

 

 

3.模板介绍(antdV 版本)

 

 

3.1目录结构

 

  ├─assets            # 静态资源 
  │  └─img 
  ├─components        # 组件库
  │  └─global         # 全局组件 <放入后不需要注册也不需要引入>
  ├─decorator         # 装饰器
  ├─filter            # 过滤器
  ├─http              # 网络请求
  ├─layouts           # layout文件
  ├─plugins           # 插件库 如需要引入第三方组件等 <推荐使用cdn>
  ├─router            # 路由
  ├─store             # vuex
  │  └─modules        # vuex 模块
  ├─style             # 样式
  │  ├─css            # css 文件
  │  └─auto.css       # css 生成器自动生成(css-generator-plugin)
  ├─utils             # 工具库目录
  └─views             # 页面文件
    └─login             # 登陆逻辑处理

 

 

 

3.2 components组件库

存放组件库的目录。

components/global目录,主要存放全局通用组件,如loading,button,modal等

PS:其中globalcomponent.js会将该目录下所有.vue文件自动注册挂载到Vue上

项目模版封装优化及管理(vant,element,antdV,小程序)

 

 

 

3.3 decorator装饰器

3.3.1什么是装饰器

Decorator 是 ES7 的一个新语法,正如其“装饰器”的叫法所表达的,他可以对一些对象进行装饰包装然后返回一个
被包装过的对象,可以装饰的对象包括:类,属性,方法等

3.3.2为什么使用装饰器

适合用来做基础库的语法糖,用来简化上层代码 - 对重复工作量大的业务进行修饰,方便维护且代码简洁

3.3.2装饰器的原理是什么

Object.defineProperty()

 

3.3.4 如何使用装饰器

 

<div @click="deleteConfirm">删除</div>


//未使用装饰器的做法
methods: {
    deleteConfirm () {
      this.$confirm({
        title: '确认删除该条信息吗',
        okText: '确认',
        cancelText: '取消',
        onOk () {
          this.deleteApi()
        }
      })
    },
    async deleteApi () {
      await this.$apis.deleteConfirm()
    }
}

//使用装饰器的做法
methods: {
    @confirm('确认删除该条信息吗')
    async deleteApi () {
      await this.$apis.deleteConfirm()
    }
}
            

 

 

3.3.5 如何实现装饰器

decorator/index.js

/**
 * 提示装饰器
 * @param {String | Object} message 需要提示用户的信息 或者 confirm 的配置
 * @param {Function} errorFn 请求异常的回调 返回this 使用function 则为你绑定
 */
export function confirm (message, errorFn) {
  const defaultConf = {
    // primary ghost dashed danger link
    okType: 'danger',
    maskClosable: false
  }
  return function (target, name, descriptor) {
    const oldFn = descriptor.value
    descriptor.value = function (...args) {
      Modal.confirm(Object.assign(
        defaultConf,
        isString(message) ? { title: message } : message, // if use string then create Object else use Object to assign
        {
          onOk: () => oldFn.apply(this, args),
          onCancel: () => {
            // 无论如何都提示
            globalWarn(`用户点击了取消:${name}`)
            if (errorFn) {
              errorFn.call(this, this)
            }
          }
        }
      ))
    }
  }
}

 

 

3.3.6 装饰器适合哪些需求

目前团队内已使用的场景主要为:confirm,loading,log,debounceFn,throttleFn

 

 

3.4 dicts字典

 

// 默认提供的方法
/**
 * 字典
 * @example
 // 根据字典名,获取list(多用于下拉框),如[{key:1,name:'类型1'},{key:2,name:'类型2'}]
 * dict.getDictList('user')  
 
  // 根据字典名,获取对象,如{ 1: '内置类型1', 2: '内置类型2' }
 * dict.getDictObj('user')   
 
 // 根据字典名和key,获取value
 * dict.getDictItem('user',2))  
*/

// 字典对象
const dicts = {
  taskType: { 1: '内置类型1', 2: '内置类型2' }
}

 

 

3.5 filter过滤器

不改变原始数据的前提下,修改数据的展现形式

 

import Vue from 'vue'

/* 格式化金额 */
export const moneyFormat = (money) => {
  return money ? `${(money / 100).toFixed(2)}` : ''
}

Vue.filter('moneyFormat', moneyFormat)


// html 
<span>{{data.money | moneyFormat}}</span>

 

3.6 http

3.6.1 拦截器封装

 

// 请求头处理
// 1.一般为token、header等请求头统一处理
http.interceptors.request.use(
  config => {
    const token = session.getSession('token')
    if (token) config.headers.token = token.replace(/"/g, '')
    return config
  },
  error => {
    throw new Error(JSON.stringify(error))
  }
)


// 响应头处理
// 1.正常接口等统一处理
// 2.登陆权限拦截统一处理
// 3.指定错误逻辑统一处理
// 4.其他错误的统一处理

http.interceptors.response.use(
  response => {
    const res = response.data
    if (!res.success) { // 逻辑错误 自定义错误码
      if (res.errorMsg) {
        ErrorMessage(res.errorMsg)
      }
      globalError('接口调用失败')
      switch (res.errorCode) {
        case 401:
        case 403:
        case 405:
        case '-3':
          session.destroy('token')
          router.push({ name: 'login', query: { from: window.this.$route.name, params: window.this.$route.query } })
          break
        default:
          break
      }
      throw new Error(JSON.stringify(res))
    }
    return res.result
  },
  error => {
    // http error
    globalError(new Date(), 'err' + error) // for debug reject
    ErrorMessage(ERROR_MSG[error?.response?.status] || `连接出错(${error.response.status})!`) // 消抖
    throw new Error(JSON.stringify(error))
  }
)

 

 

3.6.2 get、post等方式封装

ps:通过高阶函数封装,api.js内不需要维护入参

// get 方法 也调用一次 toJSON
export const get = url => {
  return (params = {}) => {
    return new Promise((resolve, reject) => {
      http.get(url, { params: JSONClone(params), paramsSerializer: x => Qs.stringify(x, { arrayFormat: 'repeat' }) })
        .then(resolve)
        .catch(reject)
    })
  }
}

// post JSON 默认调用 toJSON
export const post = url => {
  return (data = {}) => {
    return new Promise((resolve, reject) => {
      http.post(url, data)
        .then(resolve)
        .catch(reject)
    })
  }
}
// post 表单 手动 toJSON
export const form = url => {
  return (data = {}) => {
    return new Promise((resolve, reject) => {
      // 达到和直接post json 一样的效果 先调用 toJSON
      http.post(url, Qs.stringify(JSONClone(data), { arrayFormat: 'repeat' }))
        .then(resolve)
        .catch(reject)
    })
  }
}

// temp post 但是拼接URL 临时使用
export const temp = url => {
  return (params = {}) => {
    return new Promise((resolve, reject) => {
      http.post(url, {}, { params })
        .then(resolve)
        .catch(reject)
    })
  }
}

 

3.6.3 apis.js统一管理

 

// apis.js
import {
  get,
  post,
  binary, // post 上传文件 (二进制文件)
  form, // post 表单
  temp, // 临时post 拼接URL
  put, // 上传文件
  download // 下载文件
} from '@/http/request'

export default {
  /* demo -- start */
  // 调用方式1(推荐使用)  this.$apis.demoGet({id: 1, sex: 2})
  demoGet: get('/isDemo/getApi'),
  demoPost: post('isDemo/postApi'),
  demoBinary: binary('isDemo/binaryApi'),
  demoForm: form('isDemo/formApi'),
  demoTemp: temp('isDemo/tempApi'),
  demoPut: put('isDemo/tempApi'),
  demoDownload: download('isDemo/tempApi')
  /* demo -- end */
}




// main.js
Vue.prototype.$apis = apis

 

 

3.7 layouts布局管理和router路由管理

 

设立思路:将布局和页面逻辑进行解耦。由page控制页面逻辑,layout控制布局类型

3.7.1 managerLayout

管理平台布局,可用于嵌套各类管理平台页面

 

3.7.2 baseLayout

基础布局,可用于登陆、无权限、临时性的移动端页面等

PS:如管理平台某个详情页 通过OA推送后,希望用户可以在钉钉侧边栏打开看到详情页面

 

3.7.3 router.js

// router.js
{ // base 下没有任何 外部layout
    path: '/base',
    name: 'base',
    redirect: { name: 'base_home' },
    component: baseLayout,
    children: [
      {
        path: 'depDetailCharts',
        name: 'base_depDetailCharts',
        component: () => import('@/pages/depDetailCharts'),
        meta: { title: '三级部门日志图表', breadcrumb: [{ name: 'base_home', title: '日志统计' }, { name: 'base_depLogCharts', title: '部门日志图表' }] }
      }
    ]
  },
  { // manager 下 有默认layout
    path: '/manager',
    name: 'manager',
    redirect: { name: 'manager_home' },
    component: managerLayout,
    children: [
      {
        path: 'depDetailCharts',
        name: 'manager_depDetailCharts',
        component: () => import('@/pages/depDetailCharts'),
        meta: { title: '三级部门日志图表', breadcrumb: [{ name: 'manager_home', title: '日志统计' }, { name: 'manager_depLogCharts', title: '部门日志图表' }] }
      }
    ]
  }
  
  
  // 不同layout的页面间交互跳转
  // 通过this.$route.matched[0].name 获取当前layout类型
  this.$router.push({ name: `${this.$route.matched[0].name}_depDetailCharts`, query: { }})

 

3.8 pages

 

尽量login.vue来管理登陆,并推荐做一个模拟登陆

 

3.9 store

vuex管理

 

3.10 style

3.10.1 css-generator-plugin

通过正则匹配所有.vue文件,根据固有规则的class,自动生成样式代码

参考链接:https://www.npmjs.com/package/css-generator-plugin

项目模版封装优化及管理(vant,element,antdV,小程序)

 

 

4. webpack配置(模板优化)

4.1 将静态资源通过CDN引入的形式(css固定CDN引入,js仅打包时通过CDN引入(本地照常))

参考链接:https://www.npmjs.com/package/import-assets-from-cdn

 

4.1.1使用方式

 

项目模版封装优化及管理(vant,element,antdV,小程序)

项目模版封装优化及管理(vant,element,antdV,小程序)

 

4.1.2 实际效果

项目模版封装优化及管理(vant,element,antdV,小程序)

 

 

4.2 配置打包版本/环境/时间(控制台打印)

 

4.2.1 使用方式

参考链接:https://www.npmjs.com/package/log-info-webpack-plugin

项目模版封装优化及管理(vant,element,antdV,小程序)

 

 

4.2.2 实际效果:显示当前环境,打包时间等信息,便于排查快速定位问题

项目模版封装优化及管理(vant,element,antdV,小程序)

 

 

4.3 开启分析打包日志

PS:打包后展示各个包的依赖关系及大小,可用来分析优化

建议调试时,选择性开启,请勿提交,请勿常开

// 安装依赖
npm install webpack-bundle-analyzer -D

// 以下是打包依赖分析 push 请关闭 请只在本地使用
config
  .plugin('webpack-bundle-analyzer')
  .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
  .end()

 

4.3.1 CDN优化前效果图

 

项目模版封装优化及管理(vant,element,antdV,小程序)

 

4.3.2 CDN优化后效果图

项目模版封装优化及管理(vant,element,antdV,小程序)

 

4.4 去除console.log

config.optimization.minimizer('terser').tap((args) => {
  args[0].terserOptions.compress.drop_console = true // 移除 console.log
  return args
})

 

4.5 本地代理

devServer: {
  // host: "localhost",
  port: 8080, // 端口号
  https: false, // https:{type:Boolean}
  open: false, // 配置自动启动浏览器
  disableHostCheck: true, // 解决127.0.0.1指向其他域名时出现"Invalid Host header"问题
  proxy: {
    '/bscnym': {
      target: 'http://xxx:8000',
      changOrigin: true,
      pathRewrite: { '^/': '/' }
    }
  }
},

 

 

4.6 配置别名

 

config.resolve.alias.set('@', resolve('src'))

 

 

 

 

5. 小程序模板简介

5.1目录介绍

  ├── assets                            # 静态资源
  │   └── icon                          # iconfont
  │       └── iconfont.acss             # 字体文件样式库
  │   └── image                         # 图片
  │   └── style                         # 部分全局公用的样式库
  │       └── public.acss               # 基础公用样式库
  ├── commons                           # 通用UI组件(建议在js或者md文件内,标明入参及其含义)
  │   └── ...                           # 自定义通用UI组件
  ├── components                        # 业务组件
  │   └── ...                           # 其他自定义业务组件
  ├── pages                             # 页面
  │   └── ...                           # 具体页面
  ├── service                           # 服务
  │   └── api                           # 具体请求
  │   └── request                       # 接口请求的统一封装
  │   └── host                          # 域名
  ├── utils                             # 帮助类
  │   └── config.js                     # 配置
  │   └── dict.js                       # 字典
  │   └── mockData.js                   # 模拟数据
  │   └── index.js                      # 所有utils的统一输出
  │   └── ddApi.js                      # dd相关的api封装
  │   └── utils.js                      # 非dd相关的api封装
  ├── app.acss                          # 全局样式
  ├── auto.acss                         # css生成器自动生成的样式文件
  ├── css.generator.config.js           # css生成器插件版配置
  ├── app.js                            # 小程序启动入口
  ├── app.json                          # 小程序全局配置,如底部,顶部
  ├── package.json                      # 依赖配置
  ├── README.md                         # README

 

 

5.2 commons 通用组件 和 components 组件

同H5版本

PS:由于小程序,组件需要在使用文件的json文件中配置注入,故通用组件库没有做自动注入的操作

 

 

5.3 pages 页面

 

5.4 service 服务

5.4.1 api.js 请求列表

同H5

 

5.4.2 request.js 接口请求的统一封装

基本思路同H5,接口调用方式由axios改为dd.httpRequest,具体逻辑可根据服务端情况微调

 

5.4.3 host.js 域名

不同环境的地址配置,等同于H5的 .env文件

 

 

5.5 utils 帮助类

5.5.1 config.js 配置项

各类配置项,等同于H5的 .env文件

 

5.5.2 dict.js 字典

同H5

 

 

5.5.3 mockData.js 模拟数据

此处设定为obj[key]直接获取json,json直接由后端提供demo,由config.js控制mock开关。也可采用mock.js

 

 

5.5.4 ddApi.js

dd相关的api封装,便于维护

 

5.5.5 utils.js

非dd相关的api封装,便于维护

 

5.5.6 index.js

所有utils的统一输出,便于使用

 

 

5.6 auto.css 和 css.generator.config.js 生成器自动生成文件与插件的配置

css生成器插件版,通过package.json配置脚本 yarn dev启动。

自动生成auto.css文件,且该文件建议不提交git(避免每次生成不一致,导致较大冲突)

PS:方案需统一,auto.css不提交git,本地运行必须通过yarn dev生成auto.css,否则本地样式会出现异常

 

 

5.7 其他想法

login建议单独页面,可将登录逻辑与拦截器等统一逻辑尽可能解耦,逻辑拓展性较强,且尽可能与h5逻辑保持一致