.'
29 | )
30 | }
31 | }
32 | el.styleBinding = el.styleBinding
33 | ? el.styleBinding.substring(0, el.styleBinding.length - 1) + ',' + JSON.stringify(parseStyleText(staticStyle)).substring(1)
34 | : JSON.stringify(parseStyleText(staticStyle))
35 | }
36 | }
37 |
38 | function genData (el) {
39 | let data = ''
40 | if (el.styleBinding) {
41 | data += `style:(${el.styleBinding}),`
42 | }
43 | return data
44 | }
45 |
46 | export default {
47 | staticKeys: ['staticStyle'],
48 | transformNode,
49 | genData
50 | }
51 |
--------------------------------------------------------------------------------
/packages/lone-logic/component/index.js:
--------------------------------------------------------------------------------
1 | import { initOptions, sendInitCommandToPageComponent, callHook } from './helper'
2 | import initData from './state'
3 | import events, { initEvents } from './events'
4 | import router from './router'
5 | import { notifyPropsObserver } from './observer'
6 | import { looseEqual } from 'lone-util'
7 |
8 | const init = Symbol('lone-logic:init')
9 |
10 | @events
11 | @router
12 | class LogicComponent {
13 | constructor (id, options) {
14 | const vm = this
15 | vm._id = id
16 | vm[init](options)
17 | }
18 |
19 | [init] (options) {
20 | const vm = this
21 | vm._events = Object.create(null)
22 | vm._inited = false
23 | vm.$options = initOptions(options)
24 | vm._slave = vm.$options.slave
25 | initEvents(vm)
26 | callHook(vm, 'beforeCreate')
27 | initData(vm)
28 | sendInitCommandToPageComponent(vm)
29 | callHook(vm, 'created')
30 | callHook(vm, 'onLoad', vm.$options.query)
31 | callHook(vm, 'onShow')
32 | }
33 |
34 | setData (data) {
35 | const oldData = this.data
36 | this.data = Object.assign({}, oldData, data)
37 | if (looseEqual(oldData, this.data)) return
38 | notifyPropsObserver(this, oldData, this.data)
39 | if (!this._inited) return
40 | this._slave.send('component:data', this._id, this.data)
41 | }
42 |
43 | $destroy () {
44 | const vm = this
45 | if (vm._isBeingDestroyed) return
46 | callHook(vm, 'beforeDestroy')
47 | vm._isBeingDestroyed = true
48 | vm.data = Object.create(null)
49 | vm.$off()
50 | vm._slave.send('component:destroy', this._id)
51 | }
52 | }
53 |
54 | export default LogicComponent
55 |
--------------------------------------------------------------------------------
/packages/lone-compiler-core/create-compiler.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { extend } from 'lone-util'
4 | import { detectErrors } from './error-detector'
5 | import { createCompileToFunctionFn } from './to-function'
6 |
7 | export function createCompilerCreator (baseCompile) {
8 | return function createCompiler (baseOptions) {
9 | function compile (template, options) {
10 | const finalOptions = Object.create(baseOptions)
11 | const errors = []
12 | const tips = []
13 | finalOptions.warn = (msg, tip) => {
14 | (tip ? tips : errors).push(msg)
15 | }
16 |
17 | if (options) {
18 | // merge custom modules
19 | if (options.modules) {
20 | finalOptions.modules =
21 | (baseOptions.modules || []).concat(options.modules)
22 | }
23 | // merge custom directives
24 | if (options.directives) {
25 | finalOptions.directives = extend(
26 | Object.create(baseOptions.directives),
27 | options.directives
28 | )
29 | }
30 | // copy other options
31 | for (const key in options) {
32 | if (key !== 'modules' && key !== 'directives') {
33 | finalOptions[key] = options[key]
34 | }
35 | }
36 | }
37 |
38 | const compiled = baseCompile(template, finalOptions)
39 | if (process.env.NODE_ENV !== 'production') {
40 | errors.push.apply(errors, detectErrors(compiled.ast))
41 | }
42 | compiled.errors = errors
43 | compiled.tips = tips
44 | return compiled
45 | }
46 |
47 | return {
48 | compile,
49 | compileToFunctions: createCompileToFunctionFn(compile)
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/packages/lone-util/web/attrs.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { makeMap } from '../index'
4 |
5 | // these are reserved for web because they are directly compiled away
6 | // during template compilation
7 | export const isReservedAttr = makeMap('style,class')
8 |
9 | // attributes that should be using props for binding
10 | const acceptValue = makeMap('input,textarea,option,select,progress')
11 | export const mustUseProp = (tag, type, attr) => {
12 | return (
13 | // eslint-disable-next-line no-mixed-operators
14 | (attr === 'value' && acceptValue(tag)) && type !== 'button' ||
15 | (attr === 'selected' && tag === 'option') ||
16 | (attr === 'checked' && tag === 'input') ||
17 | (attr === 'muted' && tag === 'video')
18 | )
19 | }
20 |
21 | export const isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck')
22 |
23 | export const isBooleanAttr = makeMap(
24 | 'allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' +
25 | 'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' +
26 | 'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' +
27 | 'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' +
28 | 'required,reversed,scoped,seamless,selected,sortable,translate,' +
29 | 'truespeed,typemustmatch,visible'
30 | )
31 |
32 | export const xlinkNS = 'http://www.w3.org/1999/xlink'
33 |
34 | export const isXlink = (name) => {
35 | return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink'
36 | }
37 |
38 | export const getXlinkProp = (name) => {
39 | return isXlink(name) ? name.slice(6, name.length) : ''
40 | }
41 |
42 | export const isFalsyAttrValue = (val) => {
43 | return val == null || val === false
44 | }
45 |
--------------------------------------------------------------------------------
/packages/lone-ui/schedule.js:
--------------------------------------------------------------------------------
1 | import { Master } from 'lone-messenger'
2 |
3 | class Schedule {
4 | constructor ({ router, entry, mid }) {
5 | const vm = this
6 | vm.router = router
7 | vm.entry = entry
8 | this.master = new Master({
9 | mid: mid,
10 | env: 'worker',
11 | worker: new Worker(vm.entry.logic)
12 | })
13 | vm.logicEvents = {
14 | 'logic:inited': function () {
15 | // Default Route Page
16 | vm.router.navigateTo(vm.router.routes[0].path)
17 | },
18 | 'logic:navigateTo': function (channel, { url }) {
19 | return vm.router.navigateTo(url)
20 | },
21 | 'logic:redirectTo': function (channel, { url }) {
22 | return vm.router.redirectTo(url)
23 | },
24 | 'logic:navigateBack': function (channel, { delta }) {
25 | return vm.router.navigateBack(delta)
26 | }
27 | }
28 | vm.pageEvents = {
29 | 'page:navigateTo': function () {
30 | console.log('ui-schedule: view:navigateTo')
31 | }
32 | }
33 | vm.init()
34 | vm.listenVisibilityChange(vm.router)
35 | }
36 |
37 | init () {
38 | this.listenEvents(this.master, this.logicEvents)
39 | this.listenEvents(this.master, this.pageEvents)
40 | }
41 |
42 | listenEvents (messenger, events) {
43 | for (const [event, fn] of Object.entries(events)) {
44 | messenger.onmessage(event, fn)
45 | }
46 | }
47 |
48 | listenVisibilityChange (router) {
49 | document.addEventListener('visibilitychange', function () {
50 | if (document.visibilityState === 'visible') {
51 | router.triggerCurrentPageShowHook()
52 | } else {
53 | router.triggerCurrentPageHideHook()
54 | }
55 | })
56 | }
57 | }
58 |
59 | export default Schedule
60 |
--------------------------------------------------------------------------------
/doc/lone-api/list.md:
--------------------------------------------------------------------------------
1 | # 列表渲染
2 |
3 | ## 用 v-for 把一个数组对应为一组元素
4 |
5 | 我们可以用 `v-for` 指令基于一个数组来渲染一个列表。`v-for` 指令需要使用 `item in items` 形式的特殊语法,其中 `items` 是源数据数组,而 `item` 则是被迭代的数组元素的别名。
6 |
7 | ```html
8 |
9 | -
10 | {{ item.message }}
11 |
12 |
13 | ```
14 |
15 | ```javascript
16 | var example1 = new Vue({
17 | el: '#example-1',
18 | data: {
19 | items: [
20 | { message: 'Foo' },
21 | { message: 'Bar' }
22 | ]
23 | }
24 | })
25 | ```
26 | 结果:
27 |
28 | ```html
29 |
33 | ```
34 |
35 | `v-for` 还支持一个可选的第二个参数,即当前项的索引。
36 |
37 | ```html
38 |
39 | -
40 | {{ index }} - {{ item.message }}
41 |
42 |
43 | ```
44 |
45 | 你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法:
46 |
47 | ```html
48 |
49 | ```
50 |
51 | ## 在 v-for 里使用对象
52 |
53 | 你也可以用 v-for 来遍历一个对象的属性。
54 |
55 | ```html
56 |
57 | -
58 | {{ value }}
59 |
60 |
61 | ```
62 |
63 | ```javascript
64 | new Vue({
65 | el: '#v-for-object',
66 | data: {
67 | object: {
68 | title: 'How to do lists in Vue',
69 | author: 'Jane Doe',
70 | publishedAt: '2016-04-10'
71 | }
72 | }
73 | })
74 | ```
75 |
76 | 结果:
77 |
78 | ```html
79 |
80 | - How to do lists in Vue
81 | - Jane Doe
82 | - 2016-04-10
83 |
84 | ```
85 | 也可以提供第二个的参数为 property 名称 (也就是键名):
86 |
87 | ```html
88 |
89 | {{ name }}: {{ value }}
90 |
91 | ```
92 |
93 | 还可以用第三个参数作为索引:
94 |
95 | ```html
96 |
97 | {{ index }}. {{ name }}: {{ value }}
98 |
99 | ```
--------------------------------------------------------------------------------
/packages/lone-ui/page.js:
--------------------------------------------------------------------------------
1 | let pid = 0
2 |
3 | export function createPage (route, { container, entry, mid, zIndex }) {
4 | const id = pid++
5 | const view = document.createElement('iframe')
6 | setAttr(id, view, { ...route, mid })
7 | setStyle(view, container, zIndex)
8 | container.appendChild(view)
9 | insertJS(view, entry)
10 | return view
11 | }
12 |
13 | export function removePage (container, page) {
14 | container.removeChild(page)
15 | }
16 |
17 | function setAttr (id, view, attrs) {
18 | view.id = id
19 | for (const [key, val] of Object.entries(attrs)) {
20 | view.setAttribute(key, val)
21 | }
22 | }
23 |
24 | function setStyle (view, container, zIndex) {
25 | const box = container.tagName === 'BODY'
26 | ? document.documentElement
27 | : container
28 | view.style.width = box.clientWidth + 'px'
29 | view.style.height = box.clientHeight + 'px'
30 | view.style.position = 'fixed'
31 | view.style.border = 'none'
32 | view.style.zIndex = zIndex
33 | view.style.backgroundColor = 'white'
34 | }
35 |
36 | function insertJS (view, entry) {
37 | const scriptTag = [insertContainer, insertPageJS, insertUserJS].reduce((pre, gen) => pre + gen(entry), '')
38 | view.contentDocument.write(`
39 |
40 |
41 |
42 |
43 |
44 |
45 |
Document
46 |
47 |
48 | ${scriptTag}
49 |
50 | `)
51 | }
52 |
53 | function insertPageJS () {
54 | return '' // eslint-disable-line
55 | }
56 |
57 | function insertUserJS (entry) {
58 | return ''
59 | }
60 |
61 | function insertContainer () {
62 | return '
'
63 | }
64 |
--------------------------------------------------------------------------------
/doc/lone-api/class_style.md:
--------------------------------------------------------------------------------
1 | # Class 与 Style 绑定
2 |
3 | 操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是属性,所以我们可以用 `v-bind` 处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 `v-bind` 用于 `class` 和 `style` 时做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
4 |
5 | ## 绑定 HTML Class
6 |
7 | ### 对象语法
8 |
9 | 我们可以传给 `v-bind:class` 一个对象,以动态地切换 `class`:
10 |
11 | ```html
12 |
13 | ```
14 |
15 | 上面的语法表示 `active` 这个 `class` 存在与否将取决于数据属性 `isActive` 的值。
16 |
17 | 可以在对象中传入更多属性来动态切换多个 `class`。此外,`v-bind:class` 指令也可以与普通的 `class` 属性共存。当有如下模板:
18 |
19 | ```html
20 |
24 | ```
25 |
26 | 和如下 data:
27 |
28 | ```javascript
29 | data: {
30 | isActive: true,
31 | hasError: false
32 | }
33 | ```
34 |
35 | 结果渲染为:
36 |
37 | ```html
38 |
39 | ```
40 |
41 | 当 `isActive` 或者 `hasError` 变化时,class 列表将相应地更新。例如,如果 `hasError` 的值为 `true`,class 列表将变为 `"static active text-danger"`。
42 |
43 | ### 数组语法
44 |
45 | 我们可以把一个数组传给 `v-bind:class`,以应用一个 class 列表:
46 |
47 | ```html
48 |
49 | ```
50 |
51 | ```javascript
52 | data: {
53 | activeClass: 'active',
54 | errorClass: 'text-danger'
55 | }
56 | ```
57 |
58 | 渲染为:
59 |
60 | ```html
61 |
62 | ```
63 |
64 | 如果你也想根据条件切换列表中的 class,可以用三元表达式:
65 |
66 | ```html
67 |
68 | ```
69 |
70 | 这样写将始终添加 `errorClass`,但是只有在 `isActive` 为真时才添加 `activeClass`。
71 |
72 | ## 绑定内联样式
73 |
74 | ### 对象语法
75 |
76 | `v-bind:style` 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:
77 |
78 | ```html
79 |
80 | ```
81 |
82 | ```javascript
83 | data: {
84 | activeColor: 'red',
85 | fontSize: 30
86 | }
87 | ```
88 |
--------------------------------------------------------------------------------
/packages/lone-logic/schedule.js:
--------------------------------------------------------------------------------
1 | import { triggerEvent, callHook } from './component/helper'
2 | import { createComponentInstance } from './index'
3 | import { parseSearch } from 'lone-util/url'
4 |
5 | export default function (slave) {
6 | const instanceStorage = new Map()
7 |
8 | const MESSENGER_EVENTS_UI = {
9 | 'page:inited': function ({ name, id, propsData, parentListeners, search }) {
10 | const vm = createComponentInstance(name, id, { propsData, parentListeners, slave, query: parseSearch(search) })
11 | instanceStorage.set(id, vm)
12 | },
13 | 'page:ready': function ({ id }) {
14 | const vm = instanceStorage.get(id)
15 | callHook(vm, 'onReady')
16 | callHook(vm, 'mounted')
17 | },
18 | 'page:triggerEvent': function ({ id, method, event }) {
19 | const vm = instanceStorage.get(id)
20 | triggerEvent(vm, method, event)
21 | },
22 | 'page:data': function ({ id, data }) {
23 | const vm = instanceStorage.get(id)
24 | vm.setData(data)
25 | },
26 | 'page:beforeMount': function ({ id }) {
27 | const vm = instanceStorage.get(id)
28 | callHook(vm, 'beforeMount')
29 | },
30 | 'page:beforeUpdate': function ({ id }) {
31 | const vm = instanceStorage.get(id)
32 | callHook(vm, 'beforeUpdate')
33 | },
34 | 'page:updated': function ({ id }) {
35 | const vm = instanceStorage.get(id)
36 | callHook(vm, 'updated')
37 | },
38 | 'page:show': function ({ id }) {
39 | const vm = instanceStorage.get(id)
40 | callHook(vm, 'onShow')
41 | },
42 | 'page:hide': function ({ id }) {
43 | const vm = instanceStorage.get(id)
44 | callHook(vm, 'onHide')
45 | },
46 | 'page:destroyed': function ({ id }) {
47 | const vm = instanceStorage.get(id)
48 | callHook(vm, 'destroyed')
49 | instanceStorage.delete(id)
50 | }
51 | }
52 |
53 | for (const [event, fn] of Object.entries(MESSENGER_EVENTS_UI)) {
54 | slave.onmessage(event, fn)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/packages/lone-util/web/class.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { isDef, isObject } from '../index'
4 |
5 | export function genClassForVnode (vnode) {
6 | let data = vnode.data
7 | let parentNode = vnode
8 | let childNode = vnode
9 | while (isDef(childNode.componentInstance)) {
10 | childNode = childNode.componentInstance._vnode
11 | if (childNode.data) {
12 | data = mergeClassData(childNode.data, data)
13 | }
14 | }
15 | while (isDef(parentNode = parentNode.parent)) {
16 | if (parentNode.data) {
17 | data = mergeClassData(data, parentNode.data)
18 | }
19 | }
20 | return renderClass(data.staticClass, data.class)
21 | }
22 |
23 | function mergeClassData (child, parent) {
24 | return {
25 | staticClass: concat(child.staticClass, parent.staticClass),
26 | class: isDef(child.class)
27 | ? [child.class, parent.class]
28 | : parent.class
29 | }
30 | }
31 |
32 | export function renderClass (staticClass, dynamicClass) {
33 | if (isDef(staticClass) || isDef(dynamicClass)) {
34 | return concat(staticClass, stringifyClass(dynamicClass))
35 | }
36 | /* istanbul ignore next */
37 | return ''
38 | }
39 |
40 | export function concat (a, b) {
41 | return a ? b ? (a + ' ' + b) : a : (b || '')
42 | }
43 |
44 | export function stringifyClass (value) {
45 | if (Array.isArray(value)) {
46 | return stringifyArray(value)
47 | }
48 | if (isObject(value)) {
49 | return stringifyObject(value)
50 | }
51 | if (typeof value === 'string') {
52 | return value
53 | }
54 | /* istanbul ignore next */
55 | return ''
56 | }
57 |
58 | function stringifyArray (value) {
59 | let res = ''
60 | let stringified
61 | for (let i = 0, l = value.length; i < l; i++) {
62 | if (isDef(stringified = stringifyClass(value[i])) && stringified !== '') {
63 | if (res) res += ' '
64 | res += stringified
65 | }
66 | }
67 | return res
68 | }
69 |
70 | function stringifyObject (value) {
71 | let res = ''
72 | for (const key in value) {
73 | if (value[key]) {
74 | if (res) res += ' '
75 | res += key
76 | }
77 | }
78 | return res
79 | }
80 |
--------------------------------------------------------------------------------
/packages/lone-util/web/style.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { cached, extend, toObject } from '../index'
4 |
5 | export const parseStyleText = cached(function (cssText) {
6 | const res = {}
7 | const listDelimiter = /;(?![^(]*\))/g
8 | const propertyDelimiter = /:(.+)/
9 | cssText.split(listDelimiter).forEach(function (item) {
10 | if (item) {
11 | var tmp = item.split(propertyDelimiter)
12 | tmp.length > 1 && (res[tmp[0].trim()] = tmp[1].trim())
13 | }
14 | })
15 | return res
16 | })
17 |
18 | // merge static and dynamic style data on the same vnode
19 | function normalizeStyleData (data) {
20 | const style = normalizeStyleBinding(data.style)
21 | // static style is pre-processed into an object during compilation
22 | // and is always a fresh object, so it's safe to merge into it
23 | return data.staticStyle
24 | ? extend(data.staticStyle, style)
25 | : style
26 | }
27 |
28 | // normalize possible array / string values into Object
29 | export function normalizeStyleBinding (bindingStyle) {
30 | if (Array.isArray(bindingStyle)) {
31 | return toObject(bindingStyle)
32 | }
33 | if (typeof bindingStyle === 'string') {
34 | return parseStyleText(bindingStyle)
35 | }
36 | return bindingStyle
37 | }
38 |
39 | /**
40 | * parent component style should be after child's
41 | * so that parent component's style could override it
42 | */
43 | export function getStyle (vnode, checkChild) {
44 | const res = {}
45 | let styleData
46 |
47 | if (checkChild) {
48 | let childNode = vnode
49 | while (childNode.componentInstance) {
50 | childNode = childNode.componentInstance._vnode
51 | if (childNode.data && (styleData = normalizeStyleData(childNode.data))) {
52 | extend(res, styleData)
53 | }
54 | }
55 | }
56 |
57 | if ((styleData = normalizeStyleData(vnode.data))) {
58 | extend(res, styleData)
59 | }
60 |
61 | let parentNode = vnode
62 | while ((parentNode = parentNode.parent)) {
63 | if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) {
64 | extend(res, styleData)
65 | }
66 | }
67 | return res
68 | }
69 |
--------------------------------------------------------------------------------
/packages/lone-messenger/README.md:
--------------------------------------------------------------------------------
1 | # Messenger
2 |
3 | ## Install
4 |
5 | ```
6 | $ lerna add lone-messenger --scope=YourModule
7 | ```
8 |
9 | ## Usage
10 |
11 | ### Master
12 |
13 | Create a Master `messenger`:
14 |
15 | ```javascript
16 | import { Master } from 'lone-messenger'
17 |
18 | const master = new Master({
19 | env: 'postMessage'
20 | })
21 |
22 | master.onmessage('customType', function (channel, data) {
23 | console.log(data)
24 | })
25 |
26 | master.send('customType', 'channel', {name: 'Berwin'})
27 | ```
28 |
29 | #### Options
30 |
31 | * mid - 在多个小程序并存时,用 mid 区分信号
32 | * env - Switch PostMessage and NativeMessage, default is NativeMessage
33 |
34 | #### Methods
35 |
36 | * onmessage(type, fn)
37 | * type [String]
38 | * fn(data) data type is Object, 可以在该函数可以返回一个Promise,messenger内部会根据promise的状态发送反馈信息
39 | * send(type, channel, data)
40 | * type [String]
41 | * channel [String]
42 | * data [Object]
43 |
44 | ### Slave
45 |
46 | Create a Slave `messenger`:
47 |
48 | ```javascript
49 | import { Slave } from 'seapp-shared/messenger'
50 |
51 | const slave = new Slave({
52 | env: 'postMessage',
53 | channel: 'logic'
54 | })
55 |
56 | slave.onmessage('customType', function (data) {
57 | console.log(data)
58 | })
59 |
60 | slave.send('customType', 'channel', {name: 'Berwin'})
61 | ```
62 | #### Options
63 |
64 | * env - Switch PostMessage and NativeMessage, default is NativeMessage
65 | * channel - 设置自己的频道号
66 |
67 | #### Methods
68 |
69 | * onmessage(type, fn)
70 | * type [String]
71 | * fn(data) data type is Object, 可以在该函数可以返回一个Promise,messenger内部会根据promise的状态发送反馈信息
72 | * send(type, channel, data)
73 | * type [String]
74 | * channel [String]
75 | * data [Object]
76 |
77 | **slave.send**
78 |
79 | 该函数返回Promise,可以得到信号的反馈信息,已得知某项事情的状态(成功|失败)。
80 |
81 | ```javascript
82 | slave.send('customType', 'channel', {name: 'Berwin'}).then(res => {
83 | console.log('成功:', res)
84 | }).catch(err => {
85 | console.log('失败:', err)
86 | })
87 | ```
88 |
89 | **slave.onmessage(type, fn)**
90 |
91 | `fn` 可以返回一个Promise,messenger内部会根据promise的状态发送反馈信息。
92 |
93 | ```javascript
94 | slave.onmessage('customType', function (data) {
95 | return Promise.reject('事情做失败了')
96 | })
97 | ```
98 | 上面代码将会发送失败的反馈信号到`slave.send`处。
99 |
--------------------------------------------------------------------------------
/packages/lone-logic/component/events.js:
--------------------------------------------------------------------------------
1 | import { handleError, isArray } from 'lone-util'
2 |
3 | export default function events (Lone) {
4 | const proto = Lone.prototype
5 | proto.$on = on
6 | proto.$once = once
7 | proto.$off = off
8 | proto.$emit = emit
9 | }
10 |
11 | export function initEvents (vm) {
12 | const parentListeners = vm.$options.parentListeners
13 | let i = parentListeners.length
14 | while (i--) {
15 | const name = parentListeners[i]
16 | vm.$on(name, function (data) {
17 | vm._slave.send('component:triggerParentEvent', vm._id, { name, data })
18 | })
19 | }
20 | }
21 |
22 | function on (event, fn) {
23 | const vm = this
24 | if (isArray(event)) {
25 | for (let i = 0, len = event.length; i < len; i++) {
26 | vm.$on(event[i], fn)
27 | }
28 | } else {
29 | // eslint-disable-next-line
30 | (vm._events[event] || (vm._events[event] = [])).push(fn)
31 | }
32 | }
33 |
34 | function once (event, fn) {
35 | const vm = this
36 | function on () {
37 | vm.$off(event, on)
38 | fn.apply(vm, arguments)
39 | }
40 | on.fn = fn
41 | vm.$on(event, on)
42 | }
43 |
44 | function off (event, fn) {
45 | const vm = this
46 | if (!arguments.length) {
47 | vm._events = Object.create(null)
48 | return vm
49 | }
50 | if (isArray(event)) {
51 | for (let i = 0, len = event.length; i < len; i++) {
52 | vm.$off(event[i], fn)
53 | }
54 | return vm
55 | }
56 | const fns = vm._events[event]
57 | if (!fns) return vm
58 | if (arguments.length === 1) {
59 | vm._events[event] = null
60 | }
61 | if (fns) {
62 | let i = fns.length
63 | while (i--) {
64 | if (fns[i] === fn || fns[i].fn === fn) {
65 | fns.splice(i, 1)
66 | }
67 | }
68 | }
69 | return vm
70 | }
71 |
72 | function emit (event) {
73 | const vm = this
74 | const events = vm._events[event]
75 | const slice = Array.prototype.slice
76 | if (events) {
77 | const args = slice.call(arguments, 1)
78 | for (let i = 0, len = events.length; i < len; i++) {
79 | try {
80 | events[i].apply(vm, args)
81 | } catch (e) {
82 | handleError(e, vm, `event handler for "${event}"`)
83 | }
84 | }
85 | }
86 | return vm
87 | }
88 |
--------------------------------------------------------------------------------
/packages/lone-compiler-core/to-function.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { noop, extend, warn as baseWarn, tip } from 'lone-util'
4 |
5 | function createFunction (code, errors) {
6 | try {
7 | // eslint-disable-next-line no-new-func
8 | return new Function(code)
9 | } catch (err) {
10 | errors.push({ err, code })
11 | return noop
12 | }
13 | }
14 |
15 | export function createCompileToFunctionFn (compile) {
16 | const cache = Object.create(null)
17 |
18 | return function compileToFunctions (template, options, vm) {
19 | options = extend({}, options)
20 | const warn = options.warn || baseWarn
21 | delete options.warn
22 |
23 | // check cache
24 | const key = options.delimiters
25 | ? String(options.delimiters) + template
26 | : template
27 | if (cache[key]) {
28 | return cache[key]
29 | }
30 |
31 | // compile
32 | const compiled = compile(template, options)
33 |
34 | // check compilation errors/tips
35 | if (process.env.NODE_ENV !== 'production') {
36 | if (compiled.errors && compiled.errors.length) {
37 | warn(
38 | `Error compiling template:\n\n${template}\n\n` +
39 | compiled.errors.map(e => `- ${e}`).join('\n') + '\n',
40 | vm
41 | )
42 | }
43 | if (compiled.tips && compiled.tips.length) {
44 | compiled.tips.forEach(msg => tip(msg, vm))
45 | }
46 | }
47 |
48 | // turn code into functions
49 | const res = {}
50 | const fnGenErrors = []
51 | res.render = createFunction(compiled.render, fnGenErrors)
52 | res.staticRenderFns = compiled.staticRenderFns.map(code => {
53 | return createFunction(code, fnGenErrors)
54 | })
55 |
56 | // check function generation errors.
57 | // this should only happen if there is a bug in the compiler itself.
58 | // mostly for codegen development use
59 | /* istanbul ignore if */
60 | if (process.env.NODE_ENV !== 'production') {
61 | if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) {
62 | warn(
63 | 'Failed to generate render function:\n\n' +
64 | fnGenErrors.map(({ err, code }) => `${err.toString()} in\n\n${code}\n`).join('\n'),
65 | vm
66 | )
67 | }
68 | }
69 |
70 | return (cache[key] = res)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/packages/lone-messenger/master/index.js:
--------------------------------------------------------------------------------
1 | import Native from './native-messenger'
2 | import Post from './post-messenger'
3 | import WebWorker from './worker-messenger'
4 | import { feedbackType } from 'lone-util'
5 |
6 | const connection = Symbol('messenger:master#connection')
7 |
8 | export default class Master {
9 | constructor (options) {
10 | this._messages = Object.create(null)
11 | this.options = options
12 | this.native = new Native(options)
13 | this.post = new Post(options)
14 | this.worker = new WebWorker(options)
15 | this[connection]()
16 | this.listen()
17 | }
18 |
19 | [connection] () {
20 | if (this.options.env === 'native') this.native.connection()
21 | if (this.options.env === 'worker') this.worker.connection()
22 | this.post.connection()
23 | }
24 |
25 | onmessage (type, fn) {
26 | (this._messages[type] || (this._messages[type] = [])).push(fn)
27 | }
28 |
29 | send (type, targetChannel, data) {
30 | this._postMessage(type, null, data, targetChannel)
31 | }
32 |
33 | listen () {
34 | this._onmessage(evt => {
35 | // 如果是中转信号,则直接转发至Slave(存在targetChannel的为中转信号,没有则表示发送给Master的信号)
36 | if (evt.targetChannel) return this._postMessage(evt.type, evt.targetChannel, evt.data, evt.channel)
37 | const cbs = this._messages[evt.type]
38 | if (!cbs) return
39 | let i = cbs.length
40 | while (i--) {
41 | Promise.resolve(cbs[i].call(evt, evt.channel, evt.data))
42 | .then(
43 | data => this._postMessage(feedbackType(evt.type), evt.channel, { data }),
44 | err => this._postMessage(feedbackType(evt.type), evt.channel, { err })
45 | )
46 | }
47 | })
48 | }
49 |
50 | _onmessage (fn) {
51 | if (this.options.env === 'native') this.native.onmessage(fn)
52 | if (this.options.env === 'worker') this.worker.onmessage(fn)
53 | this.post.onmessage(fn)
54 | }
55 |
56 | _postMessage (type, targetChannel, data, channel) {
57 | // Only developer component logic running under the sandbox
58 | if (targetChannel === 'logic-worker') {
59 | if (this.options.env === 'native') return this.native.send(type, data, channel)
60 | if (this.options.env === 'worker') return this.worker.send(type, data, channel)
61 | }
62 | return this.post.send(type, targetChannel, data, channel)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/packages/lone-logic/component/helper.js:
--------------------------------------------------------------------------------
1 | import { LIFECYCLE_HOOKS } from 'lone-util/constants'
2 | import { warn, handleError, isArray, isPlainObject, camelize, toArray } from 'lone-util'
3 |
4 | export function initOptions (options) {
5 | normalizeHooks(options)
6 | normalizePropsData(options)
7 | return options
8 | }
9 |
10 | function normalizeHooks (options) {
11 | for (const key in options) {
12 | if (LIFECYCLE_HOOKS.includes(key)) {
13 | options[key] = isArray(options[key])
14 | ? options[key]
15 | : [options[key]]
16 | }
17 | }
18 | }
19 |
20 | function normalizePropsData (options) {
21 | const props = options.props
22 | const res = {}
23 | let i, val, name
24 | if (isArray(props)) {
25 | i = props.length
26 | while (i--) {
27 | val = props[i]
28 | if (typeof val === 'string') {
29 | name = camelize(val)
30 | res[name] = { type: null }
31 | } else if (process.env.NODE_ENV !== 'production') {
32 | warn('props must be strings when using array syntax.')
33 | }
34 | }
35 | } else if (isPlainObject(props)) {
36 | for (const key in props) {
37 | val = props[key]
38 | name = camelize(key)
39 | res[name] = isPlainObject(val)
40 | ? val
41 | : { type: val }
42 | }
43 | }
44 | options.props = res
45 | }
46 |
47 | export function sendInitCommandToPageComponent (vm) {
48 | const reservedWords = [...LIFECYCLE_HOOKS, 'data', 'methods', 'slave', 'name', 'propsData', 'parentListeners', 'props']
49 | vm._slave.send('component:inited', vm._id, {
50 | data: vm.data || {},
51 | methods: [...Object.keys(vm.$options).filter(key => !reservedWords.includes(key)), ...Object.keys(vm.$options.methods || {})]
52 | })
53 | vm._inited = true
54 | }
55 |
56 | export function triggerEvent (vm, method, event) {
57 | const handler = vm.$options[method] || vm.$options.methods[method]
58 | try {
59 | handler.call(vm, event)
60 | } catch (e) {
61 | handleError(e, vm, `"${method}" event handler`)
62 | }
63 | }
64 |
65 | export function callHook (vm, hook) {
66 | const handlers = vm.$options[hook]
67 | if (handlers) {
68 | for (let i = 0, j = handlers.length; i < j; i++) {
69 | try {
70 | handlers[i].apply(vm, toArray(arguments, 2))
71 | } catch (e) {
72 | handleError(e, vm, `${hook} hook`)
73 | }
74 | }
75 | }
76 | vm.$emit('hook:' + hook)
77 | }
78 |
--------------------------------------------------------------------------------
/packages/lone-compiler-dom/modules/model.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | /**
4 | * Expand input[v-model] with dyanmic type bindings into v-if-else chains
5 | * Turn this:
6 | *
7 | * into this:
8 | *
9 | *
10 | *
11 | */
12 |
13 | import {
14 | getBindingAttr,
15 | getAndRemoveAttr
16 | } from 'lone-compiler-core/helpers'
17 |
18 | import {
19 | processFor,
20 | processElement,
21 | addIfCondition,
22 | createASTElement
23 | } from 'lone-compiler-core/parser/index'
24 |
25 | function preTransformNode (el, options) {
26 | if (el.tag === 'input') {
27 | const map = el.attrsMap
28 | if (map['v-model'] && (map['v-bind:type'] || map[':type'])) {
29 | const typeBinding = getBindingAttr(el, 'type')
30 | const ifCondition = getAndRemoveAttr(el, 'v-if', true)
31 | const ifConditionExtra = ifCondition ? `&&(${ifCondition})` : ''
32 | // 1. checkbox
33 | const branch0 = cloneASTElement(el)
34 | // process for on the main node
35 | processFor(branch0)
36 | addRawAttr(branch0, 'type', 'checkbox')
37 | processElement(branch0, options)
38 | branch0.processed = true // prevent it from double-processed
39 | branch0.if = `(${typeBinding})==='checkbox'` + ifConditionExtra
40 | addIfCondition(branch0, {
41 | exp: branch0.if,
42 | block: branch0
43 | })
44 | // 2. add radio else-if condition
45 | const branch1 = cloneASTElement(el)
46 | getAndRemoveAttr(branch1, 'v-for', true)
47 | addRawAttr(branch1, 'type', 'radio')
48 | processElement(branch1, options)
49 | addIfCondition(branch0, {
50 | exp: `(${typeBinding})==='radio'` + ifConditionExtra,
51 | block: branch1
52 | })
53 | // 3. other
54 | const branch2 = cloneASTElement(el)
55 | getAndRemoveAttr(branch2, 'v-for', true)
56 | addRawAttr(branch2, ':type', typeBinding)
57 | processElement(branch2, options)
58 | addIfCondition(branch0, {
59 | exp: ifCondition,
60 | block: branch2
61 | })
62 | return branch0
63 | }
64 | }
65 | }
66 |
67 | function cloneASTElement (el) {
68 | return createASTElement(el.tag, el.attrsList.slice(), el.parent)
69 | }
70 |
71 | function addRawAttr (el, name, value) {
72 | el.attrsMap[name] = value
73 | el.attrsList.push({ name, value })
74 | }
75 |
76 | export default {
77 | preTransformNode
78 | }
79 |
--------------------------------------------------------------------------------
/packages/lone-util/web/element.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { inBrowser } from '../env'
4 | import { makeMap } from '../index'
5 |
6 | export const namespaceMap = {
7 | svg: 'http://www.w3.org/2000/svg',
8 | math: 'http://www.w3.org/1998/Math/MathML'
9 | }
10 |
11 | export const isHTMLTag = makeMap(
12 | 'html,body,base,head,link,meta,style,title,' +
13 | 'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +
14 | 'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +
15 | 'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +
16 | 's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +
17 | 'embed,object,param,source,canvas,script,noscript,del,ins,' +
18 | 'caption,col,colgroup,table,thead,tbody,td,th,tr,' +
19 | 'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +
20 | 'output,progress,select,textarea,' +
21 | 'details,dialog,menu,menuitem,summary,' +
22 | 'content,element,shadow,template,blockquote,iframe,tfoot'
23 | )
24 |
25 | // this map is intentionally selective, only covering SVG elements that may
26 | // contain child elements.
27 | export const isSVG = makeMap(
28 | 'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' +
29 | 'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +
30 | 'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',
31 | true
32 | )
33 |
34 | export const isPreTag = (tag) => tag === 'pre'
35 |
36 | export const isReservedTag = (tag) => {
37 | return isHTMLTag(tag) || isSVG(tag)
38 | }
39 |
40 | export function getTagNamespace (tag) {
41 | if (isSVG(tag)) {
42 | return 'svg'
43 | }
44 | // basic support for MathML
45 | // note it doesn't support other MathML elements being component roots
46 | if (tag === 'math') {
47 | return 'math'
48 | }
49 | }
50 |
51 | const unknownElementCache = Object.create(null)
52 | export function isUnknownElement (tag) {
53 | /* istanbul ignore if */
54 | if (!inBrowser) {
55 | return true
56 | }
57 | if (isReservedTag(tag)) {
58 | return false
59 | }
60 | tag = tag.toLowerCase()
61 | /* istanbul ignore if */
62 | if (unknownElementCache[tag] != null) {
63 | return unknownElementCache[tag]
64 | }
65 | const el = document.createElement(tag)
66 | if (tag.indexOf('-') > -1) {
67 | // http://stackoverflow.com/a/28210364/1070244
68 | return (unknownElementCache[tag] = (
69 | el.constructor === window.HTMLUnknownElement ||
70 | el.constructor === window.HTMLElement
71 | ))
72 | } else {
73 | return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString()))
74 | }
75 | }
76 |
77 | export const isTextInputType = makeMap('text,number,password,search,email,tel,url')
78 |
--------------------------------------------------------------------------------
/doc/lone-api/template.md:
--------------------------------------------------------------------------------
1 | # 模板
2 |
3 | 模板支持绝大多数Vue常用的语法。
4 |
5 | ## 文本
6 |
7 | 使用“Mustache”语法 (双大括号) 向文本中插值。
8 |
9 | ```html
10 |
Message: {{ msg }}
11 | ```
12 |
13 | ## 原始 HTML
14 |
15 | 双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 `v-html` 指令:
16 |
17 |
18 | ```html
19 |
Using mustaches: {{ rawHtml }}
20 |
Using v-html directive:
21 | ```
22 |
23 | ## Attribute
24 |
25 | Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用 `v-bind` 指令:
26 |
27 | ```html
28 |
29 | ```
30 |
31 | 或使用简写形式:
32 |
33 | ```html
34 |
35 | ```
36 |
37 | 对于布尔 attribute (它们只要存在就意味着值为 true),`v-bind` 工作起来略有不同,在这个例子中:
38 |
39 | ```html
40 |
41 | ```
42 |
43 | 如果 `isButtonDisabled` 的值是 `null`、`undefined` 或 `false`,则 disabled attribute 甚至不会被包含在渲染出来的 `