微信官方已经开放了插件的共享,,,那么关于微信小程序开发来说,,,怎样做到小程序打包插件呢?????下面来看看文章。。

若是你看过文档,,,相信你一定知道:
若是感受无从着手,,,可以继续看看我是怎样一步步开发并完善 mini-program-webpack-loader 来打包小程序的。。
小程序有一个牢靠的套路,,,首先需要有一个 app.json 文件来界说所有的页面路径,,,然后每个页面有四个文件组成:.js,,,.json,,,.wxml,,,.wxss。。以是我以 app.json 作为 webpack entry,,,当 webpack 执行插件的 apply 的时间,,,通过获取 entry 来知道小程序都有哪些页面。。
这里使用了两个插件 MultiEntryPlugin,,,SingleEntryPlugin。。为什么要这样做呢?????由于 webpack 会凭证你的 entry 设置(这里的 entry 不但是 webpack 设置里的 entry,,,import(), require.ensure() 都会天生一个 entry)来决议天生文件的个数,,,我们不希望把所有页面的 js 打包到一个文件,,,需要使用 SingleEntryPlugin 来天生一个新的 entry module;;;而那些静态资源,,,我们可以使用 MultiEntryPlugin 插件来处理,,,把这些文件作为一个 entry module 的依赖,,,在 loader 中设置 file-loader 即可把静态文件输出。。伪代码如下:
const MultiEntryPlugin = require('webpack/lib/MultiEntryPlugin');
const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');
class MiniPlugin {
apply (compiler) {
let options = compiler.options
let context = compiler.rootContext
let entry = options.entry
let files = loadFiles(entry)
let scripts = files.filter(file => /\.js$/.test(file))
let assets = files.filter(file => !/\.js$/.test(file))
new MultiEntryPlugin(context, assets, '__assets__').apply(compiler)
scripts.forEach((file => {
let fileName = relative(context, file).replace(extname(file), '');
new SingleEntryPlugin(context, file, fileName).apply(compiler);
})
}
}
复制代码
虽然,,,若是像上面那样做,,,你会发明最后会多出一个 main.js,,,xxx.js(使用 MultiEntryPlugin 时填的名字),,,main.js 对应的是设置的 entry 天生的文件,,,xxx.js 则是 MultiEntryPlugin 天生的。。这些文件不是我们需要的,,,以是需要去掉他。。若是熟悉 webpack 文档,,,我们有许多地方可以修改最终打包出来的文件,,,如 compiler 的 emit 事务,,,compilation 的 optimizeChunks 相关的事务都可以实现。。其实质上就是去修改 compilation.assets 工具。。
在 mini-program-webpack-loader 中就使用了 emit 事务来处理这种不需要输出的内容。。
小程序打包虽然没这么简朴,,,还得支持wxml、wxss、wxs和自界说组件的引用,,,以是这个时间就需要一个 loader 来完成了,,,loader 需要做的事情也很是简朴 —— 剖析依赖的文件,,,如 .wxml 需要剖析 import 组件的 src,,,wxs 的 src,,,.wxss 需要剖析 @import,,,wxs 的 require,,,最后在 loader 中使用 loadModule 要领添加即可。。自界说组件一最先在 add entry 方法的时间直接获取了,,,以是不需要 loader 来完成。。
这样做也没什么问题,,,可是开发体验是较量差的,,,如再添加一个自界说组件,,,一个页面,,,webpack 是无感知的,,,以是需要在页面中的 .json 爆发改变时检查是不是新增了自界说组件或者新增了页面。。这个时间遇到一个问题,,,自界说组件的 js 是不可通过 addModule 的方式来添加的,,,由于自界说组件的 js 必需作为自力的入口文件。。在 loader 中是做不了,,,以是实验把文件传到 plugin 中(由于 plugin 先于 loader 执行,,,以是是可以建设 loader 和 plugin 通讯的)。。
简朴粗暴的方式:
// loader.js
class MiniLoader {}
module.exports = function (content) {
new MiniLoader(this, content)
}
module.exports.$applyPluginInstance = function (plugin) {
MiniLoader.prototype.$plugin = plugin
}
// plugin.js
const loader = require('./loader')
class MiniPlugin {
apply (compiler) {
loader.$applyPluginInstance(this);
}
}
复制代码
可是...。。文件是传到 plugin 了,,,可是再使用 SingleEntryPlugin 时你会发明,,,没效果。。由于在 compiler make 之后 webpack 已经不可感知新的 module 添加了,,,以是是没有用的,,,这个时间就需要凭证文档猜,,,怎么样才华让 webpack 感知到新的 module,,,凭证文档中的事务做要害字盘问,,,可以发明在编译完成的时间会挪用 compilation needAdditionalPass 事务钩子:
this.emitAssets(compilation, err => {
if (err) return finalCallback(err);
if (compilation.hooks.needAdditionalPass.call()) {
compilation.needAdditionalPass = true;
const stats = new Stats(compilation);
stats.startTime = startTime;
stats.endTime = Date.now();
this.hooks.done.callAsync(stats, err => {
if (err) return finalCallback(err);
this.hooks.additionalPass.callAsync(err => {
if (err) return finalCallback(err);
this.compile(onCompiled);
});
});
return;
}
this.emitRecords(err => {
if (err) return finalCallback(err);
const stats = new Stats(compilation);
stats.startTime = startTime;
stats.endTime = Date.now();
this.hooks.done.callAsync(stats, err => {
if (err) return finalCallback(err);
return finalCallback(null, stats);
});
});
});
复制代码
若是在这个事务钩子返回一个 true 值,,,则可以使 webpack 挪用 compiler additionalPass 事务钩子,,,实验在这里添加文件,,,果真是可以的。。
虽然,,,小程序打包尚有些差别的地方,,,好比分包,,,怎样用好 splitchunk,,,就不在啰嗦了,,,当你最先以后你会发明有许多的要领来实现想要的效果。。
插件开发到这里差未几了,,,总的来说,,,webpack 就是变着名堂的回调,,,当你知道每个回调该做什么的时间,,,webpack 用起来就轻松了。。显着我不知道,,,由于在开发历程中遇到了一些问题。。
1.怎样在小程序代码中支持 resolve alias,,,node_modules?????
既然是工具,,,虽然需要做更多的事情,,,有赞的小程序那么重大,,,若是支持 resolve alias,,,node_modules 可以使得项目更利便维护,,,或许你会说这不是 webpack 最基本的功效吗,,,不是的,,,我们虽然是希望可以在任何文件中使用 alias,,,node_modules 支持的不但仅是 js。。虽然这样做就意味着事情将变得重大,,,首先就是获取文件路径,,,必需是异步的,,,由于在 webpack 4 中 resolve 不再支持 sync。。其次就是小程序的目录名不可是 node_modules,,,这时就需要一种盘算相对路径的规则,,,照旧相对打包输出的,,,而不是相对目今项目目录。。
2.多个小程序项目的合并
有赞从小程序来讲,,,有微商城版,,,有零售版,,,以及公共版,,,其中大多基础功效,,,营业都是相同的,,,虽然不可再每个小程序在开发一次,,,以是这个工具具备合并多个小程序虽然是必需的。。这样的合并稍微又要比从 node_modules 中取文件重大一些,,,由于需要包管多个小程序合并后的页面是准确的,,,并且要包管路径稳固。。
这两个问题的最终的解决方案既是以 webpack rootContext 的 src 目录为基准目录,,,以该目录所在路径盘算打包文件的绝对路径,,,然后凭证入口文件的 app.json 所在目录的路径盘算出最终输出路径。。
exports.getDistPath = (compilerContext, entryContexts) => {
/**
* webpack 以 config 所在目录的 src 为打包入口
* 以是可以凭证该目录追溯源文件地点
*/
return (path) => {
let fullPath = compilerContext
let npmReg = /node_modules/g
let pDirReg = /^[_|\.\.]\//g
if (isAbsolute(path)) {
fullPath = path
} else {
// 相对路径:webpack 最后天生的路径,,,打包入口外的文件都以 '_' 体现上级目录
while (pDirReg.test(path)) {
path = path.substr(pDirReg.lastIndex)
fullPath = join(fullPath, '../')
}
if (fullPath !== compilerContext) {
fullPath = join(fullPath, path)
}
}
// 凭证 entry 中界说的 json 文件目录获取打包后所在目录,,,若是不可获取就返回原路径
let contextReg = new RegExp(entryContexts.join('|'), 'g')
if (fullPath !== compilerContext && contextReg.exec(fullPath)) {
path = fullPath.substr(contextReg.lastIndex + 1)
console.assert(!npmReg.test(path), `文件${path}路径过失:不应该还包括 node_modules`)
}
/**
* 若是有 node_modules 字符串,,,则去模浚???槊
* 若是 app.json 在 node_modules 中,,,那 path 不应该包括 node_modules
*/
if (npmReg.test(path)) {
path = path.substr(npmReg.lastIndex + 1)
}
return path
}
}
复制代码
3.怎样把子包单独依赖的内容打包到子包内
解决这个问题的要领是通过 optimizeChunks 事务,,,在每个 chunk 的依赖的 module 中添加这个 chunk 的入口文件,,,然后在 splitChunk 的 test 设置中检查 module 被依赖的数目。。若是只有一个,,,并且是被子包依赖,,,则打包到子包内。。
4.webpack 支持单文件失败
这是一个未解决的问题,,,当实验使用 webpack 来支持单文件的时间,,,似乎没那么利便:

KESION 金狮贵宾会软件
KESION 金狮贵宾会软件是海内领先的在线教育软件及私域社交电商软件服务提供商,,,恒久专注于为企业提供在线教育软件及社交电商SaaS平台解决方案。。
公司焦点产品云开店SaaS社交电商服务平台、在线教育SaaS服务平台、教育企业数字化SaaS云平台、企微营销助手、私有化自力安排品牌网校和在线教育咨询等。。KESION 一直通过手艺立异,,,提供产品和服务,,,助力企业向数字化转型,,,通过科技驱动商业刷新,,,让商业变得更智慧!
vue和微信小程序,,,在微信小程序开发中,,,两者有许多相同之处,,,也有一些区别,,,下面就为各人总结一下vue和微信小程序的区别、较量。。...
随着支付宝,,,百度加入小程序,,,以及越来多的电商企业加入saas服务,,,给古板电商走入线下零售带来了新的机缘和空间。。...