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

wip(app): nvue

上级 2bb3a826
......@@ -9,5 +9,6 @@ declare namespace NodeJS {
UNI_CLI_CONTEXT: string
UNI_COMPILER_VERSION: string
UNI_HBUILDERX_PLUGINS: string
UNI_NVUE_COMPILER: 'uni-app' | 'weex'
}
}
export const EVENT_BACKBUTTON = 'backbutton'
export function backbuttonListener() {
uni.navigateBack({
from: 'backbutton',
} as UniApp.NavigateBackOptions)
uni
.navigateBack({
from: 'backbutton',
} as UniApp.NavigateBackOptions)
.catch(() => {})
}
......@@ -64,7 +64,6 @@ export function registerPage({
initWebview(webview, path, query, routeOptions.meta)
const route = path.substr(1)
;(webview as any).__uniapp_route = route
const pageInstance = initPageInternalInstance(
......
......@@ -23,6 +23,7 @@
"@babel/core": "^7.14.8",
"@babel/preset-env": "^7.14.8",
"babel-loader": "^8.2.2",
"de-indent": "^1.0.2",
"terser-webpack-plugin": "^5.1.4",
"vue-loader": "^15.9.6",
"webpack": "^5.45.1",
......@@ -30,6 +31,7 @@
"weex-template-compiler": "^2.5.16-weex.1"
},
"devDependencies": {
"@types/terser-webpack-plugin": "^5.0.4"
"@types/terser-webpack-plugin": "^5.0.4",
"vue-template-compiler": "^2.6.14"
}
}
/**
* 注意:该包的依赖包含了 lib 中的 vue-loader,weex-styler,weex-template-compiler
* 注意:该包的依赖包含了 lib 中的 weex-template-compiler 依赖的 de-indent
*/
export * from './webpack'
import path from 'path'
export function resolveLib(filepath: string) {
return path.resolve(__dirname, '../lib', filepath)
}
import moduleAlias from 'module-alias'
import { resolveLib } from '../utils'
const MODULES = [
'weex-styler',
'weex-template-compiler',
'@vue/component-compiler-utils',
'@vue/component-compiler-utils/package.json',
]
export function initModuleAlias() {
MODULES.forEach((name) => moduleAlias.addAlias(name, resolveLib(name)))
}
import { Configuration } from 'webpack'
import { optimization } from './optimization'
import { output } from './output'
import { module } from './module'
import { plugins } from './plugins'
import { createOptimization } from './optimization'
import { createOutput } from './output'
import { createModule } from './module'
import { createPlugins } from './plugins'
export function createConfig(
mode: 'production' | 'development'
): Configuration {
......@@ -16,9 +16,9 @@ export function createConfig(
externals: {
vue: 'Vue',
},
optimization,
output,
module,
plugins,
optimization: createOptimization(),
output: createOutput(),
module: createModule(),
plugins: createPlugins(),
}
}
import { Configuration } from 'webpack'
import { rules } from './rules'
import { createRules } from './rules'
export const module: Configuration['module'] = {
rules,
export function createModule(): Configuration['module'] {
return {
rules: createRules(),
}
}
import { RuleSetRule } from 'webpack'
export const babelLoader: RuleSetRule = {
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
babelrc: false,
export function createBabelLoader(): RuleSetRule {
return {
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
babelrc: false,
},
},
},
],
],
}
}
import { RuleSetRule } from 'webpack'
import { babelLoader } from './babelLoader'
import { vueLoader } from './vueLoader'
export const rules: RuleSetRule[] = [vueLoader, babelLoader]
import { createBabelLoader } from './babelLoader'
import { createVueLoader } from './vueLoader'
export function createRules(): RuleSetRule[] {
return [createVueLoader(), createBabelLoader()]
}
import { RuleSetRule } from 'webpack'
export const vueLoader: RuleSetRule = {
test: [/\.nvue(\?[^?]+)?$/, /\.vue(\?[^?]+)?$/],
use: [
{
loader: 'vue-loader',
options: {
hotReload: false,
compiler: require('../../../../../lib/weex-template-compiler'),
},
},
],
}
import { CompilerOptions } from 'vue-template-compiler'
import { createModules } from './modules'
export function createCompilerOptions(): CompilerOptions {
return {
modules: createModules(),
}
}
import { ModuleOptions } from 'vue-template-compiler'
export function createAssetUrlModule() {
return {} as ModuleOptions
}
import { ModuleOptions } from 'vue-template-compiler'
export function createBoolAttrModule() {
return {} as ModuleOptions
}
import { ASTElement, ModuleOptions } from 'vue-template-compiler'
import { isBuiltInComponent } from '@dcloudio/uni-shared'
export function createEasycomModule() {
return {
preTransformNode(el: ASTElement, options: any) {
if (isBuiltInComponent(el.tag) && el.tag !== 'App') {
// 挂在 isUnaryTag 上边,可以保证外部访问到
;(
options.isUnaryTag.autoComponents ||
(options.isUnaryTag.autoComponents = new Set())
).add(el.tag)
}
},
} as ModuleOptions
}
import { ModuleOptions } from 'vue-template-compiler'
import { createAssetUrlModule } from './assetUrl'
import { createBoolAttrModule } from './boolAttr'
import { createEasycomModule } from './easycom'
import { createRenderWholeModule } from './renderWhole'
import { createTagsModule } from './tags'
export function createModules(): ModuleOptions[] {
// 先处理 easycom
const modules = [createEasycomModule(), createRenderWholeModule()]
if (process.env.UNI_NVUE_COMPILER === 'uni-app') {
modules.push(createTagsModule())
}
modules.push(createAssetUrlModule())
modules.push(createBoolAttrModule())
return modules
}
import { ModuleOptions } from 'vue-template-compiler'
// render-whole => append="tree"
export function createRenderWholeModule() {
return {
preTransformNode(el) {
if (!Object.hasOwnProperty.call(el.attrsMap, 'append')) {
const name = 'render-whole'
const value = el.attrsMap[name]
if (value === true || value === 'true') {
// remove
delete el.attrsMap.append
const index = el.attrsList.findIndex((item) => item.name === name)
const attr = el.attrsList[index]
el.attrsList.splice(index, 1)
el.appendAsTree = true
el.attrsMap.append = 'tree'
el.attrsList.push({
name: 'append',
value: 'tree',
bool: false,
start: (attr as any).start,
end: (attr as any).end,
} as any)
}
}
return el
},
} as ModuleOptions
}
import { ASTNode, ASTElement, ModuleOptions } from 'vue-template-compiler'
export function createTagsModule() {
return {
postTransformNode(el) {
rewriteText(el)
rewriteTag(el)
rewriteEvents(el)
rewriteVideo(el)
},
} as ModuleOptions
}
function rewriteText(el: ASTElement) {
const tag = el.tag
if (tag === 'text' || tag === 'u-text' || tag === 'button') {
return
}
const children = el.children
children.forEach((child, index) => {
if (child.text) {
children.splice(index, 1, {
type: 1,
tag: 'u-text',
attrsList: [],
attrsMap: {},
rawAttrsMap: {},
parent: el,
children: [child],
plain: true,
} as ASTElement)
}
})
}
const TAGS = [
'text',
'image',
'input',
'textarea',
'video',
'web-view',
// 'switch',
'slider',
]
function rewriteTag(el: ASTElement) {
if (TAGS.includes(el.tag)) {
el.tag = 'u-' + el.tag
} else if (el.tag === 'match-media') {
el.tag = 'uni-match-media'
}
}
const deprecated = {
events: {
tap: 'click',
},
}
type DeprecatedEventType = keyof typeof deprecated['events']
function rewriteEvents(el: ASTElement) {
if (!el.events) {
return
}
const { events: eventsMap } = deprecated
Object.keys(el.events).forEach((name) => {
// 过时事件类型转换
const eventType = eventsMap[name as DeprecatedEventType]
if (eventType) {
if (!(name === 'tap' && el.tag === 'map')) {
// map 的 tap 事件不做转换
el.events![eventType] = el.events![name]
delete el.events![name]
}
}
})
}
function rewriteVideo(el: ASTElement) {
if (el.tag !== 'u-video') {
return
}
if (!Array.isArray(el.children)) {
return
}
if (!el.children.length) {
return
}
if ((el.children[0] as ASTElement).tag === 'u-scalable') {
return
}
el.children = [
{
type: 1,
tag: 'u-scalable',
attrsList: [],
attrsMap: {
style: 'position: absolute;left: 0;right: 0;top: 0;bottom: 0;',
},
rawAttrsMap: {
style: {
name: 'style',
value: 'position: absolute;left: 0;right: 0;top: 0;bottom: 0;',
},
},
parent: el,
plain: false,
staticStyle:
'{position:"absolute",left:"0",right:"0",top:"0",bottom:"0"}',
children: el.children,
} as ASTNode,
]
}
import { matchEasycom } from '@dcloudio/uni-cli-shared'
export function generateEasycomCode(names: string[]) {
const components: string[] = []
resolveEasycom(names).forEach(({ name, source }) => {
// 统一转换为驼峰命名
name = name.replace(/-(\w)/g, (_, str) => str.toUpperCase())
components.push(`'${name}': require('${source}').default`)
})
if (process.env.NODE_ENV === 'production') {
return `var components = {${components.join(',')}}`
}
return `var components;
try{
components = {${components.join(',')}}
}catch(e){
if(e.message.indexOf('Cannot find module') !== -1 && e.message.indexOf('.vue') !== -1){
console.error(e.message)
console.error('1. 排查组件名称拼写是否正确')
console.error('2. 排查组件是否符合 easycom 规范,文档:https://uniapp.dcloud.net.cn/collocation/pages?id=easycom')
console.error('3. 若组件不符合 easycom 规范,需手动引入,并在 components 中注册该组件')
} else {
throw e
}
}`
}
function resolveEasycom(names: string[]) {
return names.reduce<{ name: string; source: string }[]>((coms, name) => {
const source = matchEasycom(name)
if (source) {
coms.push({
name,
source,
})
}
return coms
}, [])
}
import { RuleSetRule } from 'webpack'
import { VueLoaderOptions } from 'vue-loader'
import { makeMap } from '@vue/shared'
import type { CompilerOptions, CompiledResult } from 'vue-template-compiler'
import { initEasycomsOnce } from '@dcloudio/uni-cli-shared'
import { createCompilerOptions } from './compilerOptions'
import { resolveLib } from '../../../../../utils'
import { generateEasycomCode } from './easycom'
export function createVueLoader(): RuleSetRule {
initEasycomsOnce(process.env.UNI_INPUT_DIR, process.env.UNI_PLATFORM)
return {
test: [/\.nvue(\?[^?]+)?$/, /\.vue(\?[^?]+)?$/],
use: [
{
loader: resolveLib('vue-loader'),
options: {
hotReload: false,
compiler: createCompiler(),
compilerOptions: createCompilerOptions(),
} as VueLoaderOptions,
},
],
}
}
const isUnaryTag = makeMap(
'image,area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' +
'link,meta,param,source,track,wbr'
)
type CompileFunction = (
template: string,
options?: CompilerOptions
) => CompiledResult<string>
function compileTemplate(
source: string,
options: CompilerOptions,
compile: CompileFunction
) {
const res = compile(source, options)
;(res as any).components = generateEasycomCode([
...((options as any).isUnaryTag.autoComponents || []),
])
return res
}
function createCompiler() {
const compiler = require(resolveLib('weex-template-compiler'))
const oldCompile = compiler.compile
compiler.compile = function (source: string, options: CompilerOptions = {}) {
;(options as any).isUnaryTag = isUnaryTag
// 将 autoComponents 挂在 isUnaryTag 上边
;(options as any).isUnaryTag.autoComponents = new Set()
options.preserveWhitespace = false
return compileTemplate(source, options, oldCompile)
}
return compiler
}
import TerserPlugin from 'terser-webpack-plugin'
import { Configuration } from 'webpack'
export const optimization: Configuration['optimization'] = {
nodeEnv: false, // 禁用,由 define 统一设置
minimizer: [
new TerserPlugin({
terserOptions: {
output: {
ascii_only: true,
export function createOptimization(): Configuration['optimization'] {
return {
nodeEnv: false, // 禁用,由 define 统一设置
minimizer: [
new TerserPlugin({
terserOptions: {
output: {
ascii_only: true,
},
},
},
}),
],
}),
],
}
}
import { Configuration } from 'webpack'
export const output: Configuration['output'] = {
path: process.env.UNI_OUTPUT_DIR,
filename: '[name].js',
export function createOutput(): Configuration['output'] {
return {
path: process.env.UNI_OUTPUT_DIR,
filename: '[name].js',
}
}
import { BannerPlugin } from 'webpack'
export const banner = new BannerPlugin({
banner: '"use weex:vue";',
raw: true,
exclude: 'Vue',
})
export function createBannerPlugin() {
return new BannerPlugin({
banner: '"use weex:vue";',
raw: true,
exclude: 'Vue',
})
}
import { DefinePlugin } from 'webpack'
import { extend } from '@vue/shared'
import { initDefine } from '@dcloudio/uni-cli-shared'
export const define = new DefinePlugin(
extend(
{
'process.env.UNI_CLOUD_PROVIDER': process.env.UNI_CLOUD_PROVIDER,
'process.env.HBX_USER_TOKEN': JSON.stringify(
process.env.HBX_USER_TOKEN || ''
),
},
initDefine()
export function createDefinePlugin() {
return new DefinePlugin(
extend(
{
'process.env.UNI_CLOUD_PROVIDER': process.env.UNI_CLOUD_PROVIDER,
'process.env.HBX_USER_TOKEN': JSON.stringify(
process.env.HBX_USER_TOKEN || ''
),
},
initDefine()
)
)
)
}
import { Configuration } from 'webpack'
import { define } from './define'
import { banner } from './banner'
import { provide } from './provide'
import { vueLoaderPlugin } from './vueLoader'
import { createDefinePlugin } from './define'
import { createBannerPlugin } from './banner'
import { createProvidePlugin } from './provide'
import { createVueLoaderPlugin } from './vueLoader'
export const plugins: Configuration['plugins'] = [
define,
banner,
provide,
vueLoaderPlugin,
]
export function createPlugins(): Configuration['plugins'] {
return [
createDefinePlugin(),
createBannerPlugin(),
createProvidePlugin(),
createVueLoaderPlugin(),
]
}
import path from 'path'
import { ProvidePlugin } from 'webpack'
import { initProvide } from '@dcloudio/uni-cli-shared'
import { resolveLib } from '../../../utils'
const libDir = path.resolve(__dirname, '../../../../lib')
const definitions: Record<string, string | string[]> = {
uniCloud: [
require.resolve('@dcloudio/uni-cloud/dist/uni-cloud.es.js'),
'default',
],
'uni.getCurrentSubNVue': [
path.join(libDir, 'get-current-sub-nvue.js'),
'default',
],
'uni.requireNativePlugin': [
path.join(libDir, 'require-native-plugin.js'),
'default',
],
...initProvide(),
export function createProvidePlugin() {
return new ProvidePlugin({
uniCloud: [
require.resolve('@dcloudio/uni-cloud/dist/uni-cloud.es.js'),
'default',
],
'uni.getCurrentSubNVue': [resolveLib('get-current-sub-nvue.js'), 'default'],
'uni.requireNativePlugin': [
resolveLib('require-native-plugin.js'),
'default',
],
...initProvide(),
})
}
export const provide = new ProvidePlugin(definitions)
const { VueLoaderPlugin } = require('../../../../lib/vue-loader')
export const vueLoaderPlugin = new VueLoaderPlugin()
export function createVueLoaderPlugin() {
const { VueLoaderPlugin } = require('../../../../lib/vue-loader')
return new VueLoaderPlugin()
}
import webpack from 'webpack'
import { once } from '@dcloudio/uni-shared'
import { createConfig } from './config'
import { initModuleAlias } from './alias'
const initModuleAliasOnce = once(initModuleAlias)
function runWebpack(mode: 'production' | 'development') {
initModuleAliasOnce()
return new Promise((resolve, reject) => {
webpack(createConfig(mode), (err, stats) => {
if (err) {
......
......@@ -18,7 +18,6 @@ export type EventCallback = Function
E.prototype = {
on: function (name: EventName, callback: EventCallback, ctx?: any) {
var e = this.e || (this.e = {})
;(e[name] || (e[name] = [])).push({
fn: callback,
ctx: ctx,
......
......@@ -38,6 +38,6 @@ export function initBridge(
},
subscribeHandler(event: string, args: unknown, pageId?: number): void {
emitter.emit(`${subscribeNamespace}.${event}`, args, pageId)
}
},
}
}
......@@ -24,7 +24,7 @@ function onInvokeServiceMethod(
{
id,
name,
args
args,
}: {
id: number
name: string
......
......@@ -23,7 +23,7 @@ export function subscribeViewMethod(pageId: number) {
*/
export function unsubscribeViewMethod(pageId: number) {
UniViewJSBridge.unsubscribe(normalizeViewMethodName(pageId, INVOKE_VIEW_API))
Object.keys(viewMethods).forEach(name => {
Object.keys(viewMethods).forEach((name) => {
if (name.indexOf(pageId + '.') === 0) {
delete viewMethods[name]
}
......@@ -50,7 +50,7 @@ function onInvokeViewMethod(
{
id,
name,
args
args,
}: {
id: number
name: string
......
......@@ -6,10 +6,21 @@ import { createServer, createSSRServer } from './server'
import { initEnv } from './utils'
async function runNVue(mode: 'prod' | 'dev') {
let hasCliNVue = false
try {
if (require.resolve('@dcloudio/uni-cli-nvue')) {
hasCliNVue = true
}
} catch (e) {}
if (!hasCliNVue) {
return
}
let nvue
try {
nvue = require('@dcloudio/uni-cli-nvue')
} catch (e) {}
} catch (e) {
console.error(e)
}
if (!nvue) {
return
}
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册