实现taro-ui组件的按需加载,内详
由于使用taro框架一般都会使用taro-ui来提供双线程组件的需求,但是taro-ui在h5端打包的时候,确实使用了dist目录下的全量脚本,从package.json的main就能看出。但是毕竟h5端的需求量还是很大,也有很多团队会使用taro来编写h5端的需求,因此在h5端上进行taro-ui的全量脚本导出会显得那么的“笨拙”。因此需要提供一个“按需加载”的思路,而前端应用层代码依然是从taro-ui命名空间进行使用,以下是我的解决方案,官方可参考是否可在NEXT版本中进行优化。
这个功能解决了什么问题? 解决并提供taro-ui按需加载的问题,同时应用层的代码还是通过taro-ui进行导入导出。
你期望的功能是怎样的? 无需使用接下去的解决方案来解决,最好官方能天然的进行支持。
补充信息
- 安装 babel-plugin-import 插件:
cnpm i babel-plugin-import --save-dev
- 在根目录下的 babel.config.js 中进行如下配置:
const tail = require("lodash/tail");
module.exports = {
presets: [
['taro', {
framework: 'react',
ts: true
}]
],
plugins: [
["import", {
libraryName: "taro-ui",
customName: (name, file) => {
const path = tail(name.split('-')).join('/');
return `taro-ui/lib/components/${path}`;
}
}]
]
}
- 应用层的代码依然还是这么引用:
import { AtModal, AtModalHeader, AtModalContent, AtModalAction, AtButton } from "taro-ui";
虽然在应用层开发者还是可以直接使用 taro-ui/lib 来解决问题,或者设置alias别名,但是alias还需要考虑按需加载 css 路径的问题,这让我一直很困惑,为什么css官方考虑了按需,而最重要的js层则是全量的,因此给出通过工具链的方式解决按需加载的问题,应该也是大多数开发者一直希望支持的,最后给出两张对比图结束!
未配置前:

配置后:

欢迎提交 Issue~
如果你提交的是 bug 报告,请务必遵循 Issue 模板的规范,尽量用简洁的语言描述你的问题,最好能提供一个稳定简单的复现。🙏🙏🙏
如果你的信息提供过于模糊或不足,或者已经其他 issue 已经存在相关内容,你的 issue 有可能会被关闭。
Good luck and happy coding~
补充一个包括样式的 按需引入
["import", {
libraryName: "taro-ui",
// libraryDirectory: "lib/components",
// styleLibraryDirectory: "dist/style/components",
customName: (name, file) => {
const nameSection = name.split('-')
if (nameSection.length === 4) {
// 子组件的路径跟主组件一样
nameSection.pop()
}
const path = nameSection.slice(1).join('-');
return `taro-ui/lib/components/${path}`;
},
style: (name) => {
// name 是 customName 的 return
const wholePath = name.split('/')
const compName = wholePath[wholePath.length - 1]
// XXX: 如果报错,就在这里映射
const fix = {
'tabs-pane': 'tabs'
}[compName]
return `taro-ui/dist/style/components/${fix || compName}.scss`
}
}]
补充一个包括样式的 按需引入
["import", { libraryName: "taro-ui", // libraryDirectory: "lib/components", // styleLibraryDirectory: "dist/style/components", customName: (name, file) => { const nameSection = name.split('-') if (nameSection.length === 4) { // 子组件的路径跟主组件一样 nameSection.pop() } const path = nameSection.slice(1).join('-'); return `taro-ui/lib/components/${path}`; }, style: (name) => { // name 是 customName 的 return const wholePath = name.split('/') const compName = wholePath[wholePath.length - 1] // XXX: 如果报错,就在这里映射 const fix = { 'tabs-pane': 'tabs' }[compName] return `taro-ui/dist/style/components/${fix || compName}.scss` } }]
经过这段时间我们实际线上的使用,发现所提供的这种 babel-import 的方式确实是一个好方法,但是不是最优的方法,因为 taro-ui 的路径实在是太不结构化,这里我举个场景,例如 modal 组件,内部就有 modal-content 子组件,而这个子组件的目录又是 ..../components/modal/header/index.js,这对于我们使用类似这样的硬编码实在是维护工作太大了,可能某个版本又会出现问题,我相信样式目录不会变化了,但是组件毕竟是从dist导出的,所以未来的某些小版本可能会发生变化,也就意味着我们需要根据官方的版本不断的去优化上面这段配置,工作量极大且意义不大。
目前我给出一个相对简单的方式,这需要和项目组成员全部约定好,就是在引入组件的时候不要引入 taro-ui,而是引入 taro-ui/lib,eslint上应该是可以对这个做限制的,而样式就手动引入,这样的方式相对上面的方式成本降低很多,可控性其实更强,可参考这种做法!
补充一个包括样式的 按需引入
["import", { libraryName: "taro-ui", // libraryDirectory: "lib/components", // styleLibraryDirectory: "dist/style/components", customName: (name, file) => { const nameSection = name.split('-') if (nameSection.length === 4) { // 子组件的路径跟主组件一样 nameSection.pop() } const path = nameSection.slice(1).join('-'); return `taro-ui/lib/components/${path}`; }, style: (name) => { // name 是 customName 的 return const wholePath = name.split('/') const compName = wholePath[wholePath.length - 1] // XXX: 如果报错,就在这里映射 const fix = { 'tabs-pane': 'tabs' }[compName] return `taro-ui/dist/style/components/${fix || compName}.scss` } }]
请问这种方式有自定义主题的方法吗?
补充一个包括样式的 按需引入
["import", { libraryName: "taro-ui", // libraryDirectory: "lib/components", // styleLibraryDirectory: "dist/style/components", customName: (name, file) => { const nameSection = name.split('-') if (nameSection.length === 4) { // 子组件的路径跟主组件一样 nameSection.pop() } const path = nameSection.slice(1).join('-'); return `taro-ui/lib/components/${path}`; }, style: (name) => { // name 是 customName 的 return const wholePath = name.split('/') const compName = wholePath[wholePath.length - 1] // XXX: 如果报错,就在这里映射 const fix = { 'tabs-pane': 'tabs' }[compName] return `taro-ui/dist/style/components/${fix || compName}.scss` } }]请问这种方式有自定义主题的方法吗?
我没有使用过自定义样式的特性,但是我看了官方的示例可以直接在app.ts中进行配置,具体如下,你可以尝试下,如果不行我们在进行讨论:
// src/app.js:
// 注意这段代码放在最顶部!
import './custom-variables.scss'
// custom-variables.scss:
/* Custom Theme */
$color-brand: #e93b3d;
$color-brand-light: #ef6c6e;
$color-brand-dark: #ba2f31;
补充一个包括样式的 按需引入
["import", { libraryName: "taro-ui", // libraryDirectory: "lib/components", // styleLibraryDirectory: "dist/style/components", customName: (name, file) => { const nameSection = name.split('-') if (nameSection.length === 4) { // 子组件的路径跟主组件一样 nameSection.pop() } const path = nameSection.slice(1).join('-'); return `taro-ui/lib/components/${path}`; }, style: (name) => { // name 是 customName 的 return const wholePath = name.split('/') const compName = wholePath[wholePath.length - 1] // XXX: 如果报错,就在这里映射 const fix = { 'tabs-pane': 'tabs' }[compName] return `taro-ui/dist/style/components/${fix || compName}.scss` } }]请问这种方式有自定义主题的方法吗?
我没有使用过自定义样式的特性,但是我看了官方的示例可以直接在app.ts中进行配置,具体如下,你可以尝试下,如果不行我们在进行讨论:
// src/app.js: // 注意这段代码放在最顶部! import './custom-variables.scss' // custom-variables.scss: /* Custom Theme */ $color-brand: #e93b3d; $color-brand-light: #ef6c6e; $color-brand-dark: #ba2f31;
谢谢,不过这样做是不会生效的,查了一下官网可以在config/index.js中将custom-variables.scss引用到sass中就可以了
示例:
const path = require("path"); const config = { //... sass: { resource: path.resolve(__dirname, "..", "src/custom-theme.scss") }, //... }
补充一个包括样式的 按需引入
["import", { libraryName: "taro-ui", // libraryDirectory: "lib/components", // styleLibraryDirectory: "dist/style/components", customName: (name, file) => { const nameSection = name.split('-') if (nameSection.length === 4) { // 子组件的路径跟主组件一样 nameSection.pop() } const path = nameSection.slice(1).join('-'); return `taro-ui/lib/components/${path}`; }, style: (name) => { // name 是 customName 的 return const wholePath = name.split('/') const compName = wholePath[wholePath.length - 1] // XXX: 如果报错,就在这里映射 const fix = { 'tabs-pane': 'tabs' }[compName] return `taro-ui/dist/style/components/${fix || compName}.scss` } }]请问这种方式有自定义主题的方法吗?
我没有使用过自定义样式的特性,但是我看了官方的示例可以直接在app.ts中进行配置,具体如下,你可以尝试下,如果不行我们在进行讨论:
// src/app.js: // 注意这段代码放在最顶部! import './custom-variables.scss' // custom-variables.scss: /* Custom Theme */ $color-brand: #e93b3d; $color-brand-light: #ef6c6e; $color-brand-dark: #ba2f31;谢谢,不过这样做是不会生效的,查了一下官网可以在config/index.js中将custom-variables.scss引用到sass中就可以了 示例:
const path = require("path"); const config = { //... sass: { resource: path.resolve(__dirname, "..", "src/custom-theme.scss") }, //... }
👌
@crixusshen
由于使用taro框架一般都会使用taro-ui来提供双线程组件的需求,但是taro-ui在h5端打包的时候,确实使用了dist目录下的全量脚本,从package.json的main就能看出。但是毕竟h5端的需求量还是很大,也有很多团队会使用taro来编写h5端的需求,因此在h5端上进行taro-ui的全量脚本导出会显得那么的“笨拙”。因此需要提供一个“按需加载”的思路,而前端应用层代码依然是从taro-ui命。。。
还有更简单的方式,直接添加alias: {'taro-ui$': 'taro-ui/lib/index'} 这样就会去直接加载taro-ui/lib/index 中相应的组件,同时未加载的也会被优化掉。