Skip to content
微信公众号

mpvue 源码结构与构建原理分析

mpvue 源码结构

mpvue 不是单一的项目,而是一个项目生态的集合,就像 Vue 生态一样。Vue 生态具备以下三个最重要的项目:

  • Vue.js:Vue 的 Web 核心库;
  • Vue-CLI:Vue 的脚手架,帮助用户快速搭建基于 Webpack 的 Vue 项目,实现模块化和工程化;
  • vue-loader:实现了自定义 .vue 文件的解析。

通过上述三个项目,我们可以快速地将 Vue 应用或集成到我们的项目中。mpvue 借鉴了 Vue 的实现思路,它也实现了类似的生态:

  • mpvue.js:mpvue 的小程序核心库;
  • mpvue-quickstart:基于 Vue-CLI 2.0 的 mpvue 脚手架;
  • mpvue-loader:实现自定义 .vue 文件的解析,将其解析为 wxml、wxss 和 js 文件,对应小程序源码。

通过 mpvue-quickstart 我们可以快速构建 mpvue 项目:

shell
vue init mpvue/mpvue-quickstart mpvue-project

该项目中结构如下:

js
├── README.md
├── build
│   ├── build.js
│   ├── check-versions.js
│   ├── dev-client.js
│   ├── dev-server.js
│   ├── utils.js
│   ├── vue-loader.conf.js
│   ├── webpack.base.conf.js
│   ├── webpack.dev.conf.js
│   └── webpack.prod.conf.js
├── config
│   ├── dev.env.js
│   ├── index.js
│   └── prod.env.js
├── index.html
├── package.json
├── package.swan.json
├── project.config.json
├── project.swan.json
├── src
│   ├── App.vue
│   ├── app.json
│   ├── components
│   ├── main.js
│   ├── pages
│   └── utils
└── static

其中 src 是源码目录,在 main.js 中使用了 mpvue.js 库来创建小程序实例。

shell
$ cat src/main.js 
import Vue from 'vue'
import App from './App'

Vue.config.productionTip = false
App.mpType = 'app'

const app = new Vue(App)
app.$mount()

这里的 vue 是别名,它指向 mpvue 库,配置位置在 build/webpack.base.conf.js:

js
resolve: {
  alias: {
    'vue': 'mpvue',
    '@': resolve('src')
  }
}

mpvue

mpvue 库位于 node_modules/mpvue 目录下,源码结构如下:

shell
$ tree node_modules/mpvue
node_modules/mpvue
├── README.md
├── index.js
└── package.json

所以引用 mpvue 实际引用的是 node_modules/mpvue/index.js,该文件是 mpvue 库源码打包后生成的,mpvue 库的源码位于:https://github.com/mpvue/mpvue。它是基于 Vue 2.0 源码进行了修改,所以和 Vue 2.0 源码结构一致。mpvue 源码结构如下:

js
├── BACKERS.md
├── LICENSE
├── README.md
├── benchmarks
├── build
├── circle.yml
├── dist
├── examples
├── flow
├── node_modules
├── package.json
├── packages
├── src
├── test
├── types

由于 Vue 2.0 采用 flow 进行类型判断,所以包含了一个 flow 文件夹,build 目录下代码构建的相关的文件,构建产物会输出到 dist 和 packages 目录下,关于 flow 和 Vue 源码构建的知识可以参考我在慕课网发布的 rollup.js 相关手记。Vue 源码通过 rollup.js 最终构建生成了我们在 node_modules 中看到的 index.js 文件。

mpvue-loader

mpvue 通过 webpack 构建将 .vue 文件转化为小程序能够识别的 wxml、wxss 和 js 文件,完成这一步骤依赖 mpvue 提供的的 mpvue-loader,其配置文件位于 build/webpack.base.conf.js:

js
module: {
  rules: [
    {
      test: /\.vue$/,
      loader: 'mpvue-loader',
      options: vueLoaderConfig
    }
  ]
}

该 loader 针对 .vue 文件,该 loader 的 options 为 vueLoaderConfig,其中包含了 fileExt 属性,该属性会根据不同的平台,决定导出文件的格式:

js
module.exports = {
  fileExt: config.build.fileExt
}

config.build.fileExt 的定义如下:

js
var fileExtConfig = {
    swan: {
        template: 'swan',
        script: 'js',
        style: 'css',
        platform: 'swan'
    },
    tt: {
        template: 'ttml',
        script: 'js',
        style: 'ttss',
        platform: 'tt'
    },
    wx: {
        template: 'wxml',
        script: 'js',
        style: 'wxss',
        platform: 'wx'
    },
    my: {
        template: 'axml',
        script: 'js',
        style: 'acss',
        platform: 'my'
    }
}
var fileExt = fileExtConfig[process.env.PLATFORM]

由此可以看出,mpvue-loader 会根据 process.env.PLATFORM 环境变量决定选择哪一个 fileExt,如支付宝小程序下,mpvue-loader 就会输出 axml、js 和 acss 文件,而到了百度小程序下,就会输出 swan、js 和 css 文件。

mpvue-webpack-target

在 mpvue-quickstart 脚手架构建的项目的 package.json 配置文件中还包含了 mpvue-webpack-target 库,这个库的用途是生成 mpvue 项目在 webpack 中的 target。我们知道 webpack 的 target 用来指定构建模式,因 rget 为 javascript 可以运行在不同的环境下,如 web、node、electron 等,所以可以通过 target 来打包不同的 javascript 运行环境源码。webpack 的 target 可以传入 string 或 function 两种形式,mpvue 的 target 配置如下:

js
let baseWebpackConfig = {
  target: require('mpvue-webpack-target')
}

我们查看一下 mpvue-webpack-target 的源码:

js
module.exports = function (compiler) {
  const { options } = compiler
  compiler.apply(
    new JsonpTemplatePlugin(options.output),
    new FunctionModulePlugin(options.output),
    new NodeSourcePlugin(options.node),
    new LoaderTargetPlugin(options.target)
  );
};

该源码与选择 target 为 web 是一致的。我们可以查看 webpack 的源码来论证这一点,查看 node_modules/webpack/libs/WebpackOptionsApply.js,找到对 target 的判断:

js
switch(options.target) {
  case "web":
    JsonpTemplatePlugin = require("./JsonpTemplatePlugin");
    NodeSourcePlugin = require("./node/NodeSourcePlugin");
    compiler.apply(
      new JsonpTemplatePlugin(options.output),
      new FunctionModulePlugin(options.output),
      new NodeSourcePlugin(options.node),
      new LoaderTargetPlugin(options.target)
    );
    break;
}

可以看到与 mpvue-webpack-target 的功能一致。

mpvue-template-compiler

在 mpvue-loader 中,使用了 mpvue-template-compiler 对 .vue 文件进行了解析,可以说 mpvue-loader 只是完成了解析的流程,而核心的解析逻辑全部包含在 mpvue-template-compiler 库中,mpvue-template-compiler 实际也来自于 mpvue 库,它是通过 rollup.js 打包的另外一个库文件,配置文件位于 mpvue 源码的 build/config.js 下:

js
// Web compiler (CommonJS).
'web-compiler': {
  entry: resolve('web/entry-compiler.js'),
  dest: resolve('packages/vue-template-compiler/build.js'),
  format: 'cjs',
  external: Object.keys(require('../packages/vue-template-compiler/package.json').dependencies)
},

我们只需要执行 npm run dev:mpvue:compiler 即可构建出该库,并进入 watch 的开发模式。它的源码位于 mpvue/packages/vue-template-compiler/build.js

shell
"dev:mpvue:compiler": "rollup -w -c build/config.js --environment TARGET:mpvue-template-compiler "

而 vue-template-compiler 的源码位于:src/platforms/web/entry-compiler.js,这部分源码十分复杂,足足 6000+ 行。

mpvue 构建原理

dev 模式和 build 模式

首先 mpvue 的构建过程中分为 dev 模式和 build 模式,两者的主要区别如下:

  • 配置内容不同,如 dev 使用 source-map 模式,而 build 模式关闭了 source-map 并对源码进行了压缩;
  • dev 模式增加了对文件变化的监听,而 build 模式只是一次性的构建。

所以总体来说 dev 模式下构建的代码体积较大,因为包含了源码部分,方便了我们进行调试,但是如果用这种源码上线势必会降低访问速度,所以上线时请大家务必使用 build 模式。

dev 构建流程

dev 构建的流程如下:首先 mpvue 通过 webpack.dev.conf.js 获取配置文件,然后通过:

js
var compiler = webpack(webpackConfig)

完成 webpack 的构建工作,之后通过 Express 启动了 HTTP 服务,启动 HTTP 服务后进行了用户修改行为的监听:

js
require('webpack-dev-middleware-hard-disk')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  quiet: true
})

mvpue 使用了 webpack 官方提供的 webpack-dev-middleware-hard-disk 进行文件修改的监听,我们每次对代码进行修改,都会触发 webpack 重新 compiler,从而实现了开发模式。

build 构建流程

build 模式的构建流程如下:首先删除了 dist 目录下对应的平台目录:

js
rm(path.join(config.build.assetsRoot, '*'), err => {
  // ...
})

这里的 config.build.assetsRoot 对应:

js
assetsRoot: path.resolve(__dirname, `../dist/${fileExt.platform}`),

如使用 npm run build:wx 时,fileExt.platform 为 wx,所以删除的目录为 …/dist/wx。删除成功后进行了 webpack 构建:

js
webpack(webpackConfig, function (err, stats) {
  // ...
})

构建成功了后 mpvue 做了一些日志打印和错误提示的工作。

本站总访问量次,本站总访客数人次
Released under the MIT License.