├── README.md
├── index.js
├── package.json
└── utils.js
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # vue-inset-loader
3 | #### 编译阶段在sfc模板指定位置插入自定义内容,适用于webpack构建的vue应用,常用于小程序需要全局引入组件的场景。(由于小程序没有开放根标签,没有办法在根标签下追加全局标签,所以要使用组件必须在当前页面引入组件标签)
4 |
5 | ### 第一步 安装
6 |
7 | npm install vue-inset-loader --save-dev
8 |
9 | ### 第二步 vue.config.js注入loader
10 |
11 | module: {
12 | rules: [
13 | {
14 | test: /\.vue$/,
15 | use:{
16 | loader: "vue-inset-loader"
17 | // // 针对Hbuilder工具创建的uni-app项目
18 | // loader: path.resolve(__dirname,"./node_modules/vue-inset-loader")
19 | // // 支持自定义pages.json文件路径
20 | // options: {
21 | // pagesPath: path.resolve(__dirname,'./src/pages.json')
22 | // }
23 | }
24 | }
25 | ]
26 | },
27 |
28 | ### 第三步 pages.json配置文件中添加insetLoader
29 |
30 | "insetLoader": {
31 | "config":{
32 | "confirm": "",
33 | "abc": ""
34 | },
35 | // 全局配置
36 | "label":["confirm"],
37 | "rootEle":"div"
38 | },
39 | "pages": [
40 | {
41 | "path": "pages/tabbar/index/index",
42 | "style": {
43 | "navigationBarTitleText": "测试页面",
44 | // 单独配置,用法跟全局配置一致,优先级高于全局
45 | "label": ["confirm","abc"],
46 | "rootEle":"div"
47 | }
48 | },
49 | ]
50 |
51 | ### 配置说明
52 |
53 | - `config` (default: `{}`)
54 | 定义标签名称和内容的键值对
55 | - `label`(default: `[]`)
56 | 需要全局引入的标签,打包后会在所有页面引入此标签
57 | - `rootEle`(default: `"div"`)
58 | 根元素的标签类型,缺省值为div,支持正则,比如匹配任意标签 ".*"
59 |
60 | ✔ `label` 和 `rootEle` 支持在单独页面的style里配置,优先级高于全局配置
61 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const { parseComponent } = require("../vue-template-compiler")
2 | const {
3 | generateHtmlCode,
4 | generateLabelCode,
5 | generateStyleCode,
6 | getPagesMap,
7 | initPages,
8 | getRoute
9 | } = require('./utils')
10 |
11 | // 是否初始化过
12 | let _init = false
13 | // 是否需要做处理
14 | let needHandle = false
15 | // 路由和配置的映射关系
16 | let pagesMap = {}
17 |
18 | module.exports = function(content) {
19 | if(!_init){
20 | _init = true
21 | init(this)
22 | }
23 |
24 | // 配置无效不予处理
25 | if(!needHandle){
26 | return content
27 | }
28 |
29 | // 获取当前文件的小程序路由
30 | const route = getRoute(this.resourcePath)
31 | // 根据路由并找到对应配置
32 | const curPage = pagesMap[route]
33 | if(curPage){
34 | // 解析sfc
35 | const compiler = parseComponent(content)
36 | // 生成标签代码
37 | const labelCode = generateLabelCode(curPage.label)
38 | // 匹配标签位置
39 | const insertReg = new RegExp(`(<\/${curPage.ele}>$)`)
40 | // 在匹配的标签之前插入额外标签代码
41 | const templateCode = generateHtmlCode(
42 | compiler.template.content,
43 | labelCode,
44 | insertReg
45 | )
46 | // 重组style标签及内容
47 | const style = generateStyleCode(compiler.styles || [])
48 | content = `
49 |
50 | ${templateCode}
51 |
52 |
55 | ${style}
56 | `
57 | }
58 | return content
59 | }
60 |
61 | function init(that){
62 | const isWx = process.env.VUE_APP_PLATFORM == 'mp-weixin'
63 | // 首次需要对pages配置文件做解析,并判断是否为有效配置
64 | // 非小程序环境或无效配置不予处理
65 | needHandle = isWx && initPages(that)
66 | // 转换为路由和配置的映射对象
67 | needHandle && (pagesMap = getPagesMap())
68 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-inset-loader",
3 | "version": "1.2.6",
4 | "description": "在sfc模板指定位置插入自定义内容",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/1977474741/vue-inset-loader.git"
9 | },
10 | "author": "鹅鹅鹅 <1977474741@qq.com>",
11 | "license": "ISC",
12 | "dependencies": {
13 | "vue-template-compiler": "^2.6.10",
14 | "strip-json-comments": "^2.0.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/utils.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 | const stripJsonComments = require("../strip-json-comments")
4 |
5 | // 反序列化后的pages.json对象
6 | let pagesJson = {}
7 | // 此loader配置对象
8 | let insetLoader = {}
9 | // pages.json文件所在目录
10 | let rootPath = process.env.UNI_INPUT_DIR || (process.env.INIT_CWD + '\\src')
11 |
12 |
13 | // 在template中用正则匹配并替换一段代码
14 | const generateHtmlCode = (template,labelCode,regLabel)=>{
15 | // 去除html所有注释和首尾空白
16 | const regNotes = //g
17 | const regBlank = /^\s+|\s+$/g
18 | return template
19 | .replace(regNotes,'')
20 | .replace(regBlank,'')
21 | .replace(regLabel,`${labelCode}$1`)
22 | }
23 |
24 | // 获取到需要插入的所有label标签
25 | const generateLabelCode = (labelArr) => labelArr.map(e => insetLoader.config[e] || '').join('')
26 |
27 | // 根据compiler组合成style标签字符串代码
28 | const generateStyleCode = (styles)=>styles.reduce((str,item,i)=>{
29 | return str += ``
32 | },'')
33 |
34 | // 分析pages.json,生成路由和配置的映射对象
35 | const getPagesMap = ()=>{
36 | // 获取主包路由配置
37 | const pages = pagesJson.pages || []
38 | const subpackages = pagesJson.subpackages || pagesJson.subPackages || []
39 | return pages.reduce((obj,item)=>{
40 | const curPage = getLabelConfig(item)
41 | curPage.label && (obj['/'+item.path] = curPage)
42 | return obj
43 | },subpackages.reduce((obj,item)=>{
44 | // 获取分包路由配置
45 | const root = item.root
46 | item.pages.forEach((item)=>{
47 | const curPage = getLabelConfig(item)
48 | curPage.label && (obj['/'+root+'/'+item.path] = curPage)
49 | })
50 | return obj
51 | },{}))
52 | }
53 |
54 | // 生成path对应的对象结构
55 | const getLabelConfig = (json)=>{
56 | return {
57 | label: (json.style && json.style.label) || insetLoader.label,
58 | ele: (json.style && json.style.rootEle) || insetLoader.rootEle
59 | }
60 | }
61 |
62 | // 反序列化page.json并缓存,
63 | // 并根据page.json分析是否有效并且需要后续逻辑处理
64 | const initPages = (that)=>{
65 | let pagesPath = (that.query || {}).pagesPath
66 | if(!pagesPath){
67 | // 默认读取pages.json
68 | pagesPath = path.resolve(rootPath, 'pages.json')
69 | }else{
70 | // 如有传自定义pagesPath,则截取出所在目录作为rootPath,用于后续匹配路由
71 | rootPath = path.resolve(pagesPath,'../')
72 | }
73 | pagesJson = JSON.parse(stripJsonComments(fs.readFileSync(pagesPath,'utf8')))
74 | return initInsetLoader()
75 | }
76 |
77 |
78 | // 给非必填项设置缺省值,缺少主要对象返回false
79 | initInsetLoader = ()=>{
80 | insetLoader = pagesJson.insetLoader || {}
81 | // label:全局标签配置
82 | // rootEle:根元素的类型,也支持正则,如匹配任意标签.*
83 | insetLoader.label = insetLoader.label || []
84 | insetLoader.rootEle = insetLoader.rootEle || "div"
85 |
86 | // config对象为空视为无效配置
87 | const effective = typeof insetLoader.config == 'object' && Object.keys(insetLoader.config).length
88 | return effective
89 | }
90 |
91 | // 根据resourcePath获取路由
92 | const getRoute = (resourcePath) => resourcePath
93 | .replace(rootPath,'')
94 | .replace('.vue','')
95 | .replace(/\\/g,'/')
96 |
97 | module.exports = {
98 | generateHtmlCode,
99 | generateLabelCode,
100 | generateStyleCode,
101 | initInsetLoader,
102 | getPagesMap,
103 | initPages,
104 | getRoute
105 | }
--------------------------------------------------------------------------------