[前端][新点小程序] 为什么我们构建卡片小程序的时候,dev 环境可以运行,但是小程序上传后无法如期运行
新点小程序
基础知识
大家可以查看此文档,学习基础知识后,查看下面的内容。
小程序卡片
很多开发人员在开发小程序卡片的时候,在跑dev
开发环境时没有问题,但是通过卡片构建后,会发现本来在 dev 环境可以正常访问,但是在prod
环境时,就会发现访问不了。
小程序卡片的构建
可以从构建命令中分析,在构建过程中到底发生了什么?
以下内容,依据 M8 框架carddemo
模块的构建结果,来分析。
配置项 pulgin.json
[
{
// 小程序模块名字
"moduleName": "carddemo",
// 路径
"indexURL": "carddemo/index",
"cardId": "ua-69328137",
"minEJSVersion": "3.5.0",
"version": "1.0.1",
// 类型
"miniH5Type": "card",
"updateType": "nexttime"
}
]
当点击vscode插件
的小程序-上传
的时候,则会在我们的终端执行以下命令
npm run build -- --target wc --name ua-69328137-1-0-1 src/pages/carddemo/index.vue
分析命令
从vue-cli 文档来分析行命令
npm run build
: 构建命令
--
: 环境模式(默认:prmoduction)
--traget wc
: 允许你将项目中的任何组件以 Web Components 组件的方式进行构建,可通过查看文档来学习更多构建模式
wc 指的是 Web Component
--name ua-69328137-1-0-1
:设置前缀,同时自定义元素的名称会由组件的文件名推导得出
src/pages/carddemo/index.vue
: 需构建的目标文件
构建产物
我们可以发现,卡片的构建产物与常规小程序构建产物不同,多了个ua-xxxx-1-0-1.min.js
从 vue-cli 文档里可以看出这个 js 就是web-components
文件,而且我们可以从 index.html
看出来,卡片的构建产物是在js
标签下的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover"
name="viewport"
/>
<title></title>
<!-- 加载vant.css -->
<link rel="stylesheet" href="./css/vant.css" />
<!-- 加载vue -->
<script src="./lib/vue.js"></script>
<!-- 加载js -->
<script src="./ua-69328137-1-0-1.min.js"></script>
</head>
<body>
<!-- 卡片的产物 -->
<ua-69328137-1-0-1></ua-69328137-1-0-1>
</body>
<script>
// 在卡片代码前加入css
document
.querySelector('ua-69328137-1-0-1')
.shadowRoot.querySelector('div')
.insertAdjacentHTML('beforeend', '<style>@import "./css/common.css";</style>');
document
.querySelector('ua-69328137-1-0-1')
.shadowRoot.querySelector('div')
.insertAdjacentHTML('beforeend', '<style>@import "./css/vant.css";</style>');
</script>
</html>
构建流程
webpack
进行构建,具体的构建流程如下:
通过 // build/index.js
const { WebpackPlugin } = require('./tool');
...
// line: 21行
build : {
configureWebpack: {
plugins: [
// 构建后操作dist目录,就是这个构建插件
new WebpackPlugin(plugin),
]
},
};
WebpackPlugin
的代码,可以看出我们如何进行解析卡片小程序的代码
查看 1.如果是wc
的目标,则将对应路径进行切割,以及将对应 name
设置为 app 的 id
// build/tool.js
// line: 21行
if (argv.target === 'wc') {
// 卡片模式
// 将src/pages/carddemo/index.vue进行切割
const cardName = process.argv[process.argv.length - 1].split('/');
// 根目录为cardemo
root = cardName[cardName.length - 2];
// 将ua-69328137-1-0-1设置为appid
appId = argv.name;
// 卡片模式
buildType = 'card';
isDebug = /debug/.test(rootArr[3]);
} else if (rootArr.length > 3) {
root = process.argv[process.argv.length - 1].substring(2);
buildType = 'app';
} else {
root = 'ALL';
}
2.解析plugin.json
的配置项,然后判断是否构建完成,从上文可以知道 wc 目标构建可以生产一个ua-xxxx-1-0-1.js.min.js
构建完成后再执行后续操作压缩
// build/tool.js
// line: 145行
if (buildType === 'card') {
// 移动小程序卡片内置资源
await fs.copy('./build/minih5inner/vue.js', './dist/lib/vue.js');
await fs.copy('./build/minih5inner/css', './dist/css');
await fs.copy('./public/', './dist', {
filter(src) {
if (/cardlist/.test(src) || /comdto/.test(src)) {
return false;
}
return true;
}
});
// target为plugin.json
const cardId = target[0].cardId || target[0].appId;
const cardVersion = isDebug ? 'debug' : target[0].version.replace(/\./g, '-') || '';
// 创建小程序卡片页面模板
await fs.outputFile(
'./dist/index.html',
`
<!DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8">
<!-- H5页面窗口自动调整到设备宽度,并控制用户缩放页面 -->
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover" name="viewport" />
<title></title>
<link rel="stylesheet" href="./css/vant.css" />
<script src="./lib/vue.js"></script>
<script src="./${cardId}-${cardVersion}.min.js"></script>
</head>
<body>
<${cardId}-${cardVersion}></${cardId}-${cardVersion}>
</body>
<script>
document.querySelector('${cardId}-${cardVersion}').shadowRoot.querySelector('div').insertAdjacentHTML('beforeend', '<style>@import "./css/common.css";</style>')
document.querySelector('${cardId}-${cardVersion}').shadowRoot.querySelector('div').insertAdjacentHTML('beforeend', '<style>@import "./css/vant.css";</style>')
</script>
</html>`
);
//创建完成后,将对应的js文件加入
const waitFileBuildFinish = function () {
return new Promise((resolve) => {
// 获取文件信息
fs.stat(`./dist/${cardId}-${cardVersion}.min.js`, async function (err, stats) {
//
if (err) {
resolve(await waitFileBuildFinish());
} else {
//文件大小
if (stats.size === 0) {
resolve(await waitFileBuildFinish());
} else {
setTimeout(() => {
resolve(stats.size);
}, 1000);
}
}
});
});
};
// 等待.min文件构建完成后再执行后续操作
const buildFinish = await waitFileBuildFinish();
if (buildFinish > 0) {
watcher.on('unlink', () => {
this._zip('', root, async () => {
await fs.moveSync(`./zip/${root}.zip`, `./dist/${root}.zip`);
});
watcher.close();
});
fs.remove(`./dist/${cardId}-${cardVersion}.js`);
}
}
总结 & 解决方案
从分析命令、构建产物、构建流程这几块来查看,我们很好就能看出构建卡片流程中,我们从始至终都是构建主体是命令行中的[path]
,即src/pages/carddemo/index.vue
,而非main.js
/router.js
,也就是说当我们构建一个卡片小程序的时候,我们并没有使用到main.js
/router.js
,虽然可能在dev
模式下,我们是依靠main.js
来初始化 vue 组件,但实际build wc
后,我们只是得到了au-xxx.min.js
的 Web Components 产物。
我们可以从src/pages/carddemo/index.vue
文件内容看Util, Config
都是通过'./cardboot'
来获取,无需读取 main.js
import { Util, Config } from './cardboot';
所以我们需要注意点,在于我们在调试小程序卡片的时候(dev 命令),是通过 mian.js 构建产物的,但是通过小程序build
构建,则是通单一的 vue 文件,所以在我们执行上传卡片小程序的时候,如果在 mian.js 做了什么业务操作(比如定义了全局变量、Vue.prototype
的赋值),需要在 cardboot.js 中也需要进行重复操作。