Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat support native to h5 #1410

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/core/src/platform/builtInMixins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import pageScrollMixin from './pageScrollMixin'
import componentGenericsMixin from './componentGenericsMixin'
import getTabBarMixin from './getTabBarMixin'
import pageRouteMixin from './pageRouteMixin'
import setDataMixin from './setDataMixin'

export default function getBuiltInMixins (options, type) {
let bulitInMixins = []
Expand All @@ -24,7 +25,8 @@ export default function getBuiltInMixins (options, type) {
getTabBarMixin(type),
pageRouteMixin(type),
// 由于relation可能是通过mixin注入的,不能通过当前的用户options中是否存在relations来简单判断是否注入该项mixin
relationsMixin(type)
relationsMixin(type),
setDataMixin(type)
]
} else {
// 此为差异抹平类mixins,原生模式下也需要注入也抹平平台差异
Expand Down
28 changes: 28 additions & 0 deletions packages/core/src/platform/builtInMixins/setDataMixin.web.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { isObject, isFunction, error } from '@mpxjs/utils'

export default function setDataMixin () {
return {
beforeCreate () {
Object.defineProperty(this, 'data', {
get () {
return this.$data
Blackgan3 marked this conversation as resolved.
Show resolved Hide resolved
},
configurable: true
})
},
methods: {
setData (data, callback) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

通过Vue.prototype来挂载

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

移到VuePlugin.js当中

if (!isObject(data)) {
error(`The data entry type of the setData method must be object, The type of data ${data} is incorrect`)
return
}
Object.keys(data).forEach(key => {
this[key] = data[key]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要支持路径设置

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要调用forceUpdate

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要考虑key不在原有data中的情况

})
if (callback && isFunction(callback)) {
this.$nextTick(callback.bind(this))
}
}
}
}
}
12 changes: 11 additions & 1 deletion packages/webpack-plugin/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1589,14 +1589,14 @@ try {
}
createData.resource = addQuery(createData.resource, { mpx: MPX_PROCESSED_FLAG }, true)
}

if (mpx.mode === 'web') {
const mpxStyleOptions = queryObj.mpxStyleOptions
const firstLoader = loaders[0] ? toPosix(loaders[0].loader) : ''
const isPitcherRequest = firstLoader.includes('node_modules/vue-loader/lib/loaders/pitcher')
let cssLoaderIndex = -1
let vueStyleLoaderIndex = -1
let mpxStyleLoaderIndex = -1
let nativeLoaderIndex = -1
loaders.forEach((loader, index) => {
const currentLoader = toPosix(loader.loader)
if (currentLoader.includes('node_modules/css-loader') && cssLoaderIndex === -1) {
Expand All @@ -1605,6 +1605,8 @@ try {
vueStyleLoaderIndex = index
} else if (currentLoader.includes(styleCompilerPath) && mpxStyleLoaderIndex === -1) {
mpxStyleLoaderIndex = index
} else if (currentLoader.includes('native-loader')) {
nativeLoaderIndex = index
}
})
if (mpxStyleLoaderIndex === -1) {
Expand All @@ -1621,6 +1623,14 @@ try {
})
}
}
if (queryObj.isNative && nativeLoaderIndex !== loaders.length - 1) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要考虑.ts的情况

loaders.push({
loader: require.resolve('@vue/vue-loader-v15/lib/index.js')
})
loaders.push({
loader: require.resolve(nativeLoaderPath)
})
}
}

createData.request = stringifyLoadersAndResource(loaders, createData.resource)
Expand Down
8 changes: 6 additions & 2 deletions packages/webpack-plugin/lib/json-compiler/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,12 @@ module.exports = function createJSONHelper ({ loaderContext, emitWarning, custom
outputPath = getOutputPath(resourcePath, 'component')
}
}
if (isScript(ext) && mode !== 'web') {
resource = `!!${nativeLoaderPath}!${resource}`
if (ext === '.js') {
if (mode === 'web') {
resource = `${resource}?isNative=true`
} else {
resource = `!!${nativeLoaderPath}!${resource}?isNative=true`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

统一在afterResolve中插入nativeLoader吧,这里统一加isNative Query就可以

}
}

const entry = getDynamicEntry(resource, 'component', outputPath, tarRoot, relativePath)
Expand Down
110 changes: 109 additions & 1 deletion packages/webpack-plugin/lib/native-loader.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const path = require('path')
const JSON5 = require('json5')
const fs = require('fs')
const parseRequest = require('./utils/parse-request')
const config = require('./config')
const createHelpers = require('./helpers')
Expand All @@ -11,6 +12,11 @@ const getRulesRunner = require('./platform')
const getEntryName = require('./utils/get-entry-name')
const AppEntryDependency = require('./dependencies/AppEntryDependency')
const RecordResourceMapDependency = require('./dependencies/RecordResourceMapDependency')
const processTemplate = require('./web/processTemplate')
const processStyles = require('./web/processStyles')
const processJSON = require('./web/processJSON')
const processScript = require('./web/processScript')
const RecordVueContentDependency = require('./dependencies/RecordVueContentDependency')

// todo native-loader考虑与mpx-loader或加强复用,原生组件约等于4个区块都为src的.mpx文件
module.exports = function (content) {
Expand Down Expand Up @@ -54,6 +60,8 @@ module.exports = function (content) {
const hasScoped = (queryObj.scoped || autoScope) && mode === 'ali'
const hasComment = false
const isNative = true
const parts = {}
let output = ''

const checkFileExists = (extName, callback) => {
this.resolve(parsed.dir, resourceName + extName, callback)
Expand Down Expand Up @@ -140,6 +148,36 @@ module.exports = function (content) {
})
}, callback)
},
(callback) => {
if (mode === 'web') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

只有template和json需要预读,且文件的支持类型需要保持一致

async.forEachOf(typeExtMap, (ext, key, callback) => {
// 检测到jsonjs或cssLang时跳过对应类型文件检测
if (typeResourceMap[key]) {
fs.readFile(typeResourceMap[key], (err, content) => {
if (err) return callback(err)
if (key === 'styles') {
parts[key] = [{
content: content.toString('utf-8'),
tag: 'style',
attrs: {}
}]
} else {
parts[key] = {
content: content.toString('utf-8'),
tag: key,
attrs: {}
}
}
callback()
})
} else {
callback()
}
}, callback)
} else {
callback()
}
},
(callback) => {
getJSONContent({
src: typeResourceMap.json,
Expand Down Expand Up @@ -201,6 +239,76 @@ module.exports = function (content) {
if (json.componentPlaceholder) {
componentPlaceholder = componentPlaceholder.concat(Object.values(json.componentPlaceholder))
}

if (mode === 'web') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这块不会是copy的吧,抽一个通用逻辑呗

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vueContentCache的逻辑补齐

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mainScript逻辑补齐

return async.waterfall([
(callback) => {
async.parallel([
(callback) => {
processTemplate(parts.template, {
loaderContext,
hasScoped,
hasComment,
isNative,
srcMode,
moduleId,
ctorType,
usingComponents: [],
componentGenerics: {}
}, callback)
},
(callback) => {
processStyles(parts.styles, {
ctorType,
autoScope,
moduleId
}, callback)
},
(callback) => {
processJSON(parts.json, {
loaderContext,
pagesMap,
componentsMap
}, callback)
}
], (err, res) => {
callback(err, res)
})
},
([templateRes, stylesRes, jsonRes], callback) => {
output += templateRes.output
output += stylesRes.output
output += jsonRes.output
if (ctorType === 'app' && jsonRes.jsonObj.window && jsonRes.jsonObj.window.navigationBarTitleText) {
mpx.appTitle = jsonRes.jsonObj.window.navigationBarTitleText
}

processScript(parts.script, {
loaderContext,
ctorType,
srcMode,
moduleId,
isProduction,
componentGenerics: {},
jsonConfig: jsonRes.jsonObj,
outputPath: queryObj.outputPath || '',
tabBarMap: jsonRes.tabBarMap,
tabBarStr: jsonRes.tabBarStr,
builtInComponentsMap: templateRes.builtInComponentsMap,
genericsInfo: templateRes.genericsInfo,
wxsModuleMap: templateRes.wxsModuleMap,
localComponentsMap: jsonRes.localComponentsMap,
localPagesMap: jsonRes.localPagesMap
}, callback)
}
], (err, scriptRes) => {
if (err) return callback(err)
output += scriptRes.output
this._module.addPresentationalDependency(new RecordVueContentDependency(filePath, output))
callback(null, output)
})
}

const {
getRequire
} = createHelpers(loaderContext)
Expand Down Expand Up @@ -241,7 +349,7 @@ module.exports = function (content) {
}

// 注入模块id及资源路径
let output = `global.currentModuleId = ${JSON.stringify(moduleId)}\n`
output = `global.currentModuleId = ${JSON.stringify(moduleId)}\n`
if (!isProduction) {
output += `global.currentResource = ${JSON.stringify(filePath)}\n`
}
Expand Down
4 changes: 4 additions & 0 deletions packages/webpack-plugin/lib/platform/template/wx/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,10 @@ module.exports = function getSpec ({ warn, error }) {
modifierStr
}
const isComponent = usingComponents.indexOf(el.tag) !== -1 || el.tag === 'component'
const parsed = parseMustacheWithContext(value)
if (parsed.hasBinding) {
value = parsed.result
}
// 记录event监听信息用于后续判断是否需要使用内置基础组件
el.hasEvent = true
const rPrefix = runRules(spec.event.prefix, prefix, { mode: 'web', meta })
Expand Down
4 changes: 2 additions & 2 deletions packages/webpack-plugin/lib/selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ module.exports = function (content) {
this.cacheable()
// 兼容处理处理ts-loader中watch-run/updateFile逻辑,直接跳过当前loader及后续的loader返回内容
const pathExtname = path.extname(this.resourcePath)
if (!['.vue', '.mpx'].includes(pathExtname)) {
const { queryObj } = parseRequest(this.resource)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这块不可能经过selector的

if (!['.vue', '.mpx'].includes(pathExtname) && !queryObj.isNative) {
this.loaderIndex = tsWatchRunLoaderFilter(this.loaders, this.loaderIndex)
return content
}
Expand All @@ -17,7 +18,6 @@ module.exports = function (content) {
return content
}

const { queryObj } = parseRequest(this.resource)
const ctorType = queryObj.ctorType
const type = queryObj.type
const index = queryObj.index || 0
Expand Down
Loading