提交 5b4abb66 编写于 作者: fxy060608's avatar fxy060608

feat(v3): navigator

上级 5c1d23b2
...@@ -32,7 +32,6 @@ if (process.env.UNI_PLATFORM === 'app-plus' && process.env.UNI_VIEW === 'true') ...@@ -32,7 +32,6 @@ if (process.env.UNI_PLATFORM === 'app-plus' && process.env.UNI_VIEW === 'true')
filename = 'view' filename = 'view'
entry = './lib/' + process.env.UNI_PLATFORM + '/view.js' entry = './lib/' + process.env.UNI_PLATFORM + '/view.js'
} }
service.run('build', { service.run('build', {
name, name,
filename, filename,
...@@ -40,7 +39,8 @@ service.run('build', { ...@@ -40,7 +39,8 @@ service.run('build', {
target: 'lib', target: 'lib',
formats: process.env.UNI_WATCH === 'true' ? 'umd' : 'umd-min', formats: process.env.UNI_WATCH === 'true' ? 'umd' : 'umd-min',
entry, entry,
clean: !process.env.UNI_VIEW clean: !process.env.UNI_VIEW,
mode: process.env.NODE_ENV
}).then(function () { }).then(function () {
if (process.env.UNI_UI !== 'true' && process.env.UNI_VIEW !== 'true') { if (process.env.UNI_UI !== 'true' && process.env.UNI_VIEW !== 'true') {
generateApiManifest( generateApiManifest(
......
...@@ -34,20 +34,24 @@ if (process.env.UNI_SERVICE === 'legacy') { ...@@ -34,20 +34,24 @@ if (process.env.UNI_SERVICE === 'legacy') {
output.format = 'iife' output.format = 'iife'
output.name = 'serviceContext' output.name = 'serviceContext'
output.banner = output.banner =
`export function createServiceContext(Vue, weex, plus, __uniConfig, __uniRoutes, UniServiceJSBridge,instanceContext){ `export function createServiceContext(Vue, weex, plus, UniServiceJSBridge,instanceContext){
var localStorage = plus.storage var localStorage = plus.storage
var setTimeout = instanceContext.setTimeout var setTimeout = instanceContext.setTimeout
var clearTimeout = instanceContext.clearTimeout var clearTimeout = instanceContext.clearTimeout
var setInterval = instanceContext.setInterval var setInterval = instanceContext.setInterval
var clearInterval = instanceContext.clearInterval var clearInterval = instanceContext.clearInterval
var __uniConfig = instanceContext.__uniConfig
var __uniRoutes = instanceContext.__uniRoutes
` `
output.footer = output.footer =
` `
var uni = serviceContext.uni var uni = serviceContext.uni
var getApp = serviceContext.getApp var getApp = serviceContext.getApp
var getCurrentPages = serviceContext.getCurrentPages var getCurrentPages = serviceContext.getCurrentPages
var __definePage = serviceContext.__definePage
var __registerPage = serviceContext.__registerPage
var __registerPage = serviceContext.__registerPage
return serviceContext \n} return serviceContext \n}
` `
......
...@@ -191,7 +191,8 @@ const third = [ ...@@ -191,7 +191,8 @@ const third = [
'onPush', 'onPush',
'offPush', 'offPush',
'requireNativePlugin', 'requireNativePlugin',
'upx2px' 'upx2px',
'registerPlus'
] ]
const apis = [ const apis = [
......
...@@ -4,11 +4,11 @@ import 'uni-core/view/index.css' ...@@ -4,11 +4,11 @@ import 'uni-core/view/index.css'
import { import {
definePage definePage
} from 'uni-platform/view/framework/page' } from 'uni-platform/page-factory'
import { import {
registerConfig getCurrentPages
} from 'uni-platform/view/framework/config' } from 'uni-platform/view/framework/page'
import ViewPlugin from 'uni-platform/view/framework/plugins/index' import ViewPlugin from 'uni-platform/view/framework/plugins/index'
...@@ -16,9 +16,10 @@ global.UniViewJSBridge = { ...@@ -16,9 +16,10 @@ global.UniViewJSBridge = {
publishHandler: UniViewJSBridge.publishHandler, publishHandler: UniViewJSBridge.publishHandler,
subscribeHandler: UniViewJSBridge.subscribeHandler subscribeHandler: UniViewJSBridge.subscribeHandler
} }
global.getCurrentPages = getCurrentPages
global.__registerConfig = registerConfig global.__definePage = definePage
global.__registerPage = definePage
global.Vue = Vue global.Vue = Vue
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
"build:app:v3": "npm run lint && npm run build:app:service && npm run dev:app:view", "build:app:v3": "npm run lint && npm run build:app:service && npm run dev:app:view",
"build:app:service": "cross-env UNI_PLATFORM=app-plus rollup -c build/rollup.config.app.js", "build:app:service": "cross-env UNI_PLATFORM=app-plus rollup -c build/rollup.config.app.js",
"build:app:view": "cross-env NODE_ENV=production UNI_WATCH=false UNI_PLATFORM=app-plus UNI_VIEW=true node build/build.js", "build:app:view": "cross-env NODE_ENV=production UNI_WATCH=false UNI_PLATFORM=app-plus UNI_VIEW=true node build/build.js",
"dev:app:view": "cross-env NODE_ENV=production UNI_WATCH=true UNI_PLATFORM=app-plus UNI_VIEW=true node build/build.js", "dev:app:view": "cross-env NODE_ENV=development UNI_WATCH=true UNI_PLATFORM=app-plus UNI_VIEW=true node build/build.js",
"build:app:nvue": "cross-env UNI_PLATFORM=app-plus-nvue rollup -c build/rollup.config.app.js", "build:app:nvue": "cross-env UNI_PLATFORM=app-plus-nvue rollup -c build/rollup.config.app.js",
"build:app:legacy": "cross-env UNI_PLATFORM=app-plus-nvue UNI_SERVICE=legacy rollup -c build/rollup.config.app.js", "build:app:legacy": "cross-env UNI_PLATFORM=app-plus-nvue UNI_SERVICE=legacy rollup -c build/rollup.config.app.js",
"build:mp-qq": "cross-env UNI_PLATFORM=mp-qq rollup -c build/rollup.config.mp.js", "build:mp-qq": "cross-env UNI_PLATFORM=mp-qq rollup -c build/rollup.config.mp.js",
......
...@@ -6719,7 +6719,7 @@ function updateExtras(oldVnode, vnode) { ...@@ -6719,7 +6719,7 @@ function updateExtras(oldVnode, vnode) {
if (attrs) { if (attrs) {
for (var key$1 in attrs) { for (var key$1 in attrs) {
key$1 !== '_i' && $s(id, key$1, attrs[key$1]); key$1 !== '_i' && $s(id, 'a-' + key$1, attrs[key$1]);
} }
} }
// 存储事件标记 // 存储事件标记
......
此差异已折叠。
...@@ -146,17 +146,18 @@ function isValidPage (page, root = '') { ...@@ -146,17 +146,18 @@ function isValidPage (page, root = '') {
// 存储 nvue 相关信息 // 存储 nvue 相关信息
pagePath = normalizePath(path.join(root, pagePath)) pagePath = normalizePath(path.join(root, pagePath))
// if (process.env.UNI_USING_NVUE_COMPILER) {
process.UNI_NVUE_ENTRY[pagePath] = getNVueMainJsPath(pagePath) process.UNI_NVUE_ENTRY[pagePath] = getNVueMainJsPath(pagePath)
// } else {
// process.UNI_NVUE_ENTRY[pagePath] = path.resolve(process.env.UNI_INPUT_DIR, pagePath + '.nvue') + '?entry'
// }
uniNVuePages.push({ if (process.env.UNI_USING_V3) { // 不移除
'path': pagePath + '.html', page.nvue = true
'style': page.style || {} return true
}) } else {
return false uniNVuePages.push({
'path': pagePath + '.html',
'style': page.style || {}
})
return false
}
} }
return true return true
...@@ -202,7 +203,7 @@ function parsePages (pagesJson, pageCallback, subPageCallback) { ...@@ -202,7 +203,7 @@ function parsePages (pagesJson, pageCallback, subPageCallback) {
} }
} }
function parseEntry (pagesJson) { function parseEntry (pagesJson) {
process.UNI_ENTRY = { process.UNI_ENTRY = {
'common/main': path.resolve(process.env.UNI_INPUT_DIR, getMainEntry()) 'common/main': path.resolve(process.env.UNI_INPUT_DIR, getMainEntry())
} }
......
...@@ -139,10 +139,17 @@ const PLATFORMS = { ...@@ -139,10 +139,17 @@ const PLATFORMS = {
}) })
} }
let template = [] let template = []
let view = []
if (process.env.UNI_USING_V3) { if (process.env.UNI_USING_V3) {
view = getCopyOptions([
require.resolve('@dcloudio/uni-app-plus/dist/view.css'),
// TODO view.umd.min.js
require.resolve('@dcloudio/uni-app-plus/dist/view.umd.js')
])
template = getCopyOptions([path.resolve(__dirname, '../template')]) template = getCopyOptions([path.resolve(__dirname, '../template')])
} }
return [ return [
...view,
...template, ...template,
...getStaticCopyOptions(assetsDir), ...getStaticCopyOptions(assetsDir),
...wxcomponents, ...wxcomponents,
......
此差异已折叠。
此差异已折叠。
...@@ -2,7 +2,7 @@ const compiler = require('../lib') ...@@ -2,7 +2,7 @@ const compiler = require('../lib')
const res = compiler.compile( const res = compiler.compile(
` `
<view> <view>
<component v-for="component in components" :item="component.item" :is="component.mode"/> <page-head v-if="a" v-for="item in items" :title="title"></page-head>
</view> </view>
`, { `, {
resourcePath: '/User/fxy/Documents/test.wxml', resourcePath: '/User/fxy/Documents/test.wxml',
......
const {
ID,
hasOwn
} = require('./util')
const tags = require('../../../uni-cli-shared/lib/tags')
const {
getTagName
} = require('../h5')
// 仅限 view 层
module.exports = function parseComponent (el) {
// TODO 需要把自定义组件的 attrs, props 全干掉
el.tag = getTagName(el.tag)
if (!hasOwn(tags, el.tag)) {
el.attrs = el.attrs.filter(attr => attr.name === ID)
}
}
const deprecated = {
events: {
'tap': 'click',
'longtap': 'longpress'
}
}
module.exports = function parseEvent (el) {
if (el.events) {
const {
events: eventsMap
} = deprecated
Object.keys(el.events).forEach(name => {
// 过时事件类型转换
if (eventsMap[name]) {
el.events[eventsMap[name]] = el.events[name]
delete el.events[name]
// warnLogs.add(`警告:事件${name}已过时,推荐使用${eventsMap[name]}代替`)
}
})
}
}
const {
tags,
hasOwn
} = require('@dcloudio/uni-cli-shared')
const { const {
ID, ID,
hasOwn,
addAttr addAttr
} = require('./util') } = require('./util')
module.exports = function preTransformNode (el, options) { module.exports = function preTransformNode (el, options) {
if (el.tag.indexOf('v-uni-') !== 0 && hasOwn(tags, el.tag)) {
el.tag = 'v-uni-' + el.tag
}
if (!hasOwn(options, 'nid')) { if (!hasOwn(options, 'nid')) {
options.nid = 0 options.nid = 0
} }
......
...@@ -8,6 +8,7 @@ const { ...@@ -8,6 +8,7 @@ const {
} = require('./util') } = require('./util')
const parseText = require('./text-parser') const parseText = require('./text-parser')
const parseEvent = require('./event-parser')
const preTransformNode = require('./pre-transform-node') const preTransformNode = require('./pre-transform-node')
...@@ -265,6 +266,8 @@ function processText (el) { ...@@ -265,6 +266,8 @@ function processText (el) {
} }
function postTransformNode (el) { function postTransformNode (el) {
parseEvent(el)
removeStatic(el) removeStatic(el)
renameBinding(el) renameBinding(el)
......
const {
hasOwn
} = require('./util')
const tags = require('../../../uni-cli-shared/lib/tags')
// 仅限 view 层
module.exports = function parseTag (el) {
if (el.tag.indexOf('v-uni-') !== 0 && hasOwn(tags, el.tag)) {
el.tag = 'v-uni-' + el.tag
}
}
...@@ -114,5 +114,8 @@ module.exports = { ...@@ -114,5 +114,8 @@ module.exports = {
processForKey, processForKey,
updateForEleId, updateForEleId,
getBindingAttr, getBindingAttr,
getAndRemoveAttr getAndRemoveAttr,
hasOwn: function (obj, key) {
return hasOwnProperty.call(obj, key)
}
} }
...@@ -7,7 +7,10 @@ const { ...@@ -7,7 +7,10 @@ const {
processForKey processForKey
} = require('./util') } = require('./util')
const parseTag = require('./tag-parser')
const parseText = require('./text-parser') const parseText = require('./text-parser')
const parseEvent = require('./event-parser')
const parseComponent = require('./component-parser')
const preTransformNode = require('./pre-transform-node') const preTransformNode = require('./pre-transform-node')
...@@ -134,6 +137,10 @@ function processText (el, genVar) { ...@@ -134,6 +137,10 @@ function processText (el, genVar) {
} }
function postTransformNode (el) { function postTransformNode (el) {
parseComponent(el)
parseTag(el)
parseEvent(el)
updateForEleId(el) updateForEleId(el)
const id = el.attrsMap[ID] const id = el.attrsMap[ID]
......
...@@ -36,12 +36,14 @@ const uniPath = process.env.UNI_USING_V8 ...@@ -36,12 +36,14 @@ const uniPath = process.env.UNI_USING_V8
const provide = {} const provide = {}
if (!process.env.UNI_USING_NATIVE) { if (!process.env.UNI_USING_V3) { // v3 不需要
provide['uni'] = [path.resolve(__dirname, uniPath), 'default'] if (!process.env.UNI_USING_NATIVE) {
} provide['uni'] = [path.resolve(__dirname, uniPath), 'default']
}
if (process.env.UNI_USING_V8) { if (process.env.UNI_USING_V8) {
provide['plus'] = [path.resolve(__dirname, uniPath), 'weexPlus'] provide['plus'] = [path.resolve(__dirname, uniPath), 'weexPlus']
}
} }
if ( if (
......
...@@ -34,11 +34,13 @@ const v3 = { ...@@ -34,11 +34,13 @@ const v3 = {
// disable noEmitOnErrors // disable noEmitOnErrors
webpackConfig.optimization.noEmitOnErrors = false webpackConfig.optimization.noEmitOnErrors = false
if (isAppService) { const externals = {}
if (isAppService) { // service 层需要编译时注入 vue 内核
webpackConfig.optimization.runtimeChunk = { webpackConfig.optimization.runtimeChunk = {
name: 'app-config' name: 'app-config'
} }
} else if (isAppView) { } else if (isAppView) {
externals['vue'] = 'Vue'
webpackConfig.optimization.runtimeChunk = false webpackConfig.optimization.runtimeChunk = false
} }
...@@ -63,7 +65,8 @@ const v3 = { ...@@ -63,7 +65,8 @@ const v3 = {
}, },
output: { output: {
filename: '[name].js', filename: '[name].js',
chunkFilename: '[id].js' chunkFilename: '[id].js',
globalObject: 'this'
}, },
performance: { performance: {
hints: false hints: false
...@@ -83,6 +86,7 @@ const v3 = { ...@@ -83,6 +86,7 @@ const v3 = {
loader: isAppService ? 'wrap-loader' : path.resolve(__dirname, loader: isAppService ? 'wrap-loader' : path.resolve(__dirname,
'../../packages/webpack-uni-view-main-loader'), '../../packages/webpack-uni-view-main-loader'),
options: { options: {
compiler: getPlatformCompiler(),
before: [ before: [
beforeCode + statCode beforeCode + statCode
] ]
...@@ -110,7 +114,7 @@ const v3 = { ...@@ -110,7 +114,7 @@ const v3 = {
webpackConfig.module webpackConfig.module
.rule('vue') .rule('vue')
.test([/\.vue$/, /\.nvue$/]) .test([/\.vue$/, /\.nvue$/])
.use('vue-loader') .use('vue-loader')// service 层移除 style 节点,view 层返回固定 script
.loader(path.resolve(__dirname, '../../packages/vue-loader/lib')) .loader(path.resolve(__dirname, '../../packages/vue-loader/lib'))
.tap(options => Object.assign(options, { .tap(options => Object.assign(options, {
isAppView, isAppView,
......
const postcss = require('postcss') if (process.env.UNI_USING_V3) {
const selectorParser = require('postcss-selector-parser') module.exports = require('./index.v3.js')
const valueParser = require('postcss-value-parser') } else {
const postcss = require('postcss')
const { const selectorParser = require('postcss-selector-parser')
getPlatformCssVars const valueParser = require('postcss-value-parser')
} = require('@dcloudio/uni-cli-shared')
const {
const CSS_TAGS = require('./tags') getPlatformCssVars
const { } = require('@dcloudio/uni-cli-shared')
unit,
walk const CSS_TAGS = require('./tags')
} = valueParser const {
unit,
const defaultOpts = { walk
px: false } = valueParser
}
const defaultOpts = {
const cssVars = getPlatformCssVars() px: false
}
const transformSelector = (complexSelector, transformer) => {
return selectorParser(transformer).processSync(complexSelector) const cssVars = getPlatformCssVars()
}
const transformSelector = (complexSelector, transformer) => {
const parseWord = function (node, opts) { return selectorParser(transformer).processSync(complexSelector)
const pair = unit(node.value) }
if (pair) {
const num = Number(pair.number) const parseWord = function(node, opts) {
let u = pair.unit.toLowerCase() const pair = unit(node.value)
if (u === 'px' && process.UNI_TRANSFORM_PX) { // TODO px 转换为 upx if (pair) {
u = 'upx' const num = Number(pair.number)
} let u = pair.unit.toLowerCase()
if (process.env.UNI_PLATFORM === 'h5') { if (u === 'px' && process.UNI_TRANSFORM_PX) { // TODO px 转换为 upx
if (u === 'upx' || u === 'rpx') { u = 'upx'
node.value = `%?${num}?%`
}
} else {
if (u === 'upx') {
node.value = num + 'rpx'
} }
// fixed 百度目前1rpx会转换成小数点 vw,导致边框之类的显示有问题 if (process.env.UNI_PLATFORM === 'h5') {
if (process.env.UNI_PLATFORM === 'mp-baidu') { if (u === 'upx' || u === 'rpx') {
if (num === 1 && (u === 'upx' || u === 'rpx')) { node.value = `%?${num}?%`
node.value = '1px' }
} else {
if (u === 'upx') {
node.value = num + 'rpx'
}
// fixed 百度目前1rpx会转换成小数点 vw,导致边框之类的显示有问题
if (process.env.UNI_PLATFORM === 'mp-baidu') {
if (num === 1 && (u === 'upx' || u === 'rpx')) {
node.value = '1px'
}
} }
} }
} }
} }
}
const isInsideKeyframes = function(rule) {
const isInsideKeyframes = function (rule) { return (
return ( rule.parent && rule.parent.type === 'atrule' && /^(-\w+-)?keyframes$/.test(rule.parent.name)
rule.parent && rule.parent.type === 'atrule' && /^(-\w+-)?keyframes$/.test(rule.parent.name) )
) }
}
const tranformValue = function(decl, opts) {
const tranformValue = function (decl, opts) { return valueParser(decl.value)
return valueParser(decl.value) .walk(node => {
.walk(node => { if (node.type === 'word') {
if (node.type === 'word') { parseWord(node, opts)
parseWord(node, opts) } else if (node.type === 'function') {
} else if (node.type === 'function') { if (node.value === 'url') {
if (node.value === 'url') { return false
return false }
} if (node.value === 'var') {
if (node.value === 'var') { let cssVarValue = false
let cssVarValue = false walk(node.nodes, n => {
walk(node.nodes, n => { if (n.type === 'word') {
if (n.type === 'word') { if (cssVars.hasOwnProperty(n.value)) { // 目前仅考虑 nodes 长度为0
if (cssVars.hasOwnProperty(n.value)) { // 目前仅考虑 nodes 长度为0 cssVarValue = cssVars[n.value]
cssVarValue = cssVars[n.value] }
} }
})
if (cssVarValue !== false) {
node.type = 'word'
node.value = cssVarValue
delete node.before
delete node.after
delete node.nodes
} }
}) return false
if (cssVarValue !== false) { } else {
node.type = 'word' walk(node.nodes, n => {
node.value = cssVarValue if (n.type === 'word') {
delete node.before parseWord(n, opts)
delete node.after }
delete node.nodes })
} }
return false
} else {
walk(node.nodes, n => {
if (n.type === 'word') {
parseWord(n, opts)
}
})
} }
} })
}) .toString()
.toString()
}
const TAGS = [
'ad',
'audio',
'button',
'camera',
'canvas',
'checkbox',
'checkbox-group',
'cover-image',
'cover-view',
'form',
'functional-page-navigator',
'icon',
'image',
'input',
'label',
'live-player',
'live-pusher',
'map',
'movable-area',
'movable-view',
'navigator',
'official-account',
'open-data',
'picker',
'picker-view',
'picker-view-column',
'progress',
'radio',
'radio-group',
'rich-text',
'scroll-view',
'slider',
'swiper',
'swiper-item',
'switch',
'text',
'textarea',
'video',
'view',
'web-view'
]
const BG_PROPS = [
'background',
'background-clip',
'background-color',
'background-image',
'background-origin',
'background-position',
'background-repeat',
'background-size',
'background-attachment'
]
/**
* 转换 upx
* 转换 px
*/
module.exports = postcss.plugin('postcss-uniapp-plugin', function (opts) {
opts = {
...defaultOpts,
...opts
} }
return function (root, result) {
if (process.env.UNI_PLATFORM === 'h5') { const TAGS = [
// Transform CSS AST here 'ad',
'audio',
const bgDecls = [] 'button',
'camera',
root.walkRules(rule => { 'canvas',
let hasPage = false 'checkbox',
// Transform each rule here 'checkbox-group',
if (!isInsideKeyframes(rule)) { 'cover-image',
// rule.selectors == comma seperated selectors 'cover-view',
// a, b.c {} => ["a", "b.c"] 'form',
rule.selectors = rule.selectors.map(complexSelector => 'functional-page-navigator',
// complexSelector => simpleSelectors 'icon',
// "a.b#c" => ["a", ".b", "#c"] 'image',
transformSelector(complexSelector, simpleSelectors => 'input',
// only process type selector, leave alone class & id selectors 'label',
simpleSelectors.walkTags(tag => { 'live-player',
if (tag.value === 'page') { 'live-pusher',
tag.value = 'uni-page-body' 'map',
hasPage = true 'movable-area',
} else if (~TAGS.indexOf(tag.value) && tag.value.substring( 'movable-view',
0, 4) !== 'uni-') { 'navigator',
tag.value = 'uni-' + tag.value 'official-account',
} 'open-data',
}) 'picker',
'picker-view',
'picker-view-column',
'progress',
'radio',
'radio-group',
'rich-text',
'scroll-view',
'slider',
'swiper',
'swiper-item',
'switch',
'text',
'textarea',
'video',
'view',
'web-view'
]
const BG_PROPS = [
'background',
'background-clip',
'background-color',
'background-image',
'background-origin',
'background-position',
'background-repeat',
'background-size',
'background-attachment'
]
/**
* 转换 upx
* 转换 px
*/
module.exports = postcss.plugin('postcss-uniapp-plugin', function(opts) {
opts = {
...defaultOpts,
...opts
}
return function(root, result) {
if (process.env.UNI_PLATFORM === 'h5') {
// Transform CSS AST here
const bgDecls = []
root.walkRules(rule => {
let hasPage = false
// Transform each rule here
if (!isInsideKeyframes(rule)) {
// rule.selectors == comma seperated selectors
// a, b.c {} => ["a", "b.c"]
rule.selectors = rule.selectors.map(complexSelector =>
// complexSelector => simpleSelectors
// "a.b#c" => ["a", ".b", "#c"]
transformSelector(complexSelector, simpleSelectors =>
// only process type selector, leave alone class & id selectors
simpleSelectors.walkTags(tag => {
if (tag.value === 'page') {
tag.value = 'uni-page-body'
hasPage = true
} else if (~TAGS.indexOf(tag.value) && tag.value.substring(
0, 4) !== 'uni-') {
tag.value = 'uni-' + tag.value
}
})
)
) )
)
}
// handle upx unit
rule.walkDecls(decl => {
if (hasPage) {
if (BG_PROPS.indexOf(decl.prop) !== -1) {
bgDecls.push(decl.clone())
}
} }
// Transform each property declaration here // handle upx unit
decl.value = tranformValue(decl, opts) rule.walkDecls(decl => {
if (hasPage) {
if (BG_PROPS.indexOf(decl.prop) !== -1) {
bgDecls.push(decl.clone())
}
}
// Transform each property declaration here
decl.value = tranformValue(decl, opts)
})
}) })
})
if (bgDecls.length) { if (bgDecls.length) {
const rule = postcss.rule({ const rule = postcss.rule({
selector: 'body.?%PAGE?%' selector: 'body.?%PAGE?%'
})
bgDecls.forEach(decl => rule.append(decl))
root.append(rule)
}
} else {
root.walkRules(rule => {
const selectors = transformSelector(rule.selectors.join(','), function (selectors) {
selectors.walkUniversals(node => {
node.parent.remove()
}) })
}) bgDecls.forEach(decl => rule.append(decl))
if (!selectors) { root.append(rule)
return rule.remove()
} }
rule.selectors = selectors.split(',') } else {
root.walkRules(rule => {
// handle upx unit const selectors = transformSelector(rule.selectors.join(','), function(selectors) {
rule.walkDecls(decl => { selectors.walkUniversals(node => {
const raws = decl.raws node.parent.remove()
if (raws) { })
if (raws.before && raws.before.indexOf(';') !== -1) { })
raws.before = raws.before.replace(/;/g, '') if (!selectors) {
} return rule.remove()
if (raws.after && raws.after.indexOf(';') !== -1) {
raws.after = raws.after.replace(/;/g, '')
}
} }
// Transform each property declaration here rule.selectors = selectors.split(',')
decl.value = tranformValue(decl, opts)
}) // handle upx unit
rule.walkDecls(decl => {
rule.selectors = rule.selectors.map(complexSelector => { const raws = decl.raws
return transformSelector(complexSelector, simpleSelectors => { if (raws) {
return simpleSelectors.walkTags(tag => { if (raws.before && raws.before.indexOf(';') !== -1) {
const k = tag.value raws.before = raws.before.replace(/;/g, '')
const v = CSS_TAGS[k]
if (v) {
tag.value = v === 'r'
? `._${k}` : v
} }
if (raws.after && raws.after.indexOf(';') !== -1) {
raws.after = raws.after.replace(/;/g, '')
}
}
// Transform each property declaration here
decl.value = tranformValue(decl, opts)
})
rule.selectors = rule.selectors.map(complexSelector => {
return transformSelector(complexSelector, simpleSelectors => {
return simpleSelectors.walkTags(tag => {
const k = tag.value
const v = CSS_TAGS[k]
if (v) {
tag.value = v === 'r' ?
`._${k}` : v
}
})
}) })
}) })
}) })
}) }
} }
} })
}) }
const postcss = require('postcss')
const selectorParser = require('postcss-selector-parser')
const TAGS = Object.keys(require('@dcloudio/uni-cli-shared').tags)
const transformSelector = (complexSelector, transformer) => {
return selectorParser(transformer).processSync(complexSelector)
}
const isInsideKeyframes = function(rule) {
return (
rule.parent && rule.parent.type === 'atrule' && /^(-\w+-)?keyframes$/.test(rule.parent.name)
)
}
module.exports = postcss.plugin('postcss-uniapp-plugin', function(opts) {
return function(root, result) {
root.walkRules(rule => {
// Transform each rule here
if (!isInsideKeyframes(rule)) {
// rule.selectors == comma seperated selectors
// a, b.c {} => ["a", "b.c"]
rule.selectors = rule.selectors.map(complexSelector =>
// complexSelector => simpleSelectors
// "a.b#c" => ["a", ".b", "#c"]
transformSelector(complexSelector, simpleSelectors =>
// only process type selector, leave alone class & id selectors
simpleSelectors.walkTags(tag => {
if (tag.value === 'page') {
tag.value = 'body'
} else if (~TAGS.indexOf(tag.value) && tag.value.substring(
0, 4) !== 'uni-') {
tag.value = 'uni-' + tag.value
}
})
)
)
}
})
}
})
const fs = require('fs')
const path = require('path')
const hash = require('hash-sum')
const loaderUtils = require('loader-utils')
const parser = require('@babel/parser')
const {
parse
} = require(require.resolve('@vue/component-compiler-utils', {
paths: [require.resolve('vue-loader')]
})) // 确保使用的与 vue-loader 一致
const traverse = require('@dcloudio/webpack-uni-mp-loader/lib/babel/global-component-traverse')
const genStylesCode = require('../vue-loader/lib/codegen/styleInjection')
function parseComponents(content) {
const {
state: {
components
}
} = traverse(parser.parse(content, {
sourceType: 'module',
plugins: [
'typescript',
['decorators', {
decoratorsBeforeExport: true
}],
'classProperties'
]
}), {
components: []
})
return components
}
function getDefineComponents(components) {
return components.map(({
name,
source
}) => `Vue.component('${name}',require('${source}').default)`)
}
const appVueFilePath = path.resolve(process.env.UNI_INPUT_DIR, 'app.vue')
function getStylesCode(loaderContext) {
if (!fs.existsSync(appVueFilePath)) {
return
}
const source = fs.readFileSync(appVueFilePath, 'utf8')
const {
minimize,
sourceMap,
rootContext,
resourcePath,
resourceQuery
} = loaderContext
const options = loaderUtils.getOptions(loaderContext) || {}
const filename = path.basename(resourcePath)
const context = rootContext || process.cwd()
const sourceRoot = path.dirname(path.relative(context, resourcePath))
const descriptor = parse({
source,
compiler: options.compiler,
filename,
sourceRoot,
needMap: sourceMap
})
// styles
let stylesCode = ``
if (descriptor.styles.length) {
const isServer = false
const isShadow = false
const isProduction = options.productionMode || minimize || process.env.NODE_ENV === 'production'
const stringifyRequest = r => loaderUtils.stringifyRequest(loaderContext, r)
// module id for scoped CSS & hot-reload
const rawShortFilePath = path
.relative(context, resourcePath)
.replace(/^(\.\.[/\\])+/, '')
const shortFilePath = rawShortFilePath.replace(/\\/g, '/') + resourceQuery
const needsHotReload = false
const id = hash(
isProduction ?
(shortFilePath + '\n' + source) :
shortFilePath
)
stylesCode = genStylesCode(
loaderContext,
descriptor.styles,
id,
resourcePath,
stringifyRequest,
needsHotReload,
isServer || isShadow // needs explicit injection?
)
}
return stylesCode.replace(/main\.js/g,'App.vue')
}
module.exports = function(source, map) { module.exports = function(source, map) {
return `import 'uni-pages'; // 解析自定义组件,及 App 样式
return `import 'uni-pages'
${getStylesCode(this)}
${getDefineComponents(parseComponents(source)).join('\n')}
UniViewJSBridge.publishHandler('webviewReady') UniViewJSBridge.publishHandler('webviewReady')
` `
} }
...@@ -73,7 +73,7 @@ module.exports = function (content) { ...@@ -73,7 +73,7 @@ module.exports = function (content) {
let appConfigContent = '' let appConfigContent = ''
jsonFiles.forEach(jsonFile => { jsonFiles.forEach(jsonFile => {
if (jsonFile) { if (jsonFile) {
if (jsonFile.name === 'app-config.js') { if (jsonFile.name === 'define-pages.js') {
appConfigContent = jsonFile.content appConfigContent = jsonFile.content
} else { } else {
this.emitFile(jsonFile.name, jsonFile.content) this.emitFile(jsonFile.name, jsonFile.content)
......
function parseRoutes (config) {
const __uniRoutes = []
/* eslint-disable no-mixed-operators */
const tabBarList = (config.tabBar && config.tabBar.list || []).map(item => item.pagePath)
Object.keys(config.page).forEach(function (pagePath) {
const isTabBar = tabBarList.indexOf(pagePath) !== -1
const isQuit = isTabBar || (config.pages[0] === pagePath)
const isNVue = !!config.page[pagePath].nvue
const route = {
path: '/' + pagePath,
meta: {},
window: config.page[pagePath].window || {}
}
if (isQuit) {
route.meta.isQuit = true
}
if (isNVue) {
route.meta.isNVue = true
}
if (isTabBar) {
route.meta.isTabBar = true
}
__uniRoutes.push(route)
})
return __uniRoutes
}
module.exports = function definePages (appJson) {
const __uniRoutes = parseRoutes(appJson)
delete appJson.page
delete appJson.usingComponents
delete appJson.nvueCompiler
delete appJson.renderer
return {
name: 'app-config-service.js',
content: `
var isReady=false;var onReadyCallbacks=[];
var __uniConfig = ${JSON.stringify(appJson, null)};
var __uniRoutes = ${JSON.stringify(__uniRoutes)};
__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
service.register('uni-app-config', {create(id, env, config) {if(!__uniConfig.viewport){Object.assign(__uniConfig,{"viewport": env.weex.config.env.deviceWidth,"defaultFontSize": env.weex.config.env.deviceWidth / 20})}return {instance: {__uniConfig: __uniConfig,__uniRoutes: __uniRoutes}}}});
`
}
}
function generatePageCode (pages) {
return pages.map(pagePath => {
return `__registerPage('${pagePath}',function(){
const cacheKey = '${pagePath}-VueComponent'
if(!this[cacheKey]){
this[cacheKey] = Vue.extend(require('${pagePath}.vue').default)
}
return this[cacheKey]
})`
}).join('\n')
}
module.exports = function parseConfig (appJson) {
return {
name: 'app-config.js',
content: `
import Vue from 'vue'
__registerConfig(${JSON.stringify(appJson)},Vue)
${generatePageCode(appJson.pages)}
`
}
}
function generatePageCode (pages, pageOptions) {
return pages.map(pagePath => {
if (pageOptions[pagePath].nvue) {
return ''
}
return `__definePage('${pagePath}',function(){return Vue.extend(require('${pagePath}.vue').default)})`
}).join('\n')
}
module.exports = function definePages (appJson) {
return {
name: 'define-pages.js',
content: `
uni.registerPlus && uni.registerPlus(typeof plus !== 'undefined' && plus)
${generatePageCode(appJson.pages, appJson.page)}
`
}
}
...@@ -13,7 +13,8 @@ const { ...@@ -13,7 +13,8 @@ const {
parseStyle parseStyle
} = require('../../util') } = require('../../util')
const parseV3Config = require('./config-parser') const definePages = require('./define-pages')
const appConfigService = require('./app-config-service')
function parseConfig (appJson) { function parseConfig (appJson) {
return { return {
...@@ -316,11 +317,17 @@ module.exports = function (pagesJson, userManifestJson) { ...@@ -316,11 +317,17 @@ module.exports = function (pagesJson, userManifestJson) {
} }
if (!Object.keys(appJson.nvue.pages).find(path => { if (!Object.keys(appJson.nvue.pages).find(path => {
const subNVues = appJson.nvue.pages[path].window.subNVues || [] const subNVues = appJson.nvue.pages[path].window.subNVues || []
return path.replace(/\.html$/, '.nvue') === key || subNVues.find(({ path }) => path === key.replace(/\.nvue$/, '')) return path.replace(/\.html$/, '.nvue') === key || subNVues.find(({
}) && !pagesJson.pages.find(({ style = {} }) => { path
}) => path === key.replace(/\.nvue$/, ''))
}) && !pagesJson.pages.find(({
style = {}
}) => {
style = Object.assign(style, style['app-plus']) style = Object.assign(style, style['app-plus'])
const subNVues = style.subNVues || [] const subNVues = style.subNVues || []
return subNVues.find(({ path }) => path === key.replace(/\.nvue$/, '')) return subNVues.find(({
path
}) => path === key.replace(/\.nvue$/, ''))
})) { })) {
throw new Error(`原生混淆页面未在项目内使用,错误的页面路径:${key}`) throw new Error(`原生混淆页面未在项目内使用,错误的页面路径:${key}`)
} }
...@@ -404,7 +411,8 @@ module.exports = function (pagesJson, userManifestJson) { ...@@ -404,7 +411,8 @@ module.exports = function (pagesJson, userManifestJson) {
manifestJson.plus.launchwebview.id = '2' manifestJson.plus.launchwebview.id = '2'
} else { } else {
// 首页是 tabBar 页面 // 首页是 tabBar 页面
const item = tabBar.list.find(page => page.pagePath === (process.env.UNI_USING_NATIVE ? appJson.entryPagePath : entryPagePath)) const item = tabBar.list.find(page => page.pagePath === (process.env.UNI_USING_NATIVE ? appJson.entryPagePath
: entryPagePath))
if (item) { if (item) {
tabBar.child = ['lauchwebview'] tabBar.child = ['lauchwebview']
tabBar.selected = tabBar.list.indexOf(item) tabBar.selected = tabBar.list.indexOf(item)
...@@ -432,31 +440,54 @@ module.exports = function (pagesJson, userManifestJson) { ...@@ -432,31 +440,54 @@ module.exports = function (pagesJson, userManifestJson) {
delete appJson.subPackages delete appJson.subPackages
// TODO 处理纯原生
if (process.env.UNI_USING_NATIVE) { if (process.env.UNI_USING_NATIVE) {
manifest.name = 'manifest.json' manifest.name = 'manifest.json'
manifest.content = JSON.stringify(manifest.content) manifest.content = JSON.stringify(manifest.content)
return [manifest, parseConfig(appJson)] return [manifest, parseConfig(appJson)]
} }
if (process.env.UNI_USING_V3) {
appJson.entryPagePath = appJson.pages[0] if (process.env.UNI_USING_V3) { // v3
appJson.entryPagePath = appJson.pages[0]
// timeout // timeout
normalizeNetworkTimeout(appJson) normalizeNetworkTimeout(appJson)
appJson.page = Object.create(null) appJson.page = Object.create(null)
const addPage = function (pagePath, windowOptions) { const addPage = function (pagePath, windowOptions, nvue) {
delete windowOptions.usingComponents delete windowOptions.usingComponents
appJson.page[pagePath] = { appJson.page[pagePath] = {
window: windowOptions window: windowOptions,
nvue
} }
} }
parsePages(pagesJson, function (page) { parsePages(pagesJson, function (page) {
addPage(page.path, parseStyle(page.style)) addPage(page.path, parseStyle(page.style), !!page.nvue)
}, function (root, page) { }, function (root, page) {
addPage(normalizePath(path.join(root, page.path)), parseStyle(page.style, root)) addPage(normalizePath(path.join(root, page.path)), parseStyle(page.style, root), !!page.nvue)
}) })
// nvue 权限
manifestJson.permissions.UniNView = {
'description': 'UniNView原生渲染'
}
// TODO 需要考虑 condition
manifestJson.plus.launchwebview.id = '1' // 首页 id 固定 为 1
if (appJson.page[appJson.entryPagePath].nvue) { // 首页是 nvue
manifestJson.launch_path = '' // 首页地址为空
manifestJson.plus.launchwebview.uniNView = {
path: appJson.entryPagePath
}
if (manifestJson.plus.tabBar) {
manifestJson.plus.tabBar.child = ['lauchwebview']
}
} else {
manifestJson.plus.launch_path = '__uniappview.html' // 首页地址固定
}
manifest.name = 'manifest.json' manifest.name = 'manifest.json'
manifest.content = JSON.stringify(manifest.content) manifest.content = JSON.stringify(manifest.content)
return [manifest, parseV3Config(appJson)] delete appJson.nvue
return [manifest, definePages(appJson), appConfigService(appJson)]
} }
return [app, manifest] return [app, manifest]
} }
...@@ -21,6 +21,9 @@ export function unsubscribe (event, callback) { ...@@ -21,6 +21,9 @@ export function unsubscribe (event, callback) {
} }
export function subscribeHandler (event, args, pageId) { export function subscribeHandler (event, args, pageId) {
if (process.env.NODE_ENV !== 'production') {
console.log(`[subscribeHandler][${Date.now()}]:`, event, args, pageId)
}
return emit('view.' + event, args, pageId) return emit('view.' + event, args, pageId)
} }
......
...@@ -18,6 +18,9 @@ export function unsubscribe (event, callback) { ...@@ -18,6 +18,9 @@ export function unsubscribe (event, callback) {
} }
export function subscribeHandler (event, args, pageId) { export function subscribeHandler (event, args, pageId) {
if (process.env.NODE_ENV !== 'production') {
console.log(`[subscribeHandler][${Date.now()}]:`, event, args, pageId)
}
emit('service.' + event, args, pageId) emit('service.' + event, args, pageId)
} }
......
...@@ -76,7 +76,7 @@ export function processEvent (name, $event = {}, detail = {}, target = {}, curre ...@@ -76,7 +76,7 @@ export function processEvent (name, $event = {}, detail = {}, target = {}, curre
} }
// fixed mp-vue // fixed mp-vue
return wrapperMPEvent({ const ret = wrapperMPEvent({
type: detail.type || name, type: detail.type || name,
timeStamp: $event.timeStamp || 0, timeStamp: $event.timeStamp || 0,
detail: detail, detail: detail,
...@@ -84,10 +84,20 @@ export function processEvent (name, $event = {}, detail = {}, target = {}, curre ...@@ -84,10 +84,20 @@ export function processEvent (name, $event = {}, detail = {}, target = {}, curre
currentTarget: processTarget(currentTarget), currentTarget: processTarget(currentTarget),
// 只处理系统事件 // 只处理系统事件
touches: ($event instanceof Event || $event instanceof CustomEvent) ? processTouches($event.touches) : $event.touches, touches: ($event instanceof Event || $event instanceof CustomEvent) ? processTouches($event.touches) : $event.touches,
changedTouches: ($event instanceof Event || $event instanceof CustomEvent) ? processTouches($event.changedTouches) : $event.changedTouches, changedTouches: ($event instanceof Event || $event instanceof CustomEvent) ? processTouches($event.changedTouches)
preventDefault () { }, : $event.changedTouches,
stopPropagation () { } preventDefault () {},
stopPropagation () {}
}) })
if (__PLATFORM__ === 'app-plus') {
const nid = currentTarget.getAttribute('_i')
ret.options = {
nid
}
}
return ret
} }
const LONGPRESS_TIMEOUT = 350 const LONGPRESS_TIMEOUT = 350
...@@ -124,14 +134,14 @@ function touchstart (evt) { ...@@ -124,14 +134,14 @@ function touchstart (evt) {
startPageX = pageX startPageX = pageX
startPageY = pageY startPageY = pageY
longPressTimer = setTimeout(function () { longPressTimer = setTimeout(function () {
let customEvent = new CustomEvent('longpress', { let customEvent = new CustomEvent('longpress', {
bubbles: true, bubbles: true,
cancelable: true, cancelable: true,
target: evt.target, target: evt.target,
currentTarget: evt.currentTarget currentTarget: evt.currentTarget
}) })
customEvent.touches = evt.touches customEvent.touches = evt.touches
customEvent.changedTouches = evt.changedTouches customEvent.changedTouches = evt.changedTouches
evt.target.dispatchEvent(customEvent) evt.target.dispatchEvent(customEvent)
}, LONGPRESS_TIMEOUT) }, LONGPRESS_TIMEOUT)
...@@ -163,4 +173,4 @@ export function initEvents () { ...@@ -163,4 +173,4 @@ export function initEvents () {
window.addEventListener('touchmove', touchmove, passiveOptions) window.addEventListener('touchmove', touchmove, passiveOptions)
window.addEventListener('touchend', clearLongPressTimer, passiveOptions) window.addEventListener('touchend', clearLongPressTimer, passiveOptions)
window.addEventListener('touchcancel', clearLongPressTimer, passiveOptions) window.addEventListener('touchcancel', clearLongPressTimer, passiveOptions)
} }
export {
registerPage
}
from '../../app-plus/service/framework/page'
import {
cached
} from 'uni-shared'
const pageFactory = Object.create(null)
export function definePage (name, createPageVueComponent) {
pageFactory[name] = createPageVueComponent
}
export const getPageVueComponent = cached(function (pagePath) {
return pageFactory[pagePath]()
})
export function createPage (pagePath, pageId) {
if (!pageFactory[pagePath]) {
console.error(`${pagePath} not found`)
}
let startTime = Date.now()
const pageVm = new (getPageVueComponent(pagePath))({
mpType: 'page',
pageId,
pagePath
})
if (process.env.NODE_ENV !== 'production') {
console.log(`new ${pagePath}`, Date.now() - startTime)
}
return pageVm
}
...@@ -41,7 +41,8 @@ export * from './plugin/oauth' ...@@ -41,7 +41,8 @@ export * from './plugin/oauth'
export * from './plugin/payment' export * from './plugin/payment'
export * from './plugin/push' export * from './plugin/push'
export * from './plugin/require-native-plugin' export * from './plugin/require-native-plugin'
export * from './plugin/share' export * from './plugin/share'
export * from './plugin/register-plus'
export * from './route/navigate-back' export * from './route/navigate-back'
export * from './route/navigate-to' export * from './route/navigate-to'
......
export function registerPlus (newPlus) {
// 确保 plus 是 app-service 中的
if (plus !== newPlus) {
if (process.env.NODE_ENV !== 'production') {
console.log(`[registerPlus][${Date.now()}]`)
}
plus = newPlus
}
}
import { import {
ANI_CLOSE, ANI_CLOSE,
ANI_DURATION ANI_DURATION
} from './util' } from '../../constants'
import { import {
setStatusBarStyle setStatusBarStyle
...@@ -102,4 +102,4 @@ export function navigateBack ({ ...@@ -102,4 +102,4 @@ export function navigateBack ({
return { return {
errMsg: 'navigateBack:ok' errMsg: 'navigateBack:ok'
} }
} }
...@@ -17,20 +17,20 @@ import { ...@@ -17,20 +17,20 @@ import {
import tabBar from '../../../../app-plus/service/framework/tab-bar' import tabBar from '../../../../app-plus/service/framework/tab-bar'
export function reLaunch ({ import {
url navigate
}, callbackId) { } from '../../framework/navigator'
const urls = url.split('?')
const path = urls[0]
const query = parseQuery(urls[1] || '')
function _reLaunch ({
path,
query
}, callbackId) {
const pages = getCurrentPages(true).slice(0) const pages = getCurrentPages(true).slice(0)
const routeOptions = __uniRoutes.find(route => route.path === path) const routeOptions = __uniRoutes.find(route => route.path === path)
if (routeOptions.meta.isTabBar) { if (routeOptions.meta.isTabBar) {
tabBar.switchTab(url) tabBar.switchTab(path)
} }
showWebview( showWebview(
...@@ -55,3 +55,17 @@ export function reLaunch ({ ...@@ -55,3 +55,17 @@ export function reLaunch ({
setStatusBarStyle() setStatusBarStyle()
} }
export function reLaunch ({
url
}, callbackId) {
const urls = url.split('?')
const path = urls[0]
const query = parseQuery(urls[1] || '')
navigate(path, function () {
_reLaunch({
path,
query
}, callbackId)
})
}
...@@ -15,14 +15,14 @@ import { ...@@ -15,14 +15,14 @@ import {
registerPage registerPage
} from '../../framework/page' } from '../../framework/page'
export function redirectTo ({ import {
url navigate
}, callbackId) { } from '../../framework/navigator'
const urls = url.split('?')
const path = urls[0]
const query = parseQuery(urls[1] || '')
function _redirectTo ({
path,
query
}, callbackId) {
const pages = getCurrentPages() const pages = getCurrentPages()
const lastPage = pages[pages.length - 1] const lastPage = pages[pages.length - 1]
...@@ -46,3 +46,16 @@ export function redirectTo ({ ...@@ -46,3 +46,16 @@ export function redirectTo ({
setStatusBarStyle() setStatusBarStyle()
} }
export function redirectTo ({
url
}, callbackId) {
const urls = url.split('?')
const path = urls[0]
const query = parseQuery(urls[1] || '')
navigate(path, function () {
_redirectTo({
path,
query
}, callbackId)
})
}
import { import {
ANI_CLOSE, ANI_CLOSE,
ANI_DURATION, ANI_DURATION
} from '../../constants'
import {
showWebview showWebview
} from './util' } from './util'
...@@ -17,7 +20,7 @@ import { ...@@ -17,7 +20,7 @@ import {
navigate navigate
} from '../../framework/navigator' } from '../../framework/navigator'
import tabBar from '../../../../app-plus/service/framework/tab-bar' import tabBar from '../../framework/tab-bar'
function _switchTab ({ function _switchTab ({
path, path,
......
import { import {
navigateStack ANI_SHOW,
ANI_DURATION
} from '../../constants'
import {
navigateFinish
} from '../../framework/navigator' } from '../../framework/navigator'
export const ANI_DURATION = 300 export function showWebview (webview, animationType, animationDuration, showCallback, delay) {
const ANI_SHOW = 'pop-in' if (typeof delay === 'undefined') {
export const ANI_CLOSE = 'pop-out' delay = webview.nvue ? 0 : 100
}
if (typeof animationDuration === 'undefined') {
animationDuration = ANI_DURATION
} else {
animationDuration = parseInt(animationDuration)
}
export function showWebview (webview, animationType, animationDuration, showCallback, delay = 50) { if (process.env.NODE_ENV !== 'production') {
animationDuration = typeof animationDuration === 'undefined' ? ANI_DURATION : parseInt(animationDuration) console.log(`[show][${Date.now()}]`, delay)
}
setTimeout(() => { setTimeout(() => {
webview.show( webview.show(
animationType || ANI_SHOW, animationType || ANI_SHOW,
animationDuration || ANI_DURATION, animationDuration || ANI_DURATION,
() => { () => {
if (process.env.NODE_ENV !== 'production') {
console.log(`[show.callback][${Date.now()}]`)
}
showCallback && showCallback() showCallback && showCallback()
navigateStack(webview) navigateFinish(webview)
} }
) )
}, delay) }, delay)
......
export const ANI_SHOW = 'pop-in' export const ANI_SHOW = plus.os.name === 'Android' && parseInt(plus.os.version) < 6 ? 'slide-in-right' : 'pop-in'
export const ANI_DURATION = 300 export const ANI_DURATION = 300
export const TITLEBAR_HEIGHT = 44
export const VIEW_WEBVIEW_PATH = '__uniappview.html' export const ANI_CLOSE = 'pop-out'
export const TITLEBAR_HEIGHT = 44
export const VIEW_WEBVIEW_PATH = '_www/__uniappview.html'
...@@ -102,8 +102,6 @@ function initTabBar () { ...@@ -102,8 +102,6 @@ function initTabBar () {
__uniConfig.tabBar.selected = selected __uniConfig.tabBar.selected = selected
} }
__uniConfig.__ready__ = true
tabBar.init(__uniConfig.tabBar, (item, index) => { tabBar.init(__uniConfig.tabBar, (item, index) => {
uni.switchTab({ uni.switchTab({
url: '/' + item.pagePath, url: '/' + item.pagePath,
...@@ -142,5 +140,7 @@ export function registerApp (appVm) { ...@@ -142,5 +140,7 @@ export function registerApp (appVm) {
initAppLaunch(appVm) initAppLaunch(appVm)
__uniConfig.ready = true
process.env.NODE_ENV !== 'production' && perf('registerApp') process.env.NODE_ENV !== 'production' && perf('registerApp')
} }
import plugin from './plugins/index'
function parseRoutes (config) { function parseRoutes (config) {
__uniRoutes.length = 0 __uniRoutes.length = 0
/* eslint-disable no-mixed-operators */ /* eslint-disable no-mixed-operators */
...@@ -21,10 +19,7 @@ function parseRoutes (config) { ...@@ -21,10 +19,7 @@ function parseRoutes (config) {
}) })
} }
export function registerConfig (config, Vue) { export function registerConfig (config, Vue) {
if (__PLATFORM__ === 'app-plus') {
Vue.use(plugin)
}
Object.assign(__uniConfig, config) Object.assign(__uniConfig, config)
__uniConfig.viewport = '' __uniConfig.viewport = ''
......
...@@ -4,31 +4,62 @@ import { ...@@ -4,31 +4,62 @@ import {
registerWebviewReady registerWebviewReady
} from './webview' } from './webview'
export const navigatorStack = [] let todoNavigator = false
export function navigate (path, callback) { export function navigate (path, callback) {
let isReady = true if (__PLATFORM__ === 'app-plus') {
if (navigatorStack.length) { // 已存在路由跳转 if (todoNavigator) {
isReady = false return console.error(`已存在待跳转页面${todoNavigator.path},请不要连续多次跳转页面`)
} else { }
callback.nvue = __uniConfig.page[path.slice(1)].nvue // 设置 nvue 标记 // 未创建 preloadWebview 或 preloadWebview 已被使用
// 非 nvue 且 preloadWebview 未准备好 const waitPreloadWebview = !preloadWebview || (preloadWebview && preloadWebview.__uniapp_route)
if (!callback.nvue && (!preloadWebview || !preloadWebview.loaded)) { // 已创建未 loaded
isReady = false const waitPreloadWebviewReady = preloadWebview && !preloadWebview.loaded
if (waitPreloadWebview || waitPreloadWebviewReady) {
todoNavigator = {
path: path,
nvue: __uniRoutes.find(route => route.path === path).meta.isNVue,
navigate: callback
}
if (process.env.NODE_ENV !== 'production') {
console.log(`todoNavigator:${todoNavigator.path} ${waitPreloadWebview ? 'waitForCreate' : 'waitForReady'}`)
}
} else {
callback()
}
if (waitPreloadWebviewReady) {
registerWebviewReady(preloadWebview.id, todoNavigate)
} }
} }
isReady ? callback() : navigatorStack.push(callback)
} }
export function navigateStack (webview) { function todoNavigate () {
if (!navigatorStack.length) { if (!todoNavigator) {
return (!webview.nvue && createPreloadWebview()) return
}
const {
navigate
} = todoNavigator
if (process.env.NODE_ENV !== 'production') {
console.log(`todoNavigate:${todoNavigator.path}`)
} }
const navigate = navigatorStack.shift() todoNavigator = false
if (navigate.nvue) { return navigate()
navigate() }
} else {
export function navigateFinish () {
if (__PLATFORM__ === 'app-plus') {
// 创建预加载
const preloadWebview = createPreloadWebview() const preloadWebview = createPreloadWebview()
preloadWebview.loaded ? navigate() : registerWebviewReady(preloadWebview.id, navigate) if (!todoNavigator) {
return
}
if (todoNavigator.nvue) {
return todoNavigate()
}
preloadWebview.loaded
? todoNavigator.navigate()
: registerWebviewReady(preloadWebview.id, todoNavigate)
} }
} }
import { import {
setPreloadWebview,
initWebview, initWebview,
createWebview createWebview
} from './webview' } from './webview'
import { import {
navigateStack navigateFinish
} from './navigator' } from './navigator'
import { import {
perf PAGE_CREATE
} from './perf' } from '../../constants'
import tabBar from '../framework/tab-bar' import tabBar from '../framework/tab-bar'
import {
createPage
} from '../../page-factory'
const pages = [] const pages = []
export function getCurrentPages (returnAll) { export function getCurrentPages (returnAll) {
...@@ -22,24 +25,6 @@ export function getCurrentPages (returnAll) { ...@@ -22,24 +25,6 @@ export function getCurrentPages (returnAll) {
}) })
} }
const pageFactory = Object.create(null)
export function definePage (name, createPageVueComponent) {
pageFactory[name] = createPageVueComponent
}
export function createPage (name, options) {
if (!pageFactory[name]) {
console.error(`${name} not found`)
}
let startTime = Date.now()
const pageVm = new (pageFactory[name]())(options)
if (process.env.NODE_ENV !== 'production') {
perf(`new ${name}`, startTime)
}
return pageVm
}
/** /**
* 首页需要主动registerPage,二级页面路由跳转时registerPage * 首页需要主动registerPage,二级页面路由跳转时registerPage
*/ */
...@@ -62,11 +47,6 @@ export function registerPage ({ ...@@ -62,11 +47,6 @@ export function registerPage ({
webview = createWebview(path, routeOptions) webview = createWebview(path, routeOptions)
} else { } else {
webview = plus.webview.getWebviewById(webview.id) webview = plus.webview.getWebviewById(webview.id)
// renderer:native 时,把 launchWebview 标记 preloadWebview,及 loaded,方便路由跳转识别
if (__PLATFORM__ === 'app-plus-nvue') {
webview.loaded = true
setPreloadWebview(webview)
}
} }
if (routeOptions.meta.isTabBar) { if (routeOptions.meta.isTabBar) {
...@@ -116,19 +96,27 @@ export function registerPage ({ ...@@ -116,19 +96,27 @@ export function registerPage ({
// 首页是 nvue 时,在 registerPage 时,执行路由堆栈 // 首页是 nvue 时,在 registerPage 时,执行路由堆栈
if (webview.id === '1' && routeOptions.meta.isNVue) { if (webview.id === '1' && routeOptions.meta.isNVue) {
webview.nvue = true webview.nvue = true
setTimeout(function () { __uniConfig.onReady(function () {
navigateStack(webview) navigateFinish(webview)
}) })
} }
if (__PLATFORM__ === 'app-plus') { if (__PLATFORM__ === 'app-plus') {
if (!webview.nvue) { if (!webview.nvue) {
const pageId = webview.id
const pagePath = path.slice(1) const pagePath = path.slice(1)
pageInstance.$vm = createPage(pagePath, {
mpType: 'page', // 通知页面已开始创建
pageId: webview.id, UniServiceJSBridge.publishHandler('vdSync', {
pagePath data: [
}) [PAGE_CREATE, [pageId, pagePath]]
],
options: {
timestamp: Date.now()
}
}, [pageId])
pageInstance.$vm = createPage(pagePath, pageId)
pageInstance.$vm.$scope = pageInstance pageInstance.$vm.$scope = pageInstance
pageInstance.$vm.$mount() pageInstance.$vm.$mount()
} }
......
export function perf (type, startTime) { export function perf (type, startTime) {
/* eslint-disable no-undef */ /* eslint-disable no-undef */
startTime = startTime || __UniServiceStartTime__ startTime = startTime || __UniServiceStartTime__
console.log(`[PERF] ${type} 耗时[${Date.now() - startTime}]`) const endTime = Date.now()
console.log(`[PERF][${endTime}] ${type} 耗时[${Date.now() - startTime}]`)
} }
...@@ -24,7 +24,7 @@ export function initData (Vue) { ...@@ -24,7 +24,7 @@ export function initData (Vue) {
Vue.prototype._$setData = function setData (type, data) { Vue.prototype._$setData = function setData (type, data) {
this._$vd.push(type, this._$id, data) this._$vd.push(type, this._$id, data)
this.$nextTick(this._$vd.flush.bind(this._$vd)) this._$vd.initialized && this.$nextTick(this._$vd.flush.bind(this._$vd))
} }
Object.defineProperty(Vue.prototype, '_$vd', { Object.defineProperty(Vue.prototype, '_$vd', {
...@@ -61,6 +61,10 @@ export function initData (Vue) { ...@@ -61,6 +61,10 @@ export function initData (Vue) {
this._$data = JSON.parse(JSON.stringify(this._$newData)) this._$data = JSON.parse(JSON.stringify(this._$newData))
console.log(`[${this._$id}] mounted ` + Date.now()) console.log(`[${this._$id}] mounted ` + Date.now())
this._$setData(MOUNTED_DATA, diffData) this._$setData(MOUNTED_DATA, diffData)
if (this.mpType === 'page') {
// 页面 mounted 之后,第一次同步数据
this._$vd.flush()
}
}, },
beforeUpdate () { beforeUpdate () {
if (!this._$vd) { if (!this._$vd) {
......
import { import {
PAGE_CREATE,
PAGE_CREATED PAGE_CREATED
} from '../../../constants' } from '../../../constants'
...@@ -73,8 +72,7 @@ export class VDomSync { ...@@ -73,8 +72,7 @@ export class VDomSync {
flush () { flush () {
if (!this.initialized) { if (!this.initialized) {
this.initialized = true this.initialized = true
this.batchData.unshift([PAGE_CREATE, [this.pageId]]) this.batchData.push([PAGE_CREATED, [this.pageId, this.pagePath]])
this.batchData.push([PAGE_CREATED, [this.pagePath]])
} }
if (this.batchData.length) { if (this.batchData.length) {
UniServiceJSBridge.publishHandler('vdSync', { UniServiceJSBridge.publishHandler('vdSync', {
......
...@@ -21,6 +21,8 @@ function onWebviewReady (data, pageId) { ...@@ -21,6 +21,8 @@ function onWebviewReady (data, pageId) {
const isLaunchWebview = pageId === '1' const isLaunchWebview = pageId === '1'
if (isLaunchWebview) { // 首页 if (isLaunchWebview) { // 首页
setPreloadWebview(plus.webview.getLaunchWebview()) setPreloadWebview(plus.webview.getLaunchWebview())
} else if (!preloadWebview) { // preloadWebview 不存在,重新加载一下
setPreloadWebview(plus.webview.getWebviewById(pageId))
} }
if (preloadWebview.id !== pageId) { if (preloadWebview.id !== pageId) {
return console.error(`webview[${pageId}] not found`) return console.error(`webview[${pageId}] not found`)
...@@ -79,7 +81,7 @@ export function initSubscribeHandlers () { ...@@ -79,7 +81,7 @@ export function initSubscribeHandlers () {
registerPlusMessage('subscribeHandler', (data) => { registerPlusMessage('subscribeHandler', (data) => {
subscribeHandler(data.type, data.data, data.pageId) subscribeHandler(data.type, data.data, data.pageId)
}) })
// TODO 检测目标 preloadWebview 是否已准备好,因为 preloadWebview 准备好时,此处代码还没执行
subscribe(WEBVIEW_READY, onWebviewReady) subscribe(WEBVIEW_READY, onWebviewReady)
subscribe(WEBVIEW_UI_EVENT, onWebviewUIEvent) subscribe(WEBVIEW_UI_EVENT, onWebviewUIEvent)
} }
import { import {
getApp, uni
registerApp } from 'uni-core/service/uni'
} from './framework/app'
import { import {
getCurrentPages invokeCallbackHandler
} from './framework/page' } from 'uni-helpers/api'
import { import {
registerConfig publishHandler
} from './framework/config' } from 'uni-platform/service/publish-handler'
import { import {
uni definePage
} from 'uni-core/service/uni' } from '../page-factory'
import { import {
invokeCallbackHandler getApp,
} from 'uni-helpers/api' registerApp
} from './framework/app'
import { import {
registerPage registerPage,
} from 'uni-platform/service/register-page' getCurrentPages
} from './framework/page'
import { import vuePlugin from './framework/plugins'
publishHandler
} from 'uni-platform/service/publish-handler'
UniServiceJSBridge.publishHandler = publishHandler UniServiceJSBridge.publishHandler = publishHandler
UniServiceJSBridge.invokeCallbackHandler = invokeCallbackHandler UniServiceJSBridge.invokeCallbackHandler = invokeCallbackHandler
export default { export default {
__registerConfig: registerConfig, __vuePlugin: vuePlugin,
__definePage: definePage,
__registerApp: registerApp, __registerApp: registerApp,
__registerPage: registerPage, __registerPage: registerPage,
uni, uni,
......
export {
definePage as registerPage
}
from './framework/page'
...@@ -14,12 +14,16 @@ function plusReady (callback) { ...@@ -14,12 +14,16 @@ function plusReady (callback) {
export function publishHandler (event, args = {}) { export function publishHandler (event, args = {}) {
plusReady(function () { plusReady(function () {
const pageId = plus.webview.currentWebview().id
if (process.env.NODE_ENV !== 'production') {
console.log(`[VIEW][${Date.now()}]:`, event, args, pageId)
}
plus.webview.postMessageToUniNView({ plus.webview.postMessageToUniNView({
type: 'subscribeHandler', type: 'subscribeHandler',
args: { args: {
type: event, type: event,
data: args, data: args,
pageId: plus.webview.currentWebview().id pageId
} }
}, APP_SERVICE_ID) }, APP_SERVICE_ID)
}) })
......
const pageFactory = Object.create(null) const pages = []
export function getCurrentPages () {
export function definePage (name, createPageVueComponent) { return pages
pageFactory[name] = createPageVueComponent
}
export function createPage (name, options = {}) {
return new (pageFactory[name]())(options)
} }
export function setCurrentPage (pageId, pagePath) {
pages.length = 0
pages.push({
$page: {
id: pageId,
route: pagePath
}
})
}
...@@ -10,17 +10,25 @@ import { ...@@ -10,17 +10,25 @@ import {
} from './vdom-sync' } from './vdom-sync'
import { import {
createPage setCurrentPage
} from '../page' } from '../page'
import {
getPageVueComponent
} from '../../../page-factory'
let vd let vd
let PageVueComponent
const handleData = { const handleData = {
[PAGE_CREATE]: function onPageCreate (data) { [PAGE_CREATE]: function onPageCreate (data) {
const [pageId] = data const [pageId, pagePath] = data
// 设置当前页面伪对象,方便其他地方使用 getCurrentPages 获取当前页面 id,route
__uniConfig.id = pageId setCurrentPage(pageId, pagePath)
// 初始化当前页面 VueComponent(生成页面样式代码)
PageVueComponent = getPageVueComponent(pagePath)
// 生成当前页面 vd
vd = new VDomSync(pageId) vd = new VDomSync(pageId)
}, },
[MOUNTED_DATA]: function onMounted (data) { [MOUNTED_DATA]: function onMounted (data) {
...@@ -30,11 +38,11 @@ const handleData = { ...@@ -30,11 +38,11 @@ const handleData = {
vd.updateVData.apply(vd, data) vd.updateVData.apply(vd, data)
}, },
[PAGE_CREATED]: function onPageCreated (data) { [PAGE_CREATED]: function onPageCreated (data) {
const [pagePath] = data const [pageId, pagePath] = data
createPage(pagePath, { new PageVueComponent({
mpType: 'page', mpType: 'page',
pageId: vd.pageId, pageId,
pagePath: pagePath pagePath
}).$mount('#app') }).$mount('#app')
} }
} }
...@@ -66,8 +74,6 @@ export function initData (Vue) { ...@@ -66,8 +74,6 @@ export function initData (Vue) {
if (this._$vd) { if (this._$vd) {
this._$vd.initVm(this) this._$vd.initVm(this)
console.log(`[${this._$id}] beforeCreate ` + Date.now()) console.log(`[${this._$id}] beforeCreate ` + Date.now())
} else {
console.log(`[null] beforeCreate ` + Date.now())
} }
} }
}) })
......
...@@ -4,17 +4,23 @@ import { ...@@ -4,17 +4,23 @@ import {
export function initEvent (Vue) { export function initEvent (Vue) {
Vue.prototype.$handleViewEvent = function ($vueEvent, options) { Vue.prototype.$handleViewEvent = function ($vueEvent, options) {
const $event = this.$handleEvent($vueEvent)
const cid = this._$id const cid = this._$id
const nid = $vueEvent.currentTarget.getAttribute('_i')
const {
nid
} = $event.options
if (!nid) { if (!nid) {
return console.error(`[${cid}] nid not found`) return console.error(`[${cid}] nid not found`)
} }
const $event = this.$handleEvent($vueEvent)
// 移除无属性 // 移除无属性
delete $event._processed delete $event._processed
delete $event.mp delete $event.mp
delete $event.preventDefault delete $event.preventDefault
delete $event.stopPropagation delete $event.stopPropagation
delete $event.options
UniViewJSBridge.publishHandler(WEBVIEW_UI_EVENT, { UniViewJSBridge.publishHandler(WEBVIEW_UI_EVENT, {
data: $event, data: $event,
......
...@@ -8,10 +8,14 @@ import { ...@@ -8,10 +8,14 @@ import {
import { import {
initEvent initEvent
} from './event' } from './event'
export default { export default {
install (Vue, options) { install (Vue, options) {
if (process.env.NODE_ENV !== 'production') {
Vue.config.performance = true
}
initVue(Vue) initVue(Vue)
ViewPlugin.install(Vue, options) ViewPlugin.install(Vue, options)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册