From 9714887213c343936b679bd8102d697d416ab5b1 Mon Sep 17 00:00:00 2001 From: linrui Date: Tue, 1 May 2018 21:55:19 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feature:=E6=96=B0=E5=A2=9E=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E7=BB=84=E4=BB=B6=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/mp-compiler/index.js | 109 ++++++++++++++++++++++++++++----------- lib/mp-compiler/parse.js | 32 +++++++++++- package.json | 1 + 3 files changed, 112 insertions(+), 30 deletions(-) diff --git a/lib/mp-compiler/index.js b/lib/mp-compiler/index.js index d714eba..afd30b4 100644 --- a/lib/mp-compiler/index.js +++ b/lib/mp-compiler/index.js @@ -4,8 +4,9 @@ const compiler = require('mpvue-template-compiler') const babel = require('babel-core') const path = require('path') const fs = require('fs') +const deepEqual = require('deep-equal') -const { parseConfig, parseComponentsDeps } = require('./parse') +const { parseConfig, parseComponentsDeps, parseGlobalComponents, clearGlobalComponents } = require('./parse') const { parseComponentsDeps: parseComponentsDepsTs } = require('./parse-ts') const { genScript, genStyle, genPageWxml } = require('./templates') @@ -62,7 +63,11 @@ function genComponentWxml (compiled, options, emitFile, emitError, emitWarning) return htmlBeautify(wxmlCodeStr) } +// 更新全局组件时,需要重新生成wxml,用这个字段保存所有需要更新的页面及其参数 +const cacheCreateWxmlFns = {} + function createWxml (emitWarning, emitError, emitFile, resourcePath, rootComponent, compiled, html) { + cacheCreateWxmlFns[resourcePath] = arguments const { pageType, moduleId, components, src } = getFileInfo(resourcePath) || {} // 这儿一个黑魔法,和 webpack 约定的规范写法有点偏差! @@ -124,33 +129,18 @@ function compileMPScript (script, mpOptioins, moduleId) { // 处理子组件的信息 const components = {} + const fileInfo = resolveTarget(this.resourcePath, this.options.entry) if (originComponents) { - const allP = Object.keys(originComponents).map(k => { - return new Promise((resolve, reject) => { - this.resolve(this.context, originComponents[k], (err, realSrc) => { - if (err) return reject(err) - const com = covertCCVar(k) - const comName = getCompNameBySrc(realSrc) - components[com] = { src: comName, name: comName } - resolve() - }) - }) + resolveSrc(originComponents, components, this.resolve, this.context).then(() => { + resolveComponent(this.resourcePath, fileInfo, importsMap, components, moduleId) + }).catch(err => { + console.error(err) + resolveComponent(this.resourcePath, fileInfo, importsMap, components, moduleId) }) - Promise.all(allP) - .then(res => { - components.isCompleted = true - }) - .catch(err => { - console.error(err) - components.isCompleted = true - }) } else { - components.isCompleted = true + resolveComponent(this.resourcePath, fileInfo, importsMap, components, moduleId) } - const fileInfo = resolveTarget(this.resourcePath, this.options.entry) - cacheFileInfo(this.resourcePath, fileInfo, { importsMap, components, moduleId }) - return script } @@ -158,19 +148,52 @@ function compileMPScript (script, mpOptioins, moduleId) { // 编译出 app, page 的入口js/wxml/json const startPageReg = /^\^/ - +let globalComponents function compileMP (content, mpOptioins) { const { resourcePath, emitError, emitFile, emitWarning, resolve, context, options } = this + const fileInfo = resolveTarget(resourcePath, options.entry) + cacheFileInfo(resourcePath, fileInfo) + const { src, name, isApp, isPage } = fileInfo + const babelrc = getBabelrc(mpOptioins.globalBabelrc) - const { metadata } = babel.transform(content, { extends: babelrc, plugins: [parseConfig] }) + // app入口进行全局component解析 + const { metadata } = babel.transform(content, { extends: babelrc, plugins: isApp ? [parseConfig, parseGlobalComponents] : [parseConfig] }) // metadata: config - const { config, rootComponent } = metadata + const { config, rootComponent, globalComponents: globalComps } = metadata + + if (isApp) { + // 保存旧数据,用于对比 + const oldGlobalComponents = globalComponents + // 开始解析app入口文件时把全局组件清空,解析完成后再进行赋值,标志全局组件解析完成 + globalComponents = null + clearGlobalComponents() + + const handleResult = () => { + globalComponents = components + // 热更时,如果全局组件更新,需要重新生成所有的wxml + if (oldGlobalComponents && !deepEqual(oldGlobalComponents, globalComponents)) { + // 更新所有页面的组件 + Object.keys(cacheResolveComponents).forEach(k => { + resolveComponent(...cacheResolveComponents[k]) + }) + // 重新生成所有wxml + Object.keys(cacheCreateWxmlFns).forEach(k => { + createWxml(...cacheCreateWxmlFns[k]) + }) + } + } - const fileInfo = resolveTarget(resourcePath, options.entry) - cacheFileInfo(resourcePath, fileInfo) - const { src, name, isApp, isPage } = fileInfo + // 解析全局组件的路径 + const components = {} + resolveSrc(globalComps, components, resolve, context).then(() => { + handleResult() + }).catch(err => { + console.error(err) + handleResult() + }) + } if (isApp || isPage) { // 生成入口 json @@ -213,4 +236,32 @@ function compileMP (content, mpOptioins) { return content } +function resolveSrc (originComponents, components, resolveFn, context) { + return Promise.all(Object.keys(originComponents).map(k => { + return new Promise((resolve, reject) => { + resolveFn(context, originComponents[k], (err, realSrc) => { + if (err) return reject(err) + const com = covertCCVar(k) + const comName = getCompNameBySrc(realSrc) + components[com] = { src: comName, name: comName } + resolve() + }) + }) + })) +} + +const cacheResolveComponents = {} +function resolveComponent (resourcePath, fileInfo, importsMap, localComponents, moduleId) { + // 需要等待全局组件解析完成 + if (!globalComponents) { + setTimeout(resolveComponent, 20, ...arguments) + } else { + // 保存当前所有参数,在热更时如果全局组件发生变化,需要进行组件更新 + cacheResolveComponents[resourcePath] = arguments + const components = Object.assign({}, globalComponents, localComponents) + components.isCompleted = true + cacheFileInfo(resourcePath, fileInfo, { importsMap, components, moduleId }) + } +} + module.exports = { compileWxml, compileMPScript, compileMP } diff --git a/lib/mp-compiler/parse.js b/lib/mp-compiler/parse.js index 5301542..8c5901a 100644 --- a/lib/mp-compiler/parse.js +++ b/lib/mp-compiler/parse.js @@ -98,4 +98,34 @@ function parseComponentsDeps (babel) { return { visitor: componentsVisitor } } -module.exports = { parseConfig, parseComponentsDeps } +// 解析全局components +let globalComponents = {} +const globalComponentsVisitor = { + CallExpression (path) { + const { callee, arguments: args } = path.node + const { metadata } = path.hub.file + if (!callee.object || !callee.property) { + return + } + if (callee.object.name === 'Vue' && callee.property.name === 'component') { + if (!args[0] || args[0].type !== 'StringLiteral') { + throw new Error('Vue.component()的第一个参数必须为静态字符串') + } + if (!args[1]) { + throw new Error('Vue.component()需要两个参数') + } + const { importsMap } = getImportsMap(metadata) + globalComponents[args[0].value] = importsMap[args[1].name] + } + metadata.globalComponents = globalComponents + } +} + +function parseGlobalComponents (babel) { + return { visitor: globalComponentsVisitor } +} + +function clearGlobalComponents () { + globalComponents = {} +} +module.exports = { parseConfig, parseComponentsDeps, parseGlobalComponents, clearGlobalComponents } diff --git a/package.json b/package.json index dc82f46..2ed3fc2 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "dependencies": { "babelon": "^1.0.5", "consolidate": "^0.14.0", + "deep-equal": "^1.0.1", "hash-sum": "^1.0.2", "js-beautify": "^1.6.14", "loader-utils": "^1.1.0", From 9cae1dffd5be119c7607d306e642bbdc24b85795 Mon Sep 17 00:00:00 2001 From: linrui Date: Wed, 2 May 2018 10:07:12 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/mp-compiler/index.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/mp-compiler/index.js b/lib/mp-compiler/index.js index afd30b4..7d991c0 100644 --- a/lib/mp-compiler/index.js +++ b/lib/mp-compiler/index.js @@ -155,6 +155,10 @@ function compileMP (content, mpOptioins) { const fileInfo = resolveTarget(resourcePath, options.entry) cacheFileInfo(resourcePath, fileInfo) const { src, name, isApp, isPage } = fileInfo + if (isApp) { + // 解析前将可能存在的全局组件清空 + clearGlobalComponents() + } const babelrc = getBabelrc(mpOptioins.globalBabelrc) // app入口进行全局component解析 @@ -166,11 +170,18 @@ function compileMP (content, mpOptioins) { if (isApp) { // 保存旧数据,用于对比 const oldGlobalComponents = globalComponents - // 开始解析app入口文件时把全局组件清空,解析完成后再进行赋值,标志全局组件解析完成 + // 开始解析组件路径时把全局组件清空,解析完成后再进行赋值,标志全局组件解析完成 globalComponents = null - clearGlobalComponents() - const handleResult = () => { + // 解析全局组件的路径 + const components = {} + resolveSrc(globalComps, components, resolve, context).then(() => { + handleResult(components) + }).catch(err => { + console.error(err) + handleResult(components) + }) + const handleResult = components => { globalComponents = components // 热更时,如果全局组件更新,需要重新生成所有的wxml if (oldGlobalComponents && !deepEqual(oldGlobalComponents, globalComponents)) { @@ -184,15 +195,6 @@ function compileMP (content, mpOptioins) { }) } } - - // 解析全局组件的路径 - const components = {} - resolveSrc(globalComps, components, resolve, context).then(() => { - handleResult() - }).catch(err => { - console.error(err) - handleResult() - }) } if (isApp || isPage) {