前言

此方案基于 webpack 构建实现,构建 web 端代码的流程无需做任何调整,此处只介绍如何将源码构建成小程序端代码。

编写 webpack 配置

新建一个 webpack.mp.config.js 文件,用于小程序端代码的构建,假设你要构建的小程序代码放到 ./miniprogram 目录中:

  1. const path = require('path')
  2. const webpack = require('webpack')
  3. const MiniCssExtractPlugin = require('mini-css-extract-plugin')
  4. const { VueLoaderPlugin } = require('vue-loader')
  5. const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
  6. const TerserPlugin = require('terser-webpack-plugin')
  7. const MpPlugin = require('mp-webpack-plugin') // 用于构建小程序代码的 webpack 插件
  8. const isOptimize = true // 是否压缩业务代码,开发者工具可能无法完美支持业务代码使用到的 es 特性,建议自己做代码压缩
  9. module.exports = {
  10. mode: 'production',
  11. entry: {
  12. // js 入口
  13. home: page.resolve(__dirname, '../src/home/main.mp.js'),
  14. list: page.resolve(__dirname, '../src/list/main.mp.js'),
  15. detail: page.resolve(__dirname, '../src/detail/main.mp.js'),
  16. },
  17. output: {
  18. path: path.resolve(__dirname, './miniprogram/common'), // 放到小程序代码目录中的 common 目录下
  19. filename: '[name].js', // 必需字段,不能修改
  20. library: 'createApp', // 必需字段,不能修改
  21. libraryExport: 'default', // 必需字段,不能修改
  22. libraryTarget: 'window', // 必需字段,不能修改
  23. },
  24. target: 'web', // 必需字段,不能修改
  25. optimization: {
  26. runtimeChunk: false, // 必需字段,不能修改
  27. splitChunks: { // 代码分割配置,不建议修改
  28. chunks: 'all',
  29. minSize: 1000,
  30. maxSize: 0,
  31. minChunks: 1,
  32. maxAsyncRequests: 100,
  33. maxInitialRequests: 100,
  34. automaticNameDelimiter: '~',
  35. name: true,
  36. cacheGroups: {
  37. vendors: {
  38. test: /[\\/]node_modules[\\/]/,
  39. priority: -10
  40. },
  41. default: {
  42. minChunks: 2,
  43. priority: -20,
  44. reuseExistingChunk: true
  45. }
  46. }
  47. },
  48. minimizer: isOptimize ? [
  49. // 压缩CSS
  50. new OptimizeCSSAssetsPlugin({
  51. assetNameRegExp: /\.(css|wxss)$/g,
  52. cssProcessor: require('cssnano'),
  53. cssProcessorPluginOptions: {
  54. preset: ['default', {
  55. discardComments: {
  56. removeAll: true,
  57. },
  58. minifySelectors: false, // 因为 wxss 编译器不支持 .some>:first-child 这样格式的代码,所以暂时禁掉这个
  59. }],
  60. },
  61. canPrint: false
  62. }),
  63. // 压缩 js
  64. new TerserPlugin({
  65. test: /\.js(\?.*)?$/i,
  66. parallel: true,
  67. })
  68. ] : [],
  69. },
  70. module: {
  71. rules: [
  72. // loaders 配置。这里需要注意的是,部分在 wxss 不支持的样式需要剔除,比如 ie hack 代码,可以使用 postcss 的 stylehacks 插件剔除;对于资源文件来说,需要转成 base64 或者线上资源链接,下面是一个简单的示例:
  73. // {
  74. // test: /\.(png|jpg|gif|svg|eot|woff|woff2|ttf)$/,
  75. // use: [{
  76. // loader: 'url-loader',
  77. // options: {
  78. // limit: 1024,
  79. // name: '[name]_[hash:hex:6].[ext]',
  80. // publicPath: 'https://test.miniprogram.com/res', // 对于资源文件直接使用线上的 cdn 地址
  81. // emitFile: false,
  82. // }
  83. // }],
  84. // },
  85. ]
  86. },
  87. plugins: [
  88. new webpack.DefinePlugin({
  89. 'process.env.isMiniprogram': process.env.isMiniprogram, // 注入环境变量,用于业务代码判断
  90. }),
  91. new MiniCssExtractPlugin({
  92. filename: '[name].wxss',
  93. }),
  94. new VueLoaderPlugin(),
  95. new MpPlugin({
  96. // 插件配置,下面会详细介绍
  97. }),
  98. ],
  99. }

编写 webpack 插件配置

这里的 webpack 插件配置即上面提到的 MpPlugin 的配置,内容如下:

  1. module.exports = {
  2. // 页面 origin,默认是 https://miniprogram.default
  3. origin: 'https://test.miniprogram.com',
  4. // 入口页面路由,默认是 /
  5. entry: '/',
  6. // 页面路由,用于页面间跳转
  7. router: {
  8. // 路由可以是多个值,支持动态路由
  9. home: [
  10. '/(home|index)?',
  11. '/test/(home|index)',
  12. ],
  13. list: [
  14. '/test/list/:id',
  15. ],
  16. detail: [
  17. '/test/detail/:id',
  18. ],
  19. },
  20. // 特殊路由跳转
  21. redirect: {
  22. notFound: 'home',
  23. accessDenied: 'home',
  24. },
  25. // 项目配置,会被合并到 project.config.json
  26. projectConfig: {
  27. appid: 'wx1234567890',
  28. projectname: 'kbone-demo',
  29. },
  30. }

更多详细配置信息可以点此查看

新增入口文件

此处小程序 webpack 配置所使用的入口文件和 web 端有一定区别,假设 web 端是这样的:

  1. import Vue from 'vue'
  2. import App from './App.vue'
  3. // 注入到页面上的 id 为 app 的 dom 节点上
  4. new Vue({
  5. el: '#app',
  6. render: h => h(App)
  7. })

那么小程序端所用到的入口则需要调整成如下:

  1. import Vue from 'vue'
  2. import App from './App.vue'
  3. // 需要将创建根组件实例的逻辑封装成方法
  4. export default function createApp() {
  5. // 在小程序中如果要注入到 id 为 app 的 dom 节点上,需要主动创建
  6. const container = document.createElement('div')
  7. container.id = 'app'
  8. document.body.appendChild(container)
  9. return new Vue({
  10. el: '#app',
  11. render: h => h(App)
  12. })
  13. }

这是因为小程序中各个页面的逻辑端是统一跑在 appService 上的,需要对各个页面做隔离。为了方便做后续操作,需要将创建根组件实例的逻辑封装成方法暴露给适配层,调用此方法时会返回根组件实例。

执行构建

  1. 构建小程序代码:
  1. webpack --config webpack.mp.config.js
  1. 进入小程序代码目录,安装小程序依赖包:
  1. cd miniprogram
  2. npm install
  1. 使用小程序开发者工具直接打开此目录,并点击工具菜单下的构建 npm 按钮,之后便可预览构建好的小程序了。

PS:建议使用工具的 RC 版本或者 Nightly 版本,以支持 babelSetting 的 ignore 配置。

例子

我们准备了若干 demo,可以点此查看

进阶

更多进阶用法和开发建议,可以点此查看