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

feat(runtime): createApp,createPage,createComponent and Vue@2.6.8

上级 ccf4ebdb
......@@ -22,6 +22,7 @@ const PLATFORMS = {
}
const platform = PLATFORMS[process.env.UNI_PLATFORM]
module.exports = {
input: 'src/core/runtime/index.js',
output: {
......@@ -37,5 +38,6 @@ module.exports = {
__GLOBAL__: platform.prefix,
__PLATFORM_TITLE__: platform.title
})
]
],
external: ['vue']
}
......@@ -7,7 +7,7 @@ const pkgPath = resolve('package.json')
const webpackConfig = require('./webpack.config.js')
module.exports = {
baseUrl: '/',
publicPath: '/',
outputDir: resolve('./packages/uni-' + process.env.UNI_PLATFORM + '/dist'),
lintOnSave: true, // or error
runtimeCompiler: false,
......
{
"name": "uniapp-js-framework",
"version": "0.0.1",
"scripts": {
"lint": "eslint --fix --config package.json --ext .js --ext .vue --ignore-path .eslintignore build src",
"dev:h5": "npm run lint && cross-env NODE_ENV=production UNI_WATCH=true UNI_PLATFORM=h5 node build/build.js",
"build:h5": "npm run lint && cross-env NODE_ENV=production UNI_WATCH=false UNI_PLATFORM=h5 node build/build.js",
"build:mp-weixin": "npm run lint && cross-env UNI_PLATFORM=mp-weixin rollup -c build/rollup.config.js",
"build:mp-baidu": "npm run lint && cross-env UNI_PLATFORM=mp-baidu rollup -c build/rollup.config.js",
"build:mp-alipay": "npm run lint && cross-env UNI_PLATFORM=mp-alipay rollup -c build/rollup.config.js",
"build:mp-toutiao": "npm run lint && cross-env UNI_PLATFORM=mp-toutiao rollup -c build/rollup.config.js",
"test:unit": "cross-env NODE_ENV=test UNI_PLATFORM=h5 mocha-webpack --require tests/unit/setup.js --webpack-config build/webpack.config.test.js tests/unit/**/*.spec.js"
},
"dependencies": {},
"private": true,
"devDependencies": {
"@vue/cli-plugin-babel": "^3.1.1",
"@vue/cli-plugin-eslint": "^3.1.4",
"@vue/cli-plugin-unit-mocha": "^3.1.1",
"@vue/cli-service": "^3.1.2",
"@vue/test-utils": "^1.0.0-beta.25",
"babel-eslint": "^10.0.1",
"babylon": "^6.18.0",
"chai": "^4.1.2",
"cross-env": "^5.2.0",
"eslint": "^5.5.0",
"eslint-config-standard": "^12.0.0",
"eslint-loader": "^2.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^4.0.0",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^4.7.1",
"jsdom": "^13.0.0",
"jsdom-global": "^3.0.2",
"rollup": "^0.67.4",
"rollup-plugin-alias": "^1.4.0",
"rollup-plugin-replace": "^2.1.0",
"strip-json-comments": "^2.0.1",
"vue": "^2.5.17",
"vue-router": "^3.0.1",
"vue-template-compiler": "^2.5.17",
"webpack": "^4.18.0",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-virtual-modules": "^0.1.10"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/recommended",
"standard"
],
"globals": {
"getApp": true,
"getCurrentPages": true,
"plus": true,
"uni": true,
"Vue": true,
"wx": true,
"my": true,
"swan": true,
"__uniConfig": true,
"__uniRoutes": true,
"UniViewJSBridge": true,
"UniServiceJSBridge": true,
"__PLATFORM__": true,
"__VERSION__": true,
"__GLOBAL__": true,
"__PLATFORM_TITLE__": true
},
"rules": {
"no-tabs": 0,
"standard/no-callback-literal": 0
},
"parserOptions": {
"parser": "babel-eslint"
}
},
"browserslist": [
"last 3 versions",
"Android >= 4.1",
"ios >= 8"
],
"license": "Apache-2.0",
"main": "index.js",
"description": "",
"author": ""
"name": "uniapp-js-framework",
"version": "0.0.1",
"scripts": {
"lint": "eslint --fix --config package.json --ext .js --ext .vue --ignore-path .eslintignore build src",
"dev:h5": "npm run lint && cross-env NODE_ENV=production UNI_WATCH=true UNI_PLATFORM=h5 node build/build.js",
"build:h5": "npm run lint && cross-env NODE_ENV=production UNI_WATCH=false UNI_PLATFORM=h5 node build/build.js",
"build:mp-weixin": "npm run lint && cross-env UNI_PLATFORM=mp-weixin rollup -c build/rollup.config.js",
"build:mp-baidu": "npm run lint && cross-env UNI_PLATFORM=mp-baidu rollup -c build/rollup.config.js",
"build:mp-alipay": "npm run lint && cross-env UNI_PLATFORM=mp-alipay rollup -c build/rollup.config.js",
"build:mp-toutiao": "npm run lint && cross-env UNI_PLATFORM=mp-toutiao rollup -c build/rollup.config.js",
"test:unit": "cross-env NODE_ENV=test UNI_PLATFORM=h5 mocha-webpack --require tests/unit/setup.js --webpack-config build/webpack.config.test.js tests/unit/**/*.spec.js"
},
"dependencies": {},
"private": true,
"devDependencies": {
"@vue/cli-plugin-babel": "^3.4.1",
"@vue/cli-plugin-eslint": "^3.4.1",
"@vue/cli-plugin-unit-mocha": "^3.4.1",
"@vue/cli-service": "^3.4.1",
"@vue/test-utils": "^1.0.0-beta.25",
"babel-eslint": "^10.0.1",
"babylon": "^6.18.0",
"browserslist": "^4.4.2",
"caniuse-lite": "^1.0.30000940",
"chai": "^4.1.2",
"cross-env": "^5.2.0",
"eslint": "^5.5.0",
"eslint-config-standard": "^12.0.0",
"eslint-loader": "^2.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^4.0.0",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^4.7.1",
"jsdom": "^13.0.0",
"jsdom-global": "^3.0.2",
"rollup": "^0.67.4",
"rollup-plugin-alias": "^1.4.0",
"rollup-plugin-replace": "^2.1.0",
"strip-json-comments": "^2.0.1",
"vue": "^2.6.8",
"vue-router": "^3.0.1",
"vue-template-compiler": "^2.6.8",
"webpack": "^4.18.0",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-virtual-modules": "^0.1.10"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/recommended",
"standard"
],
"globals": {
"App": true,
"Page": true,
"Component": true,
"getApp": true,
"getCurrentPages": true,
"plus": true,
"uni": true,
"Vue": true,
"wx": true,
"my": true,
"swan": true,
"__uniConfig": true,
"__uniRoutes": true,
"UniViewJSBridge": true,
"UniServiceJSBridge": true,
"__PLATFORM__": true,
"__VERSION__": true,
"__GLOBAL__": true,
"__PLATFORM_TITLE__": true
},
"rules": {
"no-tabs": 0,
"standard/no-callback-literal": 0
},
"parserOptions": {
"parser": "babel-eslint"
}
},
"browserslist": [
"last 3 versions",
"Android >= 4.1",
"ios >= 8"
],
"license": "Apache-2.0",
"main": "index.js",
"description": "",
"author": ""
}
此差异已折叠。
import Vue from 'vue';
const _toString = Object.prototype.toString;
const hasOwnProperty = Object.prototype.hasOwnProperty;
......@@ -15,7 +17,9 @@ function isPlainObject (obj) {
function hasOwn (obj, key) {
return hasOwnProperty.call(obj, key)
}
}
function noop () {}
const SYNC_API_RE = /hideKeyboard|upx2px|canIUse|^create|Sync$|Manager$/;
......@@ -264,6 +268,301 @@ var api = /*#__PURE__*/Object.freeze({
});
function initHooks (mpOptions, hooks) {
hooks.forEach(hook => {
mpOptions[hook] = function (args) {
this.$vm.__call_hook(hook, args);
};
});
}
function getData (data) {
if (typeof data === 'function') {
try {
return data()
} catch (e) {
console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。');
}
return {}
}
return data || {}
}
const PROP_TYPES = [String, Number, Boolean, Object, Array, null];
function getProperties (props) {
const properties = {};
if (Array.isArray(props)) { // ['title']
props.forEach(key => {
properties[key] = null;
});
} else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String}
Object.keys(props).forEach(key => {
const opts = props[key];
if (isPlainObject(opts)) { // title:{type:String,default:''}
let value = opts['default'];
if (isFn(value)) {
value = value();
}
properties[key] = {
type: PROP_TYPES.includes(opts.type) ? opts.type : null,
value
};
} else { // content:String
properties[key] = PROP_TYPES.includes(opts) ? opts : null;
}
});
}
return properties
}
function wrapper$1 (event) {
event.stopPropagation = noop;
event.preventDefault = noop;
event.target = event.target || {};
event.detail = event.detail || {};
// TODO 又得兼容 mpvue 的 mp 对象
event.mp = event;
event.target = Object.assign({}, event.target, event.detail);
return event
}
function processEventArgs (event, args = [], isCustom) {
if (isCustom && !args.length) { // 无参数,直接传入 detail 数组
return event.detail
}
const ret = [];
args.forEach(arg => {
if (arg === '$event') {
ret.push(isCustom ? event.detail[0] : event);
} else {
ret.push(arg);
}
});
return ret
}
const ONCE = '~';
const CUSTOM = '^';
function handleEvent (event) {
event = wrapper$1(event);
// [['tap',[['handle',[1,2,a]],['handle1',[1,2,a]]]]]
const eventOpts = (event.currentTarget || event.target).dataset.eventOpts;
if (!eventOpts) {
return console.warn(`事件信息不存在`)
}
// [['handle',[1,2,a]],['handle1',[1,2,a]]]
const eventType = event.type;
eventOpts.forEach(eventOpt => {
let type = eventOpt[0];
const eventsArray = eventOpt[1];
const isCustom = type.charAt(0) === CUSTOM;
type = isCustom ? type.slice(1) : type;
const isOnce = type.charAt(0) === ONCE;
type = isOnce ? type.slice(1) : type;
if (eventsArray && eventType === type) {
eventsArray.forEach(eventArray => {
const handler = this.$vm[eventArray[0]];
if (isOnce) {
if (handler.once) {
return
}
handler.once = true;
}
handler.apply(this.$vm, processEventArgs(event, eventArray[1], isCustom));
});
}
});
}
function handleLink (event) {
event.detail.$parent = this.$vm;
}
function initRefs (vm) {
const mpInstance = vm.$mp[vm.mpType];
Object.defineProperty(vm, '$refs', {
get () {
const $refs = Object.create(null);
const components = mpInstance.selectAllComponents('.__ref__');
components.forEach(component => {
const id = component.id;
$refs[id] = component.$vm;
});
const forComponents = mpInstance.selectAllComponents('.__ref-in-for__');
forComponents.forEach(component => {
const id = component.id;
if (!$refs[id]) {
$refs[id] = [];
}
$refs[id].push(component.$vm);
});
return $refs
}
});
}
const hooks = [
'onShow',
'onHide',
'onError',
'onPageNotFound'
];
function createApp (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
const appOptions = {
onLaunch (args) {
this.$vm = new Vue(vueOptions);
this.$vm.mpType = 'app';
this.$vm.$mp = {
app: this
};
this.$vm.$mount();
this.$vm.__call_hook('onLaunch', args);
}
};
initHooks(appOptions, hooks);
App(appOptions);
return vueOptions
}
const hooks$1 = [
'onShow',
'onHide',
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap',
'onBackPress',
'onNavigationBarButtonTap',
'onNavigationBarSearchInputChanged',
'onNavigationBarSearchInputConfirmed',
'onNavigationBarSearchInputClicked'
];
function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
const pageOptions = {
data: getData(vueOptions.data),
onLoad (args) {
this.$vm = new Vue(vueOptions);
this.$vm.mpType = 'page';
this.$vm.$mp = {
data: {},
page: this
};
initRefs(this.$vm);
this.$vm.$mount();
this.$vm.__call_hook('onLoad', args);
},
onReady () {
this.$vm._isMounted = true;
this.$vm.__call_hook('onReady');
},
onUnload () {
this.$vm.__call_hook('onUnload');
this.$vm.$destroy();
},
__e: handleEvent,
__l: handleLink
};
initHooks(pageOptions, hooks$1);
return Page(pageOptions)
}
function createComponent (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
const properties = getProperties(vueOptions.props);
const VueComponent = Vue.extend(vueOptions);
const componentOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions.data),
properties,
lifetimes: {
attached () {
// props的处理,一个是直接 与 mp 的 properties 对接,另一个是要做成 reactive,且排除掉 render watch
const options = {
propsData: this.properties,
$component: this
};
// 初始化 vue 实例
this.$vm = new VueComponent(options);
this.$vm.mpType = 'component';
this.$vm.$mp = {
data: {},
component: this
};
initRefs(this.$vm);
// 初始化渲染数据
this.$vm.$mount();
},
ready () {
this.triggerEvent('__l', this.$vm);
const eventId = this.dataset.eventId;
if (eventId) {
const listeners = this.$vm.$parent.$mp.listeners;
if (listeners) {
const listenerOpts = listeners[eventId];
Object.keys(listenerOpts).forEach(eventType => {
listenerOpts[eventType].forEach(handler => {
this.$vm[handler.once ? '$once' : '$on'](eventType, handler);
});
});
}
}
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
},
detached () {
this.$vm.$destroy();
}
},
pageLifetimes: {
show (args) {
this.$vm.__call_hook('onPageShow', args);
},
hide () {
this.$vm.__call_hook('onPageHide');
},
resize (size) {
this.$vm.__call_hook('onPageResize', size);
}
},
methods: {
__e: handleEvent,
__l: handleLink
}
};
return Component(componentOptions)
}
let uni = {};
if (typeof Proxy !== 'undefined') {
......@@ -312,3 +611,4 @@ if (typeof Proxy !== 'undefined') {
var uni$1 = uni;
export default uni$1;
export { createApp, createPage, createComponent };
......@@ -65,4 +65,8 @@ if (typeof Proxy !== 'undefined') {
})
}
export * from './wrapper/create-app'
export * from './wrapper/create-page'
export * from './wrapper/create-component'
export default uni
import Vue from 'vue'
import {
initHooks
} from './util'
const hooks = [
'onShow',
'onHide',
'onError',
'onPageNotFound'
]
export function createApp (vueOptions) {
vueOptions = vueOptions.default || vueOptions
const appOptions = {
onLaunch (args) {
this.$vm = new Vue(vueOptions)
this.$vm.mpType = 'app'
this.$vm.$mp = {
app: this
}
this.$vm.$mount()
this.$vm.__call_hook('onLaunch', args)
}
}
initHooks(appOptions, hooks)
App(appOptions)
return vueOptions
}
import Vue from 'vue'
import {
getData,
initRefs,
initMethods,
handleLink,
handleEvent,
getProperties
} from './util'
export function createComponent (vueOptions) {
vueOptions = vueOptions.default || vueOptions
const properties = getProperties(vueOptions.props)
const VueComponent = Vue.extend(vueOptions)
const componentOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions.data),
properties,
lifetimes: {
attached () {
// props的处理,一个是直接 与 mp 的 properties 对接,另一个是要做成 reactive,且排除掉 render watch
const options = {
propsData: this.properties,
$component: this
}
// 初始化 vue 实例
this.$vm = new VueComponent(options)
this.$vm.mpType = 'component'
this.$vm.$mp = {
data: {},
component: this
}
initRefs(this.$vm)
// 初始化渲染数据
this.$vm.$mount()
},
ready () {
this.triggerEvent('__l', this.$vm)
const eventId = this.dataset.eventId
if (eventId) {
const listeners = this.$vm.$parent.$mp.listeners
if (listeners) {
const listenerOpts = listeners[eventId]
Object.keys(listenerOpts).forEach(eventType => {
listenerOpts[eventType].forEach(handler => {
this.$vm[handler.once ? '$once' : '$on'](eventType, handler)
})
})
}
}
this.$vm._isMounted = true
this.$vm.__call_hook('mounted')
},
detached () {
this.$vm.$destroy()
}
},
pageLifetimes: {
show (args) {
this.$vm.__call_hook('onPageShow', args)
},
hide () {
this.$vm.__call_hook('onPageHide')
},
resize (size) {
this.$vm.__call_hook('onPageResize', size)
}
},
methods: {
__e: handleEvent,
__l: handleLink
}
}
initMethods(componentOptions.methods, vueOptions)
return Component(componentOptions)
}
import Vue from 'vue'
import {
getData,
initRefs,
initHooks,
initMethods,
handleLink,
handleEvent
} from './util'
const hooks = [
'onShow',
'onHide',
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap',
'onBackPress',
'onNavigationBarButtonTap',
'onNavigationBarSearchInputChanged',
'onNavigationBarSearchInputConfirmed',
'onNavigationBarSearchInputClicked'
]
export function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions
const pageOptions = {
data: getData(vueOptions.data),
onLoad (args) {
this.$vm = new Vue(vueOptions)
this.$vm.mpType = 'page'
this.$vm.$mp = {
data: {},
page: this
}
initRefs(this.$vm)
this.$vm.$mount()
this.$vm.__call_hook('onLoad', args)
},
onReady () {
this.$vm._isMounted = true
this.$vm.__call_hook('onReady')
},
onUnload () {
this.$vm.__call_hook('onUnload')
this.$vm.$destroy()
},
__e: handleEvent,
__l: handleLink
}
initHooks(pageOptions, hooks)
initMethods(pageOptions, vueOptions)
return Page(pageOptions)
}
import {
isFn,
noop,
isPlainObject
} from 'uni-shared'
export function initHooks (mpOptions, hooks) {
hooks.forEach(hook => {
mpOptions[hook] = function (args) {
this.$vm.__call_hook(hook, args)
}
})
}
export function initMethods (mpOptions, vueOptions) {
// if (vueOptions.methods) {
// Object.assign(mpOptions, vueOptions.methods)
// }
}
export function getData (data) {
if (typeof data === 'function') {
try {
return data()
} catch (e) {
console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。')
}
return {}
}
return data || {}
}
const PROP_TYPES = [String, Number, Boolean, Object, Array, null]
export function getProperties (props) {
const properties = {}
if (Array.isArray(props)) { // ['title']
props.forEach(key => {
properties[key] = null
})
} else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String}
Object.keys(props).forEach(key => {
const opts = props[key]
if (isPlainObject(opts)) { // title:{type:String,default:''}
let value = opts['default']
if (isFn(value)) {
value = value()
}
properties[key] = {
type: PROP_TYPES.includes(opts.type) ? opts.type : null,
value
}
} else { // content:String
properties[key] = PROP_TYPES.includes(opts) ? opts : null
}
})
}
return properties
}
function wrapper (event) {
event.stopPropagation = noop
event.preventDefault = noop
event.target = event.target || {}
event.detail = event.detail || {}
// TODO 又得兼容 mpvue 的 mp 对象
event.mp = event
event.target = Object.assign({}, event.target, event.detail)
return event
}
function processEventArgs (event, args = [], isCustom) {
if (isCustom && !args.length) { // 无参数,直接传入 detail 数组
return event.detail
}
const ret = []
args.forEach(arg => {
if (arg === '$event') {
ret.push(isCustom ? event.detail[0] : event)
} else {
ret.push(arg)
}
})
return ret
}
const ONCE = '~'
const CUSTOM = '^'
export function handleEvent (event) {
event = wrapper(event)
// [['tap',[['handle',[1,2,a]],['handle1',[1,2,a]]]]]
const eventOpts = (event.currentTarget || event.target).dataset.eventOpts
if (!eventOpts) {
return console.warn(`事件信息不存在`)
}
// [['handle',[1,2,a]],['handle1',[1,2,a]]]
const eventType = event.type
eventOpts.forEach(eventOpt => {
let type = eventOpt[0]
const eventsArray = eventOpt[1]
const isCustom = type.charAt(0) === CUSTOM
type = isCustom ? type.slice(1) : type
const isOnce = type.charAt(0) === ONCE
type = isOnce ? type.slice(1) : type
if (eventsArray && eventType === type) {
eventsArray.forEach(eventArray => {
const handler = this.$vm[eventArray[0]]
if (isOnce) {
if (handler.once) {
return
}
handler.once = true
}
handler.apply(this.$vm, processEventArgs(event, eventArray[1], isCustom))
})
}
})
}
export function handleLink (event) {
event.detail.$parent = this.$vm
}
export function initRefs (vm) {
const mpInstance = vm.$mp[vm.mpType]
Object.defineProperty(vm, '$refs', {
get () {
const $refs = Object.create(null)
const components = mpInstance.selectAllComponents('.__ref__')
components.forEach(component => {
const id = component.id
$refs[id] = component.$vm
})
const forComponents = mpInstance.selectAllComponents('.__ref-in-for__')
forComponents.forEach(component => {
const id = component.id
if (!$refs[id]) {
$refs[id] = []
}
$refs[id].push(component.$vm)
})
return $refs
}
})
}
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册