├── .gitignore ├── lerna.json ├── doc ├── lone-guide │ ├── structure.md │ └── structure │ │ └── messenger-model.gif └── lone-api │ ├── lifecycle.md │ ├── router.md │ ├── conditional.md │ ├── list.md │ ├── class_style.md │ ├── template.md │ ├── introduction.md │ └── component.md ├── packages ├── lone-messenger │ ├── index.js │ ├── package.json │ ├── slave │ │ ├── index.js │ │ ├── worker-messenger.js │ │ ├── native-messenger.js │ │ ├── post-messenger.js │ │ └── base.js │ ├── master │ │ ├── worker-messenger.js │ │ ├── native-messenger.js │ │ ├── post-messenger.js │ │ └── index.js │ └── README.md ├── lone-compiler-dom │ ├── directives │ │ ├── index.js │ │ ├── html.js │ │ ├── text.js │ │ └── model.js │ ├── modules │ │ ├── index.js │ │ ├── class.js │ │ ├── style.js │ │ └── model.js │ ├── index.js │ ├── package.json │ └── options.js ├── lone-compiler-core │ ├── directives │ │ ├── index.js │ │ ├── on.js │ │ ├── bind.js │ │ └── model.js │ ├── parser │ │ ├── entity-decoder.js │ │ ├── text-parser.js │ │ ├── filter-parser.js │ │ ├── html-parser.js │ │ └── index.js │ ├── package.json │ ├── package-lock.json │ ├── index.js │ ├── create-compiler.js │ ├── to-function.js │ ├── helpers.js │ ├── error-detector.js │ └── codegen │ │ ├── events.js │ │ └── index.js ├── lone-util │ ├── package.json │ ├── constants.js │ ├── web │ │ ├── compat.js │ │ ├── index.js │ │ ├── attrs.js │ │ ├── class.js │ │ ├── style.js │ │ └── element.js │ ├── env.js │ ├── error.js │ ├── url.js │ └── index.js ├── lone-logic-worker │ ├── index.js │ └── package.json ├── lone-logic │ ├── package.json │ ├── index.js │ ├── component │ │ ├── observer.js │ │ ├── router.js │ │ ├── index.js │ │ ├── events.js │ │ ├── helper.js │ │ └── state.js │ └── schedule.js ├── lone-ui │ ├── package.json │ ├── index.js │ ├── schedule.js │ ├── page.js │ └── router.js ├── lone-logic-master │ ├── package.json │ └── index.js ├── lone-virtualdom │ ├── package.json │ ├── package-lock.json │ ├── render-helpers │ │ ├── render-slot.js │ │ ├── h.js │ │ ├── index.js │ │ └── render-list.js │ ├── index.js │ └── create-component.js └── lone-page │ ├── package.json │ ├── component │ ├── index.js │ ├── slot.js │ ├── eventListener.js │ └── init.js │ └── index.js ├── scripts ├── webpack.prod.conf.js ├── webpack.dev.conf.js ├── replace-loader.js └── webpack.base.conf.js ├── example ├── basic │ ├── logic.html │ ├── app.config.js │ ├── index.html │ ├── app.main.js │ └── app.page.js └── multi-miniapp │ ├── app.config.js │ ├── app.main.js │ ├── app.page.js │ └── index.html ├── .eslintrc.json ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | dist/ -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "0.0.0" 6 | } 7 | -------------------------------------------------------------------------------- /doc/lone-guide/structure.md: -------------------------------------------------------------------------------- 1 | # 项目结构与运行原理 2 | 3 | ## 通信模型 4 | 5 | ![通信模型](doc/lone-guide/structure/messenger-model.gif) 6 | -------------------------------------------------------------------------------- /doc/lone-guide/structure/messenger-model.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/berwin/lone/HEAD/doc/lone-guide/structure/messenger-model.gif -------------------------------------------------------------------------------- /packages/lone-messenger/index.js: -------------------------------------------------------------------------------- 1 | import Master from './master' 2 | import Slave from './slave' 3 | 4 | export { 5 | Master, 6 | Slave 7 | } 8 | -------------------------------------------------------------------------------- /packages/lone-compiler-dom/directives/index.js: -------------------------------------------------------------------------------- 1 | import model from './model' 2 | import text from './text' 3 | import html from './html' 4 | 5 | export default { 6 | model, 7 | text, 8 | html 9 | } 10 | -------------------------------------------------------------------------------- /scripts/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge') 2 | const baseWebpackConfig = require('./webpack.base.conf') 3 | 4 | module.exports = merge(baseWebpackConfig, { 5 | mode: 'production' 6 | }) 7 | -------------------------------------------------------------------------------- /packages/lone-compiler-dom/modules/index.js: -------------------------------------------------------------------------------- 1 | import klass from './class' 2 | import style from './style' 3 | import model from './model' 4 | 5 | export default [ 6 | klass, 7 | style, 8 | model 9 | ] 10 | -------------------------------------------------------------------------------- /packages/lone-compiler-core/directives/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import on from './on' 4 | import bind from './bind' 5 | import { noop } from 'lone-util' 6 | 7 | export default { 8 | on, 9 | bind, 10 | cloak: noop 11 | } 12 | -------------------------------------------------------------------------------- /packages/lone-util/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-util", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "license": "ISC" 9 | } 10 | -------------------------------------------------------------------------------- /packages/lone-compiler-dom/index.js: -------------------------------------------------------------------------------- 1 | import { baseOptions } from './options' 2 | import { createCompiler } from 'lone-compiler-core' 3 | 4 | const { compile, compileToFunctions } = createCompiler(baseOptions) 5 | 6 | export { compile, compileToFunctions } 7 | -------------------------------------------------------------------------------- /packages/lone-compiler-dom/directives/html.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { addProp } from 'lone-compiler-core/helpers' 4 | 5 | export default function html (el, dir) { 6 | if (dir.value) { 7 | addProp(el, 'innerHTML', `_s(${dir.value})`) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/lone-compiler-dom/directives/text.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { addProp } from 'lone-compiler-core/helpers' 4 | 5 | export default function text (el, dir) { 6 | if (dir.value) { 7 | addProp(el, 'textContent', `_s(${dir.value})`) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/lone-compiler-core/parser/entity-decoder.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | let decoder 4 | 5 | export default { 6 | decode (html) { 7 | decoder = decoder || document.createElement('div') 8 | decoder.innerHTML = html 9 | return decoder.textContent 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/lone-logic-worker/index.js: -------------------------------------------------------------------------------- 1 | import { Slave } from 'lone-messenger' 2 | import schedule from 'lone-logic/schedule' 3 | 4 | export const slave = new Slave({ env: 'worker', channel: 'logic-worker' }) 5 | schedule(slave) 6 | slave.send('logic:inited') 7 | 8 | export { default } from 'lone-logic' 9 | -------------------------------------------------------------------------------- /packages/lone-logic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-logic", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "license": "ISC", 9 | "dependencies": { 10 | "lone-util": "^0.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /scripts/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | const merge = require("webpack-merge"); 2 | const baseWebpackConfig = require("./webpack.base.conf"); 3 | 4 | module.exports = merge(baseWebpackConfig, { 5 | mode: "development", 6 | // cheap-module-eval-source-map is faster for development 7 | devtool: "#source-map" 8 | }); 9 | -------------------------------------------------------------------------------- /packages/lone-messenger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-messenger", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "license": "ISC", 9 | "dependencies": { 10 | "lone-util": "^0.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /example/basic/logic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Lone.js • Basic 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/lone-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-ui", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "license": "ISC", 9 | "dependencies": { 10 | "lone-messenger": "^0.0.0", 11 | "lone-util": "^0.0.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/lone-compiler-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-compiler-core", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "license": "ISC", 9 | "dependencies": { 10 | "he": "^1.2.0", 11 | "lone-util": "^0.0.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/basic/app.config.js: -------------------------------------------------------------------------------- 1 | Lone.ui({ 2 | entry: { 3 | logic: './app.main.js', 4 | page: './app.page.js' 5 | }, 6 | routes: [ 7 | { path: '/', component: 'test' }, 8 | { path: '/official', component: 'official' }, 9 | { path: '/lifecycle', component: 'lifecycle' }, 10 | { path: '/query', component: 'query' } 11 | ] 12 | }) 13 | -------------------------------------------------------------------------------- /packages/lone-compiler-core/directives/on.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { warn } from 'lone-util' 4 | 5 | export default function on (el, dir) { 6 | if (process.env.NODE_ENV !== 'production' && dir.modifiers) { 7 | warn('v-on without argument does not support modifiers.') 8 | } 9 | el.wrapListeners = (code) => `_g(${code},${dir.value})` 10 | } 11 | -------------------------------------------------------------------------------- /packages/lone-compiler-core/directives/bind.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export default function bind (el, dir) { 4 | el.wrapData = (code) => { 5 | return `_b(${code},'${el.tag}',${dir.value},${ 6 | dir.modifiers && dir.modifiers.prop ? 'true' : 'false' 7 | }${ 8 | dir.modifiers && dir.modifiers.sync ? ',true' : '' 9 | })` 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/lone-compiler-dom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-compiler-dom", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "license": "ISC", 10 | "dependencies": { 11 | "lone-compiler-core": "^0.0.0", 12 | "lone-util": "^0.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/lone-util/constants.js: -------------------------------------------------------------------------------- 1 | export const LIFECYCLE_HOOKS = [ 2 | 'beforeCreate', 3 | 'created', 4 | 'beforeMount', 5 | 'mounted', 6 | 'beforeUpdate', 7 | 'updated', 8 | 'beforeDestroy', 9 | 'destroyed', 10 | 'activated', 11 | 'deactivated', 12 | 'errorCaptured', 13 | 'onHide', 14 | 'onLoad', 15 | 'onReady', 16 | 'onShow', 17 | 'onUnload' 18 | ] 19 | -------------------------------------------------------------------------------- /packages/lone-logic-master/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-logic-master", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "lone-logic": "^0.0.0", 13 | "lone-messenger": "^0.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/lone-logic-worker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-logic-worker", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "lone-logic": "^0.0.0", 13 | "lone-messenger": "^0.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/lone-virtualdom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-virtualdom", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "license": "ISC", 10 | "dependencies": { 11 | "lone-page": "^0.0.0", 12 | "lone-util": "^0.0.0", 13 | "snabbdom": "^0.7.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/lone-messenger/slave/index.js: -------------------------------------------------------------------------------- 1 | import NativeMessenger from './native-messenger' 2 | import PostMessenger from './post-messenger' 3 | import WorkerMessenger from './worker-messenger' 4 | 5 | const slaveMap = { 6 | postMessage: PostMessenger, 7 | native: NativeMessenger, 8 | worker: WorkerMessenger 9 | } 10 | 11 | export default function (options) { 12 | return new slaveMap[options.env](options) 13 | } 14 | -------------------------------------------------------------------------------- /packages/lone-compiler-core/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-compiler-core", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "he": { 8 | "version": "1.2.0", 9 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 10 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "es6": true 6 | }, 7 | "extends": [ 8 | "standard" 9 | ], 10 | "globals": { 11 | "Atomics": "readonly", 12 | "SharedArrayBuffer": "readonly" 13 | }, 14 | "parserOptions": { 15 | "ecmaVersion": 2018, 16 | "sourceType": "module" 17 | }, 18 | "rules": { 19 | } 20 | } -------------------------------------------------------------------------------- /packages/lone-virtualdom/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-virtualdom", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "snabbdom": { 8 | "version": "0.7.3", 9 | "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-0.7.3.tgz", 10 | "integrity": "sha512-XNh90GQiV36hWdfSL46fIVrSSvmBuZlWk3++qaEgBeQWQJCqTphcbjTODPv8/vyZHJaB3VhePsWfGxi/zBxXyw==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/lone-page/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lone-page", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "license": "ISC", 9 | "dependencies": { 10 | "lone-compiler-dom": "^0.0.0", 11 | "lone-logic-master": "^0.0.0", 12 | "lone-messenger": "^0.0.0", 13 | "lone-util": "^0.0.0", 14 | "lone-virtualdom": "^0.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /scripts/replace-loader.js: -------------------------------------------------------------------------------- 1 | const loaderUtils = require('loader-utils') 2 | 3 | module.exports = function (source) { 4 | this.cacheable && this.cacheable() 5 | const query = loaderUtils.getOptions(this) || {} 6 | source = source.toString() 7 | Object.keys(query).forEach(function (key) { 8 | const reg = new RegExp(key, 'g') 9 | source = source.replace(reg, query[key]) 10 | }) 11 | return source 12 | } 13 | 14 | module.exports.raw = true 15 | -------------------------------------------------------------------------------- /packages/lone-logic/index.js: -------------------------------------------------------------------------------- 1 | import LogicComponent from './component' 2 | 3 | const componentStorage = new Map() 4 | 5 | export default function Component (name, options = {}) { 6 | componentStorage.set(name, options) 7 | } 8 | 9 | export function createComponentInstance (name, id, otherOptions) { 10 | const options = Object.assign( 11 | componentStorage.get(name), 12 | otherOptions 13 | ) 14 | options.name = name 15 | return new LogicComponent(id, options) 16 | } 17 | -------------------------------------------------------------------------------- /packages/lone-messenger/master/worker-messenger.js: -------------------------------------------------------------------------------- 1 | class WorkerMessenger { 2 | constructor (options) { 3 | this.worker = options.worker 4 | } 5 | 6 | connection () { 7 | this.source = this.worker 8 | } 9 | 10 | onmessage (fn) { 11 | this.source.onmessage = function (evt) { 12 | fn.call(evt, evt.data) 13 | } 14 | } 15 | 16 | send (type, data, channel) { 17 | this.source.postMessage({ type, data, channel }) 18 | } 19 | } 20 | 21 | export default WorkerMessenger 22 | -------------------------------------------------------------------------------- /packages/lone-page/component/index.js: -------------------------------------------------------------------------------- 1 | import { installRenderHelpers } from 'lone-virtualdom/render-helpers' 2 | import init from './init' 3 | 4 | @installRenderHelpers 5 | @init 6 | class Component { 7 | constructor (options) { 8 | this.init(options) 9 | } 10 | 11 | static setGlobalOptions (options) { 12 | this.options = options 13 | } 14 | 15 | getLogicChannel () { 16 | return this.$official ? `${this.mid}_${this.pid}_logic` : 'logic-worker' 17 | } 18 | } 19 | 20 | export default Component 21 | -------------------------------------------------------------------------------- /example/basic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /example/multi-miniapp/app.config.js: -------------------------------------------------------------------------------- 1 | Lone.ui({ 2 | container: '#cube-one', 3 | entry: { 4 | logic: './app.main.js', 5 | page: './app.page.js' 6 | }, 7 | routes: [ 8 | { path: '/', component: 'test' }, 9 | { path: '/test2', component: 'test2' } 10 | ] 11 | }) 12 | 13 | Lone.ui({ 14 | container: '#cube-two', 15 | entry: { 16 | logic: './app.main.js', 17 | page: './app.page.js' 18 | }, 19 | routes: [ 20 | { path: '/', component: 'test' }, 21 | { path: '/test2', component: 'test2' } 22 | ] 23 | }) 24 | -------------------------------------------------------------------------------- /packages/lone-util/web/compat.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { inBrowser } from '../env' 4 | 5 | // check whether current browser encodes a char inside attribute values 6 | function shouldDecode (content, encoded) { 7 | const div = document.createElement('div') 8 | div.innerHTML = `
` 9 | return div.innerHTML.indexOf(encoded) > 0 10 | } 11 | 12 | // #3663 13 | // IE encodes newlines inside attribute values while other browsers don't 14 | export const shouldDecodeNewlines = inBrowser ? shouldDecode('\n', ' ') : false 15 | -------------------------------------------------------------------------------- /packages/lone-messenger/slave/worker-messenger.js: -------------------------------------------------------------------------------- 1 | import Messenger from './base' 2 | 3 | class WorkerMessenger extends Messenger { 4 | constructor (options) { 5 | super() 6 | this.channel = options.channel 7 | this.listen() 8 | } 9 | 10 | _postMessage (type, targetChannel, data) { 11 | self.postMessage({ type, channel: this.channel, targetChannel, data }) 12 | } 13 | 14 | _onmessage (fn) { 15 | self.onmessage = function (evt) { 16 | fn.call(evt, evt.data) 17 | } 18 | } 19 | } 20 | 21 | export default WorkerMessenger 22 | -------------------------------------------------------------------------------- /packages/lone-util/env.js: -------------------------------------------------------------------------------- 1 | // Browser environment sniffing 2 | export const inBrowser = typeof window !== 'undefined' 3 | export const inWorker = typeof self !== 'undefined' 4 | export const UA = inBrowser && window.navigator.userAgent.toLowerCase() 5 | export const isIE = UA && /msie|trident/.test(UA) 6 | export const isIE9 = UA && UA.indexOf('msie 9.0') > 0 7 | export const isEdge = UA && UA.indexOf('edge/') > 0 8 | export const isAndroid = UA && UA.indexOf('android') > 0 9 | export const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA) 10 | export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge 11 | -------------------------------------------------------------------------------- /packages/lone-util/web/index.js: -------------------------------------------------------------------------------- 1 | import { warn } from '../index' 2 | 3 | export * from './attrs' 4 | export * from './class' 5 | export * from './element' 6 | 7 | /** 8 | * Query an element selector if it's not an element already. 9 | */ 10 | export function query (el) { 11 | if (typeof el === 'string') { 12 | const selected = document.querySelector(el) 13 | if (!selected) { 14 | warn( 15 | 'Cannot find element: ' + el + '. Used body instead!' 16 | ) 17 | return document.body 18 | } 19 | return selected 20 | } else { 21 | return el 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/lone-compiler-dom/options.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { 4 | isPreTag, 5 | mustUseProp, 6 | isReservedTag, 7 | getTagNamespace 8 | } from 'lone-util/web' 9 | 10 | import modules from './modules/index' 11 | import directives from './directives/index' 12 | import { genStaticKeys, isUnaryTag, canBeLeftOpenTag } from 'lone-util' 13 | 14 | export const baseOptions = { 15 | expectHTML: true, 16 | modules, 17 | directives, 18 | isPreTag, 19 | isUnaryTag, 20 | mustUseProp, 21 | canBeLeftOpenTag, 22 | isReservedTag, 23 | getTagNamespace, 24 | staticKeys: genStaticKeys(modules) 25 | } 26 | -------------------------------------------------------------------------------- /packages/lone-virtualdom/render-helpers/render-slot.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { warn } from 'lone-util' 4 | 5 | /** 6 | * Runtime helper for rendering 7 | */ 8 | export default function renderSlot (name, fallback, props, bindObject) { 9 | const slotNodes = this.$slots[name] 10 | // warn duplicate slot usage 11 | if (slotNodes) { 12 | slotNodes._rendered && warn( 13 | `Duplicate presence of slot "${name}" found in the same render tree ` + 14 | '- this will likely cause render errors.', 15 | this 16 | ) 17 | slotNodes._rendered = true 18 | } 19 | return slotNodes || fallback 20 | } 21 | -------------------------------------------------------------------------------- /packages/lone-page/index.js: -------------------------------------------------------------------------------- 1 | import logicMaster from 'lone-logic-master' 2 | import Component from './component' 3 | 4 | export default function (options) { 5 | const mid = window.frameElement.getAttribute('mid') 6 | const pid = window.frameElement.id 7 | const pathname = window.frameElement.getAttribute('pathname') 8 | const search = window.frameElement.getAttribute('search') 9 | const name = window.frameElement.getAttribute('component') 10 | Component.setGlobalOptions({ ...options, mid, pid, pathname, search }) 11 | logicMaster(Component.options) 12 | return new Component({ name, el: document.getElementById('app') }) 13 | } 14 | -------------------------------------------------------------------------------- /packages/lone-virtualdom/render-helpers/h.js: -------------------------------------------------------------------------------- 1 | import { h } from '../index' 2 | import { isArray } from 'lone-util' 3 | 4 | export default function (sel, a, b) { 5 | if (isArray(a)) a = normalizeArrayChildren(a) 6 | if (isArray(b)) b = normalizeArrayChildren(b) 7 | return h(sel, a, b) 8 | } 9 | 10 | function normalizeArrayChildren (children) { 11 | const res = [] 12 | for (let i = 0, l = children.length; i < l; i++) { 13 | const child = children[i] 14 | if (isArray(child)) { 15 | res.push.apply(res, normalizeArrayChildren(child)) 16 | } else { 17 | res.push(child) 18 | } 19 | } 20 | return res 21 | } 22 | -------------------------------------------------------------------------------- /example/multi-miniapp/app.main.js: -------------------------------------------------------------------------------- 1 | importScripts('../../dist/lone.logic.js') 2 | 3 | Lone.logic('test', { 4 | data: () => ({ list: [1, 2] }), 5 | methods: { 6 | to (url) { 7 | this.navigateTo({ url }) 8 | } 9 | }, 10 | mounted () { 11 | const vm = this 12 | setTimeout(() => { 13 | vm.setData({ 14 | list: [...vm.data.list, 3] 15 | }) 16 | }, 1000) 17 | } 18 | }) 19 | 20 | Lone.logic('test2', { 21 | data () { 22 | return { 23 | n: 0 24 | } 25 | }, 26 | onReady () { 27 | setTimeout(() => { 28 | this.setData({ n: 1 }) 29 | }, 1000) 30 | }, 31 | back () { 32 | this.navigateBack() 33 | } 34 | }) 35 | -------------------------------------------------------------------------------- /packages/lone-messenger/master/native-messenger.js: -------------------------------------------------------------------------------- 1 | import { isObject } from 'lone-util' 2 | 3 | class NativeMessenger { 4 | connection () { 5 | window.senative.call('frontPageReady', '', function (code, msg, data) {}) 6 | } 7 | 8 | send (type, data, channel) { 9 | if (!isObject(data)) throw new TypeError('data must be plain object.') 10 | const bag = JSON.stringify({ type, data, channel }) 11 | window.senative.call('sendMessage', bag, (code, data, msg) => {}) 12 | } 13 | 14 | onmessage (fn) { 15 | window.onSeNativeMessage = function (rawData) { 16 | const data = JSON.parse(rawData) 17 | fn(data) 18 | } 19 | } 20 | } 21 | 22 | export default NativeMessenger 23 | -------------------------------------------------------------------------------- /packages/lone-compiler-core/index.js: -------------------------------------------------------------------------------- 1 | import { parse } from './parser/index' 2 | import { generate } from './codegen/index' 3 | import { createCompilerCreator } from './create-compiler' 4 | 5 | // `createCompilerCreator` allows creating compilers that use alternative 6 | // parser/optimizer/codegen, e.g the SSR optimizing compiler. 7 | // Here we just export a default compiler using the default parts. 8 | export const createCompiler = createCompilerCreator(function baseCompile (template, options) { 9 | const ast = parse(template.trim(), options) 10 | const code = generate(ast, options) 11 | return { 12 | ast, 13 | render: code.render, 14 | staticRenderFns: code.staticRenderFns 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /packages/lone-logic/component/observer.js: -------------------------------------------------------------------------------- 1 | export function notifyPropsObserver (vm, oldData, newData) { 2 | const propsOptions = vm.$options.props 3 | for (const key in newData) { 4 | if (key in propsOptions) { 5 | const cur = newData[key] 6 | const old = oldData[key] 7 | const observer = propsOptions[key].observer 8 | if (JSON.stringify(cur) !== JSON.stringify(old)) { 9 | observer && observer.call(vm, cur, old) 10 | } 11 | } 12 | } 13 | for (const key in oldData) { 14 | if (!(key in newData)) { 15 | const old = oldData[key] 16 | const observer = propsOptions[key].observer 17 | observer && observer.call(vm, null, old) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/lone-virtualdom/index.js: -------------------------------------------------------------------------------- 1 | import { init } from 'snabbdom' 2 | import attrs from 'snabbdom/modules/attributes' 3 | import cls from 'snabbdom/modules/class' // makes it easy to toggle classes 4 | import props from 'snabbdom/modules/props' // for setting properties on DOM elements 5 | import style from 'snabbdom/modules/style' // handles styling on elements with support for animations 6 | import eventlisteners from 'snabbdom/modules/eventlisteners' // attaches event listeners 7 | import createComponent from './create-component' 8 | 9 | export const patch = init([ 10 | attrs, 11 | cls, 12 | props, 13 | style, 14 | eventlisteners, 15 | createComponent 16 | ]) 17 | 18 | export { default as h } from 'snabbdom/h' 19 | -------------------------------------------------------------------------------- /example/multi-miniapp/app.page.js: -------------------------------------------------------------------------------- 1 | Lone.page({ 2 | components: [ 3 | { 4 | name: 'test', 5 | template: ` 6 |
7 |

Page 1

8 |
    9 |
  • {{item}}
  • 10 |
11 |
    12 |
  • 13 |
14 |
15 | ` 16 | }, 17 | { 18 | name: 'test2', 19 | template: ` 20 |
21 | 22 |

我是第二个页面: {{n}}

23 | 24 |
25 | ` 26 | } 27 | ] 28 | }) 29 | -------------------------------------------------------------------------------- /doc/lone-api/lifecycle.md: -------------------------------------------------------------------------------- 1 | # 生命周期 2 | 3 | * beforeCreate 4 | * created && onLoad 5 | * onShow 6 | * beforeMount 7 | * onReady && mounted 8 | * beforeUpdate 9 | * updated 10 | * onHide 11 | 12 | #### beforeCreate 13 | 14 | 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。 15 | 16 | #### created && onLoad 17 | 18 | 在实例创建完成后被立即调用。在这一步,除了UI还没有渲染,其他都已经完成。 19 | 20 | #### onShow 21 | 22 | 页面展示的时候被调用 23 | 24 | #### beforeMount 25 | 26 | 在挂载开始之前被调用:相关的 render 函数首次被调用。 27 | 28 | #### onReady && mounted 29 | 30 | 首次渲染完成后触发。 31 | 32 | #### beforeUpdate 33 | 34 | 数据更新后,发生在虚拟 DOM 打补丁之前调用。 35 | 36 | 换句话说,渲染前调用。 37 | 38 | #### updated 39 | 40 | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。 41 | 42 | 换句话说,渲染完成后调用。 43 | 44 | #### onHide 45 | 46 | 页面隐藏的时候被调用 47 | -------------------------------------------------------------------------------- /packages/lone-messenger/slave/native-messenger.js: -------------------------------------------------------------------------------- 1 | // 该文件当前不可用,因为没有 Native 消息通道 2 | import Messenger from './base' 3 | import { isObject } from 'lone-util' 4 | 5 | class NativeMessenger extends Messenger { 6 | constructor () { 7 | super() 8 | this.listen() 9 | } 10 | 11 | _onmessage (fn) { 12 | window.onSeNativeMessage = function (rawData) { 13 | const data = JSON.parse(rawData) 14 | fn(data) 15 | } 16 | } 17 | 18 | _postMessage (type, channel, data) { 19 | if (!isObject(data)) throw new TypeError('data must be plain object.') 20 | const bag = JSON.stringify({ type, channel, data }) 21 | window.senative.call('sendMessage', bag, (code, data, msg) => {}) 22 | } 23 | } 24 | 25 | export default NativeMessenger 26 | -------------------------------------------------------------------------------- /packages/lone-ui/index.js: -------------------------------------------------------------------------------- 1 | import Schedule from './schedule' 2 | import Router from './router' 3 | 4 | let id = 0 5 | 6 | class LoneUI { 7 | constructor (options) { 8 | this.options = options 9 | // ‘mid’, a shortened form of a Miniapp ID 10 | this.mid = options.mid || (Date.now() + '_' + id++) 11 | this.router = new Router({ 12 | routes: this.options.routes, 13 | entry: this.options.entry, 14 | container: this.options.container || document.body, 15 | mid: this.mid 16 | }) 17 | this.schedule = new Schedule({ 18 | router: this.router, 19 | entry: this.options.entry, 20 | mid: this.mid 21 | }) 22 | } 23 | } 24 | 25 | export default function (options) { 26 | return new LoneUI(options) 27 | } 28 | -------------------------------------------------------------------------------- /packages/lone-logic-master/index.js: -------------------------------------------------------------------------------- 1 | import { Slave } from 'lone-messenger' 2 | import schedule from 'lone-logic/schedule' 3 | import logicComponentRegister from 'lone-logic' 4 | 5 | export default function logicMaster (UIConf) { 6 | const mid = UIConf.mid 7 | const pid = UIConf.pid 8 | const components = UIConf.components 9 | const slave = new Slave({ env: 'postMessage', channel: `${mid}_${pid}_logic` }) 10 | registerComponent(components) 11 | schedule(slave) 12 | } 13 | 14 | function registerComponent (components) { 15 | for (let i = 0, len = components.length; i < len; i++) { 16 | const component = components[i] 17 | const name = component.name 18 | const official = component.official 19 | if (official) { 20 | logicComponentRegister(name, component) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/multi-miniapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 多小程序并存 8 | 15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /packages/lone-compiler-dom/modules/class.js: -------------------------------------------------------------------------------- 1 | import { 2 | getAndRemoveAttr, 3 | getBindingAttr 4 | } from 'lone-compiler-core/helpers' 5 | 6 | function transformNode (el) { 7 | const classBinding = getBindingAttr(el, 'class', false /* getStatic */) 8 | if (classBinding) { 9 | el.classBinding = classBinding 10 | } 11 | const staticClass = getAndRemoveAttr(el, 'class') 12 | if (staticClass) { 13 | const kclass = staticClass.trim().split(' ').map(name => `'${name}':true`).join(',') 14 | el.classBinding = el.classBinding 15 | ? el.classBinding.substring(0, el.classBinding.length - 1) + kclass + '}' 16 | : `{${kclass}}` 17 | } 18 | } 19 | 20 | function genData (el) { 21 | let data = '' 22 | if (el.classBinding) { 23 | data += `class:${el.classBinding},` 24 | } 25 | return data 26 | } 27 | 28 | export default { 29 | transformNode, 30 | genData 31 | } 32 | -------------------------------------------------------------------------------- /packages/lone-messenger/slave/post-messenger.js: -------------------------------------------------------------------------------- 1 | import BaseMessenger from './base' 2 | 3 | const connection = Symbol('messenger:slave#connection') 4 | 5 | class PostMessenger extends BaseMessenger { 6 | constructor (options) { 7 | super() 8 | this.channel = options.channel 9 | this.listen() 10 | this[connection]() 11 | } 12 | 13 | [connection] () { 14 | this._postMessage('connection') 15 | } 16 | 17 | _onmessage (fn) { 18 | const vm = this 19 | window.addEventListener('message', function (evt) { 20 | if (evt.data.targetChannel === vm.channel) { 21 | fn.call(evt, evt.data) 22 | } 23 | }) 24 | } 25 | 26 | _postMessage (type, targetChannel, data) { 27 | const slave = window.parent 28 | slave.postMessage({ type, channel: this.channel, targetChannel, data }, slave.origin) 29 | } 30 | } 31 | 32 | export default PostMessenger 33 | -------------------------------------------------------------------------------- /packages/lone-virtualdom/render-helpers/index.js: -------------------------------------------------------------------------------- 1 | import { toString, looseIndexOf, looseEqual } from 'lone-util' 2 | import h from './h' 3 | import renderList from './render-list' 4 | import renderSlot from './render-slot' 5 | 6 | export function installRenderHelpers (target) { 7 | const proto = target.prototype 8 | // createTextVNode 9 | proto._v = text => text 10 | // create Element Vnode 11 | proto._c = h 12 | proto._s = toString 13 | proto._l = renderList 14 | proto._t = renderSlot 15 | proto._i = looseIndexOf 16 | proto._q = looseEqual 17 | 18 | // target._o = markOnce 19 | // target._m = renderStatic 20 | // target._f = resolveFilter 21 | // target._k = checkKeyCodes 22 | // target._b = bindObjectProps 23 | // target._v = createTextVNode 24 | // target._e = createEmptyVNode 25 | // target._u = resolveScopedSlots 26 | // target._g = bindObjectListeners 27 | } 28 | -------------------------------------------------------------------------------- /packages/lone-page/component/slot.js: -------------------------------------------------------------------------------- 1 | export function resolveSlots (children) { 2 | const slots = {} 3 | if (!children) { 4 | return slots 5 | } 6 | const defaultSlot = [] 7 | for (let i = 0, l = children.length; i < l; i++) { 8 | const child = children[i] 9 | const data = child.data 10 | // remove slot attribute if the node is resolved as a Vue slot node 11 | if (data && data.attrs && data.attrs.slot) { 12 | delete data.attrs.slot 13 | } 14 | if (data && data.slot != null) { 15 | const name = child.data.slot 16 | const slot = (slots[name] || (slots[name] = [])) 17 | slot.push(child) 18 | } else { 19 | defaultSlot.push(child) 20 | } 21 | } 22 | // ignore whitespace 23 | if (!defaultSlot.every(isWhitespace)) { 24 | slots.default = defaultSlot 25 | } 26 | return slots 27 | } 28 | 29 | function isWhitespace (node) { 30 | return node.text === ' ' 31 | } 32 | -------------------------------------------------------------------------------- /packages/lone-virtualdom/render-helpers/render-list.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isObject, isDef } from 'lone-util' 4 | 5 | /** 6 | * Runtime helper for rendering v-for lists. 7 | */ 8 | export default function renderList (val, render) { 9 | let ret, i, l, keys, key 10 | if (Array.isArray(val) || typeof val === 'string') { 11 | ret = new Array(val.length) 12 | for (i = 0, l = val.length; i < l; i++) { 13 | ret[i] = render(val[i], i) 14 | } 15 | } else if (typeof val === 'number') { 16 | ret = new Array(val) 17 | for (i = 0; i < val; i++) { 18 | ret[i] = render(i + 1, i) 19 | } 20 | } else if (isObject(val)) { 21 | keys = Object.keys(val) 22 | ret = new Array(keys.length) 23 | for (i = 0, l = keys.length; i < l; i++) { 24 | key = keys[i] 25 | ret[i] = render(val[key], key, i) 26 | } 27 | } 28 | if (isDef(ret)) { 29 | ret._isVList = true 30 | } 31 | return ret 32 | } 33 | -------------------------------------------------------------------------------- /packages/lone-logic/component/router.js: -------------------------------------------------------------------------------- 1 | import { noop } from 'lone-util' 2 | 3 | export default function events (Lone) { 4 | const proto = Lone.prototype 5 | proto.navigateTo = navigateTo 6 | proto.redirectTo = redirectTo 7 | proto.navigateBack = navigateBack 8 | } 9 | 10 | function navigateTo ({ url, success = noop, fail = noop, complete = noop }) { 11 | this._slave 12 | .send('logic:navigateTo', null, { url }) 13 | .then(res => success(res), err => fail(err)) 14 | .then(complete) 15 | } 16 | 17 | function redirectTo ({ url, success = noop, fail = noop, complete = noop }) { 18 | this._slave 19 | .send('logic:redirectTo', null, { url }) 20 | .then(res => success(res), err => fail(err)) 21 | .then(complete) 22 | } 23 | 24 | function navigateBack ({ delta, success = noop, fail = noop, complete = noop } = {}) { 25 | this._slave 26 | .send('logic:navigateBack', null, { delta }) 27 | .then(res => success(res), err => fail(err)) 28 | .then(complete) 29 | } 30 | -------------------------------------------------------------------------------- /packages/lone-messenger/master/post-messenger.js: -------------------------------------------------------------------------------- 1 | const source = Symbol('messenger:master#connection') 2 | 3 | class PostMessenger { 4 | constructor (options) { 5 | this[source] = Object.create(null) 6 | this.mid = options.mid 7 | this.midRe = new RegExp('^' + this.mid) 8 | } 9 | 10 | connection () { 11 | const vm = this 12 | vm.onmessage(function ({ type, channel }) { 13 | if (type === 'connection') { 14 | vm[source][channel] = this.source 15 | } 16 | }) 17 | } 18 | 19 | onmessage (fn) { 20 | const vm = this 21 | window.addEventListener('message', function (evt) { 22 | if (evt.origin !== location.origin || !vm.midRe.test(evt.data.channel)) return 23 | fn.call(evt, evt.data) 24 | }) 25 | } 26 | 27 | send (type, targetChannel, data, channel) { 28 | const slave = this[source][targetChannel] 29 | if (!slave) throw new Error('No Slave Source, please connection first!') 30 | slave.postMessage({ type, targetChannel, data, channel }, slave.origin) 31 | } 32 | } 33 | 34 | export default PostMessenger 35 | -------------------------------------------------------------------------------- /doc/lone-api/router.md: -------------------------------------------------------------------------------- 1 | # 路由 2 | 3 | #### `this.navigateTo(options)` 4 | 5 | 跳转到新页面。 6 | 7 | ```javascript 8 | this.navigateTo({ 9 | url: '/test', 10 | success () {}, 11 | fail () {}, 12 | complete () {} 13 | }) 14 | ``` 15 | 16 | **参数具体描述:** 17 | 18 | | 属性 | 类型 | 必填 | 说明 | 19 | | ---- | ---- | ---- | ---- | 20 | | url | String | 是 | 需要跳转的页面的路由地址,路径后可以带参数。参数与路径之间使用 ? 分隔,参数键与参数值用 = 相连,不同参数用 & 分隔;如 'path?key=value&key2=value2' | 21 | | success | Function | 否 | 接口调用成功的回调函数 | 22 | | fail | Function | 否 | 接口调用失败的回调函数 | 23 | | complete | Function | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | 24 | 25 | #### `this.redirectTo(options)` 26 | 27 | 参数与 `this.navigateTo` 相同,只是先移除当前页面,然后在跳转到新页面。 28 | 29 | #### `this.navigateBack(options)` 30 | 31 | 关闭当前页面,返回上一页面或多级页面。 32 | 33 | **参数具体描述:** 34 | 35 | | 属性 | 类型 | 必填 | 说明 | 36 | | ---- | ---- | ---- | ---- | 37 | | delta | Number | 否 | 返回的页面数,如果 delta 大于现有页面数,则返回到首页。默认值:1 | 38 | | success | Function | 否 | 接口调用成功的回调函数 | 39 | | fail | Function | 否 | 接口调用失败的回调函数 | 40 | | complete | Function | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) | 41 | 42 | -------------------------------------------------------------------------------- /packages/lone-util/error.js: -------------------------------------------------------------------------------- 1 | export function handleError (err, vm, info) { 2 | warn(`Error in ${info}: "${err.toString()}"`, vm, err) 3 | } 4 | 5 | const classifyRE = /(?:^|[-_])(\w)/g 6 | const classify = str => 7 | str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '') 8 | 9 | export function warn (msg, vm, err) { 10 | const trace = vm ? generateComponentTrace(vm) : '' 11 | const hasErr = err 12 | 13 | err = err || { toString () { return this.name + this.message } } 14 | err.name = '[Lone warn]' 15 | err.message = ` ${msg}${trace}` 16 | 17 | console.error(hasErr ? err : String(err)) 18 | } 19 | 20 | export function tip (msg, vm) { 21 | const trace = vm ? generateComponentTrace(vm) : '' 22 | console.warn(`[Lone tip]: ${msg}${trace}`) 23 | } 24 | 25 | export function formatComponentName (vm) { 26 | const name = vm.$options && vm.$options.name 27 | return (name ? `<${classify(name)}>` : '') 28 | } 29 | 30 | export function generateComponentTrace (vm) { 31 | return `\n\n(found in ${formatComponentName(vm)})` 32 | } 33 | -------------------------------------------------------------------------------- /packages/lone-util/url.js: -------------------------------------------------------------------------------- 1 | import { isArray } from './index' 2 | 3 | export function parse (url) { 4 | const a = document.createElement('a') 5 | a.href = url 6 | return { 7 | hash: a.hash, 8 | host: a.host, 9 | hostname: a.hostname, 10 | href: a.href, 11 | origin: a.origin, 12 | password: a.password, 13 | pathname: a.pathname, 14 | port: a.port, 15 | protocol: a.protocol, 16 | search: a.search, 17 | username: a.username 18 | } 19 | } 20 | 21 | // parseSearch('abc=123&abc=xyz') => { abc: ['123', 'xyz'] } 22 | export function parseSearch (search, sep = '&', eq = '=') { 23 | search = search.trim().replace(/^\?/, '').trim() 24 | const res = Object.create(null) 25 | if (search === '') return res 26 | return search.split(sep).reduce((res, param) => { 27 | const [key, value] = param.split(eq) 28 | if (!key) return res 29 | return { 30 | ...res, 31 | [key]: res[key] 32 | ? isArray(res[key]) 33 | ? [...res[key], value] 34 | : [res[key], value] 35 | : value 36 | } 37 | }, res) 38 | } 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "version": "1.0.0", 5 | "main": "dist/lone.js", 6 | "scripts": { 7 | "dev": "webpack -w --config scripts/webpack.dev.conf.js", 8 | "build": "webpack --config scripts/webpack.prod.conf.js", 9 | "test": "echo \"Error: no test specified\" && exit 1", 10 | "bootstrap": "lerna bootstrap" 11 | }, 12 | "devDependencies": { 13 | "@babel/core": "^7.6.4", 14 | "@babel/plugin-proposal-decorators": "^7.6.0", 15 | "@babel/plugin-transform-modules-commonjs": "^7.7.5", 16 | "@babel/preset-env": "^7.6.3", 17 | "babel-eslint": "^10.0.3", 18 | "babel-loader": "^8.0.6", 19 | "eslint": "^6.5.1", 20 | "eslint-config-standard": "^14.1.0", 21 | "eslint-friendly-formatter": "^4.0.1", 22 | "eslint-loader": "^3.0.2", 23 | "eslint-plugin-import": "^2.18.2", 24 | "eslint-plugin-node": "^10.0.0", 25 | "eslint-plugin-promise": "^4.2.1", 26 | "eslint-plugin-standard": "^4.0.1", 27 | "lerna": "^3.15.0", 28 | "webpack": "^4.41.1", 29 | "webpack-cli": "^3.3.9", 30 | "webpack-merge": "^4.2.2" 31 | } 32 | } -------------------------------------------------------------------------------- /packages/lone-virtualdom/create-component.js: -------------------------------------------------------------------------------- 1 | import { isReservedTag } from 'lone-util/web' 2 | import Component from 'lone-page/component' 3 | 4 | export default { 5 | create: function (oldVnode, vnode) { 6 | if (!isReservedTag(vnode.sel) && isComponent(vnode.sel)) { 7 | const { attrs, on } = vnode.data 8 | const component = new Component({ 9 | name: vnode.sel, 10 | el: vnode.elm, 11 | propsData: attrs, 12 | _parentListeners: on, 13 | _renderChildren: vnode.children 14 | }) 15 | vnode.elm.component = component 16 | } 17 | }, 18 | update (oldVnode, vnode) { 19 | if (!isReservedTag(vnode.sel) && isComponent(vnode.sel)) { 20 | const oldAttrs = oldVnode.data.attrs 21 | const attrs = vnode.data.attrs 22 | const component = vnode.elm.component 23 | if ((oldAttrs || attrs) && JSON.stringify(oldAttrs) !== JSON.stringify(attrs)) { 24 | component.updatePropsData(attrs, oldAttrs) 25 | } 26 | } 27 | } 28 | } 29 | 30 | function isComponent (name) { 31 | return Component.options.components.find(component => component.name === name) 32 | } 33 | -------------------------------------------------------------------------------- /doc/lone-api/conditional.md: -------------------------------------------------------------------------------- 1 | # 条件渲染 2 | 3 | ## v-if 4 | 5 | v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 `truthy` 值的时候被渲染。 6 | 7 | ```html 8 |

Vue is awesome!

9 | ``` 10 | 11 | 也可以用 `v-else` 添加一个“else 块”: 12 | 13 | ```html 14 |

Vue is awesome!

15 |

Oh no 😢

16 | ``` 17 | 18 | ## v-else-if 19 | 20 | `v-else-if`,顾名思义,充当 `v-if` 的 “else-if 块”,可以连续使用: 21 | 22 | ```html 23 |
24 | A 25 |
26 |
27 | B 28 |
29 |
30 | C 31 |
32 |
33 | Not A/B/C 34 |
35 | ``` 36 | 37 | ## v-show 38 | 39 | 另一个用于根据条件展示元素的选项是 `v-show` 指令。用法大致一样: 40 | 41 | ```html 42 |

Hello!

43 | ``` 44 | 45 | 不同的是带有 `v-show` 的元素始终会被渲染并保留在 DOM 中。`v-show` 只是简单地切换元素的 CSS 属性 `display`。 46 | 47 | ## v-if vs v-show 48 | 49 | `v-if` 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。 50 | 51 | `v-if` 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。 52 | 53 | 相比之下,`v-show` 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。 54 | 55 | 一般来说,`v-if` 有更高的切换开销,而 `v-show` 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 `v-show` 较好;如果在运行时条件很少改变,则使用 `v-if` 较好。 56 | -------------------------------------------------------------------------------- /packages/lone-compiler-core/parser/text-parser.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { cached } from 'lone-util' 4 | import { parseFilters } from './filter-parser' 5 | 6 | const defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g 7 | // eslint-disable-next-line no-useless-escape 8 | const regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g 9 | 10 | const buildRegex = cached(delimiters => { 11 | const open = delimiters[0].replace(regexEscapeRE, '\\$&') 12 | const close = delimiters[1].replace(regexEscapeRE, '\\$&') 13 | return new RegExp(open + '((?:.|\\n)+?)' + close, 'g') 14 | }) 15 | 16 | export function parseText (text, delimiters) { 17 | const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE 18 | if (!tagRE.test(text)) { 19 | return 20 | } 21 | const tokens = [] 22 | let lastIndex = tagRE.lastIndex = 0 23 | let match, index 24 | while ((match = tagRE.exec(text))) { 25 | index = match.index 26 | // 先把 {{ 双大括号前边的 text push 到 tokens 中 27 | // push text token 28 | if (index > lastIndex) { 29 | tokens.push(JSON.stringify(text.slice(lastIndex, index))) 30 | } 31 | // tag token 32 | const exp = parseFilters(match[1].trim()) 33 | tokens.push(`_s(${exp})`) 34 | lastIndex = index + match[0].length 35 | } 36 | if (lastIndex < text.length) { 37 | tokens.push(JSON.stringify(text.slice(lastIndex))) 38 | } 39 | return tokens.join('+') 40 | } 41 | -------------------------------------------------------------------------------- /packages/lone-page/component/eventListener.js: -------------------------------------------------------------------------------- 1 | import { proxy } from 'lone-util' 2 | 3 | const customType = 'custom' 4 | 5 | export function initParentListener (vm) { 6 | vm._parentListeners = Object.create(null) 7 | const listeners = vm.options._parentListeners 8 | if (listeners) { 9 | vm._parentListeners = listeners 10 | } 11 | vm.slave.onmessage('component:triggerParentEvent', function ({ name, data }) { 12 | vm._parentListeners[name]({ type: customType, data }) 13 | }) 14 | } 15 | 16 | export function initEventListener (vm, methods) { 17 | vm._eventListener = Object.create(null) 18 | let i = methods.length 19 | while (i--) { 20 | const method = methods[i] 21 | vm._eventListener[method] = function (event) { 22 | vm.slave.send('page:triggerEvent', vm.getLogicChannel(), { 23 | id: vm.id, 24 | event: event.type === customType 25 | ? event.data 26 | : getEvent(event), 27 | method 28 | }) 29 | } 30 | proxy(vm, '_eventListener', method) 31 | } 32 | } 33 | 34 | function getEvent (event) { 35 | // Custom parameters 36 | if (!event.type && !event.timeStamp && !event.target) return event 37 | return { 38 | type: event.type, 39 | timeStamp: event.timeStamp, 40 | target: { 41 | value: event.target.value 42 | }, 43 | detail: { 44 | x: event.x, 45 | y: event.y 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/lone-messenger/slave/base.js: -------------------------------------------------------------------------------- 1 | import { feedbackType } from 'lone-util' 2 | 3 | class Messenger { 4 | constructor () { 5 | this._messages = Object.create(null) 6 | } 7 | 8 | onmessage (type, fn) { 9 | (this._messages[type] || (this._messages[type] = [])).push(fn) 10 | } 11 | 12 | send (type, channel, data) { 13 | const vm = this 14 | return new Promise(function (resolve, reject) { 15 | vm._postMessage(type, channel, data) 16 | vm.onmessage(feedbackType(type), function ({ err, data }) { 17 | vm._messages[feedbackType(type)] = [] 18 | return err 19 | ? reject(err) 20 | : resolve(data) 21 | }) 22 | }) 23 | } 24 | 25 | listen () { 26 | const vm = this 27 | vm._onmessage(evt => { 28 | const cbs = vm._messages[evt.type] 29 | if (!cbs) return 30 | let i = cbs.length 31 | while (i--) { 32 | Promise.resolve(cbs[i].call(evt, evt.data)) 33 | .then( 34 | data => vm._postMessage(feedbackType(evt.type), evt.channel, { data }), 35 | err => vm._postMessage(feedbackType(evt.type), evt.channel, { err }) 36 | ) 37 | } 38 | }) 39 | } 40 | 41 | _postMessage () { 42 | throw new TypeError('Subclass of Messenger doesn\'t provide the \'_postMessage\' method.') 43 | } 44 | 45 | _onmessage () { 46 | throw new TypeError('Subclass of Messenger doesn\'t provide the \'_onmessage\' method.') 47 | } 48 | } 49 | 50 | export default Messenger 51 | -------------------------------------------------------------------------------- /scripts/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | function resolve (dir) { 4 | return path.resolve(__dirname, '../', dir) 5 | } 6 | 7 | module.exports = { 8 | entry: { 9 | logic: resolve('packages/lone-logic-worker/index.js'), 10 | ui: resolve('packages/lone-ui/index.js'), 11 | page: resolve('packages/lone-page/index.js') 12 | }, 13 | output: { 14 | path: resolve('dist'), 15 | filename: 'Lone.[name].js', 16 | library: ['Lone', '[name]'], 17 | libraryExport: 'default' 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.(js)$/, 23 | loader: 'eslint-loader', 24 | enforce: 'pre', 25 | exclude: /node_modules/, 26 | options: { 27 | formatter: require('eslint-friendly-formatter') 28 | } 29 | }, 30 | { 31 | test: /\.js$/, 32 | exclude: /node_modules/, 33 | loader: 'babel-loader', 34 | options: { 35 | presets: [ 36 | ['@babel/preset-env', { targets: { ie: '8' } }] 37 | ], 38 | plugins: [ 39 | ['@babel/plugin-transform-modules-commonjs', { loose: true }], 40 | ['@babel/plugin-proposal-decorators', { legacy: true }] 41 | ] 42 | } 43 | }, 44 | { 45 | test: /\.js$/, 46 | loader: resolve('scripts/replace-loader.js'), 47 | exclude: /node_modules/, 48 | options: { 49 | __PAGEJS__: '"../../dist/lone.page.js"' 50 | } 51 | } 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/lone-compiler-dom/modules/style.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parseText } from 'lone-compiler-core/parser/text-parser' 4 | import { parseStyleText } from 'lone-util/web/style' 5 | import { 6 | getAndRemoveAttr, 7 | getBindingAttr, 8 | baseWarn 9 | } from 'lone-compiler-core/helpers' 10 | 11 | function transformNode (el, options) { 12 | const warn = options.warn || baseWarn 13 | const styleBinding = getBindingAttr(el, 'style', false /* getStatic */) 14 | if (styleBinding) { 15 | el.styleBinding = styleBinding 16 | } 17 | 18 | const staticStyle = getAndRemoveAttr(el, 'style') 19 | if (staticStyle) { 20 | /* istanbul ignore if */ 21 | if (process.env.NODE_ENV !== 'production') { 22 | const expression = parseText(staticStyle, options.delimiters) 23 | if (expression) { 24 | warn( 25 | `style="${staticStyle}": ` + 26 | 'Interpolation inside attributes has been removed. ' + 27 | 'Use v-bind or the colon shorthand instead. For example, ' + 28 | 'instead of
, use
.' 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 |
    30 |
  • Foo
  • 31 |
  • Bar
  • 32 |
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 甚至不会被包含在渲染出来的 `