Medusa 微信小程序工程化实践方案详解

网友投稿 309 2024-06-15

相关学习推荐:微信小程序教程

前言

我曾发布过《实战篇--微信小程序工程化探索之webpack》一文,当时是我探索微信小程序工程化的第一阶段。起初我只是为了验证微信小程序与 webpack 是否能够相结合(很大程度是被对于技术的好奇心驱使),对于工程化的持续交付并没有过多的思考。但是在内部需求的不断冲击下,我开始萌生以工程化手段持续简化微信小程序开发难度的想法,最终衍生的产物就是这套以 Medusa 命名的微信小程序快速开发方案。

接下来我将较为详细的分享达成这一方案的实践过程,下文中将提到的工具我也已经发布在 npm 上供大家-使用。这篇文章将会覆盖之前发表的那篇文章的全部内容并且内容更加丰富,所以篇幅方面也较为长请读者们耐心阅读。

webpack-build-miniprogram

Medusa 微信小程序工程化实践方案详解

webpack-build-miniprogram 是 Medusa 方案的基础也是核心,这一工具包提供了以 webpack 构建微信小程序的能力,并且我们可以利用 webpack 的生态持续丰富 Medusa 的功能。在讲述基础构建配置之前,我们先来看看 Medusa 的目录结构基础,有了相应的目录约束才使得项目更加规范化。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

|-- dist                        编译结果目录

|-- src                         源代码目录

|   |-- app.js                  项目入口文件

|   |-- app.json                小程序配置文件

|   |-- sitemap.json            sitemap配置文件

|   |-- assets                  静态资源存放目录

|   |   |-- .gitkeep

|   |-- components              公共组件存放目录

|   |   |-- .gitkeep

|   |-- dicts                   公共字典存放目录

|   |   |-- .gitkeep

|   |-- libs                    第三方工具库存放目录(外部引入)

|   |   |-- .gitkeep

|   |-- pages                   页面文件存放目录

|   |   |-- index

|   |       |-- index.js

|   |       |-- index.json

|   |       |-- index.less

|   |       |-- index.wxml

|   |-- scripts                 公共脚本存放目录(wxs)

|   |   |-- .gitkeep

|   |-- services                API服务存放目录

|   |   |-- .gitkeep

|   |-- styles

|   |   |-- index.less          项目总通用样式

|   |   |-- theme.less          项目主题样式

|   |-- templates               公共模板存放目录

|   |   |-- .gitkeep

|   |-- utils                   公共封装函数存放目录(自我封装)

|       |-- .gitkeep

|-- .env                        环境变量配置文件

|-- config.yaml                 编译配置文件

|-- webpack.config.js           webpack 配置扩展文件

|-- project.config.json         开发者工具配置文件

└── package.json复制代码

-

基础篇

webpack 这一工具现在已经成为前端工程师的必备技能,复杂的工作原理让我们对它总是有种敬畏感,所以在做微信小程序构建策略过程中,我们先将它简单的理解为一个“搬运工具”。它将源代码目录中的文件加以某些处理之后再输出到目标目录中。现在我们明确一下我们要搬运哪些文件,微信小程序中涉及到的主要有:

逻辑文件 .js 配置文件 .json 模板文件 .wxml 样式文件 .wxss .less .scss 脚本文件 .wxs 静态资源文件 assets/ 基础搬运功能

接下来我们将书写 webpack 的公共部分配置,利用 copy-webpack-plugin 这一插件来完成大部分文件的搬运工作。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

/** config/webpack.common.js */const CopyPlugin = require("copy-webpack-plugin");const config = {  context: SOURCE,  devtool: 'none',  entry: {        app: './app.js'

},  output: {    filename: '[name].js',    path: DESTINATION

},  plugins: [    new CopyPlugin([

{        from: 'assets/',        to: 'assets/',        toType: 'dir'

},

{        from: '**/*.wxml',        toType: 'dir'

},

{        from: '**/*.wxss',        toType: 'dir'

},

{        from: '**/*.json',        toType: 'dir'

},

{        from: '**/*.wxs',        toType: 'dir'

}

])

]

};复制代码

-

以上简单的配置我们就实现了除逻辑文件与预编译语言文件以外的搬运工作,在配置中出现了 SOURCE 、 DESTINATION 两个常量,它们分别代表的是源代码目录与目标代码目录的绝对路径,我们将它们抽离在单独的字典文件中:

1

2

3

4

5

6

/** libs/dicts.js */const path = require("path");

exports.ROOT = process.cwd();

exports.SOURCE = path.resolve(this.ROOT, 'src');

exports.DESTINATION = path.resolve(this.ROOT, 'dist');

exports.NODE_ENV = process.argv.splice(2, 1)[0];复制代码

-

上面搬运的文件因为不需要特殊的内容处理,所以完全交由插件去实现,剩余两种类型的文件我们就需要使用到 webpack 的入口(entry)插件(plugin)loader 协同合作才能完成搬运工作。

核心入口功能

首先我们要解决如何生成入口的问题,解决了入口生成的问题才能借助 loader 去完成文件内容的转化。对于入口生成这一问题,我开发了另外一个插件 entry-extract-webpack-plugin 去解决。这一插件我并不打算详细的讲解实现的过程,我只会阐述它的核心实现思路(如果你有兴趣进一步了解可以-下来直接看源码)。

微信小程序需要建立入口网络其实是有规律可循的,主包、分包都会配置在 app.json 文件中,页面所需要的组件也会配置在 [page].json 文件中。抓住这一特点,我们可以将实现插件功能的核心罗列为以下几点:通过 node.js 提供的 path 与 fs 模块功能,以 app.json 文件中配置的路径为基础,递归的去寻找每个 page 所依赖的 component 路径,最终整合在同个数组中。利用 webpack 提供的 SingleEntryPlugin 和 MultiEntryPlugin 插件,在 entryOption 生命周期钩子中将第一步收集的路径数组注入到构建当中形成入口(entry)。构建监听的过程中如果有新的页面添加,则通过 watchRun 生命周期将新的入口加入到之前的入口(entry)中。

以上三点是实现生成入口这一功能的核心思路,除了核心的实现思路外,我还想简单的讲解下我们如何去写一个 webpack 插件:

1

2

3

4

5

6

7

8

9

10

11

class EntryExtractPlugin {  constructor(options) {}

apply(compiler) {

compiler.hooks.entryOption.tap('EntryExtractPlugin', () => {

...

});

compiler.hooks.watchRun.tap('EntryExtractPlugin', () => {

...

});

}

}复制代码

-

webpack 的插件大致是以的形式存在,当你使用插件时,它会自动执行 apply 方法, 然后使用 compiler.hooks 对象上的各种生命周期属性便可以将我们需要的处理逻辑植入到 webpack 的构建流程当中。

逻辑与样式

上面解决了生成入口(entry)的问题,接下来我们在原有的基础上完善一下策略。由于预编译语言的类型较多,我为了策略的可扩展性将样式部分的策略抽离为单独的部件,然后在通过 webpack-merge 这一工具将它们合并起来,完整的实现如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

/** config/webpack.parts.js */exports.loadCSS = ({ reg = /\.css$/, include, exclude, use = [] }) => ({    module: {    rules: [

{

include,

exclude,        test: reg,        use: [

{            loader: require('mini-css-extract-plugin').loader

},

{            loader: 'css-loader'

}

].concat(use)

}

]

}

});复制代码

-

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

/** config/webpack.common.js */const { merge } = require('webpack-merge');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const parts = require('./webpack.parts.js');const config = {

...

module: {    rules: [

{        test: /\.js$/,        loader: 'babel-loader',        exclude: /node_modules/

}

]

},  plugins: [

...

new MiniCssExtractPlugin({ filename: '[name].wxss' })

]

};module.export = merge([

config,

parts.loadCSS({    reg: /\.less$/,    use: ['less-loader']

})

]);复制代码

-

以上就是基础的 webpack 策略,接下来我们书写一个工具包的可执行文件便可以在项目当中通过 medusa-server {mode} 使用 webpack 提供的功能了。我们需要在工具包的 package.json 文件中配置 bin 字段,它标志了我们通过命令会自动执行哪个文件。

1

2

3

4

5

6

{

...

main: "index.js",

bin: {    "medusa-server": "index.js"

}

}复制代码

-

1

2

3

4

5

6

7

8

9

10

11

/** index.js */const webpack = require('webpack');const { merge } = require('webpack-merge');const chalk = require('chalk');const commonConfig = require('./config/webpack.common');const { NODE_ENV } = require('./libs/dicts');const config = (function(mode) {  if (mode === 'production') {    return merge([commonConfig, { mode }]);

}  return merge([commonConfig, {    mode: 'development',    watch: true,    watchOptions: { ignored: /node_modules/ }

}])

})(NODE_ENV);

webpack(config, (err, stats) => {  if (err) {    console.log(chalk.red(err.stack || err));    if (err.details) {        console.log(chalk.red(err.details));

}    return undefined;

}  const info = stats.toJson();  if (stats.hasErrors()) {    console.log(chalk.red(info.errors));

}  if (stats.hasWarnings()) {    console.log(chalk.yellow(info.warnings));

}

});复制代码

-

进阶篇

基础篇当中完成的 webpack 配置已经可以满足两点功能,一是对常规文件的输出,二是具备开发与生产两种不同的模式。对于基础篇我还有两点要说明一下:

为什么没有应用ES6(更改版本)转为ES5的相关插件?因为在实践当中发现IOS的10.x版本存在async/await语法无法正常使用的情况,所以索性就让构建更加纯粹一些 ,然后启用微信开发者工具的 ES6 转 ES5增强编译

这两项功能,由官方工具去处理新特性。

为什么 devtool 要设置为 none,难道不需要 sourceMap 吗?

微信官方已经原生提供了SourceMap功能,这在你上传版本时开发者工具中就已经有体现了。

接下来就进入进阶篇的梳理,在满足正常的输出后其实与原生开发好像并没有太大差异,这完全体现不出 webpack 的作用,所以我们要利用 webpack 的能力及其相关的工具生态来扩展下 Medusa 的功能。接下来我们将赋予 Medusa 以下几点功能:路径别名 @根据环境自动注入相应的环境域名ESLint、StyleLint代码规范检查路由功能公共代码抽取环境变量webpack 可扩展路径别名 @

原生微信小程序只支持相对路径的引入方式,但是我们难免会遇到必须移动某些文件的情况,假设这个文件在多处被引用,那就头疼了。所以我们通过 webpack 的能力以及搭配 jsconfig.json 配置文件可以让我们有更好的开发体验。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:length 与 size()使用的对比分析
下一篇:MinUI 组件 abnor 异常流详细解读
相关文章

 发表评论

暂时没有评论,来抢沙发吧~