diff --git a/packages/vue-cli-plugin-uni/commands/build.js b/packages/vue-cli-plugin-uni/commands/build.js index 9c7193e0addbcbfa7b7fcb59fe11a0e55d13ecd6..34b57103d9fd73317c18690fac436dd02d7a7818 100644 --- a/packages/vue-cli-plugin-uni/commands/build.js +++ b/packages/vue-cli-plugin-uni/commands/build.js @@ -1,7 +1,7 @@ const path = require('path') const { - runByHBuilderX, + runByHBuilderX, isInHBuilderX } = require('@dcloudio/uni-cli-shared') @@ -27,7 +27,8 @@ module.exports = (api, options) => { '--watch': 'watch for changes', '--minimize': 'Tell webpack to minimize the bundle using the TerserPlugin.', '--auto-host': 'specify automator host', - '--auto-port': 'specify automator port' + '--auto-port': 'specify automator port', + '--subpackage': 'specify subpackage' } }, async (args) => { for (const key in defaults) { @@ -36,6 +37,13 @@ module.exports = (api, options) => { } } + if ( + args.subpackage && + process.env.UNI_PLATFORM === 'mp-weixin' + ) { + process.env.UNI_SUBPACKGE = args.subpackage + } + require('./util').initAutomator(args) args.entry = args.entry || args._[0] @@ -164,12 +172,12 @@ async function build (args, api, options) { if (!args.watch) { const dirMsg = runByHBuilderX ? '' : `The ${chalk.cyan(targetDirShort)} directory is ready to be deployed.` - done(`Build complete. ${dirMsg}`) - - if (process.env.UNI_PLATFORM === 'h5' && !isInHBuilderX) { - console.log() - console.log('欢迎将H5站部署到uniCloud前端网页托管平台,高速、免费、安全、省心,详见:') - console.log('https://uniapp.dcloud.io/uniCloud/hosting') + done(`Build complete. ${dirMsg}`) + + if (process.env.UNI_PLATFORM === 'h5' && !isInHBuilderX) { + console.log() + console.log('欢迎将H5站部署到uniCloud前端网页托管平台,高速、免费、安全、省心,详见:') + console.log('https://uniapp.dcloud.io/uniCloud/hosting') } } else { const dirMsg = runByHBuilderX ? '' : `The ${chalk.cyan(targetDirShort)} directory is ready. ` diff --git a/packages/vue-cli-plugin-uni/lib/mp/index.js b/packages/vue-cli-plugin-uni/lib/mp/index.js index 0c298e5ca56bbf8fc2b9b155a64845eb767a3715..4607efc63ec46f69fe7c933825a9d6d0dc2d8ef7 100644 --- a/packages/vue-cli-plugin-uni/lib/mp/index.js +++ b/packages/vue-cli-plugin-uni/lib/mp/index.js @@ -4,12 +4,15 @@ const webpack = require('webpack') const { parseEntry, getMainEntry, + normalizePath, getPlatformExts, getPlatformCssnano } = require('@dcloudio/uni-cli-shared') const WebpackUniAppPlugin = require('../../packages/webpack-uni-app-loader/plugin/index') +const CustomModuleIdsPlugin = require('../../packages/webpack-custom-module-ids-plugin/index') + const modifyVueLoader = require('../vue-loader') const { @@ -63,6 +66,88 @@ function getProvides () { return provides } +class PreprocessAssetsPlugin { + apply (compiler) { + compiler.hooks.emit.tap('PreprocessAssetsPlugin', compilation => { + const assets = compilation.assets + const hasVendor = assets['common/vendor.js'] + Object.keys(assets).forEach(name => { + const extname = path.extname(name) + if (extname !== '.js') { + return + } + if (name.startsWith('common')) { + return + } + const dirname = path.dirname(name) + const runtimeJsCode = `require('${path.relative(dirname, 'common/runtime.js')}');` + const vendorJsCode = hasVendor ? `require('${path.relative(dirname, 'common/vendor.js')}');` : '' + const code = `${runtimeJsCode}${vendorJsCode}` + assets[name].source().toString() + assets[name] = { + size () { + return Buffer.byteLength(code, 'utf8') + }, + source () { + return code + } + + } + }) + delete assets['common/main.js'] + delete assets['app.js'] + delete assets['app.json'] + delete assets['app.wxss'] + delete assets['project.config.json'] + console.log(Object.keys(assets)) + }) + } +} + +function initSubpackageConfig (webpackConfig, vueOptions) { + webpackConfig.node.set('global', false) + webpackConfig.plugins.delete('hash-module-ids') + // 与子包共享的模块 + const sharedModules = { + 'uni-mp-weixin/dist/index.js': 'uniWeixin', + 'mp-vue/dist/mp.runtime.esm.js': 'uniVue' + } + const sharedModulePaths = Object.keys(sharedModules) + webpackConfig + .plugin('custom-hash-module-ids') + .use(CustomModuleIdsPlugin, [{ + prefix: process.env.UNI_SUBPACKGE, + custom (libIdent) { + if (!libIdent) { + return + } + const normalizedLibIdent = normalizePath(libIdent) + const name = sharedModulePaths.find(p => normalizedLibIdent.endsWith(p)) + if (name) { + return sharedModules[name] + } + } + }]) + if (process.env.UNI_SUBPACKGE !== 'main') { // 非主包 + process.env.UNI_OUTPUT_DIR = path.resolve(process.env.UNI_OUTPUT_DIR, process.env.UNI_SUBPACKGE) + vueOptions.outputDir = process.env.UNI_OUTPUT_DIR + webpackConfig.output.path(process.env.UNI_OUTPUT_DIR) + webpackConfig.output.jsonpFunction('webpackJsonp_' + process.env.UNI_SUBPACKGE) + webpackConfig.externals([ + function (context, request, callback) { + if (request === 'vue') { + return callback(null, 'root global["webpackMain"]["uniVue"]') + } + const normalizedRequest = normalizePath(request) + const name = sharedModulePaths.find(p => normalizedRequest.endsWith(p)) + if (name) { + return callback(null, `root global["webpackMain"]["${sharedModules[name]}"]`) + } + callback() + } + ]) + } +} + module.exports = { vueConfig: { parallel: false @@ -84,7 +169,23 @@ module.exports = { const statCode = process.env.UNI_USING_STAT ? 'import \'@dcloudio/uni-stat\';' : '' - const beforeCode = 'import \'uni-pages\';' + let beforeCode = 'import \'uni-pages\';' + + if (process.env.UNI_SUBPACKGE === 'main') { + const uniPath = require('@dcloudio/uni-cli-shared/lib/platform').getMPRuntimePath() + beforeCode += + `import uniVue from 'vue';import * as uniWeixin from '${uniPath}';global['webpackMain']={uniVue,uniWeixin};` + } + + const plugins = [ + new WebpackUniAppPlugin(), + createUniMPPlugin(), + new webpack.ProvidePlugin(getProvides()) + ] + + if (process.env.UNI_SUBPACKGE && process.env.UNI_SUBPACKGE !== 'main') { + plugins.push(new PreprocessAssetsPlugin()) + } return { mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', @@ -147,11 +248,7 @@ module.exports = { }] }] }, - plugins: [ - new WebpackUniAppPlugin(), - createUniMPPlugin(), - new webpack.ProvidePlugin(getProvides()) - ] + plugins } }, chainWebpack (webpackConfig, vueOptions, api) { @@ -196,6 +293,10 @@ module.exports = { })) } + if (process.env.UNI_SUBPACKGE) { + initSubpackageConfig(webpackConfig, vueOptions) + } + webpackConfig.plugins.delete('hmr') webpackConfig.plugins.delete('html') webpackConfig.plugins.delete('copy') diff --git a/packages/vue-cli-plugin-uni/packages/webpack-custom-module-ids-plugin/index.js b/packages/vue-cli-plugin-uni/packages/webpack-custom-module-ids-plugin/index.js new file mode 100644 index 0000000000000000000000000000000000000000..d41f3b7322ab8db0fb3379b177fddda81e005bb3 --- /dev/null +++ b/packages/vue-cli-plugin-uni/packages/webpack-custom-module-ids-plugin/index.js @@ -0,0 +1,43 @@ +const createHash = require('webpack/lib/util/createHash') + +module.exports = class CustomModuleIdsPlugin { + constructor (options) { + this.options = Object.assign({ + prefix: '', + hashFunction: 'md4', + hashDigest: 'base64', + hashDigestLength: 4 + }, options || {}) + } + + apply (compiler) { + const options = this.options + compiler.hooks.compilation.tap('CustomModuleIdsPlugin', compilation => { + const usedIds = new Set() + compilation.hooks.beforeModuleIds.tap('CustomModuleIdsPlugin', modules => { + for (const module of modules) { + if (module.id !== null) { + continue + } + const libIdent = module.libIdent + ? module.libIdent({ + context: options.context || compiler.options.context + }) : null + let id = options.custom && options.custom(libIdent, module) + if (!id && libIdent) { + const hash = createHash(options.hashFunction) + hash.update(options.prefix + libIdent) + const hashId = (hash.digest(options.hashDigest)) + let len = options.hashDigestLength + while (usedIds.has(hashId.substr(0, len))) len++ + id = hashId.substr(0, len) + usedIds.add(id) + } + if (id) { + module.id = id + } + } + }) + }) + } +} diff --git a/packages/webpack-uni-mp-loader/lib/plugin/generate-component.js b/packages/webpack-uni-mp-loader/lib/plugin/generate-component.js index 1e017f99532df5b3e9b1c2bf47c0202eb0c6146c..5095400c469fe98c5a7fd963398bc79af459251f 100644 --- a/packages/webpack-uni-mp-loader/lib/plugin/generate-component.js +++ b/packages/webpack-uni-mp-loader/lib/plugin/generate-component.js @@ -61,7 +61,7 @@ function findComponentModuleId (modules, concatenatedModules, resource, altResou let lastComponents = [] // TODO 解决方案不太理想 -module.exports = function generateComponent (compilation) { +module.exports = function generateComponent (compilation, jsonpFunction = 'webpackJsonp') { const curComponents = [] const components = getComponentSet() if (components.size) { @@ -69,7 +69,9 @@ module.exports = function generateComponent (compilation) { const modules = compilation.modules const concatenatedModules = modules.filter(module => module.modules) - const uniModuleId = modules.find(module => module.resource && normalizePath(module.resource) === uniPath).id + const uniModule = !process.env.UNI_SUBPACKAGE && modules.find(module => module.resource && normalizePath(module.resource) === + uniPath) + const uniModuleId = uniModule && uniModule.id const styleImports = {} const fixSlots = {} @@ -107,13 +109,19 @@ module.exports = function generateComponent (compilation) { if (process.env.UNI_PLATFORM === 'mp-alipay') { beforeCode = ';my.defineComponent || (my.defineComponent = Component);' } + let requireCode = + `__webpack_require__('${uniModuleId}')['createComponent'](__webpack_require__(${JSON.stringify(moduleId)}))` + if (process.env.UNI_SUBPACKGE) { + requireCode = + `global['webpackMain']['uniWeixin']['createComponent'](__webpack_require__(${JSON.stringify(moduleId)}))` + } const source = beforeCode + origSource + ` -;(${globalVar}["webpackJsonp"] = ${globalVar}["webpackJsonp"] || []).push([ +;(${globalVar}["${jsonpFunction}"] = ${globalVar}["${jsonpFunction}"] || []).push([ '${chunkName}', { '${chunkName}':(function(module, exports, __webpack_require__){ - __webpack_require__('${uniModuleId}')['createComponent'](__webpack_require__(${JSON.stringify(moduleId)})) + ${requireCode} }) }, [['${chunkName}']] @@ -230,4 +238,4 @@ function removeUnusedComponent (name) { fs.renameSync(path.join(process.env.UNI_OUTPUT_DIR, name + '.json'), path.join(process.env.UNI_OUTPUT_DIR, name + '.bak.json')) } catch (e) {} -} +} diff --git a/packages/webpack-uni-mp-loader/lib/plugin/generate-json.js b/packages/webpack-uni-mp-loader/lib/plugin/generate-json.js index 433af4443f514870d9c7ed95f60cf572b87c71ab..bc56b462acc77ac1b023f59673e83062279e8634 100644 --- a/packages/webpack-uni-mp-loader/lib/plugin/generate-json.js +++ b/packages/webpack-uni-mp-loader/lib/plugin/generate-json.js @@ -85,6 +85,18 @@ function analyzeUsingComponents () { // }, {}) } +function normalizeUsingComponents (file, usingComponents) { + const names = Object.keys(usingComponents) + if (!names.length) { + return usingComponents + } + file = path.dirname('/' + file) + names.forEach(name => { + usingComponents[name] = path.relative(file, usingComponents[name]) + }) + return usingComponents +} + module.exports = function generateJson (compilation) { analyzeUsingComponents() @@ -167,6 +179,11 @@ module.exports = function generateJson (compilation) { delete jsonObj.navigationBarShadow } + if (process.env.UNI_SUBPACKGE && process.env.UNI_SUBPACKGE !== 'main') { + if (jsonObj.usingComponents) { + jsonObj.usingComponents = normalizeUsingComponents(name, jsonObj.usingComponents) + } + } const source = JSON.stringify(jsonObj, null, 2) const jsFile = name.replace('.json', '.js') @@ -208,4 +225,4 @@ module.exports = function generateJson (compilation) { require('@dcloudio/uni-cli-shared/lib/cache').store() }, 50) } -} +} diff --git a/packages/webpack-uni-mp-loader/lib/plugin/index-new.js b/packages/webpack-uni-mp-loader/lib/plugin/index-new.js index 7817fc4e78feaa3081ad0065676f839844e1e75a..4f5eb9af9af3621e7e36201527ab7e523927c834 100644 --- a/packages/webpack-uni-mp-loader/lib/plugin/index-new.js +++ b/packages/webpack-uni-mp-loader/lib/plugin/index-new.js @@ -81,7 +81,7 @@ class WebpackUniMPPlugin { source }) => emitFile(file, source, compilation)) - generateComponent(compilation) + generateComponent(compilation, compiler.options.output.jsonpFunction) resolve() })