# test-demo **Repository Path**: ychaoweb/test-demo ## Basic Information - **Project Name**: test-demo - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-09-16 - **Last Updated**: 2024-09-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 预览项目 npm i yarn -g yarn yarn build yarn preview ```bash ├── cli # CLI 程序 ├── config # 项目 不同开发模式的配置(development, product) ├── mock # 项目 mock 模拟数据 ├── public # 根静态资源 │ │── favicon.ico # favicon 图标 │ └── index.html # html模板 ├── src # 源码: 业务代码主要集中在此目录 │ ├── api # api 封装 │ ├── assets # 静态资源 │ ├── components # 全局公用组件 │ ├── config # 全局公用配置、全局常量 │ ├── core # 项目引导, 全局配置初始化,依赖包引入等 │ ├── directives # 全局directives │ ├── filters # 全局filters │ ├── hooks # 全局composition │ ├── layouts # 布局组件 │ ├── models # 数据操作的层的封装 │ ├── permission # 全局路由拦截、逻辑处理 │ ├── router # 路由管理 │ ├── store # 全局 vuex store modules │ ├── utils # 全局公用方法 │ ├── views # 所有的具体页面放在该文件夹中 │ ├── App.vue # 入口组件 │ ├── main.js # 入口文件 加载组件 初始化等 ├── .env.development # 开发环境变量配置 ├── .env.production # 生产环境变量配置 ├── .eslintrc.js # eslint 配置项 ├── .babel.config.js # babel 配置 ├── vue.config.js # vue-cli 配置 └── package.json # package.json ``` - ## Api 层设计 - 封装 api - 隔离 api 实现 (ajax, axios, fetch), 换实现时, 只需修改 Api 相关文件的部分实现, 不会影响到业务层 - Api 基类 ```javascript import request from '@/utils/request' import axios from 'axios' class Api { get(path, params = {}) { let config = { params } return request.get(path, config) } post(path, data = {}) { return request.post(path, data) } // customerGet, customerPost // 自定义请求, 需指定绝对 url // 自己处理请求和响应逻辑 (如: 指定 headers 等) customerGet(url, config) { return axios.get(url, config) } customerPost(url, data, config = {}) { return axios.post(url, data, config) } } ``` - request 统一处理服务器数据逻辑 - 根据返回数据的 code, 返回业务需要的实际数据 - 如果是非正常情况, 会全局提示返回的 message, 同时在 reject 中返回错误的信息 ```javascript import axios from 'axios' import { Message } from 'element-ui' const request = axios.create({ baseURL: process.env.PUBLIC_API_BASE_URL, timeout: 5000 }) request.interceptors.request.use( config => { return config }, error => { console.log('request error', error) return Promise.reject(error) } ) const reLoinCode = [501, 502] request.interceptors.response.use( response => { let info = response.data const { code, message = 'Error', data } = info if (code !== 200) { Message({ message: message, type: 'error', duration: 5 * 1000 }) if (reLoinCode.includes(code)) { // show tips } return Promise.reject(message) } else { return data } }, error => { Message({ message: error.message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) } ) export default request ``` - todo api ```javascript class TodoApi extends Api { add(data) { let path = '/todo/add' return this.post(path, data) } delete(id) { let path = '/todo/delete/' + id return this.get(path) } update(todoId, data) { let path = '/todo/update/' + todoId return this.post(path, data) } detail(todoId) { let path = '/todo/' + todoId return this.get(path) } records() { let path = '/todo/records' return this.get(path) } } ``` - 如果后端的 api 设计保持一致性, 可以写一个公共的 Api 类, 其他所有的 api 只需要简单设置一下 scope ```````javascript // api/common.js import Api from './api' class CommonApi extends Api { constructor(scope) { super() this.scope = scope } add(data) { let path = this.scope + '/add' return this.post(path, data) } delete(id) { let path = this.scope + '/delete/' + id return this.get(path) } update(id, data) { let path = this.scope + '/update/' + id return this.post(path, data) } detail(id) { let path = this.scope + '/' + id return this.get(path) } records(cond) { let path = this.scope + '/records' return this.get(path, cond) } } export default CommonApi ​``````javascript import Model from './model' import CommonApi from '../api/common' class TodoApi1 extends CommonApi { constructor() { let scope = '/todo' super(scope) } } ``````` - 这样 TodoApi 只需要设置一个 `/todo` 就可以直接具有增删查改的能力 ##### Model 层设计 - 目前大部分的前端框架在业务层相关的数据操作会直接在业务层完成 (api, localstorage, mock) - 在项目很小的情况下没任何问题, 在项目大的时候, 这样做的问题在于 - 把操作数据的实现细节(localstorage, sever, mock 等)带入到业务层中, 导致业务层代码膨胀 - 对于数据来说, 前端有自己的格式, 后端有自己的格式, 实际上应该有一个类似于过滤器的东西来把数据过滤一遍, 保证业务层拿到的数据格式符合前端自己的预期 - 另外可以在 model 层做额外的东西, 如缓存数据, 统一添加日志等, 这基本是直接在业务层操作数据很难做到的事情 - 业务中的数据操作主要都是增删查改, 这类操作比较类似, 适合统一封装一层 - 添加一层 Model 的封装之后, 作为业务方来说, 我就不再用去关心我操作的数据是放在什么地方的. 要从哪里操作数据. 我只需要知道, 我要加数据, model 就会给我去加数据, 取数据就去给我取数据. 同时保证给到业务层的数据是干净的 - 相当于把所有数据相关的操作都放到 Model 中, 业务方的调用就会变得很简单 - 日常主要修改集中在业务代码中, 业务代码越简单越好定位和修改 - 代码示例 - Model 基类 ```javascript class Model { add(data) { return this.api.add(data) } records(cond) { return this.api.records(cond) } delete(id) { return this.api.delete(id) } detail(id) { return this.api.detail(id) } update(id, data) { return this.api.update(id, data) } } export default Model ``` - 在 todoModel 中洗数据, 设置 api 之后直接使用 Model 默认的增删查改 ```javascript import Model from './model' import TodoApi from '../api/todo' class TodoModel extends Model { constructor() { super() this.api = new TodoApi() } // 洗数据, 假设前端业务层使用的创建时间字段为: createdTime, 而后端使用的是 ct _transform(data) { Object.assign(data, { createdTime: data.ct }) return data } detail(id) { return this.api.detail(id).then(r => this._transform(r)) } } export default TodoModel ``` - 在业务方(组件中)使用 - 不需要保存实例化后的对象,不建议在 data 中定义变量保存 - 推荐尽可能使用类静态方法形式调用 - 类中区通过下划线\_methos 的形式区分私有方法 ```javascript import Menu from '../menu/Menue' import ModelTodo from '../../model/todo' import TodoList from './TodoList' import TodoCounter from './TodoCounter' export default { name: 'Todo', components: { Menue, TodoList, TodoCounter }, data() { return { text: '', todos: [], model: new ModelTodo() } }, created() { this.model.records().then(r => { this.todos = r }) } } ```