1.1.0 • Published 1 year ago

@jlchain/fe-pack v1.1.0

Weekly downloads
-
License
MIT
Repository
-
Last release
1 year ago

统一打包服务 fe-pack

一、npm工具

yarn add @jlchain/fe-pack 或者 npm install @jlchain/fe-pack

二、fe-pack的约定

  • typescript应用
  • react应用
  • 使用css(适合编写通用样式)、less(带有 css module,适合编写组件样式)、less(编译node_modules下的less文件,不带有css module,适合加载ant类型的组件)
  • 可配置修改 默认入口文件为src/main.tsx
  • 可配置修改 默认document.title 取值为 package.json中的description
  • 可配置修改 默认端口号为8080
  • 可配置修改webpack打包时解析别名(resolve.alias),与tsconfig.json中的compilerOptions.paths配置一致

三、fe-pack做了哪些事

1. 提供3个实用命令

  • 开发模式:fe-pack start

支持子命令:

  • --log 会输出所有请求信息
  • --disk 磁盘模式,会将编译结果输出到项目根目录下的dist文件夹中
  • --analyzer 分析模式,会分析编译结果,并以图形方式展现
  • 发布模式:fe-pack build

支持子命令:

  • --analyzer 分析模式,会分析编译结果,并以图形方式展现
  • 启动服务:fe-pack serve

2. 目前fe-pack已支持的能力

  • react应用

  • 资源服务

    • 访问日志
    • 资源压缩
    • 静态资源
    • 接口代理 proxy
    • 服务集群 cluster(仅生产模式)
  • 单入口打包

    • 开发编译、压缩编译、dll编译(方便的dll拆包能力)

    • 自动选取端口号避免端口占用问题

    • less 文件预编译

    • 自动定位浏览器页面

    • 远程访问开发环境(程序会自动获取当前主机ip地址,并在编译结束时显示在终端,方便告诉你的伙伴)

    • 开发模式接口代理 proxy

    • 支持插件plugins

    • 开发编译热重启

      // 开发编译监听的文件
      const watchList = [
        'feconfig.dev.js',
        'tsconfig.json',
        'package.json',
        'babel.config.js',
        '.babelrc',
      ];

3. fe-pack 的配置文件

所有的根配置都是可选的,你不配置,应用也能正常启动。

这些配置都是为了让应用具有更多的能力。

1) 开发配置

// feconfig.dev.js 很多时候会在开发环境修改,建议使用.gitignore,禁止提交git
interface IUserConf {
  // 应用标题,这个内容将会成为页面的tab名称、左侧菜单上面的应用标题栏内容,不配置时默认从package.json的description字段获取
  title?: string
  // 基准端口号,如果这个端口已被占用将自动选取其他空闲端口号,默认8080
  port?: number
  /**
   * 入口文件,默认为 main.tsx,无论入口文件命名如何,编译后的文件都是 main.js
   * fe-pack将会根据baseUrl和entryFile综合计算入口文件的完整路径
   * 单入口打包模式下,默认完整路径为 src/main.tsx
   */
  entryFile?: string
  /**
   * 项目入口文件根路径,默认为 src
   * fe-pack将会根据baseUrl和entryFile综合计算入口文件的完整路径
   * 单入口打包模式下,默认完整路径为 src/main.tsx
   */
  baseUrl?: string
  // 应用使用的mvvm框架: react(default) / vue,目前仅支持react
  mvvm?: 'react' | 'vue'
  // 打包模式: single(default): 单入口打包,multiple: 多入口打包,目前仅支持single
  mode?: 'single' | 'multiple'
  /**
   * 自定义插件
   */
  plugins?: ((context: IfeContext) => void)[]
}
/**
 * plugin可用的上下文api
 */
export interface IfeContext {
  /**
   * 获取当前node环境 dev / qa / pre / prod
   */
  getEnv: () => IEnvEnum
  /**
   * 获取命令行指令
   */
  getCommands: () => string[]
  /**
   * 获取配置内容,只读
   */
  getConfig: () => any
  /**
   * 注册命令
   * 仅支持
   * fe-pack start
   * fe-pack build
   * 下面的子命令
   * 比如 fe-pack start --log 中,--log就是使用 registCommand 注入的
   * fe-pack serve 暂时不支持注入命令
   */
  registCommand: (executor: () => [string, string] | Promise<[string, string]>) => void
  /**
   * 配置webpack的钩子,返回的json会自动合并到webpack配置中
   */
  configureWebpack: (executor: () => webpack.Configuration | Promise<webpack.Configuration>) => void
  /**
   * 编译前钩子
   */
  beforeCompile: (hook: () => void | Promise<void>) => void
  /**
   * 编译后钩子
   */
  afterCompile: (hook: () => void | Promise<void>) => void
  /**
   * 服务启动前的钩子,插件可以使用app注册任意中间件
   */
  beforeServer: (executor: (app: Express) => void | Promise<void>) => void
  /**
   * 服务监听端口后的钩子
   */
  afterServerListening: (hook: () => void | Promise<void>) => void
}
1.1)plugin插件系统

在fe-pack中,一个接受上下文并使用这个上下文进行扩展功能定义的函数,就是一个plugin。

这个plugin返回值必须是void,所以plugin不支持异步写法,但是所有的上下文钩子,都支持异步写法。

  • 内置plugin

fe-pack已经提供一些内置plugin,用来实现基本功能:

/**
 * 内置fe plugins
 */
export const plugins = [
  // 打包目标文件夹预删除功能
  predelPlugin,
  // 写入磁盘功能
  diskPlugin,
  // 打包分析功能
  analyzerPlugin,
  // 请求日志功能
  logPlugin,
  // less编译功能
  lessCompilePlugin,
  // 服务基础功能:(gzip/cookie/健康检查/静态资源服务/...)
  serverBasePlugin,
];

一个简单的内置plugin实现方式:

/**
 * 编译结果写入磁盘插件,根据--disk命令决定
 * 仅对开发模式生效,压缩模式固定写入磁盘
 * @param context
 * @returns
 */
export function diskPlugin(context: IfeContext) {
  const {
    getEnv, getCommands, registCommand, configureWebpack,
  } = context;
  const env = getEnv();
  if (env !== IEnvEnum.dev) return;
  registCommand(() => ['--disk', '磁盘模式,会将编译结果输出到项目根目录下的dist文件夹中']);
  const commands = getCommands();
  if (!commands.includes('--disk')) return;
  configureWebpack(() => ({
    devServer: {
      writeToDisk: true,
    },
  }));
}
  • 用户plugin

fe-pack 目前提供了2个用户plugin,来实现更高级的功能:

// 支持进行dll拆包
dllPlugin(option: IDllConfig | IDllConfig[])
// 支持自定义proxy代理
proxyPlugin(conf: IProxy | Record<IEnvEnum, IProxy>)

对于用户plugin,一个例子:

// feconfig.dev.js
module.exports = {

  // ...other configs

  plugins: [
    dllPlugin([
      {
        name: 'rbase',
        include: [
          'react',
          'react-dom',
        ],
      },
      {
        name: 'tools',
        include: 'moment',
      },
    ]),
    proxyPlugin({
      '/v1': {
        target: 'http://xxx.demo.com',
        changeOrigin: true,
        secure: false,
      },
    }),
  ],
};

由于plugin可能需要根据当前环境来决定具体行为,某些plugin可以使用如下的方式配置:

// feconfig.js
module.exports = {

  // ...other configs

  plugins: [
    proxyPlugin({
      qa: proxyconf('qa'),
      pre: proxyconf('pre'),
      prod: proxyconf('prod'),
    }),
  ],
};
  • 自定义plugin

如果上面所有内置plugin都无法满足你的项目需要,你可以编写自定义plugin,比如:

module.exports = {
  plugins: [
    (context) => {
      const {
        getEnv,
        getCommands,
        getConfig,
        registCommand,
        configureWebpack,
        beforeCompile,
        afterCompile,
        beforeServer,
        afterServerListening,
      } = context;
      // 判断当前环境
      const env = getEnv();
      const isDev = env === 'dev';
      // 注入monaco需要的语言
      configureWebpack(() => ({
        plugins: [
          new MonacoWebpackPlugin({
            languages: ['json', 'shell', 'python', 'java', 'javascript', 'typescript'],
          }),
        ],
      }));
      // 注册指令
      registCommand(() => ['--debug', '调试模式']);
      // 获取当前命令
      const commands = getCommands();
      // 判断是否是调试模式
      const isDebug = commands.includes('--debug');
      // 添加服务中间件
      beforeServer((app) => {
        app.use((req, res) => {
          // todo something
        });
      });
    },
  ],
};

2) tsconfig.json

对于fe-pack来说tsconfig.json文件是必须的,这意味着你的应用必须是一个ts应用

fe-pack会根据你的ts配置中的 compilerOptions.paths 配置自动生成 webpack alias 别名

不配置将不会生成别名

3) package.json

fe-pack会观察package.json中的一些配置,来确定具体行为:

  • description : 应用描述,用户如果没有在 feconfig.js 配置 title ,fe-pack会降级使用description
  • dependencies : fe-pack会根据你的依赖包,自动确定应用使用的mvvm框架,所以你可以不配置mvvm属性

4) babel.config.js 或者 .bablerc

fe-pack 支持babel零配置

fe-pack 会观察 babel配置,来确定具体行为

如果用户配置了babel,fe-pack会将用户配置与默认配置合并

如果用户不配置babel,fe-pack会降级使用默认配置,默认配置如下:

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'usage',
        corejs: 3,
      },
    ],
    '@babel/preset-typescript',
    '@babel/preset-react',
  ],
  plugins: [
    '@babel/plugin-transform-runtime',
  ],
  env: {
    dev: {
      plugins: [
        ['react-refresh/babel', { skipEnvCheck: true }],
      ],
    },
  },
};

该默认配置决定了应用必须是react,并没有指定更多定制化场景,比如你如果想要使用antd,你的.babelrc可以配置如下:

{
  "plugins": [
    ["babel-plugin-import", {
      "libraryName": "antd",
      "style": true
    }, "antd"], // 如果你想要使用antd
    ["@babel/plugin-proposal-decorators", { "legacy": true }] // 如果你想要使用decorator
  ]
}

4. 你还需要在应用中配置哪些内容

  • package.json

    {
      //...
      "scripts": {
        "start": "fe-pack start",
        "build": "fe-pack build",
        "serve": "fe-pack serve"
      },
      //...
    }
  • .gitignore

    # ...
    feconfig.dev.js
  • .eslintrc.js 推荐使用@jlchain/fe-lint-react

  • .stylelintrc.js 推荐使用@jlchain/fe-lint-react包下的@jlchain/fe-lint-react/stylelint