Skip to content

Commit 4cc294f

Browse files
authored
Merge pull request #17 from linrui1994/feature/global-components
feature:新增全局组件声明功能
2 parents fdae6ff + 9cae1df commit 4cc294f

File tree

3 files changed

+115
-31
lines changed

3 files changed

+115
-31
lines changed

lib/mp-compiler/index.js

+83-30
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,73 @@ 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

165-
const babelrc = getBabelrc(mpOptioins.globalBabelrc)
166-
const { metadata } = babel.transform(content, { extends: babelrc, plugins: [parseConfig] })
167-
168-
// metadata: config
169-
const { config, rootComponent } = metadata
170-
171155
const fileInfo = resolveTarget(resourcePath, options.entry)
172156
cacheFileInfo(resourcePath, fileInfo)
173157
const { src, name, isApp, isPage } = fileInfo
158+
if (isApp) {
159+
// 解析前将可能存在的全局组件清空
160+
clearGlobalComponents()
161+
}
162+
163+
const babelrc = getBabelrc(mpOptioins.globalBabelrc)
164+
// app入口进行全局component解析
165+
const { metadata } = babel.transform(content, { extends: babelrc, plugins: isApp ? [parseConfig, parseGlobalComponents] : [parseConfig] })
166+
167+
// metadata: config
168+
const { config, rootComponent, globalComponents: globalComps } = metadata
169+
170+
if (isApp) {
171+
// 保存旧数据,用于对比
172+
const oldGlobalComponents = globalComponents
173+
// 开始解析组件路径时把全局组件清空,解析完成后再进行赋值,标志全局组件解析完成
174+
globalComponents = null
175+
176+
// 解析全局组件的路径
177+
const components = {}
178+
resolveSrc(globalComps, components, resolve, context).then(() => {
179+
handleResult(components)
180+
}).catch(err => {
181+
console.error(err)
182+
handleResult(components)
183+
})
184+
const handleResult = components => {
185+
globalComponents = components
186+
// 热更时,如果全局组件更新,需要重新生成所有的wxml
187+
if (oldGlobalComponents && !deepEqual(oldGlobalComponents, globalComponents)) {
188+
// 更新所有页面的组件
189+
Object.keys(cacheResolveComponents).forEach(k => {
190+
resolveComponent(...cacheResolveComponents[k])
191+
})
192+
// 重新生成所有wxml
193+
Object.keys(cacheCreateWxmlFns).forEach(k => {
194+
createWxml(...cacheCreateWxmlFns[k])
195+
})
196+
}
197+
}
198+
}
174199

175200
if (isApp || isPage) {
176201
// 生成入口 json
@@ -213,4 +238,32 @@ function compileMP (content, mpOptioins) {
213238
return content
214239
}
215240

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

lib/mp-compiler/parse.js

+31-1
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

+1
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)