结合公司项目对目前vue项目整体框架总结

6 篇文章 0 订阅
1 篇文章 0 订阅
1.框架周边生态使用
  • 框架限于vue2.x
  • 目前公司研发中台,交付中台均属于中大型项目,因此vuex 和 vue-router必须使用
  • UI基于elementui ,使用体验设计部开发的md-ui 后续开发剔除掉elementui
  • 工具库使用lodash,包括青鸟组件库的 md-hooks的使用
2.脚手架
  • 目前项目框架搭建由体验设计部提供通用框架,部门工具需求进行一定的自定义修改
  • 部门可以搭建 npm 私服,结合通用框架使用Node环境开发CLI工具,提供一套部门的通用框架
  • 可根据需求完善开发的工具(如:快速创建组件,快速创建页面,过滤器,指令,工具函数等)
3.开发规范
  • 代码风格,命名规范,目录结构统一
  • git提交记录,多人协作开发规范
  • 静态资源使用的规范
  • 页面结构
    • 根据业务需要划分,按照路由层级在 views 中创建相对应的页面组件,以文件夹的形式创建,并在文件夹内创建 index.vue 文件作为页面的入口文件。
    • 页面内的组件:在页面文件夹下创建 components 文件夹,在其内部对应创建相应的组件文件,如果是复杂组件,应以文件夹的形式创建组件。
    • 工具模块:能够高度抽象的工具模块,应创建在 @/lib 内创建 js 文件。
4.样式
  • 目前按照项目框架依赖,部门项目都使用 Sass/Scss 预处理器

  • 局部样式使用scoped 方案

  • 全局样式定义variable.scss文件书写,多项目公共样式使用common.scss,布局样式使用 layout.scss

    • 可以不通过 import 的方式在项目中任何位置使用这些变量和公共样式。

    • // vue.config.js
      module.exports = {
        css: {
          loaderOptions: {
            sass: {
              prependData: `
              @import '@/styles/variable.scss';
              @import '@/styles/mixins.scss';
              `,
            },
          },
        },
      }
      
  • 使用nprogress 对路由跳转做伪进度条,解决网络不好情况下用户知道页面已经在加载中了

    • import NProgress from 'nprogress';
      
      router.beforeEach(() => {
        NProgress.start();
      });
      
      router.afterEach(() => {
        NProgress.done();
      });
      
  • 首次加载页面时,会产生大量的白屏时间,按照目前交付,研发,需求中心等项目, 直接在 public/index.html 里写一些静态的样式即可。

  • 关于100vh的使用,目前项目大量使用100vh,移动端100vh会出现视口高度与想象不一致的情况

    • 100vh !== 视口高度 100vh === 视口高度 + 浏览器工具栏(地址栏等等)的高度

    • 安装 vh-check npm install vh-check --save 解决

    • import vhCheck from 'vh-check';
      vhCheck('browser-address-bar');
      
      // 定义css mixin
      @mixin vh($height: 100vh) {
        height: $height;
        height: calc(#{$height} - var(--browser-address-bar, 0px));
      }
      
5.组件库
  • 使用 md-ui 进行开发,不要多UI框架一起使用,不要引入element-ui
  • 对md-ui 按需引入,避免打包后css 体积过大
  • 设计师设计图区别于md-ui的样式定义单独的样式文件进行覆盖,局部更改则在组件中v-deep穿透修改
  • 一些体积较大的组件不要全局引用,尽量局部引用,减缓首屏加载压力
  • 第三方工具进行二次封装,不论是之后由于各种原因更换工具,还是使用上都会更加灵活
6.异步请求,封装Axios

根据之前项目结合其他开源项目,在 @/libs/http.js 路径下对 Axios 进行封装,封装了请求参数,请求头,以及错误提示信息、 request 拦截器、response 拦截器、统一的错误处理、baseURL 设置等。

import axios from 'axios';
import get from 'lodash/get';
import storage from 'store';
// 创建 axios 实例
const request = axios.create({
 // API 请求的默认前缀
 baseURL: '/',
 timeout: 50000, // 请求超时时间
});

// 异常拦截处理器
const errorHandler = (error) => {
 const status = get(error, 'response.status');
 switch (status) {
   /* eslint-disable no-param-reassign */
   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;
   /* eslint-disabled */
 }
 return Promise.reject(error);
};

// request interceptor
request.interceptors.request.use((config) => {
 // 如果 token 存在
 // 让每个请求携带自定义 token 请根据实际情况自行修改
 // eslint-disable-next-line no-param-reassign
 config.headers.Authorization = `bearer ${storage.get('ACCESS_TOKEN')}`;
 return config;
}, errorHandler);

// response interceptor
request.interceptors.response.use((response) => {
 const dataAxios = response.data;
 // 这个状态码是和后端约定的
 const { code } = dataAxios;
 // 根据 code 进行判断
 if (code === undefined) {
   // 如果没有 code 代表这不是项目后端开发的接口
   return dataAxios;
 // eslint-disable-next-line no-else-return
 } else {
   // 有 code 代表这是一个后端接口 可以进行进一步的判断
   switch (code) {
     case 200:
       // [ 示例 ] code === 200 代表没有错误
       return dataAxios.data;
     case 'xxx':
       // [ 示例 ] 其它和后台约定的 code
       return 'xxx';
     default:
       // 不是正确的 code
       return '不是正确的code';
   }
 }
}, errorHandler);

export default request;
  • 我们项目是通过cookie 存储通证,此处使用的是sotre 的包作为本地储存的工具用来存储 token
  • 跨域问题的话使用proxy解决,根据之前项目,都是在config文件夹定义proxy.js来做开发时跨域处理
7.mock数据

Mock 数据功能是基于 mock.js开发,通过webpack配置,自动加载mock配置文件

  • 所有的 mock 配置文件均应放置在 @/mock/services 路径内

  • @/mock/services 内部可以建立业务相关的文件夹分类存放配置文件。

  • 所有的配置文件应按照 ***.mock.js 的命名规范创建。

  • 入口

    • import Mock from 'mockjs';
      
      Mock.setup({
        timeout: '500-800',
      });
      
      const context = require.context('./services', true, /\.mock.js$/);
      
      context.keys().forEach((key) => {
        Object.keys(context(key)).forEach((paramKey) => {
          Mock.mock(...context(key)[paramKey]);
        });
      });
      
8.路由
  • Layout:

    • 目前体验设计部提供的Layout组件有三个,分别为 Basic ,User,Blank。 分别为有侧边栏,无侧边栏,空白三种

    • 结合一些开源项目,可以对于登录或权限认证的路由进行划分,拆出FrameIn,FrameOut两种,一个是需要权限验证的页面,一个是不需要权限验证的页面,如登录页

    • 在路由中,集成了权限验证的功能,需要为页面增加权限时,在 meta 下添加相应的 key:

    • auth
      • 类型:Boolean , 当 auth 为 true 时,此页面需要进行登陆权限验证,只针对 frameIn 路由有效。
    • permissions
      • 类型:Object , permissions 每一个 key 对应权限功能的验证,当 key 的值为 true 时,代表具有权限,若 key 为 false,配合 v-permission 指令,可以隐藏相应的 DOM
      • https://blog.csdn.net/weixin_45646663/article/details/112277206 【网上关于permissions做权限的博文】
import router from '@/router';
import store from '@/store';
import storage from 'store';
import util from '@/libs/utils';

// 进度条
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';

const loginRoutePath = '/user/login';
const defaultRoutePath = '/home';

/**
 * 路由拦截
 * 权限验证
 */
router.beforeEach(async (to, from, next) => {
  // 进度条
  NProgress.start();
  // 验证当前路由所有的匹配中是否需要有登录验证的
  if (to.matched.some((r) => r.meta.auth)) {
    // 是否存有token作为验证是否登录的条件
    const token = storage.get('ACCESS_TOKEN');
    if (token && token !== 'undefined') {
      // 是否处于登录页面
      if (to.path === loginRoutePath) {
        next({ path: defaultRoutePath });
        // 查询是否储存用户信息
      } else if (Object.keys(store.state.system.user.info).length === 0) {
        store.dispatch('system/user/getInfo').then(() => {
          next();
        });
      } else {
        next();
      }
    } else {
      // 没有登录的时候跳转到登录界面
      // 携带上登陆成功之后需要跳转的页面完整路径
      next({
        name: 'Login',
        query: {
          redirect: to.fullPath,
        },
      });
      NProgress.done();
    }
  } else {
    // 不需要身份校验 直接通过
    next();
  }
});

router.afterEach((to) => {
  // 进度条
  NProgress.done();
  util.title(to.meta.title);
});
9.构建优化
  • 使用 vue 的图形化界面可以查看界面资源的一些具体情况

  • 开启 Gzip

    • chainWebpack: (config) => {
        config
          .plugin('CompressionPlugin')
          .use(CompressionPlugin, []);
      },
      
  • 路由懒加载

    • {
        path: 'home',
        name: 'Home',
        component: () => import(
          /* webpackChunkName: "home" */ '@/views/home/index.vue'
        ),
      },
      // webpackChunkName 这条注释必须加上,方便打包后知道那个页面过于冗余。
      
  • preload & prefetch 优化页面打开速度

10.单元测试
  • 根据具体项目进行推进,暂时项目均未做单元测试
11.Vuex
  • 用户信息管理(储存信息、对 token 进行操作等)
  • 登陆(调接口)
  • 菜单管理(储存路由信息,生成菜单,模糊查询等功能)
  • 全屏操作
  • Loading
12.过滤器

常用过滤器全局注册

  • 日期时间
  • 文件大小
13.指令

常用指令全局注册

  • 组件权限验证
  • 焦点
  • 图片懒加载
  • 滚动至指定位置
14.代码基础规范
  • 降低css选择器复杂性,增强性能【浏览器读取选择器,遵循的原则是从选择器的右边到左边读取。】

  • 避免使用通配符选择器*

  • 不考虑IE 678 情况下尽量使用 flexbox布局,性能更好

  • transforms 和 opacity 这两个属性更改不会触发重排与重绘,做动画尽量使用他们

  • 当数值为 0 - 1 之间的小数时,建议省略整数部分的 0。

  • 当长度为 0 时建议省略单位

  • 不使用命名色值。

  • 当元素需要撑起高度以包含内部的浮动元素时,通过对伪类设置 clear 或触发 BFC 的方式进行 clearfix。尽量不使用增加空标签的方式。

  • 除公共样式之外,在业务代码中尽量不能使用 !important。

  • 将 z-index 进行分层,对文档流外绝对定位元素的视觉层级关系进行管理。

  • 字号应不小于 12px(PC端)。

  • font-weight 属性使用数值方式描述。

15.vue规范
  • 当在组件中使用 data 属性的时候 (除了 new Vue 外的任何地方),它的值必须是返回一个对象的函数 data() { return {...} }
  • prop 的定义应该尽量详细,至少需要指定其类型。
  • 布尔类型的 attribute, 为 true 时直接写属性值。
  • 不要在computed中对vue变量进行操作。
  • 应该优先通过 prop 和事件进行父子组件之间的通信,而不是 this.$parent 或改变 prop。
  • 在组件上总是必须用 key 配合 v-for,以便维护内部组件及其子树的状态。
  • v-if 和 v-for 不能同时使用
  • 公共方法尽量不要挂到原型上, 可以写在 utils 文件,也可以使用 mixin 文件。不要将业务公共组件注册到全局。
  • 不要将任何第三方插件挂载到 vue 原型上。
  • 具有高度通用性的方法,要封装到 libs、全局组件或指令集里。
  • 为组件样式设置作用域。
  • 尽量使用指令缩写。
16.vuex规范

State为单一状态树,在 state 中需要定义我们所需要管理的数组、对象、字符串等等,只有在这里定义了,在 vue 的组件中才能获取你定义的这个对象的状态

  • 修改 state 中数据必须通过 mutations
  • 每一个可能发生改变的 state 必须同步创建一条或多条用来改变它的 mutations
  • 服务端获取的数据存放在 state 中,作为原始数据保留,不可变动。

Getters 类似 vue.js 的计算属性,当我们需要从 store 的 state 中派生出一些状态,那么我们就需要使用 getters,getters 会接收 state 作为第一个参数,而且 getters 的返回值会根据它的依赖被缓存起来,只有 getters 中的依赖值(state 中的某个需要派生状态的值)发生改变的时候才会被重新计算。

  • 通过 getters 处理你需要得到的数据格式,而不是通过修改 state 原始数据。
  • 组件内不强制使用 mapGetters,因为你可能需要使用 gettersetter
  • 改变 state 的唯一方法就是提交 mutations
  • 组件内使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用。
  • 命名采用 大写字母 + 下划线 的规则。

Actions 处理复杂数据处理以及异步操作时使用

  • 页面重的数据接口尽量在 actions 中调用。
  • 服务端返回的数据尽量不作处理,保留原始数据。
  • 获取到的数据必须通过调用 mutations 改变 state

Modules 按照页面情况划分modules, 之前项目都有做这一块

  • 通常情况下按照页面划分 modules
  • 默认内置了 system 保证了脚手架的基础功能。
  • 每个页面模块或页面的子模块添加属性 namespaced: true
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值