1. 程式人生 > 實用技巧 >關於uni-build打包小程式的原始碼分析

關於uni-build打包小程式的原始碼分析

  我們使用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() }) }) }
View Code

二、打包微信小程式的程式碼入口地址

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')
  }
View Code

三、執行打包前所捕獲到的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://uniapp.dcloud.io/

https://developers.weixin.qq.com/miniprogram/dev/framework/structure.html