ice-scripts 是飞冰(ICE)React 链路的工程工具,类似 Vue CLI 之于 Vue 的关系。ice-scripts 提供了丰富的功能帮助我们开发 React 项目:

  • 完善的命令行工具
  • 支持项目/业务组件/区块的开发&构建
  • 基于 Fusion 体系的主题配置
  • 完善的自定义 webpack 配置

    安装

    1. $ npm i -g ice-scripts
    2. $ ice --help
    当然你也可以将其作为项目级依赖:npm i —save—dev ice-scripts

    命令行能力

    ice-scripts 提供了init/dev/build 的开发命令,如果使用 Iceworks 开发,那么大多数时候你不需要关心这些命令。

    ice init

    根据模板初始化项目:
    1. $ ice init --help
    2. Usage: ice-init [options]
    3. Options:
    4. -s, --scaffold 模板 npm 包名,可不传
    5. -h, --help output usage information

    ice dev

    启动调试服务:
    1. $ ice dev --help
    2. Usage: ice-dev [options]
    3. Options:
    4. -p, --port <port> 服务端口号
    5. -h, --host <host> 服务主机名
    6. --https 开启 https
    7. --analyzer 开启构建分析
    8. --analyzer-port 设置分析端口号
    9. --disabled-reload 关闭 hot reload
    10. --project-type <type> 项目类型, node|web (default: "web")
    11. --inject-babel <type> 注入 babel 运行环境, Enum: polyfill|runtime (default: "polyfill")
    比如使用 3000 端口启动 dev server
    1. $ ice dev -p=3000
    2. # 或者
    3. $ npm run start -- -p=3000
    比如开启 https
    1. $ ice dev --https

    ice build

    构建项目代码
    1. $ ice build --help
    2. Usage: ice-build [options]
    3. Options:
    4. --debug debug 模式下不压缩
    5. --hash 构建后的资源带 hash 版本
    6. --sourcemap <type> 构建后的资源带 sourcemap 文件
    7. --project-type <type> 项目类型, node|nodejs|web
    8. -s, --skip-install 跳过安装依赖
    9. --skip-demo 跳过构建 build/index.html 的环节
    10. --inject-babel <type> 注入 babel 运行环境, Enum: polyfill|runtime
    11. -h, --help output usage information

    构建配置 - buildConfig

    除了提供主题配置和代理配置之外, ice-scripts 还提供了一些常用的构建配置项,方便开发者快速自定义配置,在 package.json 中新增 bulidConfig 字段,包含以下配置项:

    配置项

    配置多 entry

    默认会以src/index.js文件作为入口文件,配置在项目的 package.json 的buildConfig字段中,如果你需要改变默认的入口文件,可以自行修改即可生效。
    1. "buildConfig": {
    2. "entry": "src/index.js"
    3. }
    如果你的项目是多页应用,希望把 src/pages 的文件作为入口,那么可以这样配置:
    1. "buildConfig": {
    2. "entry": {
    3. "dashboard": "src/pages/dashboard/index.js",
    4. "about": "src/pages/about/index.js"
    5. }
    6. }
    多 entry 的情况构建时会额外生成 vendor.js/css,需要自行在 html 里引入(public 目录会自动引入),也可以通过下面的 buildConfig.disableVendor 禁止生成 vendor 文件。
    如果没有配置 entry 字段,ice-scripts 会将 src/pages/*/index.js 作为 entry,每个 page 都会作为一个 entry。

    babelPluginImportConfig

    babel-plugin-import 用于支持一些 UI 组件包的按需加载以及样式自动引入,ice-scripts 默认支持了@icedesign/base 和@alifd/next,如果有其他需求可以使用该配置,比如antd :
    1. "buildConfig": {
    2. "babelPluginImportConfig": {
    3. "libraryName": "antd",
    4. "libraryDirectory": "es",
    5. "style": true
    6. }
    7. // 或者多个 UI 组件的话也可以用数组的方式
    8. "babelPluginImportConfig": [{}, {}]
    9. }

    uniteBaseComponent

    社区用户无需关心该问题

如果项目里依赖了多个不同名称的基础包,可以通过uniteBaseComponent来统一基础包,减少重复的代码:

  1. "buildConfig": {
  2. "uniteBaseComponent": "@alife/next"
  3. }

如上所示,项目需要保证依赖了当前配置的这个 npm 包,这样不同名的基础包都会重定向到配置的基础包,减少 bundle 的大小。

修改构建后的文件目录

  1. "buildConfig": {
  2. "output": {
  3. "path": "dist"
  4. }
  5. }

修改构建后的 css/js 文件目录

默认情况下 css 在build/css下,js 在build/js下,可以通过outputAssetPath 配置修改:

  1. "buildConfig": {
  2. "outputAssetsPath": {
  3. // 示例1:修改为 build/css-dist/ build/js-dist/
  4. "css": "css-dist",
  5. "js": "js-dist",
  6. // 示例2js css 都直接放在 build/
  7. "css": "",
  8. "js": ""
  9. }
  10. }

修改 externals

  1. "buildConfig": {
  2. "externals": {
  3. "jquery": "window.$"
  4. }
  5. }

另外,ice-scripts 会根据 public/*.html 里是否通过 script 标签引入了 React 来推导是否需要生成 React 相关的 externals,这个 externals 会跟用户配置直接 merge:

  1. {
  2. "react": "window.React",
  3. "react-dom": "window.ReactDOM"
  4. }

css 中的网络资源本地化

有些组件会依赖一些字体文件,通常都是以 url 的方式引入,比如http://i.alicdn.com/artascope-font/20160419204543/font/roboto-regular.woff2 ,如果因为一些特殊原因无法访问这些资源,可以通过localization 将这些资源本地化:

  1. {
  2. "buildConfig": {
  3. "localization": true
  4. }
  5. }

配置后类似资源都会被提取到本地 build/ 目录里。

禁用生成 vendor

在多 entry 的情况下,构建时除了每个 entry 会生成一个 bundle 文件外,同时会根据依赖生成 vendor.css&vendor.js,如果不需要生成这个文件,可以通过下面的方式配置:

  1. "buildConfig": {
  2. "disableVendor": true
  3. }

babelExclude

不推荐使用

babel-loader 有一个 exclude 的配置,用于过滤某些目录下的文件不需要经过 babel 编译,按照前端社区的规范三方 npm 包的代码都应该经过 babel 编译,因此大多数工程(包括 ice-scripts)都会将该字段设置为node_modules,用于加快构建的速度。如果你用到了某个未经 babel 编译的包,我们首先推荐你看下是否有其他选择,实在没有选择的话可以通过这个配置支持:

  1. "buildConfig": {
  2. // 配置特定包要经过 babel 编译
  3. // 该字符串会经过 new RegExp() 转换为正则然后传递给 babel-loader
  4. // 不同的 npm client 的路径可能有差异,请按照具体路径书写正则
  5. "babelExclude": "node_modules\\/(?!_@ali_lib-ucc)"
  6. }

自定义配置 - .webpackrc.js

ice-scripts 除了提供 buildConfig 用于快速的配置入口之外,也支持自定义配置需求,几乎可完全自定义 webpack 的所有配置项;在项目根目录新建.webpackrc.js 文件对默认配置进行定制和覆盖。.webpackrc.js 文件需要导出一个函数,其支持的参数可以参考 .webpackrc 官方文档
.webpackrc.js 文件采用操作系统中安装的 Node.js 所支持的语法,所以可以使用除了import,export 等之外的几乎所有 ES6 语法。

  1. module.exports = (context) => {
  2. const { webpack } = context;
  3. // webpack config
  4. return {
  5. plugins: [
  6. new webpack.DefinePlugin({
  7. ASSETS_VERSION: '0.0.1',
  8. }),
  9. ],
  10. };
  11. };

以下为一些常见的自定义需求:

修改 publicPath

配置 webpack 的 output.publicPath 属性。

  1. module.exports = (context) => {
  2. return {
  3. output: {
  4. path: path.resolve(__dirname, 'public/assets'),
  5. publicPath: 'https://cdn.example.com/assets/',
  6. },
  7. };
  8. };

详细说明

使用 DefinePlugin

官方文档
通过 webpack 的 DefinePlugin 可以配置全局的变量,针对开发环境和发布环境的构建配置不同的行为非常有用。
比如需要在代码里使用当前仓库的版本号:

  1. module.exports = (context) => {
  2. const { webpack } = context;
  3. return {
  4. plugins: [
  5. new webpack.DefinePlugin({
  6. // 此处不能省略 JSON.stringify,否则构建过程会出现语法问题
  7. ASSETS_VERSION: JSON.stringify('0.0.1'),
  8. }),
  9. ],
  10. };
  11. };

在代码里使用该变量(当做全局变量使用):

  1. console.log(ASSETS_VERSION);

alias

配置 webpack 的 resolve.alias 属性,创建 import 或 require 的别名,来确保模块的引用变得更简单

  1. const path = require('path');
  2. module.exports = () => {
  3. return {
  4. //...
  5. resolve: {
  6. alias: {
  7. '@components': path.resolve(__dirname, 'src/components/'),
  8. },
  9. },
  10. };
  11. };

现在,替换「在导入时使用相对路径」这种方式,就像这样:

  1. -import CustomTips from '../../../components/CustomTips';
  2. +import CustomTips from '@components/CustomTips';

Mock

ice-scripts 支持 mock 功能,在项目根目录的mock/index.js 中进行配置,支持基于 require 动态分析的实时刷新,支持 ES6 语法,以及友好的出错提示:

  1. export default {
  2. // 支持值为 Object Array
  3. 'GET /api/users': { users: [1, 2] },
  4. // GET POST 可省略
  5. '/api/users/1': { id: 1 },
  6. // 支持自定义函数,API 参考 express@4
  7. 'POST /api/users/create': (req, res) => {
  8. res.end('OK');
  9. },
  10. };

使用 public 目录

我们约定 public 目录下的文件会在 dev 和 build 时被自动 copy 到输出目录(默认是 ./build)下。所以可以在这里存放 favicon, index.html 等静态文件。

CSS Modules

CSS Modules 可以有效解决样式的冲突等问题,ice-scripts 支持 CSS Modules 能力。只需要将样式文件的后缀名改为.module.[css/scss/less],即可使用 CSS Modules 的能力:

  1. /* index.module.scss */
  2. .btn {
  3. color: green;
  4. }
  5. .tips {
  6. font-size: 12px;
  7. }
  1. // index.js
  2. import styles from './index.module.scss';
  3. <Button className={styles.btn}>OK</Button>;

Moment.js 按需加载

基础组件@alifd/next 里的时间相关组件依赖了 moment,但是为了业务可以优化 moment 的包大小,所以@alifd/next里将 moment 作为自己的 peerDependencies 而非 dependencies,因此项目使用到时间组件时需要自行引入 moment 依赖:

  1. $ npm install moment --save

moment 里有针对国际化语言的大量代码,如果不做任何处理的话会导致 bundle 变大,因此 ice-scripts 默认对 moment 文案做了按需加载,只有通过import ‘moment/locale/zh-cn.js’才会引入对应文案代码,比如如果想支持中文需要在项目入口文件里引入中文的语言文件:

  1. // index.js
  2. import 'moment/locale/zh-cn.js';

主题配置 - themeConfig

参考 切换项目主题

代理配置 - proxyConfig

参考 Iceworks 插件-代理配置