背景
显示一个全局组件,在一般的终端是个小事情,奈何小程序不允许操作DOM。
uni也是一个笔者不怎么like的框架,奈何历史原因就偏偏选中了他。
为啥要突然注册一个全局组件,奈何** 的产品需求。
一两百个的页面引入如何处理,网上搜了一遍也,奈何还是无解。
于是,有了利用webpack的loader机制,在编译前完成一次转义的想法。
思考过程
1)webpack的配置
vue.config.js的配置,相信vue的小伙伴都认识他。
需求明显,明显是需要用loader进行转换。主要利用什么机制进行转换。首先要考虑,使用configureWebpack还是chainWebpack的适合。
configureWebpack更倾向简单文件转换:
手写chainWebpack而是更适合""
这里直接赋源码(全局引入global-test为例):
module.exports = {
chainWebpack: config => {
config.module
.rule('vue')
.use('./loaders/global-loader')
.loader('./loaders/global-loader')
.options({
globalComponents: '<global-test></global-test>',
})
.end()
},
}
2)全局引入组件
这个看uni-app官方文档就可以知道结果,只需改pages.json的去全局组件即可。
这里:
"globalStyle": {
"usingComponents": {
"van-button": "/components/global/test",
}
}
3)代码如何转换
按照代码理解,我们应该去找template的第一个元素。因为vue2,必须dom层是个树状结构。所以, 我们应该找出第一个元素,在它的第一个子集,插入对应的全局组件。这里假设第一个元素为view,(如果该页面没有用view包裹的话,需要修改一下源码,或者要手动补充,这里满足98%以上的场景)
source = source.replace(/<view(.*?)>/, s => s + config.globalComponents)
4)如何遍历所有路由
这里需要用到fs跟path,思路就是根据pages.json配置的路由,读取到对应的代码,再执行步骤3的转换。
const getFileMatchReg = function(publicPath, path) {
const fullPath = Path__default['default'].join(publicPath, `/${path}`)
const regStr = JSON.stringify(`^${fullPath}.?vue$`)
const reg = new RegExp(regStr.substring(1, regStr.length - 1))
return reg
}
/**
* 获取 所有已注册的路由文件的正则规则(vue)
* @returns
*/
const getRouteFileMatchRegAll = function(config) {
try {
const jsonStr = Fs__default['default'].readFileSync(Path__default['default'].join(process.env.UNI_INPUT_DIR, './pages.json'), 'utf8')
const { pages, subPackages = [] } = JSON.parse(jsonStr)
const list = []
pages.forEach(({ path }) => {
list.push(getFileMatchReg(process.env.UNI_INPUT_DIR, path))
})
subPackages.forEach(({ pages, root }) => {
pages.forEach(({ path }) => {
list.push(getFileMatchReg(process.env.UNI_INPUT_DIR, root + '/' + path))
})
})
return list
} catch (e) {
console.log(e, '获取路由失败')
}
}
5)完整loader代码
var Path = require('path')
var Fs = require('fs')
function _interopDefaultLegacy(e) {
return e && typeof e === 'object' && 'default' in e ? e : { default: e }
}
var Path__default = /*#__PURE__*/ _interopDefaultLegacy(Path)
var Fs__default = /*#__PURE__*/ _interopDefaultLegacy(Fs)
/**
* pages.json 在项目中的相对路径
*/
/**
* 获取 文件匹配正则(vue)
* @param {string} publicPath
* @param {string} path
* @returns
*/
const getFileMatchReg = function(publicPath, path) {
const fullPath = Path__default['default'].join(publicPath, `/${path}`)
const regStr = JSON.stringify(`^${fullPath}.?vue$`)
const reg = new RegExp(regStr.substring(1, regStr.length - 1))
return reg
}
/**
* 获取 所有已注册的路由文件的正则规则(vue)
* @returns
*/
const getRouteFileMatchRegAll = function(config) {
try {
const jsonStr = Fs__default['default'].readFileSync(Path__default['default'].join(process.env.UNI_INPUT_DIR, './pages.json'), 'utf8')
const { pages, subPackages = [] } = JSON.parse(jsonStr)
const list = []
pages.forEach(({ path }) => {
list.push(getFileMatchReg(process.env.UNI_INPUT_DIR, path))
})
subPackages.forEach(({ pages, root }) => {
pages.forEach(({ path }) => {
list.push(getFileMatchReg(process.env.UNI_INPUT_DIR, root + '/' + path))
})
})
return list
} catch (e) {
console.log(e, '获取路由失败')
}
}
function default(source) {
const config = Object.assign({}, this.query)
const routeFilePathRegList = getRouteFileMatchRegAll()
// 匹配 路由文件
if (routeFilePathRegList.some(reg => reg.test(this.resourcePath))) {
source = source.replace(/<view(.*?)>/, s => s + config.ylGlobalComponents)
}
return source
}
exports.default = default
至此,一个无需手动引入DOM的全局组件,已经实现。