目录结构 
这里罗列了 WinJS 项目中约定(或推荐)的目录结构,在项目开发中,请遵照这个目录结构组织代码。
提示
WinJS 框架的设计原则之一是【约定优于配置(Convention over Configuration)】,多数情况下可以按约定直接写代码,不需要做任何配置。
.
├── config
│   └── config.ts
├── dist
├── mock
│   └── app.ts|tsx
├── public
├── src
│   ├── .win
│   ├── .win-production
│   ├── layouts
│   │   ├── BasicLayout.vue
│   │   ├── index.less
│   ├── models
│   │   ├── global.ts
│   │   └── index.ts
│   ├── pages
│   │   ├── index.less
│   │   └── index.vue
│   ├── utils // 推荐目录
│   │   └── index.ts
│   ├── services // 推荐目录
│   │   └── api.ts
│   ├── app.(ts|tsx)
│   ├── constant.(ts|js)
│   ├── global.ts
│   ├── global.(css|less|sass|scss)
│   ├── overrides.(css|less|sass|scss)
│   └── favicon.(ico|gif|png|jpg|jpeg|svg|avif|webp)
├── node_modules
│   └── .cache
│       ├── bundler-webpack
│       ├── mfsu
│       └── mfsu-deps
├── .env
├── .editorconfig // 编辑器编码风格配置
├── .eslintignore // eslint 校验忽略文件
├── .eslintrc.js // eslint 校验配置
├── .gitignore // git 忽略文件
├── .npmrc // npm 源地址配置
├── .markdownlint.json // markdown lint 工具
├── .markdownlintignore // markdown lint 忽略文件
├── .prettierignore // prettier 代码格式化忽略文件
├── .prettierrc.js // prettier 代码格式化配置
├── .stylelintrc.js // stylelint css 代码规范配置
├── .stylelintignore // stylelint css 忽略文件
├── commitlint.config.js // git commit 规范配置
├── f2elint.config.js // f2elint 配置文件
├── plugin.ts 
├── .winrc.ts // 与 config/config 文件 2 选一
├── package.json
├── tsconfig.json
└── typings.d.ts根目录 
package.json 
WinJS 不会自动注册 package.json 中以 @winner-fed/preset-、@winner-fed/plugin-、win-preset- 和 win-plugin- 开头的插件、预设,若你需要自定义额外的插件、预设,需要手动配置到 plugins 。
.env 
环境变量,比如:
PORT=8888
COMPRESS=none.winrc.ts 
与
config/config.ts文件功能相同,2 选 1 。.winrc.ts文件优先级较高
配置文件,包含 WinJS 所有非运行时配置(运行时配置一般定义于 app.ts)。
若你需要在不同环境中加载不同配置,这在 WinJS 中是根据 WIN_ENV 或 WIN_APP_ENV来实现的,一个不同环境启动的例子:
// package.json
{
  "scripts": {
    "dev": "win dev",
    "dev:pre": "cross-env WIN_ENV=pre win dev"
  }
}config/config.ts 
与
.winrc.ts文件功能相同,2 选 1 。.winrc.ts文件优先级较高
与 .winrc.ts 相同,区别是你可以单独在一个 config 文件夹下集中管理所有的配置,保持项目根目录整洁。
dist 目录 
执行 win build 后产物的默认输出文件夹。可通过 outputPath 配置修改产物输出文件夹。
mock 目录 
存放 mock 文件,此目录下所有 .ts / .js 文件会被 mock 服务加载,从而提供模拟数据,使用方法详见 Mock 。
node_modules/.cache 
WinJS 在构建时生成的缓存文件目录,存放 babel 缓存,MFSU 缓存等
提示
如果 MFSU 构建异常,可以删除 .cache 目录,它将会在再次构建的时候重新生成。 也可以直接执行 win cache clean 进行清除。
public 目录 
public/ 目录作为框架默认的静态资源目录,除了存放页面模版 index.html 文件之外,不被工程构建工具进行编译的资源都可以放在该目录下。
比如 favicon.ico 文件,我们并不希望该文件名编译(默认静态资源文件名在编译后会生成独立 hash,favicon.ico 希望保持原有文件名),在使用时直接在 html 模版中进行引用:
<!DOCTYPE html>
<html>
  <head>
    <link rel="icon" href="/favicon.ico" />
  </head>
</html>另外像不被源码引入的资源也存放在 public 目录下,比如设置 externals 后的 umd 链接,在不使用 CDN 的情况下,同样可以直接放在 public 目录下。
public 目录中的资源在开发时能直接通过 / 根路径访问到,并且打包时会被完整复制到目标目录的根目录下。
注意
- 请避免在源代码中通过 import 来引用 public 目录下的文件,正确的方式是通过 URL 引用。
- 通过 URL 引用 public 目录中的资源时,请使用绝对路径,而不是相对路径。
- 在生产环境构建过程中,public 目录中的文件将会被拷贝到构建产物目录(默认为 dist)下,请注意不要和产物文件出现名称冲突。在构建器「Rsbuild」下, 当 public 下的文件和产物重名时,构建产物具有更高的优先级,会覆盖 public 下的同名文件。这个功能可以通过将 server.publicDir.copyOnBuild 设置为 false 来禁用。
src 目录 
.win 目录 
警告
不要提交 .win 临时文件到 git 仓库,默认已在 .gitignore 被忽略。
dev 时的临时文件目录,比如入口文件、路由等,都会被临时生成到这里。
.win-production 目录 
警告
不要提交 .win-production 临时文件到 git 仓库,默认已在 .gitignore 被忽略。
build 时的临时文件目录,是整个 WinJS 项目的发动机。临时目录和文件是 WinJS 中非常重要的一部分,框架或插件会根据你的代码生成临时文件,比如入口文件、路由等。因为它的临时性,每次启动 WinJS 时都会被删除并重新生成。
app.[ts|tsx] 
运行时配置 文件,可以在这里扩展运行时的能力,比如修改路由、修改 render 方法等。
运行时配置带来的逻辑会在浏览器中运行,因此当有远程配置、动态内容时,这些我们在本地开发时还不确定,不能写死,所以需要在浏览器实际运行项目时动态获取他们。
constant.(js|ts) 
定义常量(大驼峰,单词之间以下划线连接)。编写业务程序时,会有一些所需要的常量值,比如订单状态,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
};layouts/index.vue 
全局布局,默认会在所有路由下生效,比如有以下路由关系:
[
  { path: '/', component: '@/pages/index' },
  { path: '/users', component: '@/pages/users' },
]输出为:
<Layout>
  <Page>index</Page>
  <Page>users</Page>
</Layout>当你需要关闭 layout 时可以使用 layout: false ,当你需要更多层 layout 时,可以考虑使用 wrappers ,仅在配置式路由可用:
  routes: [
    { path: '/', component: './index', layout: false },
    { 
      path: '/users', 
      component: './users', 
      wrappers: ['@/wrappers/auth']
    }
  ]pages 目录 
约定式路由默认以 pages/* 文件夹的文件层级结构来生成路由表。
在配置式路由中,component 若写为相对路径,将从该文件夹为起点开始寻找文件:
  routes: [
    // `./index` === `@/pages/index`
    { path: '/', component: './index' }
  ]基础路由 
假设 pages 目录结构如下:
+ pages/
  + users/
    - index.tsx
  - index.tsx那么,会自动生成路由配置如下:
[
  { path: '/', component: '@/pages/index.tsx' },
  { path: '/users/', component: '@/pages/users/index.tsx' },
]动态路由 
约定带 $ 前缀的目录或文件为动态路由。若 $ 后不指定参数名,则代表 * 通配,比如以下目录结构:
+ pages/
  + foo/
    - $slug.tsx
  + $bar/
    - $.tsx
  - index.tsx会生成路由配置如下:
[
  { path: '/', component: '@/pages/index.tsx' },
  { path: '/foo/:slug', component: '@/pages/foo/$slug.tsx' },
  { path: '/:bar/*', component: '@/pages/$bar/$.tsx' },
]pages/404.tsx 
在使用约定式路由时,该文件会自动被注册为全局 404 的 fallback 页面。若你使用配置式路由,需要自行配置兜底路由到路由表最后一个:
  routes: [
    // other routes ...
    { path: '/*', component: '@/pages/404.tsx' }
  ]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/
- 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'
  ...
}HTTP,HTTPS 或自定义协议组成部分,可以参考如下:

global.(j|t)sx? 
全局前置脚本文件。
WinJS 区别于其他前端框架,没有显式的程序主入口(如 src/index.ts),所以当你有需要在应用前置、全局运行的逻辑时,优先考虑写入 global.ts 。
当你需要添加全局 Context 、修改应用运行时,请使用 app.tsx 。
global.(css|less|sass|scss) 
全局样式文件。
当你有需要全局使用的样式时,请考虑加入此文件。
提示
需要注意的是,此文件的优先级在第三方组件库的样式之后,所以当你有覆盖第三方库样式的需求时,请使用 overrides.css 。
overrides.(css|less|sass|scss) 
高优先级全局样式文件。
该文件一般专用于覆盖第三方库样式,其中所有 CSS 选择器都会附加 body 前缀以抬高优先级。
plugin.ts 
项目级 WinJS 插件。
当你有 WinJS 定制需求时,往往会用到 插件 API (比如 修改产物 html),此时可创建该文件进行自定义:
import type { IApi } from 'win';
export default (api: IApi) => {
  api.onDevCompileDone((opts) => {
    opts;
    // console.log('> onDevCompileDone', opts.isFirstCompile);
  });
  api.modifyHTML(($) => {
    $;
  });
  api.chainWebpack((memo) => {
    memo;
  });
};favicon 
站点 favicon 图标文件。
当存在 src/favicon.(ico|gif|png|jpg|jpeg|svg|avif|webp) 文件时,将会自动在产物中添加站点 favicon :
<link rel="shortcut icon" href="/favicon.png">若使用外部资源等,可以使用 favicons 手动配置站点图标,配置值优先于约定。
.editorconfig 
该文件是一套用于统一代码格式的解决方案,可以帮助开发者在不同的编辑器和 IDE 之间定义和维护一致的代码风格。常见的 IDE 如 WebStorm 都可以配置。可以参考 editorconfig
prettier.config.js[.prettierrc.js] 
prettier 配置文件,用于代码格式化,如 .vue,.js,.ts,.css,.less 等文件
module.exports = {
  ...require('@winner-fed/prettier-config-win'),
  plugins: ['prettier-plugin-organize-imports', 'prettier-plugin-packagejson']
}检测命令:
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 
F2ELint 是《前端规约》的配套 Lint 工具,可用于为项目一键接入规约、一键扫描和修复规约问题,保障项目的编码规范和代码质量。
 module.exports = {
  enableStylelint: true,
  enableMarkdownlint: true,
  enablePrettier: true
};.markdownlint.json 
配套的 markdownlint 可共享配置。可参考 @winner-fed/markdownlint-config-win
 {
  "extends": "@winner-fed/markdownlint-config-win"
}commitlint.config.js 
commitlint 配置文件,用于对 git commit message 进行校验。继承 @winner-fed/commitlint-config-win
 module.exports = {
  extends: ['@winner-fed/commitlint-config-win']
};