Meituan-Dianping/mpvue

mpvue 如何对小程序进行分包?

Closed this issue · 7 comments

jy418 commented

曾试过直接在main里面按照小程序的分包配置,然而貌似不行

auven commented

修改相关源码

你可以修改一下 mpvue-loader,打开 node_modules/mpvue-loader/lib/mp-compiler/index.js,从第220行开始,添加如下代码

// 移除 app.json 中 pages 中包含的小程序分包页面
if (Array.isArray(configObj.subPackages)) {
  for (let subPackages of configObj.subPackages) {
    if (Array.isArray(subPackages.pages)) {
      for (let page of subPackages.pages) {
        const pageIndex = pages.findIndex(v => v === `${subPackages.root}/${page}`)
        pageIndex > -1 && pages.splice(pageIndex, 1)
      }
    }
  }
}

如果使用了 mpvue-entry ,则修改 node_modules/mpvue-entry/lib/utils/compiler.js ,第75行开始,添加如下代码

// app.pages = pages.map(page => page.path.replace(/^\//, ''));

const newPages = pages.map(page => page.path.replace(/^\//, ''));
// 移除 app.json 中 pages 中包含的小程序分包页面
if (Array.isArray(app.subPackages)) {
  for (let subPackages of app.subPackages) {
    if (Array.isArray(subPackages.pages)) {
      for (let page of subPackages.pages) {
        const pageIndex = newPages.findIndex(v => v === `${subPackages.root}/${page}`)
        pageIndex > -1 && newPages.splice(pageIndex, 1)
      }
    }
  }
}
app.pages = newPages

使用

src/main.js

export default {
    ...
    subPackages: [
      {
        // 登录注册模块
        root: 'pages/auth',
        pages: [
          'login/login',
          'register/register'
        ]
      },
      {
        // 购物车
        root: 'pages/shopping',
        pages: [
          'checkout/checkout',
          'confirmOrder/confirmOrder',
          'store/store',
          'store/storeInMap/storeInMap',
          'store/storeInMap/calendar/calendar'
        ]
      },
      {
        // 商品展示
        root: 'pages/goods',
        pages: [
          'goods',
          'goodsList/goodsList',
          'brandGoodsList/brandGoodsList',
          'hotGoodsList/hotGoodsList'
        ]
      },
      {
        // 我的页面
        root: 'pages/mine',
        pages: [
          'address/address',
          'address/addressAdd/addressAdd',
          'collect/collect',
          'coupon/coupon',
          'history/history'
        ]
      },
      {
        // 我的个人资料
        root: 'pages/mineInfo',
        pages: [
          'mineInfo',
          'editPw/editPw',
          'editUavatar/editUavatar',
          'editUname/editUname',
          'editUphone/editUphone'
        ]
      },
      {
        // 订单相关
        root: 'pages/order',
        pages: [
          'order',
          'addComment/addComment',
          'express/express',
          'orderDetail/orderDetail',
          'selfExDetail/selfExDetail'
        ]
      },
      {
        // 设置
        root: 'pages/setting',
        pages: [
          'setting',
          'about/about',
          'feedback/feedback'
        ]
      }
    ]
    ...
}

微信开发者工具
1831528171325_ pic

auven commented

按照上面配置,可以实现小程序分包,但是你会发现主包会很大。为什么呢?

wechatimg193

来了解一下 mpvue 资源文件的打包机制

如图
wechatimg189

这就解释了为什么分包之后主包那么大,因为我那些 次包包含的js/wxml/wxss并不在次包里面,而是被打包进主包了

那如果我们 把各个页面引用的资源文件打包进页面所在文件夹 呢,是不是可以解决这个问题了。我们来试一试。

尝试

修改 mpvue-loader

主要是为了处理将页面对应的 components/xxx.wxml 打包进页面所在的文件夹下。

node_modules/mpvue-loader/lib/mp-compiler/index.js

# 第26行添加
// 缓存组件对应的入口路径
const pagesCompMap = Object.create(null)
function cachePagesComp(componentName, src) {
  pagesCompMap[componentName] = src
}

# 紧接着找到 createAppWxml 函数
function createAppWxml (emitFile, resourcePath, rootComponent) {
  const { src } = getFileInfo(resourcePath) || {}
  const componentName = getCompNameBySrc(rootComponent)
  # 添加这一句
  cachePagesComp(componentName, src)
  const wxmlContent = genPageWxml(componentName, src)
  const wxmlSrc = src
  emitFile(`${wxmlSrc}.wxml`, wxmlContent)
}

# createWxml 函数
function createWxml (emitWarning, emitError, emitFile, resourcePath, rootComponent, compiled, html) {
  cacheCreateWxmlFns[resourcePath] = arguments
  const { pageType, moduleId, components } = getFileInfo(resourcePath) || {}

  const name = getCompNameBySrc(resourcePath)
  const options = { components, pageType, name, moduleId }
  const wxmlContent = genComponentWxml(compiled, options, emitFile, emitError, emitWarning)
  # 修改这里
  const wxmlSrc = pagesCompMap[name] ? `${pagesCompMap[name]}/components/${name}` : `components/${name}`

  emitFile(`${wxmlSrc}.wxml`, wxmlContent)
}

# resolveSrc 函数
function resolveSrc (originComponents, components, resolveFn, context) {
  return Promise.all(Object.keys(originComponents).map(k => {
    return new Promise((resolve, reject) => {
      resolveFn(context, originComponents[k], (err, realSrc) => {
        if (err) return reject(err)
        const com = covertCCVar(k)
        const comName = getCompNameBySrc(realSrc)
        # 修改这里
        components[com] = { src: `/components/${comName}`, name: comName } 
        resolve()
      })
    })
  }))
}

node_modules/mpvue-loader/lib/mp-compiler/templates.js,直接复制粘贴

const { getPathPrefix } = require('./util')

function genScript (name, isPage, src) {
  const prefix = isPage ? getPathPrefix(src) : './'

  return `
require('${prefix}manifest/js/main.js')
require('${prefix}vendor/js/main.js')
require('${prefix}${name}/js/main.js')
`
}

function genStyle (name, isPage, src) {
  const prefix = isPage ? getPathPrefix(src) : './'
  return `@import "/${name}/css/main.wxss";`
}

function genPageWxml (templateName, src) {
  return `<import src="/${src}/components/${templateName}" /><template is="${templateName}" data="{{ ...$root['0'], $root }}"/>`
}

module.exports = { genScript, genStyle, genPageWxml }

修改项目打包配置

这里修改是为了处理将 jswxss 打包到对应页面文件夹下。

/build/webpack.dev.conf.js

...
module.exports = merge(baseWebpackConfig, {
  ...
  output: {
    path: config.build.assetsRoot,
    // filename: utils.assetsPath('js/[name].js'),
    filename: path.posix.join('[name]/js/main.js'),
    // chunkFilename: utils.assetsPath('js/[id].js')
    chunkFilename: path.posix.join('[id]/js/main.js')
  },
  plugins: [
    ...
    new ExtractTextPlugin({
      // filename: utils.assetsPath('css/[name].wxss')
      filename: path.posix.join('[name]/css/main.wxss')
    }),
    ...
  ]
})

/build/webpack.prod.conf.js

...
const webpackConfig = merge(baseWebpackConfig, {
  ...
  output: {
    path: config.build.assetsRoot,
    // filename: utils.assetsPath('js/[name].js'),
    filename: path.posix.join('[name]/js/main.js'),
    // chunkFilename: utils.assetsPath('js/[id].js')
    chunkFilename: path.posix.join('[id]/js/main.js')
  },
  plugins: [
    ...
    new ExtractTextPlugin({
      // filename: utils.assetsPath('css/[name].wxss')
      filename: path.posix.join('[name]/css/main.wxss')
    }),
    ...
  ]
})
...

打包试一试

如图
wechatimg192

可以看到,主包大小由原来的 295k 变为 249k

mpvue-entry v1.4.0 起支持直接配置分包,因为会重写一次 app.json,所以不需要改动 mpvue-loader 中生成 app.json 相关的代码了

集成了一下上面分享的方法,可以直接使用这个模板建立项目 https://github.com/F-loat/mpvue-quickstart

aOrz commented

#672

@auven 问一下 是哪个版本的mpvue-loader

aOrz commented

@cwc845982120 看下 #672

我按照楼主的那个方式分包,但是报错,configObj is not defined