我们使用uni-app的脚手架开发微信小程序其实已经比较方便。但是uni-app框架是怎么实现将vue项目打包成微信小程序项目,很少有人关注。关于uni-build打包小程序的源码分析。底层还是使用的webpack打包框架,uni-app实现了自己的一套算法。针对小程序所要求的文件结构,将VUE项目转换成微信小程序可识别的项目结构。看了一下源码,不是很好读,我接下来主要把找到的编译入口方法展示给大家看下,有需要的朋友可以顺着入口往里解读。
前言
小程序包含一个描述整体程序的 app
和多个描述各自页面的 page
。
一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下:
一个小程序页面由四个文件组成,分别是:
注意:为了方便开发者减少配置项,描述页面的四个文件必须具有相同的路径与文件名。
https://developers.weixin.qq.com/miniprogram/dev/framework/structure.html
一、打包实现在依赖包vue-cli-plugin-uni中,入口看build.js文件
uni-build打包的build源码
async function build (args, api, options) { const fs = require(\'fs-extra\') const chalk = require(\'chalk\') const webpack = require(\'webpack\') const { log, done, logWithSpinner, stopSpinner } = require(\'@vue/cli-shared-utils\') const runByAliIde = process.env.BUILD_ENV === \'ali-ide\' log() if (!runByHBuilderX && !runByAliIde) { logWithSpinner(`开始编译当前项目至 ${process.env.UNI_SUB_PLATFORM || process.env.UNI_PLATFORM} 平台...`) } // 解析文件输出目录 const targetDir = api.resolve(options.outputDir) // 获取打包配置文件 const webpackConfigs = getWebpackConfigs(api, args, options) // 生成环境打包,会先清理掉 node_modules/.cache 下的缓存文件 if (process.env.NODE_ENV === \'production\') { try { fs.emptyDir(path.resolve(process.env.UNI_CLI_CONTEXT, \'node_modules/.cache\')) } catch (e) {} } // 有clean参数,或者是app-plus打包,则清空输出目录 if (args.clean || process.env.UNI_PLATFORM === \'app-plus\') { await fs.emptyDir(targetDir) } if (process.env.UNI_USING_NATIVE || process.env.UNI_USING_V3_NATIVE) { webpackConfigs.length = 0 } if ( process.env.UNI_USING_NATIVE || process.env.UNI_USING_V3_NATIVE || (process.UNI_NVUE_ENTRY && Object.keys(process.UNI_NVUE_ENTRY).length) ) { webpackConfigs.push(require(\'@dcloudio/vue-cli-plugin-hbuilderx/build/webpack.nvue.conf.js\')(process.UNI_NVUE_ENTRY)) } return new Promise((resolve, reject) => { webpack(webpackConfigs, (err, stats) => { if (!runByHBuilderX && !runByAliIde) { stopSpinner(false) } if (err) { return reject(err) } if (stats.hasErrors()) { /* eslint-disable prefer-promise-reject-errors */ return reject(\'Build failed with errors.\') } if (!args.silent && (process.env.UNI_PLATFORM !== \'app-plus\' || process.env.UNI_AUTOMATOR_WS_ENDPOINT)) { const targetDirShort = path.relative( api.service.context, process.env.UNI_OUTPUT_DIR ) 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\') } } else { const dirMsg = runByHBuilderX ? \'\' : `The ${chalk.cyan(targetDirShort)} directory is ready. ` done(`Build complete. ${dirMsg}Watching for changes...`) } } resolve() }) }) }
二、打包微信小程序的代码入口地址
mp的入口文件index.js中定义了微信小程序打包的相关配置
webpackConfig (webpackConfig, vueOptions, api) { if (!webpackConfig.optimization) { webpackConfig.optimization = {} } // disable noEmitOnErrors webpackConfig.optimization.noEmitOnErrors = false webpackConfig.optimization.runtimeChunk = { name: \'common/runtime\' } webpackConfig.optimization.splitChunks = require(\'../split-chunks\')() parseEntry() const statCode = process.env.UNI_USING_STAT ? \'import \\'@dcloudio/uni-stat\\';\' : \'\' const beforeCode = \'import \\'uni-pages\\';\' return { mode: process.env.NODE_ENV === \'production\' ? \'production\' : \'development\', entry () { return process.UNI_ENTRY }, output: { filename: \'[name].js\', chunkFilename: \'[id].js\', globalObject: process.env.UNI_PLATFORM === \'mp-alipay\' ? \'my\' : \'global\' // sourceMapFilename: \'../.sourcemap/\' + process.env.UNI_PLATFORM + \'/[name].js.map\' }, performance: { hints: false }, resolve: { extensions: [\'.nvue\'], alias: { // 仅 mp-weixin \'mpvue-page-factory\': require.resolve( \'@dcloudio/vue-cli-plugin-uni/packages/mpvue-page-factory\') } }, module: { rules: [{ test: path.resolve(process.env.UNI_INPUT_DIR, getMainEntry()), use: [{ loader: path.resolve(__dirname, \'../../packages/wrap-loader\'), options: { before: [ beforeCode + require(\'../util\').getAutomatorCode() + statCode ] } }, { loader: \'@dcloudio/webpack-uni-mp-loader/lib/main\' }] }, { resourceQuery: /vue&type=script/, use: [{ loader: \'@dcloudio/webpack-uni-mp-loader/lib/script\' }] }, { resourceQuery: /vue&type=template/, use: [{ loader: \'@dcloudio/webpack-uni-mp-loader/lib/template\' }, { loader: \'@dcloudio/vue-cli-plugin-uni/packages/webpack-uni-app-loader/page-meta\' }] }, createTemplateCacheLoader(api), { resourceQuery: [ /lang=wxs/, /lang=filter/, /lang=sjs/, /blockType=wxs/, /blockType=filter/, /blockType=sjs/ ], use: [{ loader: require.resolve( \'@dcloudio/vue-cli-plugin-uni/packages/webpack-uni-filter-loader\') }] }] }, plugins: [ new WebpackUniAppPlugin(), createUniMPPlugin(), new webpack.ProvidePlugin(getProvides()) ] } }, chainWebpack (webpackConfig, vueOptions, api) { if (process.env.UNI_PLATFORM === \'mp-baidu\') { webpackConfig.module .rule(\'js\') .exclude .add(/\.filter\.js$/) } const compilerOptions = process.env.UNI_USING_COMPONENTS ? {} : require(\'../mp-compiler-options\') modifyVueLoader(webpackConfig, {}, compilerOptions, api) const styleExt = getPlatformExts().style webpackConfig.plugin(\'extract-css\') .init((Plugin, args) => new Plugin({ filename: \'[name]\' + styleExt })) if ( process.env.NODE_ENV === \'production\' && process.env.UNI_PLATFORM !== \'app-plus\' ) { const OptimizeCssnanoPlugin = require(\'../../packages/@intervolga/optimize-cssnano-plugin/index.js\') webpackConfig.plugin(\'optimize-css\') .init((Plugin, args) => new OptimizeCssnanoPlugin({ sourceMap: false, filter (assetName) { return path.extname(assetName) === styleExt }, cssnanoOptions: { preset: [ \'default\', Object.assign({}, getPlatformCssnano(), { discardComments: true }) ] } })) } webpackConfig.plugins.delete(\'hmr\') webpackConfig.plugins.delete(\'html\') webpackConfig.plugins.delete(\'copy\') webpackConfig.plugins.delete(\'preload\') webpackConfig.plugins.delete(\'prefetch\') }
三、运行打包前所捕获到的 webpackConfigs 相关参数
[ { mode: \'production\', context: \'D:\\02code\\app-wechat-supply\', devtool: false, node: { setImmediate: false, process: \'mock\', dgram: \'empty\', fs: \'empty\', net: \'empty\', tls: \'empty\', child_process: \'empty\' }, output: { path: \'D:\\02code\\app-wechat-supply\\dist\\mit\', filename: \'[name].js\', publicPath: \'/\', chunkFilename: \'[id].js\', globalObject: \'global\' }, resolve: { alias: [Object], extensions: [Array], modules: [Array], plugins: [Array] }, resolveLoader: { modules: [Array], plugins: [Array] }, module: { noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/, rules: [Array] }, optimization: { splitChunks: [Object], minimizer: [Array], noEmitOnErrors: false, runtimeChunk: [Object], namedModules: false }, plugins: [ VueLoaderPlugin {}, [DefinePlugin], [CaseSensitivePathsPlugin], [FriendlyErrorsWebpackPlugin], [MiniCssExtractPlugin], [OptimizeCssnanoPlugin], [HashedModuleIdsPlugin], [NamedChunksPlugin], [DefinePlugin], [CopyPlugin], [CopyPlugin], WebpackUniAppPlugin {}, WebpackUniMPPlugin {}, [ProvidePlugin], [CopyPlugin] ], entry: [Function: entry], performance: { assetFilter: [Function: assetFilter], hints: false } } ]
四、小程序打包后的项目结构
参考资料:
https://developers.weixin.qq.com/ebook?action=get_post_info&docid=000668c6910b784b00860870a5ac0a
https://developers.weixin.qq.com/miniprogram/dev/framework/structure.html
请发表评论