Skip to content

Commit

Permalink
Dev (#589)
Browse files Browse the repository at this point in the history
* fix: 修复核心全量后已知 bugs (#546)

* fix: mip 链接使用默认行为 (#548)

* fix: Register built-in components when document interactive (#555)

* fix: Fix external resources protocol (#558)

* feat: 增强 templates 判断逻辑 (#567)

* 新增 mip 前端小流量机制 (#560)

* feat: 新增 mip 前端小流量机制

* 修改注释

* 调整变量名和 cookie 的失效时间

* fixed: 无需在 js 中指向 mip cahce url

* mip.js 沙盒对象增加 BMap\BMapLib (#577)

* 增加 sandbox 白名单

* 将 BMap 改成 readywrite

* 升级 mip.js 核心的 sandbox

* MIP 核心中移除 Vue 代码 (#357)
  • Loading branch information
PengXing committed Apr 29, 2019
1 parent e58afca commit f039162
Show file tree
Hide file tree
Showing 20 changed files with 451 additions and 315 deletions.
3 changes: 2 additions & 1 deletion packages/mip/build/alias.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ module.exports = {
sfc: resolve('src/vue/sfc'),
deps: resolve('deps'),
'script-loader!deps': resolve('deps'),
'script-loader!document-register-element': resolve('node_modules/document-register-element')
'script-loader!document-register-element': resolve('node_modules/document-register-element'),
'mip-vue': resolve('src/vue-custom-element/index.js')
}
8 changes: 7 additions & 1 deletion packages/mip/build/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@ const resolve = p => {
}

const builds = {
'mip-prod': {
'mip': {
entry: resolve('mip'),
dest: resolve('dist/mip.js'),
format: 'umd',
env: 'production',
banner: 'window._mipStartTiming=Date.now();'
},
'mip-vue': {
entry: resolve('mip-vue'),
dest: resolve('dist/mip-vue.js'),
format: 'iife',
env: 'production'
}
}

Expand Down
3 changes: 2 additions & 1 deletion packages/mip/build/webpack.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ module.exports = merge.smart(baseConfig, {
mode: 'development',
devtool: 'inline-source-map',
entry: {
mip: resolve('src/index.js')
mip: resolve('src/index.js'),
'mip-vue': resolve('src/vue-custom-element/index.js')
},
resolve: {
alias: {
Expand Down
25 changes: 18 additions & 7 deletions packages/mip/src/base-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {hasOwn} from './util'
import {customEmit} from './util/custom-event'
import dom from './util/dom/dom'
import css from './util/dom/css'
import {camelize} from './util/string'
import {camelize, capitalize} from './util/string'
import {parseSizeList} from './size-list'

/** @param {!Element} element */
Expand Down Expand Up @@ -104,7 +104,7 @@ class BaseElement extends HTMLElement {

this.propTypes = this.vueCompat.getPropTypes(this.name, CustomElementImpl)

this.defaultValues = this.vueCompat.getDefaultValues(this.name, CustomElementImpl)
this.defaultProps = this.vueCompat.getDefaultProps(this.name, CustomElementImpl)

this.customElement.props = {}

Expand All @@ -131,7 +131,18 @@ class BaseElement extends HTMLElement {

attributeChangedCallback (name, oldValue, newValue) {
const propName = camelize(name)
this.customElement.props[propName] = this.vueCompat.parseAttribute(newValue, this.propTypes[propName])

if (this.isBuilt() && hasOwn(this.propTypes, propName) && oldValue !== newValue) {
const prevProps = this.customElement.props[propName]
const nextProps = this.vueCompat.parseAttribute(newValue, this.propTypes[propName])
const handler = `handle${capitalize(propName)}Change`

this.customElement.props[propName] = nextProps
if (typeof this.customElement[handler] === 'function' &&
!(oldValue === null && nextProps === this.defaultProps[propName])) {
this.customElement[handler](prevProps, nextProps)
}
}
this.customElement.attributeChangedCallback(name, oldValue, newValue)
}

Expand Down Expand Up @@ -318,7 +329,7 @@ class BaseElement extends HTMLElement {
} catch (e) {
this.error = e
customEmit(this, 'build-error', e)
console.warn('build error:', e)
console.error(e)
}
}

Expand All @@ -332,18 +343,18 @@ class BaseElement extends HTMLElement {
*/
getProps () {
const propTypes = this.propTypes
const defaultValues = this.defaultValues
const defaultProps = this.defaultProps
const props = this.vueCompat.getProps(this, propTypes)
const names = Object.keys(propTypes)

for (let i = 0; i < names.length; i++) {
const name = names[i]

if (typeof props[name] !== 'undefined' || !hasOwn(defaultValues, name)) {
if (typeof props[name] !== 'undefined' || !hasOwn(defaultProps, name)) {
continue
}

const def = defaultValues[name]
const def = defaultProps[name]

props[name] = typeof def === 'function' ? def() : def
}
Expand Down
2 changes: 1 addition & 1 deletion packages/mip/src/components/mip-bind/watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import Deps from './deps'
import * as util from './util'

import {MAX_UPDATE_COUNT} from '../../vue/core/observer/scheduler'
const MAX_UPDATE_COUNT = 100

const queue = []
let has = {}
Expand Down
11 changes: 10 additions & 1 deletion packages/mip/src/custom-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,17 @@ class CustomElement {

/**
* Called when the MIPElement is first inserted into the document.
*
* @deprecated
*/
build () {
this.buildCallback()
}

/**
* Executes after the element attached to DOM.
*/
build () {}
buildCallback () {}

/**
* Requests the element to unload any expensive resources when the element
Expand Down
2 changes: 1 addition & 1 deletion packages/mip/src/mip1-polyfill/element.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ function createBaseElementProto () {
customEmit(this, 'build')
} catch (e) {
customEmit(this, 'build-error', e)
console.warn('build error:', e)
console.error(e)
}
}

Expand Down
146 changes: 132 additions & 14 deletions packages/mip/src/services/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,31 @@ import {templates, Deferred, event} from '../util'
import {whenDocumentInteractive} from '../util/dom/dom'
import registerMip1Element from '../mip1-polyfill/element'
import registerCustomElement from '../register-element'
import '../vue-custom-element'

const {listen} = event

const UNKNOWN_EXTENSION_ID = 'unknown'

// const LATEST_MIP_VERSION = '2'

/**
* Inserts a script element in `<head>` with specified url. Returns that script element.
*
* @param {string} url of script.
* @returns {!HTMLScriptElement}
* @private
*/
function insertScript (url) {
const script = document.createElement('script')

script.async = true
script.src = url

document.head.appendChild(script)

return script
}

export class Extensions {
constructor () {
/**
Expand Down Expand Up @@ -61,7 +80,8 @@ export class Extensions {
resolve: null,
reject: null,
loaded: null,
error: null
error: null,
script: null
}
}

Expand All @@ -78,6 +98,63 @@ export class Extensions {
return this.getExtensionHolder(this.currentExtensionId || UNKNOWN_EXTENSION_ID)
}

/**
* Returns the script url of extension.
*
* @param {string} extensionId of extension.
* @param {string=} version of extension.
* @returns {string}
* @private
*/
/*
getExtensionScriptUrl (extensionId, version = LATEST_MIP_VERSION) {
return `https://c.mipcdn.com/static/v${version}/${extensionId}/${extensionId}.js`
}
*/

/**
* Returns the script element of extension or null.
*
* @param {string} extensionId of extension.
* @param {string=} version of extension.
* @returns {?HTMLScriptElement}
* @private
*/
/*
findExtensionScript (extensionId, version = LATEST_MIP_VERSION) {
const holder = this.getExtensionHolder(extensionId)
if (holder.script) {
return holder.script
}
const url = this.getExtensionScriptUrl(extensionId, version)
holder.script = document.querySelector(`script[src="${url}"]`)
return holder.script
}
*/

/**
* Appends the extension script in `<head>` if there's no existing script element of extension.
*
* @param {string} extensionId of extension.
* @param {string=} version of extension.
* @private
*/
/*
insertExtensionScriptIfNeeded (extensionId, version = LATEST_MIP_VERSION) {
const holder = this.getExtensionHolder(extensionId)
if (holder.loaded || holder.error || this.findExtensionScript(extensionId, version)) {
return
}
holder.script = insertScript(this.getExtensionScriptUrl(extensionId, version))
}
*/

/**
* Returns or creates a promise waiting for extension loaded.
*
Expand Down Expand Up @@ -124,6 +201,8 @@ export class Extensions {
*/
/*
preloadExtension (extensionId) {
this.insertExtensionScriptIfNeeded(extensionId)
return this.waitForExtension(extensionId)
}
/*
Expand Down Expand Up @@ -166,6 +245,32 @@ export class Extensions {
this.currentExtensionId = extensionId
factory(...args)

/**
* This extension needs `mip-vue` service.
*/
if (
document.documentElement.hasAttribute('mip-vue') &&
!Services.getServiceOrNull('mip-vue')
) {
/**
* Inserts script of `mip-vue` service if needed.
*/
if (!document.querySelector('script[src*="mip-vue.js"]')) {
const baseUrl = document.querySelector('script[src*="mip.js"]').src.replace(/\/[^/]+$/, '')

insertScript(`${baseUrl}/mip-vue.js`)
}

/**
* Interrupts current registration.
* Reregisters this extension while `mip-vue` service is loaded.
*/
Services.getServicePromise('mip-vue')
.then(() => this.registerExtension(extensionId, factory, ...args))

return
}

/**
* It still possible that all element instances in current extension call lifecycle `build` synchronously.
* Executes callback in microtask to make sure all these elements are built.
Expand Down Expand Up @@ -230,16 +335,20 @@ export class Extensions {
* If `element.version === '1'`, then it will fallback to the registration of MIP1 elements.
*
* @param {!Object} element contains implementation, css and version.
* @returns {!function(string, !Function | !Object, string)}
* @returns {?function(string, !Function | !Object, string):?HTMLElement[]}
* @private
*/
getElementRegistrator (element) {
if (element.version && element.version.split('.')[0] === '1') {
return registerMip1Element
if (typeof element.implementation === 'object') {
const vue = Services.getServiceOrNull('mip-vue')

document.documentElement.setAttribute('mip-vue', '')

return vue && vue.registerElement
}

if (typeof element.implementation === 'object') {
return Services.getService('mip-vue').registerElement
if (element.version && element.version.split('.')[0] === '1') {
return registerMip1Element
}

return registerCustomElement
Expand All @@ -262,30 +371,39 @@ export class Extensions {
element.version = version
}

holder.extension.elements[name] = element
if (!holder.extension.elements[name]) {
holder.extension.elements[name] = element
}

/** @type {HTMLElement[]} */
let elementInstances = this.getElementRegistrator(element)(name, implementation, css)
const registrator = this.getElementRegistrator(element)

if (!registrator) {
return
}

/** @type {?HTMLElement[]} */
let elementInstances = registrator(name, implementation, css)

if (elementInstances && elementInstances.length) {
holder.elementInstances = holder.elementInstances.concat(elementInstances)
for (let i = 0, len = elementInstances.length; i < len; i++) {
let el = elementInstances[i]

// Delay to last processing extension resolve.
if (el.isBuilt()) {
continue
}

// It can't catch error of customElements.define with try/catch.
// @see https://github.com/w3c/webcomponents/issues/547
/**
* Errors occurred in `customElements.define` cannot be caught.
* @see {@link https://github.com/w3c/webcomponents/issues/547}
*/
if (el.error) {
this.tryToRejectError(holder, el.error)
break
}

/**
* Lifecycle `build` of element instances is probably delayed with `setTimeout`.
* Lifecycle `build` of element instances are probably delayed with `setTimeout`.
* If they are not, these event listeners would not be registered before they emit events.
*/
let unlistenBuild = listen(el, 'build', () => {
Expand Down
Loading

0 comments on commit f039162

Please sign in to comment.