Skip to content

Commit 9714887

Browse files
author
linrui
committed
feature:新增全局组件功能
1 parent 9f157b6 commit 9714887

File tree

3 files changed

+112
-30
lines changed

3 files changed

+112
-30
lines changed

lib/mp-compiler/index.js

Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ const compiler = require('mpvue-template-compiler')
44
const babel = require('babel-core')
55
const path = require('path')
66
const fs = require('fs')
7+
const deepEqual = require('deep-equal')
78

8-
const { parseConfig, parseComponentsDeps } = require('./parse')
9+
const { parseConfig, parseComponentsDeps, parseGlobalComponents, clearGlobalComponents } = require('./parse')
910
const { parseComponentsDeps: parseComponentsDepsTs } = require('./parse-ts')
1011
const { genScript, genStyle, genPageWxml } = require('./templates')
1112

@@ -62,7 +63,11 @@ function genComponentWxml (compiled, options, emitFile, emitError, emitWarning)
6263
return htmlBeautify(wxmlCodeStr)
6364
}
6465

66+
// 更新全局组件时,需要重新生成wxml,用这个字段保存所有需要更新的页面及其参数
67+
const cacheCreateWxmlFns = {}
68+
6569
function createWxml (emitWarning, emitError, emitFile, resourcePath, rootComponent, compiled, html) {
70+
cacheCreateWxmlFns[resourcePath] = arguments
6671
const { pageType, moduleId, components, src } = getFileInfo(resourcePath) || {}
6772

6873
// 这儿一个黑魔法,和 webpack 约定的规范写法有点偏差!
@@ -124,53 +129,71 @@ function compileMPScript (script, mpOptioins, moduleId) {
124129

125130
// 处理子组件的信息
126131
const components = {}
132+
const fileInfo = resolveTarget(this.resourcePath, this.options.entry)
127133
if (originComponents) {
128-
const allP = Object.keys(originComponents).map(k => {
129-
return new Promise((resolve, reject) => {
130-
this.resolve(this.context, originComponents[k], (err, realSrc) => {
131-
if (err) return reject(err)
132-
const com = covertCCVar(k)
133-
const comName = getCompNameBySrc(realSrc)
134-
components[com] = { src: comName, name: comName }
135-
resolve()
136-
})
137-
})
134+
resolveSrc(originComponents, components, this.resolve, this.context).then(() => {
135+
resolveComponent(this.resourcePath, fileInfo, importsMap, components, moduleId)
136+
}).catch(err => {
137+
console.error(err)
138+
resolveComponent(this.resourcePath, fileInfo, importsMap, components, moduleId)
138139
})
139-
Promise.all(allP)
140-
.then(res => {
141-
components.isCompleted = true
142-
})
143-
.catch(err => {
144-
console.error(err)
145-
components.isCompleted = true
146-
})
147140
} else {
148-
components.isCompleted = true
141+
resolveComponent(this.resourcePath, fileInfo, importsMap, components, moduleId)
149142
}
150143

151-
const fileInfo = resolveTarget(this.resourcePath, this.options.entry)
152-
cacheFileInfo(this.resourcePath, fileInfo, { importsMap, components, moduleId })
153-
154144
return script
155145
}
156146

157147
// checkMPEntry 针对 entry main.js 的入口处理
158148
// 编译出 app, page 的入口js/wxml/json
159149

160150
const startPageReg = /^\^/
161-
151+
let globalComponents
162152
function compileMP (content, mpOptioins) {
163153
const { resourcePath, emitError, emitFile, emitWarning, resolve, context, options } = this
164154

155+
const fileInfo = resolveTarget(resourcePath, options.entry)
156+
cacheFileInfo(resourcePath, fileInfo)
157+
const { src, name, isApp, isPage } = fileInfo
158+
165159
const babelrc = getBabelrc(mpOptioins.globalBabelrc)
166-
const { metadata } = babel.transform(content, { extends: babelrc, plugins: [parseConfig] })
160+
// app入口进行全局component解析
161+
const { metadata } = babel.transform(content, { extends: babelrc, plugins: isApp ? [parseConfig, parseGlobalComponents] : [parseConfig] })
167162

168163
// metadata: config
169-
const { config, rootComponent } = metadata
164+
const { config, rootComponent, globalComponents: globalComps } = metadata
165+
166+
if (isApp) {
167+
// 保存旧数据,用于对比
168+
const oldGlobalComponents = globalComponents
169+
// 开始解析app入口文件时把全局组件清空,解析完成后再进行赋值,标志全局组件解析完成
170+
globalComponents = null
171+
clearGlobalComponents()
172+
173+
const handleResult = () => {
174+
globalComponents = components
175+
// 热更时,如果全局组件更新,需要重新生成所有的wxml
176+
if (oldGlobalComponents && !deepEqual(oldGlobalComponents, globalComponents)) {
177+
// 更新所有页面的组件
178+
Object.keys(cacheResolveComponents).forEach(k => {
179+
resolveComponent(...cacheResolveComponents[k])
180+
})
181+
// 重新生成所有wxml
182+
Object.keys(cacheCreateWxmlFns).forEach(k => {
183+
createWxml(...cacheCreateWxmlFns[k])
184+
})
185+
}
186+
}
170187

171-
const fileInfo = resolveTarget(resourcePath, options.entry)
172-
cacheFileInfo(resourcePath, fileInfo)
173-
const { src, name, isApp, isPage } = fileInfo
188+
// 解析全局组件的路径
189+
const components = {}
190+
resolveSrc(globalComps, components, resolve, context).then(() => {
191+
handleResult()
192+
}).catch(err => {
193+
console.error(err)
194+
handleResult()
195+
})
196+
}
174197

175198
if (isApp || isPage) {
176199
// 生成入口 json
@@ -213,4 +236,32 @@ function compileMP (content, mpOptioins) {
213236
return content
214237
}
215238

239+
function resolveSrc (originComponents, components, resolveFn, context) {
240+
return Promise.all(Object.keys(originComponents).map(k => {
241+
return new Promise((resolve, reject) => {
242+
resolveFn(context, originComponents[k], (err, realSrc) => {
243+
if (err) return reject(err)
244+
const com = covertCCVar(k)
245+
const comName = getCompNameBySrc(realSrc)
246+
components[com] = { src: comName, name: comName }
247+
resolve()
248+
})
249+
})
250+
}))
251+
}
252+
253+
const cacheResolveComponents = {}
254+
function resolveComponent (resourcePath, fileInfo, importsMap, localComponents, moduleId) {
255+
// 需要等待全局组件解析完成
256+
if (!globalComponents) {
257+
setTimeout(resolveComponent, 20, ...arguments)
258+
} else {
259+
// 保存当前所有参数,在热更时如果全局组件发生变化,需要进行组件更新
260+
cacheResolveComponents[resourcePath] = arguments
261+
const components = Object.assign({}, globalComponents, localComponents)
262+
components.isCompleted = true
263+
cacheFileInfo(resourcePath, fileInfo, { importsMap, components, moduleId })
264+
}
265+
}
266+
216267
module.exports = { compileWxml, compileMPScript, compileMP }

lib/mp-compiler/parse.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,34 @@ function parseComponentsDeps (babel) {
9898
return { visitor: componentsVisitor }
9999
}
100100

101-
module.exports = { parseConfig, parseComponentsDeps }
101+
// 解析全局components
102+
let globalComponents = {}
103+
const globalComponentsVisitor = {
104+
CallExpression (path) {
105+
const { callee, arguments: args } = path.node
106+
const { metadata } = path.hub.file
107+
if (!callee.object || !callee.property) {
108+
return
109+
}
110+
if (callee.object.name === 'Vue' && callee.property.name === 'component') {
111+
if (!args[0] || args[0].type !== 'StringLiteral') {
112+
throw new Error('Vue.component()的第一个参数必须为静态字符串')
113+
}
114+
if (!args[1]) {
115+
throw new Error('Vue.component()需要两个参数')
116+
}
117+
const { importsMap } = getImportsMap(metadata)
118+
globalComponents[args[0].value] = importsMap[args[1].name]
119+
}
120+
metadata.globalComponents = globalComponents
121+
}
122+
}
123+
124+
function parseGlobalComponents (babel) {
125+
return { visitor: globalComponentsVisitor }
126+
}
127+
128+
function clearGlobalComponents () {
129+
globalComponents = {}
130+
}
131+
module.exports = { parseConfig, parseComponentsDeps, parseGlobalComponents, clearGlobalComponents }

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"dependencies": {
5252
"babelon": "^1.0.5",
5353
"consolidate": "^0.14.0",
54+
"deep-equal": "^1.0.1",
5455
"hash-sum": "^1.0.2",
5556
"js-beautify": "^1.6.14",
5657
"loader-utils": "^1.1.0",

0 commit comments

Comments
 (0)