乐趣区

关于前端:一套代码发布多个微信小程序的实践

之前接手了公司的一个微信小程序我的项目,上线了一段时间之后,须要以这个我的项目为根底再公布多个小程序。这些小程序的内容基本上都是一样的,只不过它们有不同的名称、主题、图标等等;或者,某几个小程序须要加一些定制化页面,性能等。本文次要记录下我从纯手工复制我的项目进化到应用命令行工具复制我的项目的实际过程。这个简略的命令行工具简略粗犷地叫做 quickcopy,文档在这里。

我是应用 Taro 2.2.13 开发小程序的,所以这个工具目前也是在这个环境下开发的。除了 Taro 插件性能 以外,2.x 都能够应用这个工具。

开发工具前

defineConstants

一开始,因为我的项目也不多,所以我就间接纯手工操作了。比方,咱们曾经有了一个小程序叫做 小程序 A ,当初咱们须要以这个小程序为根底复制出一个新的小程序,并且在打包之后实现以下 3 个简略的需要:

  1. 设置 config.window.navigationBarTitleText,在 navigation bar 显示各自的小程序名称;
  2. 设置 config.tabBar.selectedColor,在 tabBar 选中时显示不同的色彩;
  3. 为新的小程序定制 config.tabBar 图标,小程序 A 则持续应用原来的图标。

首先,咱们应用 全局常量 来革新 app.jsx 中的 config

// app.jsx
config = {
  tabBar: {
    // 革新前:selectedColor: '#000'
    selectedColor: __MAIN_COLOR,
    list: [
      {
        // 革新前:'assets/icons/tabbar-home-s.png'
        selectedIconPath: 'assets/' + __ICON_DIR + '/tabbar-home-s.png'
      }
    ]
  },
  window: {
    // 革新前:navigaionBarTitleText: '小程序 A'
    navigationBarTitleText: __APP_NAME
  }
}

而后,在 config 目录下别离为这两个小程序创立 Taro 编译配置文件 build-configA.jsbuild-configB.js,写入 defineConstants

// build-configA.js
module.exports = {
  defineConstants: {__APP_NAME: JSON.stringify('小程序 A'),
    __MAIN_COLOR: JSON.stringify('#000'),
    __ICON_DIR: JSON.stringify('icons')
  }
}
// build-configB.js
module.exports = {
  defineConstants: {__APP_NAME: JSON.stringify('小程序 B'),
    __MAIN_COLOR: JSON.stringify('#111'),
    __ICON_DIR: JSON.stringify('icons-b')
  }
}

最初,编译打包 小程序 A 的时候,我须要在 config/index.js 的最初将 build-configA.js 与根底的编译配置 merge。当编译打包 小程序 B 的时候也是一样。

module.exports = function(merge) {return merge({}, config, require('./dev'), require('./build-configA.js'))
}

运行这两个小程序,咱们就能够看到它们会显示各自的名称与主题色,小程序 B 还会显示定制化的 tabBar 图标。

sass.resource

既然在下面的全局常量中咱们曾经定义了一个主题色 __MAIN_COLOR,那么,咱们必定也须要为不同的小程序编写不同的主题款式。
首先,在 src/style/themes 目录下别离为两个小程序创立主题款式文件。而后在 build-configA.js 以及 build-configB.js 中进行全局注入:

// build-configA.js
sass: {
  resource: [
    'src/style/themes/themeA.scss', // build-configB.js 中写 src/style/themes/themeB.scss
    'src/style/variable.scss',
    'src/style/mixins.scss'
  ]
}

全局注入后也就不须要在款式文件中一次次写 @import 'xxx.scss' 了。但在这里须要留神的是,必须残缺的列出须要注入的 3 个文件。尽管像 variable.scssmixins.scss 这种款式文件显著能够在所有我的项目共享,但如果只在 config/index.js 中注入,而在 build-configA.js 或者 build-configB.js 中只注入主题款式文件的话,是行不通的。

// build-configA.js
sass: {resource: ['src/style/themes/themeA.scss']
}
// config/index.js
sass: {
  resource: [
    'src/style/variable.scss',
    'src/style/mixins.scss'
  ],
  projectDirectory: path.resolve(__dirname, '..')
}
// 以上两个配置 `merge` 之后的后果是
sass: {
  resource: [
    'src/style/themes/themeA.scss',
    'src/style/mixins.scss'
  ],
  projectDirectory: path.resolve(__dirname, '..')
}

也就是说,对于数组来说,是按索引地位进行 merge 的。

到当初为止,咱们实现了为不同的小程序配置不同的名称,icon 以及主题款式。然而本着能偷懒就偷懒的准则,我感觉这些步骤曾经有点麻烦了,能够设想下,如果又有新的我的项目须要公布,咱们须要手动做这些事件:

  1. config 目录下创立我的项目的编译配置文件 config-project.js
  2. 如果须要,为新我的项目建设定制化的 icons 目录;
  3. 为新我的项目创立主题款式文件,并在 config-project.js 中全局注入;
  4. config-project.js 编写 defineConstants,写入不同我的项目间 有差别的常量,其余的常量则写入 config/index.js
  5. config/index.js 合并新我的项目的编译配置;
  6. 目前所有的我的项目都共享了根目录下的 project.config.json,所以在编译前须要批改 appid

如果哪一天这些我的项目都须要进行更新,能够设想下:

  1. 首先批改 config/index.js 中须要 merge 的我的项目配置门路;
  2. 而后批改 project.config.json 中的 appid
  3. 最初编译打包;
  4. 如此循环;

那么,下面这些步骤是不是能够交给程序来实现呢?为了尽可能偷懒,我就写了一个简略的命令行工具。它能够代替咱们实现以下事件:

  1. config/index.js 为模版,提取局部编译配置,创立并写入到新我的项目的 Taro 编译配置文件
  2. 以根目录 project.config.json 为模版,创立新我的项目的小程序我的项目配置文件;
  3. 创立新我的项目的主题款式文件,并在编译配置全局注入;
  4. 在打包时寻找新我的项目有没有定制化图标,如果有,则替换。

开发工具后

假设咱们曾经有了一份现有我的项目 小程序 A 的编译配置:

// config/index.js
const config = {
  projectName: 'projectA',
  outputRoot: 'dist',
  copy: {
    patterns: [{ from: 'src/components/wxs', to: 'dist/components/wxs'},
      // ...
    ]
  },
  sass: {
    resource: [
      'src/style/variable.scss',
      'src/style/mixins.scss',
      // ...
    ],
    projectDirectory: path.resolve(__dirname, '..')
  },
  defineConstants: {HOST: JSON.stringify('www.baidu.com'),
    APP_NAME: JSON.stringify('小程序 A'),
    MAIN_COLOR: JSON.stringify('#999'),
    // ...
  }
}

在这份编译配置里,指定了我的项目的输入目录是 dist,全局注入了 variable.scssmixins.scss 文件,并指定了 3 个常量。因为 Taro 不会打包 wxs,所以在 copy.patterns 手动将 wxs 复制到了输入目录。

在复制我的项目之前,咱们先对编译配置进行一点革新。在 defineConstants 中,咱们找到那些不同我的项目间存在差别的常量,在这里就是 APP_NAMEMAIN_COLOR,增加双下划线 __ 作为结尾,这样工具就晓得这些常量是存在差别的,而残余的常量在所有我的项目中都是一样的。而后在 variable.scss 中找到那些与主题无关的变量,这些变量随后须要写入我的项目各自的主题款式文件中。

对于已存在的我的项目 projectA,咱们最好也进行一次复制操作。这样一来它就能够领有独立的编译配置,config/index.js 不仅会作为一份根底的编译配置被所有我的项目共享,也会作为创立新我的项目独立编译配置时的一份模版

复制我的项目

以拆散已有的 projectA 我的项目为例(复制新我的项目也是相似的),在根目录执行:

qc copy projectA wx123456789a

工具能够代替咱们实现这些工作:

  1. 创立 Taro 编译配置文件,门路为 config/config-projectA/index.js
  2. 以根目录 project.config.json 为模版创立微信小程序我的项目配置文件,门路为 config/config-prjectA/project.config.json

    {
      "miniprogramRoot": "dist-projectA/",
      "projectname": "projectA",
      "appid": "wx123456789a"
    }

    其余的内容则会与根目录下的 project.config.json 保持一致;

  3. src/stylesrc/styles 以及 src/css 为程序查找是否存在这些款式目录。如果存在,则在对应目录下创立 themes/projectA.scss 主题款式文件;如果以上几个目录都不存在,则默认在 src/style 下创立。具体的款式则须要手动写入;
  4. config/index.js 找到须要全局注入的款式文件,即 sass.resource,与上一步创立的主题款式文件一起注入到 config/config-projectA/index.js

    sass: {
      resource: [
        'src/style/themes/projectA.scss',
        'src/style/variable.scss',
        'src/style/mixins.scss',
      ]
    }

    主题款式文件会放在第一位,以便 variable.scssmixins.scss 能够依赖主题款式。

  5. config/index.js 找到须要复制到输入目录的文件,即 copy.patterns,批改 to 指定的门路;

    copy: {
      patterns: [
        {
          from: 'src/components/wxs',
          to: 'dist-projectA/components/wxs'
        }
      ]
    }
  6. config/index.js 中找到不同我的项目间 具备差别的常量,即 defineConstants__ 结尾的常量,并主动增加一个名为 __PROJECT 的新常量;

    defineConstants: {__APP_NAME: JSON.stringify('小程序 A'),
      __MAIN_COLOR: JSON.stringify('#999'),
      __PROJECT: JSON.stringify('projectA')
    }

所以最终的 config/config-projectA/index.js 就像这样:

module.exports = {
  projectName: 'projectA',
  outputRoot: 'dist-projectA',
  defineConstants: {__APP_NAME: JSON.stringify('小程序 A'),
    __MAIN_COLOR: JSON.stringify('#999'),
    __PROJECT: JSON.stringify('prjectA')
  },
  copy: {
    patterns: [
      {
        from: 'src/components/wxs',
        to: 'dist-projectA/components/wxs'
      }
    ]
  },
  sass: {
    resource: [
      'src/style/themes/projectA.scss',
      'src/style/variable.scss',
      'src/style/mixins.scss'
    ]
  }
}

至于上文的 icon 问题,因为 Taro 提供了插件能力,所以咱们不再须要像上文一样引入 __ICON_DIR 常量并革新 selectedIconPath。只须要在 config/index.jsplugins 中增加 quickcopy/plugin-copy-assets 即可。

举个例子,咱们本来将 icon 放在 src/assets/icons 目录下,如果咱们想为 projectA 指定定制化的 tabBar.list.selectedIconPath,只须要新建一个名为 src/assets/icons-projectA 的目录,在这个目录下寄存 projectA 定制化的 icon 即可。

当打包 projectA 的时候,这个插件会去 assets/icons-projectA 查找是否存在定制化的 icon,如果存在,则应用这个 icon,如果不存在,则应用 assets/icons 中默认的 icon

其余的 icon 也是同样的情理。

编译前筹备

当咱们须要编译 projectA 的时候,在根目录执行:

qc prep projectA

工具会做以下两件事件:

  1. 创立 config/build.export.js 文件,并将 config/config-projectA/index.js 导出;

    const buildConfig = require('./config-projectA/index')
    module.exports = buildConfig
  2. config/config-projectA/project.config.json 复制到根目录。

咱们只须要在 config/index.js 的最初 merge build.export.js,随后在根目录执行 Taro 编译指令

如何增加定制化页面

兴许在将来的有一天,咱们接到一个需要,须要为 小程序 A 增加一个定制化的页面。咱们将这个页面门路增加到 app.jsxconfig,但又不心愿其余小程序打包的时候把这个页面也打包进去。

一开始我应用的办法简略粗犷:在打包其余小程序的时候把这个页面门路正文起来,在打包 小程序 A 的时候再把正文关上。

咱们能够借助 babel-plugin-preval(在 Taro 文档中也有提到)以及上文的 __PROJECT 常量编写逻辑,来确定哪个我的项目须要打包定制化页面,哪些项目又不须要打包。

首先,把 config.pages 提取进去作为一个独立文件,比方:

// pages.js
module.exports = function(project) {
  const pages = [
    'pages/tabBar/home/index',
    'pages/tabBar/profile/index'
  ]
  if (project == 'projectA') {pages.push('pages/special/index')
  }
  return pages
}

而后革新 _app.jsx_:

const project = __PROJECT
class App extends Component {
  config = {
    // 这里应用了 project 而没有间接传入 __PROJECT 是因为我在测试的时候发现间接应用 __PROJECT 编译的时候会报错
    pages: preval.require('./pages.js', project)
  }
}

这样一来咱们只须要批改 pages.js 就能够增加定制化页面,不仅防止被不须要的我的项目打包,也能分明地看出哪些项目有定制化页面哪些没有。对于 subpackagestabBar.list 也能够做同样的解决。

最初

这个工具到目前为止是依据公司的业务需要开发的,次要性能也并不多,还是有挺大的局限。我也还在摸索如何更不便地打包为不同我的项目编写的定制化页面,所以这个工具还会持续更新上来。

退出移动版