项目模版封装优化及管理(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
2.模板生成
2.1 平台创建项目(前置条件--未完成)
通过平台,录入项目信息,通过接口在git创建对应的git项目,并初始化生成init.js文件
2.2创建模版
执行指令
node init.js
2.3自动生成模板
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上
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
4. webpack配置(模板优化)
4.1 将静态资源通过CDN引入的形式(css固定CDN引入,js仅打包时通过CDN引入(本地照常))
参考链接:https://www.npmjs.com/package/import-assets-from-cdn
4.1.1使用方式
4.1.2 实际效果
4.2 配置打包版本/环境/时间(控制台打印)
4.2.1 使用方式
参考链接:https://www.npmjs.com/package/log-info-webpack-plugin
4.2.2 实际效果:显示当前环境,打包时间等信息,便于排查快速定位问题
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优化前效果图
4.3.2 CDN优化后效果图
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逻辑保持一致