目录及约定

在文件和目录的组织上,更倾向于选择约定的方式。

一个应用的基本目录结构如下:

.
├── build
├── docs
├── public
└── src
    ├── assets
    │   ├── fonts
    │   ├── img
    │   └── style
    ├── components
    │   ├── NetworkBroken
    │   ├── SendCode
    │   └── global
    ├── filters
    ├── icons
    │   └── svg
    ├── models
    ├── router
    ├── services
    ├── vendor
    └── views
        └── hello

build

构建工具(webpack/vite) 及 node 命令行工具相关配置

  • index.js - 利用 tasksfile 插件,自定义打包构建之后的异常,便于捕获。
  • zip.js - 将构建的 dist 包压缩成 zip 格式。

public

该目录下的文件不会被构建工具(webpack/vite)处理,构建的包最终是原样输出。而其他的静态资源都会被构建工具(webpack/vite)当做模块处理。

  • 前端配置文件,config.local.js,一般用于配置动态的后端接口路径等
  • 移动端调试工具,如腾讯的 vconsole.min.jsopen in new window。考虑到此文件并不是开发时必须引入的,因此 console.js 主要用来实现动态加载此文件的。

src

约定 src 为源码目录。

src/assets

用于静态资源文件,作用于全局

  • fonts - font 字体图标,默认是 iconfont.cnopen in new window 生成的全部文件
  • img - 图片资源文件
  • style - CSS 相关样式文件,reset 样式文件及全局定义的 less 变量文件,自定义的 mixins 函数库,换肤文件等
  • js - 不经过 npm 或 Yarn 下载的第三方依赖包。(扩展出来的文件)

src/components

组件目录,统一采用大驼峰拼接,如 SendCode

src/filters

过滤器,过滤器是 vue2 的叫法,在 Vue3 是不存在的。Vue3 可以理解成是函数库

src/icons

放置 svg 相关的图标。可以作为 iconfont 的替代品,因为 iconfont 只支持单色。项目里推荐使用这种方式。

src/models

数据生产者。可以按照前端领域模型来组织。项目中的数据来源,主要是后端接口(HTTP 协议的接口),及混合式开发时,壳子这边提供的桥接接口。或者是接入的第三方插件提供的数据。

  • 复用,解耦,使用方便。
  • 注释清晰,遵循 jsdoc 的注释规范,利用 npm scripts里的 gen:docs生成可视化的前端接口文档。
  • 纯前端的 BFF 层,视图和业务逻辑的胶水层。

src/router

路由及路由拦截器

src/services

网络请求库的封装。对 HTTP 协议接口做的封装。

  • autoMatchBaseUrl.js - 项目中若存在多个服务端接口的对接,则需要在 config.local.js 里定义多个接口请求路径,此文件就是针对配置不同的 prefix,做自动适配的。可参考下面一段示例: autoMatchBaseUrl 与 config.local.js 配合使用
  • request.js - 针对 axios 的封装,主要函数是 URL, URL, formData)
  • RESTFULURL.js - 所有服务端接口的映射表,对应的 value 值不建议添加接口具体的 path,可参考下面一段示例: RESTFULURL.js 示例, 如标准的 URL http://xx.com/v1/func_get_user_info
    • HTTP 协议路径 http://xx.com/open in new window
    • path 路径:v1/
    • 接口名:[func_get_user_info](http://xx.com/v1/func_get_user_info)
    • 因为有时候开发环境和生产环境接口对应 path 会有修改,所以在配置 API_HOME 的时候,尽量是 HTTP 协议路径 + path 路径。
// autoMatchBaseUrl与config.local.js配合使用
// config.local.js
window.LOCAL_CONFIG = {
  API_HOME: 'http://api.github.com/',
  CLINET_API_HOME: 'http://client.github.com/',
  MALL_API_HOME: 'http://mall.github.com/'
};

// constant.js
const QUOTE_PREFIX = 'v1/';
const CLIENT_PREFIX = 'client/';
const MALL_PREFIX = 'shop/';
export { QUOTE_PREFIX, CLIENT_PREFIX, MALL_PREFIX };

// autoMatchBaseUrl.js
export default function autoMatchBaseUrl(prefix) {
  const options = {};
  switch (prefix) {
    case QUOTE_PREFIX:
      options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
      options.baseUrl = window.LOCAL_CONFIG.QUOTE_HOME + QUOTE_PREFIX;
      break;
    case CLIENT_PREFIX:
      options.data = {
        user_token: store.getters.clientToken
      };
      options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
      options.baseUrl = window.LOCAL_CONFIG.IFS_API_HOME + CLIENT_PREFIX;
      break;
    case MALL_PREFIX:
      options.data = {
        fansToken: encrypt.encrypt(store.getters.userInfo.fundAccount)
      };
      options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
      options.baseUrl = window.LOCAL_CONFIG.MALL_API_HOME + MALL_PREFIX;
      break;
    default:
      // 默认
      options.data = {
        user_token: store.getters.apiToken
      };
      options.headers = {
        Accept: 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
      };
      options.baseUrl = window.LOCAL_CONFIG.API_HOME + HOME_PREFIX;
  }

  return options;
}
// RESTFULURL.js
export default {
  ...
  getUserInfo: 'func_get_user_info'
  ...
}

src/views

视图层,数据的消费者。尽可能的让视图层更“轻”。

  • 统一采用小驼峰(切记),如 helloWorld

src/constant.js

定义常量(大驼峰,单词之间以下划线连接)。编写业务程序时,会有一些所需要的常量值,比如订单状态,1,2,3...。一般情况下,这些值都是固定不变的。如果直接将这些值硬编码到代码里,就可以理解成“魔法值”。魔法值的存在,从语法上来说是合理的,但是从业务上却让人无法理解其中 0,1,2,3 的含义。对于这些魔法值,我们往往需要通过上下文推断出来逻辑,如果是非常复杂的业务或者 10 年前的代码那就更惨了,搞不好连文档注释也没有。为了可读性,所以我们要尽量避免出现魔法值。 举个例子,如下

<!-- 服务期限 -->
<div class="card-subscription viability-info">
  <div class="header">
    <span>服务期限</span>
  </div>
  <div class="content">
    <div v-if="+combinePrice.farePerMonth !== 999999999"
         class="viability-box"
         :class="{active: viability === '1' }"
         @click="viability = '1'">
      <p>1个月</p>
      <p class="price"><b>¥</b>{{combinePrice.farePerMonth}}</p>
    </div>
    <div v-if="+combinePrice.farePerQuarter !== 999999999"
         class="viability-box"
         :class="{active: viability === '3' }"
         @click="viability = '3'">
      <p>3个月</p>
      <p class="price"><b>¥</b>{{combinePrice.farePerQuarter}}</p>
    </div>
    <div v-if="+combinePrice.farePerHalfyear !== 999999999"
         class="viability-box"
         :class="{active: viability === '6' }"
         @click="viability = '6'">
      <p>6个月</p>
      <p class="price"><b>¥</b>{{combinePrice.farePerHalfyear}}</p>
    </div>
</div>

999999999 是什么玩意。。。

// constant.js
...
// 中台与前端做的协定
// 特殊 999999999 价格,不做界面展示
const SPECIAL_PRICE = 999999999;

export {
  ...
  SPECIAL_PRICE
};

index.html

遵循于 EJSopen in new window 嵌入式 JavaScript 模板引擎语法,比如 <%% EJS %%>。可以动态设置并解析所定义的变量。

  • 配置文件 config.local.js 的引用方式这里做下说明,由于之前生产环境上遇到过,更改配置文件并上传服务器更新后,有缓存的问题,因此用了 document.write 方式实现,用于清除此文件的缓存
<script>
  document.write(unescape('%3Cscript src=\'./config.local.js?_t=' + Math.random() + '\'
  type=\'text/javascript\'%3E%3C/script%3E'));
</script>

src/public/index.html

遵循 vue-cli 放置的目录规范。借助于 html-webpack-pluginopen in new window 来实现 EJS 的模板引擎语法。

src/index.html

遵循 vite 放置的目录规范。借助于 vite-plugin-htmlopen in new window 来实现 EJS 的模板引擎语法。

.env

环境变量配置文件,会在所有的环境中被载入。

.env.development

定义开发模式下的环境变量,只在开发模式下才被引入。

.env.production

定义生产模式下的环境变量,只在生产模式下才被引入。

.browserslistrc

用于在不同的前端工具之间共用目标浏览器和 node 版本的配置文件。主要用于 Autoprefixeropen in new windowBabelopen in new window 等,详细可参考 Browserslistopen in new window。移动端 H5 及 PC Web 的配置各有不同。

一个在线工具,用于查看 browserlist 配置文件匹配到哪些浏览器:https://browserl.ist/?q=defaultsopen in new window 注意:并不是项目中所有依赖都支持低版本浏览器,例如 vue 本身不支持低于 IE9 的浏览器;

.editorconfig

该文件是一套用于统一代码格式的解决方案,可以帮助开发者在不同的编辑器和 IDE 之间定义和维护一致的代码风格。常见的 IDE 如 WebStorm 都可以配置。可以参考 editor-configopen in new window

.escheckrc

使用简单的 shell 命令检查 JavaScript 文件中使用的 ES 版本。构建的前端包(dist 包)需要支持安卓 5.0 及 iOS9.0 机型,所以为了避免包里出现 ES6 语法,增加了新的 npm scripts 命令: escheck。执行完 npm run build 之后,可以使用 npm run escheck 进行检测。

{
  "ecmaVersion": "es5",
  "module": false,
  "files": "./dist/**/*.js",
  "not": []
}

postcss.config.js

postcss 配置文件,集成了 autoprefixer ,px2rem 等插件

module.exports = {
  plugins: {
    autoprefixer: {},
    'postcss-pxtorem': {
      rootValue: 37.5,
      unitPrecision: 2,
      propList: [
        'height',
        'line-height',
        'width',
        'padding',
        'margin',
        'top',
        'left',
        'right',
        'bottom',
        'font-size'
      ],
      selectorBlackList: [],
      replace: true,
      mediaQuery: false,
      minPixelValue: 1
    }
  }
};

prettier.config.js

prettier 配置文件,用于代码格式化,如 .vue,.js,.ts,.css,.less 等文件

module.exports = {
  // 行尾需要有分号
  semi: true,
  // 不使用缩进符,而使用空格
  useTabs: false,
  // 使用 2 个空格缩进
  tabWidth: 2,
  // 一行最多 120 字符
  printWidth: 120,
  // 使用单引号
  singleQuote: true,
  // 末尾无逗号
  trailingComma: 'none',
  // 使用默认的折行标准
  proseWrap: 'preserve',
  // 大括号内的首尾需要空格
  bracketSameLine: true,
  // 箭头函数,只有一个参数的时候,也需要括号
  arrowParens: 'always',
  // 对象的 key 仅在必要时用引号
  quoteProps: 'as-needed',
  // jsx 不使用单引号,而使用双引号
  jsxSingleQuote: false,
  // vue 文件中的 script 和 style 内需要缩进
  vueIndentScriptAndStyle: true,
  // 根据显示样式决定 html 要不要折行
  htmlWhitespaceSensitivity: 'css',
  // 不需要自动在文件开头插入 @prettier
  insertPragma: false,
  // 不需要写文件开头的 @prettier
  requirePragma: false,
  // 换行符使用 lf
  endOfLine: 'lf',
  // 每个文件格式化的范围是文件的全部内容
  rangeStart: 0,
  rangeEnd: Infinity,
  // 格式化内嵌代码
  embeddedLanguageFormatting: 'auto',
  overrides: [
    {
      files: ['*.less', '*.css'],
      options: {
        singleQuote: false
      }
    }
  ]
};

检测命令:

prettier --write \"src/**/*.{js,jsx,json,ts,tsx,css,less,scss,vue,html,md}\"

stylelint.config.js

样式编码规范的配置文件,用于检测 .less,.css 等样式文件

module.exports = {
  extends: '@winner-fed/stylelint-config-win'
};

.eslintrc.js

JavaScript 编码规范,用于检测 .vue,.js,.ts 等文件

module.exports = {
  extends: ['@winner-fed/win', '@winner-fed/win/vue']
};

f2elint.config.js

F2ELintopen in new window 是《恒生前端规约》的配套 Lint 工具,可以为项目一键接入规约、一键扫描和修复规约问题,保障项目的编码规范和代码质量。

 module.exports = {
  enableStylelint: true,
  enableMarkdownlint: true,
  enablePrettier: true
};

.markdownlint.json

配套的 markdownlint 可共享配置。可参考 @winner-fed/markdownlint-config-winopen in new window

 {
  "extends": "@winner-fed/markdownlint-config-win"
}

commitlint.config.js

commitlint 配置文件,用于对 git commit message 进行校验。继承 @winner-fed/commitlint-config-winopen in new window

 module.exports = {
  extends: ['@winner-fed/commitlint-config-win']
};
上次更新:
贡献者: cklwblove