├── .babelrc.js ├── .circleci └── config.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .flowconfig ├── .github ├── CODE_OF_CONDUCT.md ├── COMMIT_CONVENTION.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── BACKERS.md ├── LICENSE ├── README.md ├── benchmarks ├── big-table │ ├── demo.css │ ├── index.html │ └── style.css ├── dbmon │ ├── ENV.js │ ├── app.js │ ├── index.html │ └── lib │ │ ├── memory-stats.js │ │ ├── monitor.js │ │ └── styles.css ├── reorder-list │ └── index.html ├── ssr │ ├── README.md │ ├── common.js │ ├── renderToStream.js │ └── renderToString.js ├── svg │ └── index.html └── uptime │ └── index.html ├── dist ├── README.md ├── vue.common.dev.js ├── vue.common.js ├── vue.common.prod.js ├── vue.esm.browser.js ├── vue.esm.browser.min.js ├── vue.esm.js ├── vue.js ├── vue.min.js ├── vue.runtime.common.dev.js ├── vue.runtime.common.js ├── vue.runtime.common.prod.js ├── vue.runtime.esm.js ├── vue.runtime.js └── vue.runtime.min.js ├── examples ├── commits │ ├── app.js │ ├── index.html │ └── mock.js ├── elastic-header │ ├── index.html │ └── style.css ├── firebase │ ├── app.js │ ├── index.html │ └── style.css ├── grid │ ├── grid.js │ ├── index.html │ └── style.css ├── markdown │ ├── index.html │ └── style.css ├── modal │ ├── index.html │ └── style.css ├── move-animations │ └── index.html ├── select2 │ └── index.html ├── svg │ ├── index.html │ ├── style.css │ └── svg.js ├── todomvc │ ├── app.js │ ├── index.html │ └── readme.md └── tree │ ├── index.html │ └── tree.js ├── flow ├── compiler.js ├── component.js ├── global-api.js ├── modules.js ├── options.js ├── ssr.js ├── vnode.js └── weex.js ├── package.json ├── packages ├── vue-server-renderer │ ├── README.md │ ├── basic.js │ ├── build.dev.js │ ├── build.prod.js │ ├── client-plugin.d.ts │ ├── client-plugin.js │ ├── index.js │ ├── package.json │ ├── server-plugin.d.ts │ ├── server-plugin.js │ └── types │ │ ├── index.d.ts │ │ ├── plugin.d.ts │ │ └── tsconfig.json ├── vue-template-compiler │ ├── README.md │ ├── browser.js │ ├── build.js │ ├── index.js │ ├── package.json │ └── types │ │ ├── index.d.ts │ │ ├── test.ts │ │ └── tsconfig.json ├── weex-template-compiler │ ├── README.md │ ├── build.js │ ├── index.js │ └── package.json └── weex-vue-framework │ ├── README.md │ ├── factory.js │ ├── index.js │ └── package.json ├── scripts ├── alias.js ├── build.js ├── config.js ├── feature-flags.js ├── gen-release-note.js ├── get-weex-version.js ├── git-hooks │ ├── commit-msg │ └── pre-commit ├── release-weex.sh ├── release.sh └── verify-commit-msg.js ├── src ├── compiler │ ├── codeframe.js │ ├── codegen │ │ ├── events.js │ │ └── index.js │ ├── create-compiler.js │ ├── directives │ │ ├── bind.js │ │ ├── index.js │ │ ├── model.js │ │ └── on.js │ ├── error-detector.js │ ├── helpers.js │ ├── index.js │ ├── optimizer.js │ ├── parser │ │ ├── entity-decoder.js │ │ ├── filter-parser.js │ │ ├── html-parser.js │ │ ├── index.js │ │ └── text-parser.js │ └── to-function.js ├── core │ ├── components │ │ ├── index.js │ │ └── keep-alive.js │ ├── config.js │ ├── global-api │ │ ├── assets.js │ │ ├── extend.js │ │ ├── index.js │ │ ├── mixin.js │ │ └── use.js │ ├── index.js │ ├── instance │ │ ├── events.js │ │ ├── index.js │ │ ├── init.js │ │ ├── inject.js │ │ ├── lifecycle.js │ │ ├── proxy.js │ │ ├── render-helpers │ │ │ ├── bind-dynamic-keys.js │ │ │ ├── bind-object-listeners.js │ │ │ ├── bind-object-props.js │ │ │ ├── check-keycodes.js │ │ │ ├── index.js │ │ │ ├── render-list.js │ │ │ ├── render-slot.js │ │ │ ├── render-static.js │ │ │ ├── resolve-filter.js │ │ │ ├── resolve-scoped-slots.js │ │ │ └── resolve-slots.js │ │ ├── render.js │ │ └── state.js │ ├── observer │ │ ├── array.js │ │ ├── dep.js │ │ ├── index.js │ │ ├── scheduler.js │ │ ├── traverse.js │ │ └── watcher.js │ ├── util │ │ ├── debug.js │ │ ├── env.js │ │ ├── error.js │ │ ├── index.js │ │ ├── lang.js │ │ ├── next-tick.js │ │ ├── options.js │ │ ├── perf.js │ │ └── props.js │ └── vdom │ │ ├── create-component.js │ │ ├── create-element.js │ │ ├── create-functional-component.js │ │ ├── helpers │ │ ├── extract-props.js │ │ ├── get-first-component-child.js │ │ ├── index.js │ │ ├── is-async-placeholder.js │ │ ├── merge-hook.js │ │ ├── normalize-children.js │ │ ├── normalize-scoped-slots.js │ │ ├── resolve-async-component.js │ │ └── update-listeners.js │ │ ├── modules │ │ ├── directives.js │ │ ├── index.js │ │ └── ref.js │ │ ├── patch.js │ │ └── vnode.js ├── platforms │ ├── web │ │ ├── compiler │ │ │ ├── directives │ │ │ │ ├── html.js │ │ │ │ ├── index.js │ │ │ │ ├── model.js │ │ │ │ └── text.js │ │ │ ├── index.js │ │ │ ├── modules │ │ │ │ ├── class.js │ │ │ │ ├── index.js │ │ │ │ ├── model.js │ │ │ │ └── style.js │ │ │ ├── options.js │ │ │ └── util.js │ │ ├── entry-compiler.js │ │ ├── entry-runtime-with-compiler.js │ │ ├── entry-runtime.js │ │ ├── entry-server-basic-renderer.js │ │ ├── entry-server-renderer.js │ │ ├── runtime │ │ │ ├── class-util.js │ │ │ ├── components │ │ │ │ ├── index.js │ │ │ │ ├── transition-group.js │ │ │ │ └── transition.js │ │ │ ├── directives │ │ │ │ ├── index.js │ │ │ │ ├── model.js │ │ │ │ └── show.js │ │ │ ├── index.js │ │ │ ├── modules │ │ │ │ ├── attrs.js │ │ │ │ ├── class.js │ │ │ │ ├── dom-props.js │ │ │ │ ├── events.js │ │ │ │ ├── index.js │ │ │ │ ├── style.js │ │ │ │ └── transition.js │ │ │ ├── node-ops.js │ │ │ ├── patch.js │ │ │ └── transition-util.js │ │ ├── server │ │ │ ├── compiler.js │ │ │ ├── directives │ │ │ │ ├── index.js │ │ │ │ ├── model.js │ │ │ │ └── show.js │ │ │ ├── modules │ │ │ │ ├── attrs.js │ │ │ │ ├── class.js │ │ │ │ ├── dom-props.js │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ └── util.js │ │ └── util │ │ │ ├── attrs.js │ │ │ ├── class.js │ │ │ ├── compat.js │ │ │ ├── element.js │ │ │ ├── index.js │ │ │ └── style.js │ └── weex │ │ ├── compiler │ │ ├── directives │ │ │ ├── index.js │ │ │ └── model.js │ │ ├── index.js │ │ └── modules │ │ │ ├── append.js │ │ │ ├── class.js │ │ │ ├── index.js │ │ │ ├── props.js │ │ │ ├── recycle-list │ │ │ ├── component-root.js │ │ │ ├── component.js │ │ │ ├── index.js │ │ │ ├── recycle-list.js │ │ │ ├── text.js │ │ │ ├── v-bind.js │ │ │ ├── v-for.js │ │ │ ├── v-if.js │ │ │ ├── v-on.js │ │ │ └── v-once.js │ │ │ └── style.js │ │ ├── entry-compiler.js │ │ ├── entry-framework.js │ │ ├── entry-runtime-factory.js │ │ ├── runtime │ │ ├── components │ │ │ ├── index.js │ │ │ ├── richtext.js │ │ │ ├── transition-group.js │ │ │ └── transition.js │ │ ├── directives │ │ │ └── index.js │ │ ├── index.js │ │ ├── modules │ │ │ ├── attrs.js │ │ │ ├── class.js │ │ │ ├── events.js │ │ │ ├── index.js │ │ │ ├── style.js │ │ │ └── transition.js │ │ ├── node-ops.js │ │ ├── patch.js │ │ ├── recycle-list │ │ │ ├── render-component-template.js │ │ │ └── virtual-component.js │ │ └── text-node.js │ │ └── util │ │ ├── element.js │ │ ├── index.js │ │ └── parser.js ├── server │ ├── bundle-renderer │ │ ├── create-bundle-renderer.js │ │ ├── create-bundle-runner.js │ │ └── source-map-support.js │ ├── create-basic-renderer.js │ ├── create-renderer.js │ ├── optimizing-compiler │ │ ├── codegen.js │ │ ├── index.js │ │ ├── modules.js │ │ ├── optimizer.js │ │ └── runtime-helpers.js │ ├── render-context.js │ ├── render-stream.js │ ├── render.js │ ├── template-renderer │ │ ├── create-async-file-mapper.js │ │ ├── index.js │ │ ├── parse-template.js │ │ └── template-stream.js │ ├── util.js │ ├── webpack-plugin │ │ ├── client.js │ │ ├── server.js │ │ └── util.js │ └── write.js ├── sfc │ └── parser.js └── shared │ ├── constants.js │ └── util.js ├── test ├── e2e │ ├── .eslintrc.json │ ├── nightwatch.config.js │ ├── runner.js │ └── specs │ │ ├── async-edge-cases.html │ │ ├── async-edge-cases.js │ │ ├── basic-ssr.html │ │ ├── basic-ssr.js │ │ ├── commits.js │ │ ├── grid.js │ │ ├── markdown.js │ │ ├── modal.js │ │ ├── select2.js │ │ ├── svg.js │ │ ├── todomvc.js │ │ └── tree.js ├── helpers │ ├── .eslintrc.json │ ├── classlist.js │ ├── test-object-option.js │ ├── to-equal.js │ ├── to-have-been-warned.js │ ├── trigger-event.js │ ├── vdom.js │ └── wait-for-update.js ├── ssr │ ├── .eslintrc │ ├── async-loader.js │ ├── compile-with-webpack.js │ ├── fixtures │ │ ├── app.js │ │ ├── async-bar.js │ │ ├── async-foo.js │ │ ├── cache-opt-out.js │ │ ├── cache.js │ │ ├── error.js │ │ ├── nested-cache.js │ │ ├── promise-rejection.js │ │ ├── split.js │ │ ├── test.css │ │ ├── test.png │ │ └── test.woff2 │ ├── jasmine.js │ ├── ssr-basic-renderer.spec.js │ ├── ssr-bundle-render.spec.js │ ├── ssr-stream.spec.js │ ├── ssr-string.spec.js │ └── ssr-template.spec.js ├── unit │ ├── .eslintrc.json │ ├── features │ │ ├── component │ │ │ ├── component-async.spec.js │ │ │ ├── component-keep-alive.spec.js │ │ │ ├── component-scoped-slot.spec.js │ │ │ ├── component-slot.spec.js │ │ │ └── component.spec.js │ │ ├── debug.spec.js │ │ ├── directives │ │ │ ├── bind.spec.js │ │ │ ├── class.spec.js │ │ │ ├── cloak.spec.js │ │ │ ├── for.spec.js │ │ │ ├── html.spec.js │ │ │ ├── if.spec.js │ │ │ ├── model-checkbox.spec.js │ │ │ ├── model-component.spec.js │ │ │ ├── model-dynamic.spec.js │ │ │ ├── model-file.spec.js │ │ │ ├── model-parse.spec.js │ │ │ ├── model-radio.spec.js │ │ │ ├── model-select.spec.js │ │ │ ├── model-text.spec.js │ │ │ ├── on.spec.js │ │ │ ├── once.spec.js │ │ │ ├── pre.spec.js │ │ │ ├── show.spec.js │ │ │ ├── static-style-parser.spec.js │ │ │ ├── style.spec.js │ │ │ └── text.spec.js │ │ ├── error-handling.spec.js │ │ ├── filter │ │ │ └── filter.spec.js │ │ ├── global-api │ │ │ ├── assets.spec.js │ │ │ ├── compile.spec.js │ │ │ ├── config.spec.js │ │ │ ├── extend.spec.js │ │ │ ├── mixin.spec.js │ │ │ ├── observable.spec.js │ │ │ ├── set-delete.spec.js │ │ │ └── use.spec.js │ │ ├── instance │ │ │ ├── init.spec.js │ │ │ ├── methods-data.spec.js │ │ │ ├── methods-events.spec.js │ │ │ ├── methods-lifecycle.spec.js │ │ │ ├── properties.spec.js │ │ │ └── render-proxy.spec.js │ │ ├── options │ │ │ ├── _scopeId.spec.js │ │ │ ├── comments.spec.js │ │ │ ├── components.spec.js │ │ │ ├── computed.spec.js │ │ │ ├── data.spec.js │ │ │ ├── delimiters.spec.js │ │ │ ├── directives.spec.js │ │ │ ├── el.spec.js │ │ │ ├── errorCaptured.spec.js │ │ │ ├── extends.spec.js │ │ │ ├── functional.spec.js │ │ │ ├── inheritAttrs.spec.js │ │ │ ├── inject.spec.js │ │ │ ├── lifecycle.spec.js │ │ │ ├── methods.spec.js │ │ │ ├── mixins.spec.js │ │ │ ├── name.spec.js │ │ │ ├── parent.spec.js │ │ │ ├── props.spec.js │ │ │ ├── propsData.spec.js │ │ │ ├── render.spec.js │ │ │ ├── renderError.spec.js │ │ │ ├── template.spec.js │ │ │ └── watch.spec.js │ │ ├── ref.spec.js │ │ └── transition │ │ │ ├── inject-styles.js │ │ │ ├── transition-group.spec.js │ │ │ ├── transition-mode.spec.js │ │ │ └── transition.spec.js │ ├── index.js │ ├── karma.base.config.js │ ├── karma.cover.config.js │ ├── karma.dev.config.js │ ├── karma.sauce.config.js │ ├── karma.unit.config.js │ └── modules │ │ ├── compiler │ │ ├── codeframe.spec.js │ │ ├── codegen.spec.js │ │ ├── compiler-options.spec.js │ │ ├── optimizer.spec.js │ │ └── parser.spec.js │ │ ├── observer │ │ ├── dep.spec.js │ │ ├── observer.spec.js │ │ ├── scheduler.spec.js │ │ └── watcher.spec.js │ │ ├── server-compiler │ │ ├── compiler-options.spec.js │ │ └── optimizer.spec.js │ │ ├── sfc │ │ └── sfc-parser.spec.js │ │ ├── util │ │ ├── error.spec.js │ │ └── next-tick.spec.js │ │ └── vdom │ │ ├── create-component.spec.js │ │ ├── create-element.spec.js │ │ ├── modules │ │ ├── attrs.spec.js │ │ ├── class.spec.js │ │ ├── directive.spec.js │ │ ├── dom-props.spec.js │ │ ├── events.spec.js │ │ └── style.spec.js │ │ └── patch │ │ ├── children.spec.js │ │ ├── edge-cases.spec.js │ │ ├── element.spec.js │ │ ├── hooks.spec.js │ │ └── hydration.spec.js └── weex │ ├── .eslintrc │ ├── cases │ ├── cases.spec.js │ ├── event │ │ ├── click.after.vdom.js │ │ ├── click.before.vdom.js │ │ └── click.vue │ ├── recycle-list │ │ ├── attrs.vdom.js │ │ ├── attrs.vue │ │ ├── classname.vdom.js │ │ ├── classname.vue │ │ ├── components │ │ │ ├── banner.vue │ │ │ ├── counter.vue │ │ │ ├── editor.vue │ │ │ ├── footer.vue │ │ │ ├── lifecycle.vue │ │ │ ├── poster.vue │ │ │ ├── stateful-lifecycle.vdom.js │ │ │ ├── stateful-lifecycle.vue │ │ │ ├── stateful-v-model.vdom.js │ │ │ ├── stateful-v-model.vue │ │ │ ├── stateful.vdom.js │ │ │ ├── stateful.vue │ │ │ ├── stateless-multi-components.vdom.js │ │ │ ├── stateless-multi-components.vue │ │ │ ├── stateless-with-props.vdom.js │ │ │ ├── stateless-with-props.vue │ │ │ ├── stateless.vdom.js │ │ │ └── stateless.vue │ │ ├── inline-style.vdom.js │ │ ├── inline-style.vue │ │ ├── text-node.vdom.js │ │ ├── text-node.vue │ │ ├── v-else-if.vdom.js │ │ ├── v-else-if.vue │ │ ├── v-else.vdom.js │ │ ├── v-else.vue │ │ ├── v-for-iterator.vdom.js │ │ ├── v-for-iterator.vue │ │ ├── v-for.vdom.js │ │ ├── v-for.vue │ │ ├── v-if.vdom.js │ │ ├── v-if.vue │ │ ├── v-on-inline.vdom.js │ │ ├── v-on-inline.vue │ │ ├── v-on.vdom.js │ │ ├── v-on.vue │ │ ├── v-once.vdom.js │ │ └── v-once.vue │ └── render │ │ ├── class.vdom.js │ │ ├── class.vue │ │ ├── sample.vdom.js │ │ └── sample.vue │ ├── compiler │ ├── append.spec.js │ ├── class.spec.js │ ├── compile.spec.js │ ├── parser.spec.js │ ├── props.spec.js │ ├── style.spec.js │ └── v-model.spec.js │ ├── helpers │ └── index.js │ ├── jasmine.js │ └── runtime │ ├── attrs.spec.js │ ├── class.spec.js │ ├── components │ └── richtext.spec.js │ ├── events.spec.js │ ├── framework.spec.js │ ├── node.spec.js │ └── style.spec.js ├── types ├── index.d.ts ├── options.d.ts ├── plugin.d.ts ├── test │ ├── augmentation-test.ts │ ├── es-module.ts │ ├── options-test.ts │ ├── plugin-test.ts │ ├── ssr-test.ts │ ├── tsconfig.json │ └── vue-test.ts ├── tsconfig.json ├── typings.json ├── vnode.d.ts └── vue.d.ts └── yarn.lock /.babelrc.js: -------------------------------------------------------------------------------- 1 | const babelPresetFlowVue = { 2 | plugins: [ 3 | require('@babel/plugin-proposal-class-properties'), 4 | // require('@babel/plugin-syntax-flow'), // not needed, included in transform-flow-strip-types 5 | require('@babel/plugin-transform-flow-strip-types') 6 | ] 7 | } 8 | 9 | module.exports = { 10 | presets: [ 11 | require('@babel/preset-env'), 12 | // require('babel-preset-flow-vue') 13 | babelPresetFlowVue 14 | ], 15 | plugins: [ 16 | require('babel-plugin-transform-vue-jsx'), 17 | require('@babel/plugin-syntax-dynamic-import') 18 | ], 19 | ignore: [ 20 | 'dist/*.js', 21 | 'packages/**/*.js' 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | flow 2 | dist 3 | packages 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | parser: require.resolve('babel-eslint'), 5 | ecmaVersion: 2018, 6 | sourceType: 'module' 7 | }, 8 | env: { 9 | es6: true, 10 | node: true, 11 | browser: true 12 | }, 13 | plugins: [ 14 | "flowtype" 15 | ], 16 | extends: [ 17 | "eslint:recommended", 18 | "plugin:flowtype/recommended" 19 | ], 20 | globals: { 21 | "__WEEX__": true, 22 | "WXEnvironment": true 23 | }, 24 | rules: { 25 | 'no-console': process.env.NODE_ENV !== 'production' ? 0 : 2, 26 | 'no-useless-escape': 0, 27 | 'no-empty': 0 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/.* 3 | .*/test/.* 4 | .*/scripts/.* 5 | .*/examples/.* 6 | .*/benchmarks/.* 7 | 8 | [include] 9 | 10 | [libs] 11 | flow 12 | 13 | [options] 14 | unsafe.enable_getters_and_setters=true 15 | module.name_mapper='^compiler/\(.*\)$' -> '/src/compiler/\1' 16 | module.name_mapper='^core/\(.*\)$' -> '/src/core/\1' 17 | module.name_mapper='^shared/\(.*\)$' -> '/src/shared/\1' 18 | module.name_mapper='^web/\(.*\)$' -> '/src/platforms/web/\1' 19 | module.name_mapper='^weex/\(.*\)$' -> '/src/platforms/weex/\1' 20 | module.name_mapper='^server/\(.*\)$' -> '/src/server/\1' 21 | module.name_mapper='^entries/\(.*\)$' -> '/src/entries/\1' 22 | module.name_mapper='^sfc/\(.*\)$' -> '/src/sfc/\1' 23 | suppress_comment= \\(.\\|\n\\)*\\$flow-disable-line 24 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of the level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | **What kind of change does this PR introduce?** (check at least one) 10 | 11 | - [ ] Bugfix 12 | - [ ] Feature 13 | - [ ] Code style update 14 | - [ ] Refactor 15 | - [ ] Build-related changes 16 | - [ ] Other, please describe: 17 | 18 | **Does this PR introduce a breaking change?** (check one) 19 | 20 | - [ ] Yes 21 | - [ ] No 22 | 23 | If yes, please describe the impact and migration path for existing applications: 24 | 25 | **The PR fulfills these requirements:** 26 | 27 | - [ ] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch 28 | - [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number) 29 | - [ ] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup 30 | - [ ] New/updated tests are included 31 | 32 | If adding a **new feature**, the PR's description includes: 33 | - [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it) 34 | 35 | **Other information:** 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.log 4 | explorations 5 | TODOs.md 6 | dist/*.gz 7 | dist/*.map 8 | dist/vue.common.min.js 9 | test/e2e/reports 10 | test/e2e/screenshots 11 | coverage 12 | RELEASE_NOTE*.md 13 | dist/*.js 14 | packages/vue-server-renderer/basic.js 15 | packages/vue-server-renderer/build.js 16 | packages/vue-server-renderer/server-plugin.js 17 | packages/vue-server-renderer/client-plugin.js 18 | packages/vue-template-compiler/build.js 19 | .vscode 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-present, Yuxi (Evan) You 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /benchmarks/big-table/demo.css: -------------------------------------------------------------------------------- 1 | form { 2 | margin-bottom: 15px; 3 | } 4 | 5 | td.hidden { 6 | color: #ccc; 7 | } 8 | 9 | table.filtered td.item { 10 | background-color: #FFFFBF; 11 | } 12 | 13 | table.filtered td.item.hidden { 14 | background-color: transparent; 15 | } 16 | -------------------------------------------------------------------------------- /benchmarks/dbmon/app.js: -------------------------------------------------------------------------------- 1 | var app = new Vue({ 2 | el: '#app', 3 | data: { 4 | databases: [] 5 | } 6 | }) 7 | 8 | function loadSamples() { 9 | app.databases = Object.freeze(ENV.generateData().toArray()); 10 | Monitoring.renderRate.ping(); 11 | setTimeout(loadSamples, ENV.timeout); 12 | } 13 | 14 | loadSamples() 15 | -------------------------------------------------------------------------------- /benchmarks/dbmon/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | dbmon (Vue) 7 | 8 | 9 |

10 | Reference: js-repaint-perfs 11 |

12 |
13 | 14 | 15 | 16 | 17 | 20 | 27 | 28 | 29 |
{{db.dbname}} 18 | {{db.lastSample.nbQueries}} 19 | 21 | {{q.formatElapsed}} 22 |
23 |
{{q.query}}
24 |
25 |
26 |
30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /benchmarks/ssr/README.md: -------------------------------------------------------------------------------- 1 | # Vue.js SSR benchmark 2 | 3 | This benchmark renders a table of 1000 rows with 10 columns (10k components), with around 30k normal elements on the page. Note this is not something likely to be seen in a typical app. This benchmark is mostly for stress/regression testing and comparing between `renderToString` and `renderToStream`. 4 | 5 | To view the results follow the run section. Note that the overall completion time for the results is variable, this is due to other system related variants at run time (available memory, processing power, etc). In ideal circumstances, both should finish within similar results. 6 | 7 | `renderToStream` pipes the content through a stream which provides considerable performance benefits (faster time-to-first-byte and non-event-loop-blocking) over `renderToString`. This can be observed through the benchmark. 8 | 9 | ### run 10 | 11 | ``` bash 12 | npm run bench:ssr 13 | ``` 14 | -------------------------------------------------------------------------------- /benchmarks/ssr/renderToStream.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | 'use strict' 4 | 5 | process.env.NODE_ENV = 'production' 6 | 7 | const Vue = require('../../dist/vue.runtime.common.js') 8 | const createRenderer = require('../../packages/vue-server-renderer').createRenderer 9 | const renderToStream = createRenderer().renderToStream 10 | const gridComponent = require('./common.js') 11 | 12 | console.log('--- renderToStream --- ') 13 | const self = (global || root) 14 | const s = self.performance.now() 15 | 16 | const stream = renderToStream(new Vue(gridComponent)) 17 | let str = '' 18 | let first 19 | let complete 20 | stream.once('data', () => { 21 | first = self.performance.now() - s 22 | }) 23 | stream.on('data', chunk => { 24 | str += chunk 25 | }) 26 | stream.on('end', () => { 27 | complete = self.performance.now() - s 28 | console.log(`first chunk: ${first.toFixed(2)}ms`) 29 | console.log(`complete: ${complete.toFixed(2)}ms`) 30 | console.log() 31 | }) 32 | -------------------------------------------------------------------------------- /benchmarks/ssr/renderToString.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.NODE_ENV = 'production' 4 | 5 | const Vue = require('../../dist/vue.runtime.common.js') 6 | const createRenderer = require('../../packages/vue-server-renderer').createRenderer 7 | const renderToString = createRenderer().renderToString 8 | const gridComponent = require('./common.js') 9 | 10 | console.log('--- renderToString --- ') 11 | const self = (global || root) 12 | self.s = self.performance.now() 13 | 14 | renderToString(new Vue(gridComponent), (err, res) => { 15 | if (err) throw err 16 | // console.log(res) 17 | console.log('Complete time: ' + (self.performance.now() - self.s).toFixed(2) + 'ms') 18 | console.log() 19 | }) 20 | -------------------------------------------------------------------------------- /dist/vue.common.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === 'production') { 2 | module.exports = require('./vue.common.prod.js') 3 | } else { 4 | module.exports = require('./vue.common.dev.js') 5 | } 6 | -------------------------------------------------------------------------------- /dist/vue.runtime.common.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === 'production') { 2 | module.exports = require('./vue.runtime.common.prod.js') 3 | } else { 4 | module.exports = require('./vue.runtime.common.dev.js') 5 | } 6 | -------------------------------------------------------------------------------- /examples/commits/app.js: -------------------------------------------------------------------------------- 1 | /* global Vue */ 2 | 3 | var apiURL = 'https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha=' 4 | 5 | /** 6 | * Actual demo 7 | */ 8 | 9 | new Vue({ 10 | 11 | el: '#demo', 12 | 13 | data: { 14 | branches: ['master', 'dev'], 15 | currentBranch: 'master', 16 | commits: null 17 | }, 18 | 19 | created: function () { 20 | this.fetchData() 21 | }, 22 | 23 | watch: { 24 | currentBranch: 'fetchData' 25 | }, 26 | 27 | filters: { 28 | truncate: function (v) { 29 | var newline = v.indexOf('\n') 30 | return newline > 0 ? v.slice(0, newline) : v 31 | }, 32 | formatDate: function (v) { 33 | return v.replace(/T|Z/g, ' ') 34 | } 35 | }, 36 | 37 | methods: { 38 | fetchData: function () { 39 | var self = this 40 | if (navigator.userAgent.indexOf('PhantomJS') > -1) { 41 | // use mocks in e2e to avoid dependency on network / authentication 42 | setTimeout(function () { 43 | self.commits = window.MOCKS[self.currentBranch] 44 | }, 0) 45 | } else { 46 | var xhr = new XMLHttpRequest() 47 | xhr.open('GET', apiURL + self.currentBranch) 48 | xhr.onload = function () { 49 | self.commits = JSON.parse(xhr.responseText) 50 | console.log(self.commits[0].html_url) 51 | } 52 | xhr.send() 53 | } 54 | } 55 | } 56 | }) 57 | -------------------------------------------------------------------------------- /examples/commits/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vue.js github commits example 5 | 21 | 22 | 23 | 24 | 25 |
26 |

Latest Vue.js Commits

27 | 35 |

vuejs/vue@{{ currentBranch }}

36 | 44 |
45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /examples/elastic-header/style.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-weight: 300; 3 | font-size: 1.8em; 4 | margin-top: 0; 5 | } 6 | a { 7 | color: #fff; 8 | } 9 | .draggable-header-view { 10 | background-color: #fff; 11 | box-shadow: 0 4px 16px rgba(0,0,0,.15); 12 | width: 320px; 13 | height: 560px; 14 | overflow: hidden; 15 | margin: 30px auto; 16 | position: relative; 17 | font-family: 'Roboto', Helvetica, Arial, sans-serif; 18 | color: #fff; 19 | font-size: 14px; 20 | font-weight: 300; 21 | -webkit-user-select: none; 22 | -moz-user-select: none; 23 | -ms-user-select: none; 24 | user-select: none; 25 | } 26 | .draggable-header-view .bg { 27 | position: absolute; 28 | top: 0; 29 | left: 0; 30 | z-index: 0; 31 | } 32 | .draggable-header-view .header, .draggable-header-view .content { 33 | position: relative; 34 | z-index: 1; 35 | padding: 30px; 36 | box-sizing: border-box; 37 | } 38 | .draggable-header-view .header { 39 | height: 160px; 40 | } 41 | .draggable-header-view .content { 42 | color: #333; 43 | line-height: 1.5em; 44 | } 45 | -------------------------------------------------------------------------------- /examples/firebase/app.js: -------------------------------------------------------------------------------- 1 | var emailRE = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 2 | 3 | // Setup Firebase 4 | var config = { 5 | apiKey: "AIzaSyAi_yuJciPXLFr_PYPeU3eTvtXf8jbJ8zw", 6 | authDomain: "vue-demo-537e6.firebaseapp.com", 7 | databaseURL: "https://vue-demo-537e6.firebaseio.com" 8 | } 9 | firebase.initializeApp(config) 10 | 11 | var usersRef = firebase.database().ref('users') 12 | 13 | // create Vue app 14 | var app = new Vue({ 15 | // element to mount to 16 | el: '#app', 17 | // initial data 18 | data: { 19 | newUser: { 20 | name: '', 21 | email: '' 22 | } 23 | }, 24 | // firebase binding 25 | // https://github.com/vuejs/vuefire 26 | firebase: { 27 | users: usersRef 28 | }, 29 | // computed property for form validation state 30 | computed: { 31 | validation: function () { 32 | return { 33 | name: !!this.newUser.name.trim(), 34 | email: emailRE.test(this.newUser.email) 35 | } 36 | }, 37 | isValid: function () { 38 | var validation = this.validation 39 | return Object.keys(validation).every(function (key) { 40 | return validation[key] 41 | }) 42 | } 43 | }, 44 | // methods 45 | methods: { 46 | addUser: function () { 47 | if (this.isValid) { 48 | usersRef.push(this.newUser) 49 | this.newUser.name = '' 50 | this.newUser.email = '' 51 | } 52 | }, 53 | removeUser: function (user) { 54 | usersRef.child(user['.key']).remove() 55 | } 56 | } 57 | }) 58 | -------------------------------------------------------------------------------- /examples/firebase/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vue.js firebase + validation example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
    18 |
  • 19 | {{user.name}} - {{user.email}} 20 | 21 |
  • 22 |
23 |
24 | 25 | 26 | 27 |
28 |
    29 |
  • Name cannot be empty.
  • 30 |
  • Please provide a valid email address.
  • 31 |
32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /examples/firebase/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica, Arial, sans-serif; 3 | } 4 | 5 | ul { 6 | padding: 0; 7 | } 8 | 9 | .user { 10 | height: 30px; 11 | line-height: 30px; 12 | padding: 10px; 13 | border-top: 1px solid #eee; 14 | overflow: hidden; 15 | transition: all .25s ease; 16 | } 17 | 18 | .user:last-child { 19 | border-bottom: 1px solid #eee; 20 | } 21 | 22 | .v-enter, .v-leave-to { 23 | height: 0; 24 | padding-top: 0; 25 | padding-bottom: 0; 26 | border-top-width: 0; 27 | border-bottom-width: 0; 28 | } 29 | 30 | .errors { 31 | color: #f00; 32 | } 33 | -------------------------------------------------------------------------------- /examples/grid/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js grid component example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 36 | 37 | 38 |
39 | 42 | 46 | 47 |
48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /examples/grid/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica Neue, Arial, sans-serif; 3 | font-size: 14px; 4 | color: #444; 5 | } 6 | 7 | table { 8 | border: 2px solid #42b983; 9 | border-radius: 3px; 10 | background-color: #fff; 11 | } 12 | 13 | th { 14 | background-color: #42b983; 15 | color: rgba(255,255,255,0.66); 16 | cursor: pointer; 17 | -webkit-user-select: none; 18 | -moz-user-select: none; 19 | -ms-user-select: none; 20 | user-select: none; 21 | } 22 | 23 | td { 24 | background-color: #f9f9f9; 25 | } 26 | 27 | th, td { 28 | min-width: 120px; 29 | padding: 10px 20px; 30 | } 31 | 32 | th.active { 33 | color: #fff; 34 | } 35 | 36 | th.active .arrow { 37 | opacity: 1; 38 | } 39 | 40 | .arrow { 41 | display: inline-block; 42 | vertical-align: middle; 43 | width: 0; 44 | height: 0; 45 | margin-left: 5px; 46 | opacity: 0.66; 47 | } 48 | 49 | .arrow.asc { 50 | border-left: 4px solid transparent; 51 | border-right: 4px solid transparent; 52 | border-bottom: 4px solid #fff; 53 | } 54 | 55 | .arrow.dsc { 56 | border-left: 4px solid transparent; 57 | border-right: 4px solid transparent; 58 | border-top: 4px solid #fff; 59 | } 60 | -------------------------------------------------------------------------------- /examples/markdown/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js markdown editor example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 |
18 | 19 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/markdown/style.css: -------------------------------------------------------------------------------- 1 | html, body, #editor { 2 | margin: 0; 3 | height: 100%; 4 | font-family: 'Helvetica Neue', Arial, sans-serif; 5 | color: #333; 6 | } 7 | 8 | textarea, #editor div { 9 | display: inline-block; 10 | width: 49%; 11 | height: 100%; 12 | vertical-align: top; 13 | -webkit-box-sizing: border-box; 14 | -moz-box-sizing: border-box; 15 | box-sizing: border-box; 16 | padding: 0 20px; 17 | } 18 | 19 | textarea { 20 | border: none; 21 | border-right: 1px solid #ccc; 22 | resize: none; 23 | outline: none; 24 | background-color: #f6f6f6; 25 | font-size: 14px; 26 | font-family: 'Monaco', courier, monospace; 27 | padding: 20px; 28 | } 29 | 30 | code { 31 | color: #f66; 32 | } -------------------------------------------------------------------------------- /examples/modal/style.css: -------------------------------------------------------------------------------- 1 | .modal-mask { 2 | position: fixed; 3 | z-index: 9998; 4 | top: 0; 5 | left: 0; 6 | width: 100%; 7 | height: 100%; 8 | background-color: rgba(0, 0, 0, .5); 9 | display: table; 10 | transition: opacity .3s ease; 11 | } 12 | 13 | .modal-wrapper { 14 | display: table-cell; 15 | vertical-align: middle; 16 | } 17 | 18 | .modal-container { 19 | width: 300px; 20 | margin: 0px auto; 21 | padding: 20px 30px; 22 | background-color: #fff; 23 | border-radius: 2px; 24 | box-shadow: 0 2px 8px rgba(0, 0, 0, .33); 25 | transition: all .3s ease; 26 | font-family: Helvetica, Arial, sans-serif; 27 | } 28 | 29 | .modal-header h3 { 30 | margin-top: 0; 31 | color: #42b983; 32 | } 33 | 34 | .modal-body { 35 | margin: 20px 0; 36 | } 37 | 38 | .modal-default-button { 39 | float: right; 40 | } 41 | 42 | /* 43 | * The following styles are auto-applied to elements with 44 | * transition="modal" when their visibility is toggled 45 | * by Vue.js. 46 | * 47 | * You can easily play with the modal transition by editing 48 | * these styles. 49 | */ 50 | 51 | .modal-enter { 52 | opacity: 0; 53 | } 54 | 55 | .modal-leave-to { 56 | opacity: 0; 57 | } 58 | 59 | .modal-enter .modal-container, 60 | .modal-leave-to .modal-container { 61 | -webkit-transform: scale(1.1); 62 | transform: scale(1.1); 63 | } 64 | -------------------------------------------------------------------------------- /examples/svg/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica Neue, Arial, sans-serif; 3 | } 4 | 5 | polygon { 6 | fill: #42b983; 7 | opacity: .75; 8 | } 9 | 10 | circle { 11 | fill: transparent; 12 | stroke: #999; 13 | } 14 | 15 | text { 16 | font-family: Helvetica Neue, Arial, sans-serif; 17 | font-size: 10px; 18 | fill: #666; 19 | } 20 | 21 | label { 22 | display: inline-block; 23 | margin-left: 10px; 24 | width: 20px; 25 | } 26 | 27 | #raw { 28 | position: absolute; 29 | top: 0; 30 | left: 300px; 31 | } -------------------------------------------------------------------------------- /examples/todomvc/readme.md: -------------------------------------------------------------------------------- 1 | # Vue.js TodoMVC Example 2 | 3 | > Vue.js is a library for building interactive web interfaces. 4 | It provides data-driven, nestable view components with a simple and flexible API. 5 | 6 | > _[Vue.js - vuejs.org](https://vuejs.org)_ 7 | 8 | ## Learning Vue.js 9 | The [Vue.js website](https://vuejs.org/) is a great resource to get started. 10 | 11 | Here are some links you may find helpful: 12 | 13 | * [Official Guide](https://vuejs.org/guide/) 14 | * [API Reference](https://vuejs.org/api/) 15 | * [Examples](https://vuejs.org/examples/) 16 | 17 | Get help from other Vue.js users: 18 | 19 | * [Vue.js official forum](http://forum.vuejs.org) 20 | * [Vue.js on Twitter](https://twitter.com/vuejs) 21 | * [Vue.js on Gitter](https://gitter.im/vuejs/vue) 22 | 23 | _If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._ 24 | 25 | ## Credit 26 | 27 | This TodoMVC application was created by [Evan You](http://evanyou.me). 28 | -------------------------------------------------------------------------------- /examples/tree/tree.js: -------------------------------------------------------------------------------- 1 | // demo data 2 | var data = { 3 | name: 'My Tree', 4 | children: [ 5 | { name: 'hello' }, 6 | { name: 'wat' }, 7 | { 8 | name: 'child folder', 9 | children: [ 10 | { 11 | name: 'child folder', 12 | children: [ 13 | { name: 'hello' }, 14 | { name: 'wat' } 15 | ] 16 | }, 17 | { name: 'hello' }, 18 | { name: 'wat' }, 19 | { 20 | name: 'child folder', 21 | children: [ 22 | { name: 'hello' }, 23 | { name: 'wat' } 24 | ] 25 | } 26 | ] 27 | } 28 | ] 29 | } 30 | 31 | // define the item component 32 | Vue.component('item', { 33 | template: '#item-template', 34 | props: { 35 | model: Object 36 | }, 37 | data: function () { 38 | return { 39 | open: false 40 | } 41 | }, 42 | computed: { 43 | isFolder: function () { 44 | return this.model.children && 45 | this.model.children.length 46 | } 47 | }, 48 | methods: { 49 | toggle: function () { 50 | if (this.isFolder) { 51 | this.open = !this.open 52 | } 53 | }, 54 | changeType: function () { 55 | if (!this.isFolder) { 56 | Vue.set(this.model, 'children', []) 57 | this.addChild() 58 | this.open = true 59 | } 60 | }, 61 | addChild: function () { 62 | this.model.children.push({ 63 | name: 'new stuff' 64 | }) 65 | } 66 | } 67 | }) 68 | 69 | // boot up the demo 70 | var demo = new Vue({ 71 | el: '#demo', 72 | data: { 73 | treeData: data 74 | } 75 | }) 76 | -------------------------------------------------------------------------------- /flow/global-api.js: -------------------------------------------------------------------------------- 1 | declare interface GlobalAPI { 2 | cid: number; 3 | options: Object; 4 | config: Config; 5 | util: Object; 6 | 7 | extend: (options: Object) => Function; 8 | set: (target: Object | Array, key: string | number, value: T) => T; 9 | delete: (target: Object| Array, key: string | number) => void; 10 | nextTick: (fn: Function, context?: Object) => void | Promise<*>; 11 | use: (plugin: Function | Object) => GlobalAPI; 12 | mixin: (mixin: Object) => GlobalAPI; 13 | compile: (template: string) => { render: Function, staticRenderFns: Array }; 14 | 15 | directive: (id: string, def?: Function | Object) => Function | Object | void; 16 | component: (id: string, def?: Class | Object) => Class; 17 | filter: (id: string, def?: Function) => Function | void; 18 | 19 | observable: (value: T) => T; 20 | 21 | // allow dynamic method registration 22 | [key: string]: any 23 | }; 24 | -------------------------------------------------------------------------------- /flow/modules.js: -------------------------------------------------------------------------------- 1 | declare module 'he' { 2 | declare function escape(html: string): string; 3 | declare function decode(html: string): string; 4 | } 5 | 6 | declare module 'source-map' { 7 | declare class SourceMapGenerator { 8 | setSourceContent(filename: string, content: string): void; 9 | addMapping(mapping: Object): void; 10 | toString(): string; 11 | } 12 | declare class SourceMapConsumer { 13 | constructor (map: Object): void; 14 | originalPositionFor(position: { line: number; column: number; }): { 15 | source: ?string; 16 | line: ?number; 17 | column: ?number; 18 | }; 19 | } 20 | } 21 | 22 | declare module 'lru-cache' { 23 | declare var exports: { 24 | (): any 25 | } 26 | } 27 | 28 | declare module 'de-indent' { 29 | declare var exports: { 30 | (input: string): string 31 | } 32 | } 33 | 34 | declare module 'serialize-javascript' { 35 | declare var exports: { 36 | (input: string, options: { isJSON: boolean }): string 37 | } 38 | } 39 | 40 | declare module 'lodash.template' { 41 | declare var exports: { 42 | (input: string, options: { interpolate: RegExp, escape: RegExp }): Function 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /flow/ssr.js: -------------------------------------------------------------------------------- 1 | declare type ComponentWithCacheContext = { 2 | type: 'ComponentWithCache'; 3 | bufferIndex: number; 4 | buffer: Array; 5 | key: string; 6 | }; 7 | 8 | declare type ElementContext = { 9 | type: 'Element'; 10 | children: Array; 11 | rendered: number; 12 | endTag: string; 13 | total: number; 14 | }; 15 | 16 | declare type ComponentContext = { 17 | type: 'Component'; 18 | prevActive: Component; 19 | }; 20 | 21 | declare type RenderState = ComponentContext | ComponentWithCacheContext | ElementContext; 22 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/README.md: -------------------------------------------------------------------------------- 1 | # vue-server-renderer 2 | 3 | > This package is auto-generated. For pull requests please see [src/platforms/web/entry-server-renderer.js](https://github.com/vuejs/vue/blob/dev/src/platforms/web/entry-server-renderer.js). 4 | 5 | This package offers Node.js server-side rendering for Vue 2.0. 6 | 7 | - [API Reference](https://ssr.vuejs.org/en/api.html) 8 | - [Vue.js Server-Side Rendering Guide](https://ssr.vuejs.org) 9 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/client-plugin.d.ts: -------------------------------------------------------------------------------- 1 | import { WebpackPlugin } from './types/plugin'; 2 | declare const Plugin: WebpackPlugin; 3 | export = Plugin; 4 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | var vueVersion = require('vue').version 3 | } catch (e) {} 4 | 5 | var packageName = require('./package.json').name 6 | var packageVersion = require('./package.json').version 7 | if (vueVersion && vueVersion !== packageVersion) { 8 | throw new Error( 9 | '\n\nVue packages version mismatch:\n\n' + 10 | '- vue@' + vueVersion + '\n' + 11 | '- ' + packageName + '@' + packageVersion + '\n\n' + 12 | 'This may cause things to work incorrectly. Make sure to use the same version for both.\n' 13 | ) 14 | } 15 | 16 | if (process.env.NODE_ENV === 'production') { 17 | module.exports = require('./build.prod.js') 18 | } else { 19 | module.exports = require('./build.dev.js') 20 | } 21 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-server-renderer", 3 | "version": "2.6.10", 4 | "description": "server renderer for Vue 2.0", 5 | "main": "index.js", 6 | "types": "types/index.d.ts", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/vuejs/vue.git" 10 | }, 11 | "keywords": [ 12 | "vue", 13 | "server", 14 | "ssr" 15 | ], 16 | "author": "Evan You", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/vuejs/vue/issues" 20 | }, 21 | "dependencies": { 22 | "chalk": "^1.1.3", 23 | "hash-sum": "^1.0.2", 24 | "he": "^1.1.0", 25 | "lodash.template": "^4.4.0", 26 | "lodash.uniq": "^4.5.0", 27 | "resolve": "^1.2.0", 28 | "serialize-javascript": "^1.3.0", 29 | "source-map": "0.5.6" 30 | }, 31 | "devDependencies": { 32 | "vue": "file:../.." 33 | }, 34 | "homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer#readme" 35 | } 36 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/server-plugin.d.ts: -------------------------------------------------------------------------------- 1 | import { WebpackPlugin } from './types/plugin'; 2 | declare const Plugin: WebpackPlugin; 3 | export = Plugin; 4 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/types/plugin.d.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'webpack'; 2 | 3 | interface WebpackPluginOptions { 4 | filename?: string; 5 | } 6 | 7 | export interface WebpackPlugin { 8 | new (options?: WebpackPluginOptions): Plugin; 9 | } 10 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "noEmit": true 8 | }, 9 | "compileOnSave": false, 10 | "include": [ 11 | "**/*.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/vue-template-compiler/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | var vueVersion = require('vue').version 3 | } catch (e) {} 4 | 5 | var packageName = require('./package.json').name 6 | var packageVersion = require('./package.json').version 7 | if (vueVersion && vueVersion !== packageVersion) { 8 | var vuePath = require.resolve('vue') 9 | var packagePath = require.resolve('./package.json') 10 | throw new Error( 11 | '\n\nVue packages version mismatch:\n\n' + 12 | '- vue@' + vueVersion + ' (' + vuePath + ')\n' + 13 | '- ' + packageName + '@' + packageVersion + ' (' + packagePath + ')\n\n' + 14 | 'This may cause things to work incorrectly. Make sure to use the same version for both.\n' + 15 | 'If you are using vue-loader@>=10.0, simply update vue-template-compiler.\n' + 16 | 'If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump ' + packageName + ' to the latest.\n' 17 | ) 18 | } 19 | 20 | module.exports = require('./build') 21 | -------------------------------------------------------------------------------- /packages/vue-template-compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-template-compiler", 3 | "version": "2.6.10", 4 | "description": "template compiler for Vue 2.0", 5 | "main": "index.js", 6 | "unpkg": "browser.js", 7 | "jsdelivr": "browser.js", 8 | "browser": "browser.js", 9 | "types": "types/index.d.ts", 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/vuejs/vue.git" 13 | }, 14 | "keywords": [ 15 | "vue", 16 | "compiler" 17 | ], 18 | "author": "Evan You", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/vuejs/vue/issues" 22 | }, 23 | "homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-template-compiler#readme", 24 | "dependencies": { 25 | "he": "^1.1.0", 26 | "de-indent": "^1.0.2" 27 | }, 28 | "devDependencies": { 29 | "vue": "file:../.." 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/vue-template-compiler/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "noEmit": true 8 | }, 9 | "compileOnSave": false, 10 | "include": [ 11 | "**/*.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/weex-template-compiler/README.md: -------------------------------------------------------------------------------- 1 | # weex-template-compiler 2 | 3 | > This package is auto-generated. For pull requests please see [src/platforms/weex/entry-compiler.js](https://github.com/vuejs/vue/tree/dev/src/platforms/weex/entry-compiler.js). 4 | -------------------------------------------------------------------------------- /packages/weex-template-compiler/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | var vueVersion = require('weex-vue-framework').version 3 | } catch (e) {} 4 | 5 | var packageName = require('./package.json').name 6 | var packageVersion = require('./package.json').version 7 | if (vueVersion && vueVersion !== packageVersion) { 8 | throw new Error( 9 | '\n\nVue packages version mismatch:\n\n' + 10 | '- vue@' + vueVersion + '\n' + 11 | '- ' + packageName + '@' + packageVersion + '\n\n' + 12 | 'This may cause things to work incorrectly. Make sure to use the same version for both.\n' + 13 | 'If you are using weex-vue-loader, re-installing them should bump ' + packageName + ' to the latest.\n' 14 | ) 15 | } 16 | 17 | module.exports = require('./build') 18 | -------------------------------------------------------------------------------- /packages/weex-template-compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weex-template-compiler", 3 | "version": "2.4.2-weex.1", 4 | "description": "Weex template compiler for Vue 2.0", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/vuejs/vue.git" 9 | }, 10 | "keywords": [ 11 | "vue", 12 | "compiler" 13 | ], 14 | "author": "Evan You", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/vuejs/vue/issues" 18 | }, 19 | "homepage": "https://github.com/vuejs/vue/tree/dev/packages/weex-template-compiler#readme", 20 | "dependencies": { 21 | "acorn": "^5.2.1", 22 | "escodegen": "^1.8.1", 23 | "he": "^1.1.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/weex-vue-framework/README.md: -------------------------------------------------------------------------------- 1 | # weex-vue-framework 2 | 3 | > This package is auto-generated. For pull requests please see [src/platforms/weex/entry-framework.js](https://github.com/vuejs/vue/blob/dev/src/platforms/weex/entry-framework.js). 4 | -------------------------------------------------------------------------------- /packages/weex-vue-framework/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weex-vue-framework", 3 | "version": "2.4.2-weex.1", 4 | "description": "Vue 2.0 Framework for Weex", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/vuejs/vue.git" 9 | }, 10 | "keywords": [ 11 | "vue", 12 | "compiler" 13 | ], 14 | "author": "Evan You", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/vuejs/vue/issues" 18 | }, 19 | "homepage": "https://github.com/vuejs/vue/tree/dev/packages/weex-vue-framework#readme" 20 | } 21 | -------------------------------------------------------------------------------- /scripts/alias.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | const resolve = p => path.resolve(__dirname, '../', p) 4 | 5 | module.exports = { 6 | vue: resolve('src/platforms/web/entry-runtime-with-compiler'), 7 | compiler: resolve('src/compiler'), 8 | core: resolve('src/core'), 9 | shared: resolve('src/shared'), 10 | web: resolve('src/platforms/web'), 11 | weex: resolve('src/platforms/weex'), 12 | server: resolve('src/server'), 13 | sfc: resolve('src/sfc') 14 | } 15 | -------------------------------------------------------------------------------- /scripts/feature-flags.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NEW_SLOT_SYNTAX: true, 3 | VBIND_PROP_SHORTHAND: false 4 | } 5 | -------------------------------------------------------------------------------- /scripts/gen-release-note.js: -------------------------------------------------------------------------------- 1 | const version = process.argv[2] || process.env.VERSION 2 | const cc = require('conventional-changelog') 3 | const file = `./RELEASE_NOTE${version ? `_${version}` : ``}.md` 4 | const fileStream = require('fs').createWriteStream(file) 5 | 6 | cc({ 7 | preset: 'angular', 8 | pkg: { 9 | transform (pkg) { 10 | pkg.version = `v${version}` 11 | return pkg 12 | } 13 | } 14 | }).pipe(fileStream).on('close', () => { 15 | console.log(`Generated release note at ${file}`) 16 | }) 17 | -------------------------------------------------------------------------------- /scripts/get-weex-version.js: -------------------------------------------------------------------------------- 1 | const coreVersion = require('../package.json').version 2 | const weexVersion = require('../packages/weex-vue-framework/package.json').version 3 | let weexBaseVersion = weexVersion.match(/^[\d.]+/)[0] 4 | let weexSubVersion = Number(weexVersion.match(/-weex\.(\d+)$/)[1]) 5 | 6 | if (weexBaseVersion === coreVersion) { 7 | // same core version, increment sub version 8 | weexSubVersion++ 9 | } else { 10 | // new core version, reset sub version 11 | weexBaseVersion = coreVersion 12 | weexSubVersion = 1 13 | } 14 | 15 | if (process.argv[2] === '-c') { 16 | console.log(weexVersion) 17 | } else { 18 | console.log(weexBaseVersion + '-weex.' + weexSubVersion) 19 | } 20 | 21 | module.exports = { 22 | base: weexBaseVersion, 23 | sub: weexSubVersion 24 | } 25 | -------------------------------------------------------------------------------- /scripts/git-hooks/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Validate commit log 4 | commit_regex='^Merge.+|(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert|types)(\(.+\))?: .{1,50}' 5 | 6 | if ! grep -iqE "$commit_regex" "$1"; then 7 | echo 8 | echo " Error: proper commit message format is required for automated changelog generation." 9 | echo 10 | echo " - Use \`npm run commit\` to interactively generate a commit message." 11 | echo " - See .github/COMMIT_CONVENTION.md for more details." 12 | echo 13 | exit 1 14 | fi 15 | -------------------------------------------------------------------------------- /scripts/git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | files_to_lint=$(git diff --cached --name-only --diff-filter=ACM | grep '\.js$') 4 | 5 | if [ -n "$files_to_lint" ]; then 6 | NODE_ENV=production eslint --quiet $files_to_lint 7 | fi 8 | -------------------------------------------------------------------------------- /scripts/release-weex.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | CUR_VERSION=$(node build/get-weex-version.js -c) 4 | NEXT_VERSION=$(node build/get-weex-version.js) 5 | 6 | echo "Current: $CUR_VERSION" 7 | read -p "Enter new version ($NEXT_VERSION): " -n 1 -r 8 | if ! [[ -z $REPLY ]]; then 9 | NEXT_VERSION=$REPLY 10 | fi 11 | 12 | read -p "Releasing weex-vue-framework@$NEXT_VERSION - are you sure? (y/n) " -n 1 -r 13 | echo 14 | if [[ $REPLY =~ ^[Yy]$ ]]; then 15 | echo "Releasing weex-vue-framework@$NEXT_VERSION ..." 16 | npm run lint 17 | npm run flow 18 | npm run test:weex 19 | 20 | # build 21 | WEEX_VERSION=$NEXT_VERSION npm run build:weex 22 | 23 | # update package 24 | # using subshells to avoid having to cd back 25 | ( cd packages/weex-vue-framework 26 | npm version "$NEXT_VERSION" 27 | npm publish 28 | ) 29 | 30 | ( cd packages/weex-template-compiler 31 | npm version "$NEXT_VERSION" 32 | npm publish 33 | ) 34 | 35 | # commit 36 | git add packages/weex* 37 | git commit -m "[release] weex-vue-framework@$NEXT_VERSION" 38 | fi 39 | -------------------------------------------------------------------------------- /scripts/verify-commit-msg.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk') 2 | const msgPath = process.env.GIT_PARAMS 3 | const msg = require('fs').readFileSync(msgPath, 'utf-8').trim() 4 | 5 | const commitRE = /^(revert: )?(feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types|build)(\(.+\))?: .{1,50}/ 6 | 7 | if (!commitRE.test(msg)) { 8 | console.log() 9 | console.error( 10 | ` ${chalk.bgRed.white(' ERROR ')} ${chalk.red(`invalid commit message format.`)}\n\n` + 11 | chalk.red(` Proper commit message format is required for automated changelog generation. Examples:\n\n`) + 12 | ` ${chalk.green(`feat(compiler): add 'comments' option`)}\n` + 13 | ` ${chalk.green(`fix(v-model): handle events on blur (close #28)`)}\n\n` + 14 | chalk.red(` See .github/COMMIT_CONVENTION.md for more details.\n`) + 15 | chalk.red(` You can also use ${chalk.cyan(`npm run commit`)} to interactively generate a commit message.\n`) 16 | ) 17 | process.exit(1) 18 | } 19 | -------------------------------------------------------------------------------- /src/compiler/codeframe.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | const range = 2 4 | 5 | export function generateCodeFrame ( 6 | source: string, 7 | start: number = 0, 8 | end: number = source.length 9 | ): string { 10 | const lines = source.split(/\r?\n/) 11 | let count = 0 12 | const res = [] 13 | for (let i = 0; i < lines.length; i++) { 14 | count += lines[i].length + 1 15 | if (count >= start) { 16 | for (let j = i - range; j <= i + range || end > count; j++) { 17 | if (j < 0 || j >= lines.length) continue 18 | res.push(`${j + 1}${repeat(` `, 3 - String(j + 1).length)}| ${lines[j]}`) 19 | const lineLength = lines[j].length 20 | if (j === i) { 21 | // push underline 22 | const pad = start - (count - lineLength) + 1 23 | const length = end > count ? lineLength - pad : end - start 24 | res.push(` | ` + repeat(` `, pad) + repeat(`^`, length)) 25 | } else if (j > i) { 26 | if (end > count) { 27 | const length = Math.min(end - count, lineLength) 28 | res.push(` | ` + repeat(`^`, length)) 29 | } 30 | count += lineLength + 1 31 | } 32 | } 33 | break 34 | } 35 | } 36 | return res.join('\n') 37 | } 38 | 39 | function repeat (str, n) { 40 | let result = '' 41 | if (n > 0) { 42 | while (true) { // eslint-disable-line 43 | if (n & 1) result += str 44 | n >>>= 1 45 | if (n <= 0) break 46 | str += str 47 | } 48 | } 49 | return result 50 | } 51 | -------------------------------------------------------------------------------- /src/compiler/directives/bind.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export default function bind (el: ASTElement, dir: ASTDirective) { 4 | el.wrapData = (code: string) => { 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 | -------------------------------------------------------------------------------- /src/compiler/directives/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import on from './on' 4 | import bind from './bind' 5 | import { noop } from 'shared/util' 6 | 7 | export default { 8 | on, 9 | bind, 10 | cloak: noop 11 | } 12 | -------------------------------------------------------------------------------- /src/compiler/directives/on.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { warn } from 'core/util/index' 4 | 5 | export default function on (el: ASTElement, dir: ASTDirective) { 6 | if (process.env.NODE_ENV !== 'production' && dir.modifiers) { 7 | warn(`v-on without argument does not support modifiers.`) 8 | } 9 | el.wrapListeners = (code: string) => `_g(${code},${dir.value})` 10 | } 11 | -------------------------------------------------------------------------------- /src/compiler/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parse } from './parser/index' 4 | import { optimize } from './optimizer' 5 | import { generate } from './codegen/index' 6 | import { createCompilerCreator } from './create-compiler' 7 | 8 | // `createCompilerCreator` allows creating compilers that use alternative 9 | // parser/optimizer/codegen, e.g the SSR optimizing compiler. 10 | // Here we just export a default compiler using the default parts. 11 | export const createCompiler = createCompilerCreator(function baseCompile ( 12 | template: string, 13 | options: CompilerOptions 14 | ): CompiledResult { 15 | // 解析html并生成astElement树 16 | const ast = parse(template.trim(), options) 17 | console.log(ast) 18 | if (options.optimize !== false) { 19 | optimize(ast, options) 20 | } 21 | 22 | // 根据astElement树转成生成vnode的代码 23 | const code = generate(ast, options) 24 | console.log(code.render) 25 | return { 26 | ast, 27 | render: code.render, 28 | staticRenderFns: code.staticRenderFns 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /src/compiler/parser/entity-decoder.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | let decoder 4 | 5 | export default { 6 | decode (html: string): string { 7 | decoder = decoder || document.createElement('div') 8 | decoder.innerHTML = html 9 | return decoder.textContent 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/core/components/index.js: -------------------------------------------------------------------------------- 1 | import KeepAlive from './keep-alive' 2 | 3 | export default { 4 | KeepAlive 5 | } 6 | -------------------------------------------------------------------------------- /src/core/global-api/assets.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { ASSET_TYPES } from 'shared/constants' 4 | import { isPlainObject, validateComponentName } from '../util/index' 5 | 6 | // 初始化'component','directive','filter' 7 | export function initAssetRegisters (Vue: GlobalAPI) { 8 | /** 9 | * Create asset registration methods. 10 | */ 11 | ASSET_TYPES.forEach(type => { 12 | Vue[type] = function ( 13 | id: string, 14 | definition: Function | Object 15 | ): Function | Object | void { 16 | if (!definition) { 17 | return this.options[type + 's'][id] 18 | } else { 19 | /* istanbul ignore if */ 20 | if (process.env.NODE_ENV !== 'production' && type === 'component') { 21 | validateComponentName(id) 22 | } 23 | if (type === 'component' && isPlainObject(definition)) { 24 | // Vue.component定义 25 | definition.name = definition.name || id 26 | // 调用vue.extend方法创建一个组件的构造函数 27 | definition = this.options._base.extend(definition) 28 | } 29 | if (type === 'directive' && typeof definition === 'function') { 30 | definition = { bind: definition, update: definition } 31 | } 32 | this.options[type + 's'][id] = definition 33 | return definition 34 | } 35 | } 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /src/core/global-api/mixin.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { mergeOptions } from '../util/index' 4 | 5 | // mixin方法初始化 6 | export function initMixin (Vue: GlobalAPI) { 7 | Vue.mixin = function (mixin: Object) { 8 | this.options = mergeOptions(this.options, mixin) 9 | return this 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/core/global-api/use.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { toArray } from '../util/index' 4 | 5 | export function initUse (Vue: GlobalAPI) { 6 | // use方法定义 7 | Vue.use = function (plugin: Function | Object) { 8 | // 如果没有_installedPlugins字段,则将_installedPlugins初始化为空数组 9 | const installedPlugins = (this._installedPlugins || (this._installedPlugins = [])) 10 | // 如果已经安装过则返回,保证每个plugin只安装过一次 11 | if (installedPlugins.indexOf(plugin) > -1) { 12 | return this 13 | } 14 | 15 | // additional parameters 16 | const args = toArray(arguments, 1) 17 | // 这将Vue放到第一个参数 18 | args.unshift(this) 19 | if (typeof plugin.install === 'function') { 20 | // 如果plugin提供了install方法则直接调用install 21 | plugin.install.apply(plugin, args) 22 | } else if (typeof plugin === 'function') { 23 | // 如果plugin直接就是一个方法则直接调用 24 | plugin.apply(null, args) 25 | } 26 | // 将当前的plugin放到已安装队列中 27 | installedPlugins.push(plugin) 28 | return this 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/core/index.js: -------------------------------------------------------------------------------- 1 | import Vue from './instance/index' 2 | import { initGlobalAPI } from './global-api/index' 3 | import { isServerRendering } from 'core/util/env' 4 | import { FunctionalRenderContext } from 'core/vdom/create-functional-component' 5 | 6 | initGlobalAPI(Vue) 7 | 8 | // 设置Vue的$isServer字段,返回当前是否为服务端渲染 9 | Object.defineProperty(Vue.prototype, '$isServer', { 10 | get: isServerRendering 11 | }) 12 | 13 | Object.defineProperty(Vue.prototype, '$ssrContext', { 14 | get () { 15 | /* istanbul ignore next */ 16 | return this.$vnode && this.$vnode.ssrContext 17 | } 18 | }) 19 | 20 | // expose FunctionalRenderContext for ssr runtime helper installation 21 | Object.defineProperty(Vue, 'FunctionalRenderContext', { 22 | value: FunctionalRenderContext 23 | }) 24 | 25 | Vue.version = '__VERSION__' 26 | 27 | export default Vue 28 | -------------------------------------------------------------------------------- /src/core/instance/index.js: -------------------------------------------------------------------------------- 1 | import { initMixin } from './init' 2 | import { stateMixin } from './state' 3 | import { renderMixin } from './render' 4 | import { eventsMixin } from './events' 5 | import { lifecycleMixin } from './lifecycle' 6 | import { warn } from '../util/index' 7 | 8 | // 声明Vue原型 9 | function Vue (options) { 10 | if (process.env.NODE_ENV !== 'production' && 11 | !(this instanceof Vue) 12 | ) { 13 | warn('Vue is a constructor and should be called with the `new` keyword') 14 | } 15 | // 开始初始化(_init方法在src/core/init文件中实现) 16 | this._init(options) 17 | } 18 | 19 | initMixin(Vue) 20 | stateMixin(Vue) 21 | eventsMixin(Vue) 22 | lifecycleMixin(Vue) 23 | renderMixin(Vue) 24 | 25 | export default Vue 26 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/bind-dynamic-keys.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | // helper to process dynamic keys for dynamic arguments in v-bind and v-on. 4 | // For example, the following template: 5 | // 6 | //
7 | // 8 | // compiles to the following: 9 | // 10 | // _c('div', { attrs: bindDynamicKeys({ "id": "app" }, [key, value]) }) 11 | 12 | import { warn } from 'core/util/debug' 13 | 14 | export function bindDynamicKeys (baseObj: Object, values: Array): Object { 15 | for (let i = 0; i < values.length; i += 2) { 16 | const key = values[i] 17 | if (typeof key === 'string' && key) { 18 | baseObj[values[i]] = values[i + 1] 19 | } else if (process.env.NODE_ENV !== 'production' && key !== '' && key !== null) { 20 | // null is a speical value for explicitly removing a binding 21 | warn( 22 | `Invalid value for dynamic directive argument (expected string or null): ${key}`, 23 | this 24 | ) 25 | } 26 | } 27 | return baseObj 28 | } 29 | 30 | // helper to dynamically append modifier runtime markers to event names. 31 | // ensure only append when value is already string, otherwise it will be cast 32 | // to string and cause the type check to miss. 33 | export function prependModifier (value: any, symbol: string): any { 34 | return typeof value === 'string' ? symbol + value : value 35 | } 36 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/bind-object-listeners.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { warn, extend, isPlainObject } from 'core/util/index' 4 | 5 | export function bindObjectListeners (data: any, value: any): VNodeData { 6 | if (value) { 7 | if (!isPlainObject(value)) { 8 | process.env.NODE_ENV !== 'production' && warn( 9 | 'v-on without argument expects an Object value', 10 | this 11 | ) 12 | } else { 13 | const on = data.on = data.on ? extend({}, data.on) : {} 14 | for (const key in value) { 15 | const existing = on[key] 16 | const ours = value[key] 17 | on[key] = existing ? [].concat(existing, ours) : ours 18 | } 19 | } 20 | } 21 | return data 22 | } 23 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/check-keycodes.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from 'core/config' 4 | import { hyphenate } from 'shared/util' 5 | 6 | function isKeyNotMatch (expect: T | Array, actual: T): boolean { 7 | if (Array.isArray(expect)) { 8 | return expect.indexOf(actual) === -1 9 | } else { 10 | return expect !== actual 11 | } 12 | } 13 | 14 | /** 15 | * Runtime helper for checking keyCodes from config. 16 | * exposed as Vue.prototype._k 17 | * passing in eventKeyName as last argument separately for backwards compat 18 | */ 19 | export function checkKeyCodes ( 20 | eventKeyCode: number, 21 | key: string, 22 | builtInKeyCode?: number | Array, 23 | eventKeyName?: string, 24 | builtInKeyName?: string | Array 25 | ): ?boolean { 26 | const mappedKeyCode = config.keyCodes[key] || builtInKeyCode 27 | if (builtInKeyName && eventKeyName && !config.keyCodes[key]) { 28 | return isKeyNotMatch(builtInKeyName, eventKeyName) 29 | } else if (mappedKeyCode) { 30 | return isKeyNotMatch(mappedKeyCode, eventKeyCode) 31 | } else if (eventKeyName) { 32 | return hyphenate(eventKeyName) !== key 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { toNumber, toString, looseEqual, looseIndexOf } from 'shared/util' 4 | import { createTextVNode, createEmptyVNode } from 'core/vdom/vnode' 5 | import { renderList } from './render-list' 6 | import { renderSlot } from './render-slot' 7 | import { resolveFilter } from './resolve-filter' 8 | import { checkKeyCodes } from './check-keycodes' 9 | import { bindObjectProps } from './bind-object-props' 10 | import { renderStatic, markOnce } from './render-static' 11 | import { bindObjectListeners } from './bind-object-listeners' 12 | import { resolveScopedSlots } from './resolve-scoped-slots' 13 | import { bindDynamicKeys, prependModifier } from './bind-dynamic-keys' 14 | 15 | export function installRenderHelpers (target: any) { 16 | target._o = markOnce 17 | target._n = toNumber // 将参数转成数字 18 | target._s = toString // 返回参数的字符串表达 19 | target._l = renderList // 渲染数组 20 | target._t = renderSlot // 渲染slot 21 | target._q = looseEqual // 判断两个对象是否相等(浅拷贝对比) 22 | target._i = looseIndexOf // 返回数组中指定元素的位置 23 | target._m = renderStatic 24 | target._f = resolveFilter 25 | target._k = checkKeyCodes 26 | target._b = bindObjectProps 27 | target._v = createTextVNode // 创建文本节点 28 | target._e = createEmptyVNode // 创建空节点 29 | target._u = resolveScopedSlots 30 | target._g = bindObjectListeners 31 | target._d = bindDynamicKeys 32 | target._p = prependModifier 33 | } 34 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/render-list.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isObject, isDef, hasSymbol } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for rendering v-for lists. 7 | */ 8 | // 渲染数组 9 | export function renderList ( 10 | val: any, 11 | render: ( 12 | val: any, 13 | keyOrIndex: string | number, 14 | index?: number 15 | ) => VNode 16 | ): ?Array { 17 | let ret: ?Array, i, l, keys, key 18 | if (Array.isArray(val) || typeof val === 'string') { 19 | ret = new Array(val.length) 20 | for (i = 0, l = val.length; i < l; i++) { 21 | ret[i] = render(val[i], i) 22 | } 23 | } else if (typeof val === 'number') { 24 | ret = new Array(val) 25 | for (i = 0; i < val; i++) { 26 | ret[i] = render(i + 1, i) 27 | } 28 | } else if (isObject(val)) { 29 | if (hasSymbol && val[Symbol.iterator]) { 30 | ret = [] 31 | const iterator: Iterator = val[Symbol.iterator]() 32 | let result = iterator.next() 33 | while (!result.done) { 34 | ret.push(render(result.value, ret.length)) 35 | result = iterator.next() 36 | } 37 | } else { 38 | keys = Object.keys(val) 39 | ret = new Array(keys.length) 40 | for (i = 0, l = keys.length; i < l; i++) { 41 | key = keys[i] 42 | ret[i] = render(val[key], key, i) 43 | } 44 | } 45 | } 46 | if (!isDef(ret)) { 47 | ret = [] 48 | } 49 | (ret: any)._isVList = true 50 | return ret 51 | } 52 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/render-slot.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { extend, warn, isObject } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for rendering 7 | */ 8 | // 渲染slot 9 | export function renderSlot ( 10 | name: string, 11 | fallback: ?Array, 12 | props: ?Object, 13 | bindObject: ?Object 14 | ): ?Array { 15 | const scopedSlotFn = this.$scopedSlots[name] 16 | let nodes 17 | if (scopedSlotFn) { // scoped slot 18 | props = props || {} 19 | if (bindObject) { 20 | if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) { 21 | warn( 22 | 'slot v-bind without argument expects an Object', 23 | this 24 | ) 25 | } 26 | props = extend(extend({}, bindObject), props) 27 | } 28 | nodes = scopedSlotFn(props) || fallback 29 | } else { 30 | nodes = this.$slots[name] || fallback 31 | } 32 | 33 | const target = props && props.slot 34 | if (target) { 35 | return this.$createElement('template', { slot: target }, nodes) 36 | } else { 37 | return nodes 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/resolve-filter.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { identity, resolveAsset } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for resolving filters 7 | */ 8 | export function resolveFilter (id: string): Function { 9 | return resolveAsset(this.$options, 'filters', id, true) || identity 10 | } 11 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/resolve-scoped-slots.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function resolveScopedSlots ( 4 | fns: ScopedSlotsData, // see flow/vnode 5 | res?: Object, 6 | // the following are added in 2.6 7 | hasDynamicKeys?: boolean, 8 | contentHashKey?: number 9 | ): { [key: string]: Function, $stable: boolean } { 10 | res = res || { $stable: !hasDynamicKeys } 11 | for (let i = 0; i < fns.length; i++) { 12 | const slot = fns[i] 13 | if (Array.isArray(slot)) { 14 | resolveScopedSlots(slot, res, hasDynamicKeys) 15 | } else if (slot) { 16 | // marker for reverse proxying v-slot without scope on this.$slots 17 | if (slot.proxy) { 18 | slot.fn.proxy = true 19 | } 20 | res[slot.key] = slot.fn 21 | } 22 | } 23 | if (contentHashKey) { 24 | (res: any).$key = contentHashKey 25 | } 26 | return res 27 | } 28 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/resolve-slots.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import type VNode from 'core/vdom/vnode' 4 | 5 | /** 6 | * Runtime helper for resolving raw children VNodes into a slot object. 7 | */ 8 | // 解析slot 9 | export function resolveSlots ( 10 | children: ?Array, 11 | context: ?Component 12 | ): { [key: string]: Array } { 13 | if (!children || !children.length) { 14 | return {} 15 | } 16 | const slots = {} 17 | for (let i = 0, l = children.length; i < l; i++) { 18 | const child = children[i] 19 | const data = child.data 20 | // remove slot attribute if the node is resolved as a Vue slot node 21 | if (data && data.attrs && data.attrs.slot) { 22 | delete data.attrs.slot 23 | } 24 | // named slots should only be respected if the vnode was rendered in the 25 | // same context. 26 | if ((child.context === context || child.fnContext === context) && 27 | data && data.slot != null 28 | ) { 29 | const name = data.slot 30 | const slot = (slots[name] || (slots[name] = [])) 31 | if (child.tag === 'template') { 32 | slot.push.apply(slot, child.children || []) 33 | } else { 34 | slot.push(child) 35 | } 36 | } else { 37 | (slots.default || (slots.default = [])).push(child) 38 | } 39 | } 40 | // ignore slots that contains only whitespace 41 | for (const name in slots) { 42 | if (slots[name].every(isWhitespace)) { 43 | delete slots[name] 44 | } 45 | } 46 | return slots 47 | } 48 | 49 | function isWhitespace (node: VNode): boolean { 50 | return (node.isComment && !node.asyncFactory) || node.text === ' ' 51 | } 52 | -------------------------------------------------------------------------------- /src/core/observer/array.js: -------------------------------------------------------------------------------- 1 | /* 2 | * not type checking this file because flow doesn't play well with 3 | * dynamically accessing methods on Array prototype 4 | */ 5 | 6 | import { def } from '../util/index' 7 | 8 | const arrayProto = Array.prototype // 所有的Array方法 9 | export const arrayMethods = Object.create(arrayProto) // 根据Array方法创建一个新的对象 10 | 11 | const methodsToPatch = [ 12 | 'push', 13 | 'pop', 14 | 'shift', 15 | 'unshift', 16 | 'splice', 17 | 'sort', 18 | 'reverse' 19 | ] 20 | 21 | /** 22 | * Intercept mutating methods and emit events 23 | */ 24 | // 对数组的原生方法二次封装 25 | methodsToPatch.forEach(function (method) { 26 | // cache original method 27 | const original = arrayProto[method] 28 | def(arrayMethods, method, function mutator (...args) { 29 | // 原生方法先执行 30 | const result = original.apply(this, args) 31 | const ob = this.__ob__ 32 | let inserted // 新插入的元素 33 | // 只有push、unshift、splice方法会插入新的元素 34 | switch (method) { 35 | case 'push': 36 | case 'unshift': 37 | inserted = args 38 | break 39 | case 'splice': 40 | inserted = args.slice(2) 41 | break 42 | } 43 | // 如果有新插入的元素,则对所有的元素进行一次遍历绑定观察者 44 | if (inserted) ob.observeArray(inserted) 45 | // notify change 46 | ob.dep.notify() // 对所有依赖的watcher触发notify 47 | return result // 返回结果 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /src/core/observer/traverse.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { _Set as Set, isObject } from '../util/index' 4 | import type { SimpleSet } from '../util/index' 5 | import VNode from '../vdom/vnode' 6 | 7 | const seenObjects = new Set() 8 | 9 | /** 10 | * Recursively traverse an object to evoke all converted 11 | * getters, so that every nested property inside the object 12 | * is collected as a "deep" dependency. 13 | */ 14 | // 遍历对象的每一个字段以触发所有的getter 15 | export function traverse (val: any) { 16 | _traverse(val, seenObjects) 17 | seenObjects.clear() // 清空对象的所有字段 18 | } 19 | 20 | function _traverse (val: any, seen: SimpleSet) { 21 | let i, keys 22 | const isA = Array.isArray(val) 23 | // 下列情况不需要深度遍历 24 | // 1、val既不是数组,也不是对象 25 | // 2、对象已经Frozen 26 | // 3、对象为VNode 27 | if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) { 28 | return 29 | } 30 | if (val.__ob__) { 31 | // __ob__的原型为Observe 32 | const depId = val.__ob__.dep.id 33 | if (seen.has(depId)) { 34 | return 35 | } 36 | seen.add(depId) 37 | } 38 | if (isA) { 39 | i = val.length 40 | // 如果是一个数组则对每一个元素遍历 41 | while (i--) _traverse(val[i], seen) 42 | } else { 43 | keys = Object.keys(val) 44 | i = keys.length 45 | // 如果是一个对象则对每一字段遍历 46 | while (i--) _traverse(val[keys[i]], seen) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/core/util/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export * from 'shared/util' 4 | export * from './lang' 5 | export * from './env' 6 | export * from './options' 7 | export * from './debug' 8 | export * from './props' 9 | export * from './error' 10 | export * from './next-tick' 11 | export { defineReactive } from '../observer/index' 12 | -------------------------------------------------------------------------------- /src/core/util/lang.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | /** 4 | * unicode letters used for parsing html tags, component names and property paths. 5 | * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname 6 | * skipping \u10000-\uEFFFF due to it freezing up PhantomJS 7 | */ 8 | // 校验是否为unicode字符以及a-zA-Z 9 | export const unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/ 10 | 11 | /** 12 | * Check if a string starts with $ or _ 13 | */ 14 | export function isReserved (str: string): boolean { 15 | const c = (str + '').charCodeAt(0) 16 | return c === 0x24 || c === 0x5F 17 | } 18 | 19 | /** 20 | * Define a property. 21 | */ 22 | // 对象的指定字段定义 23 | export function def (obj: Object, key: string, val: any, enumerable?: boolean) { 24 | Object.defineProperty(obj, key, { 25 | value: val, 26 | enumerable: !!enumerable, 27 | writable: true, 28 | configurable: true 29 | }) 30 | } 31 | 32 | /** 33 | * Parse simple path. 34 | */ 35 | // 匹配到unicodeRegExp中的字符、.、$、_、0-9以外的字符 36 | const bailRE = new RegExp(`[^${unicodeRegExp.source}.$_\\d]`) 37 | export function parsePath (path: string): any { 38 | if (bailRE.test(path)) { 39 | return 40 | } 41 | const segments = path.split('.') // 以.做分隔符 42 | return function (obj) { 43 | // 逐层遍历path路径到最深层 44 | for (let i = 0; i < segments.length; i++) { 45 | if (!obj) return 46 | obj = obj[segments[i]] 47 | } 48 | return obj 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/core/util/perf.js: -------------------------------------------------------------------------------- 1 | import { inBrowser } from './env' 2 | 3 | export let mark 4 | export let measure 5 | 6 | if (process.env.NODE_ENV !== 'production') { 7 | // window.performance 8 | // https://developer.mozilla.org/zh-CN/docs/Web/API/Performance 9 | const perf = inBrowser && window.performance // 浏览器环境下返回window.performance 10 | /* istanbul ignore if */ 11 | if ( 12 | perf && 13 | perf.mark && 14 | perf.measure && 15 | perf.clearMarks && 16 | perf.clearMeasures 17 | ) { 18 | // 创建一个性能记录 19 | // https://developer.mozilla.org/zh-CN/docs/Web/API/Performance/mark 20 | mark = tag => perf.mark(tag) 21 | // 进行性能测量 22 | // https://developer.mozilla.org/zh-CN/docs/Web/API/Performance/measure 23 | measure = (name, startTag, endTag) => { 24 | perf.measure(name, startTag, endTag) 25 | perf.clearMarks(startTag) 26 | perf.clearMarks(endTag) 27 | // perf.clearMeasures(name) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/get-first-component-child.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isDef } from 'shared/util' 4 | import { isAsyncPlaceholder } from './is-async-placeholder' 5 | 6 | export function getFirstComponentChild (children: ?Array): ?VNode { 7 | if (Array.isArray(children)) { 8 | for (let i = 0; i < children.length; i++) { 9 | const c = children[i] 10 | if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { 11 | return c 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export * from './merge-hook' 4 | export * from './extract-props' 5 | export * from './update-listeners' 6 | export * from './normalize-children' 7 | export * from './resolve-async-component' 8 | export * from './get-first-component-child' 9 | export * from './is-async-placeholder' 10 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/is-async-placeholder.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function isAsyncPlaceholder (node: VNode): boolean { 4 | return node.isComment && node.asyncFactory 5 | } 6 | -------------------------------------------------------------------------------- /src/core/vdom/helpers/merge-hook.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import VNode from '../vnode' 4 | import { createFnInvoker } from './update-listeners' 5 | import { remove, isDef, isUndef, isTrue } from 'shared/util' 6 | 7 | export function mergeVNodeHook (def: Object, hookKey: string, hook: Function) { 8 | if (def instanceof VNode) { 9 | def = def.data.hook || (def.data.hook = {}) 10 | } 11 | let invoker 12 | const oldHook = def[hookKey] 13 | 14 | function wrappedHook () { 15 | hook.apply(this, arguments) 16 | // important: remove merged hook to ensure it's called only once 17 | // and prevent memory leak 18 | remove(invoker.fns, wrappedHook) 19 | } 20 | 21 | if (isUndef(oldHook)) { 22 | // no existing hook 23 | invoker = createFnInvoker([wrappedHook]) 24 | } else { 25 | /* istanbul ignore if */ 26 | if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { 27 | // already a merged invoker 28 | invoker = oldHook 29 | invoker.fns.push(wrappedHook) 30 | } else { 31 | // existing plain hook 32 | invoker = createFnInvoker([oldHook, wrappedHook]) 33 | } 34 | } 35 | 36 | invoker.merged = true 37 | def[hookKey] = invoker 38 | } 39 | -------------------------------------------------------------------------------- /src/core/vdom/modules/index.js: -------------------------------------------------------------------------------- 1 | import directives from './directives' 2 | import ref from './ref' 3 | 4 | export default [ 5 | ref, 6 | directives 7 | ] 8 | -------------------------------------------------------------------------------- /src/core/vdom/modules/ref.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { remove, isDef } from 'shared/util' 4 | 5 | export default { 6 | create (_: any, vnode: VNodeWithData) { 7 | registerRef(vnode) 8 | }, 9 | update (oldVnode: VNodeWithData, vnode: VNodeWithData) { 10 | if (oldVnode.data.ref !== vnode.data.ref) { 11 | registerRef(oldVnode, true) 12 | registerRef(vnode) 13 | } 14 | }, 15 | destroy (vnode: VNodeWithData) { 16 | registerRef(vnode, true) 17 | } 18 | } 19 | 20 | export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) { 21 | const key = vnode.data.ref 22 | if (!isDef(key)) return 23 | 24 | const vm = vnode.context 25 | const ref = vnode.componentInstance || vnode.elm 26 | const refs = vm.$refs 27 | if (isRemoval) { 28 | if (Array.isArray(refs[key])) { 29 | remove(refs[key], ref) 30 | } else if (refs[key] === ref) { 31 | refs[key] = undefined 32 | } 33 | } else { 34 | if (vnode.data.refInFor) { 35 | if (!Array.isArray(refs[key])) { 36 | refs[key] = [ref] 37 | } else if (refs[key].indexOf(ref) < 0) { 38 | // $flow-disable-line 39 | refs[key].push(ref) 40 | } 41 | } else { 42 | refs[key] = ref 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/directives/html.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { addProp } from 'compiler/helpers' 4 | 5 | export default function html (el: ASTElement, dir: ASTDirective) { 6 | if (dir.value) { 7 | addProp(el, 'innerHTML', `_s(${dir.value})`, dir) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/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 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/directives/text.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { addProp } from 'compiler/helpers' 4 | 5 | export default function text (el: ASTElement, dir: ASTDirective) { 6 | if (dir.value) { 7 | addProp(el, 'textContent', `_s(${dir.value})`, dir) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { baseOptions } from './options' 4 | import { createCompiler } from 'compiler/index' 5 | 6 | const { compile, compileToFunctions } = createCompiler(baseOptions) 7 | 8 | export { compile, compileToFunctions } 9 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/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 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/options.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { 4 | isPreTag, 5 | mustUseProp, 6 | isReservedTag, 7 | getTagNamespace 8 | } from '../util/index' 9 | 10 | import modules from './modules/index' 11 | import directives from './directives/index' 12 | import { genStaticKeys } from 'shared/util' 13 | import { isUnaryTag, canBeLeftOpenTag } from './util' 14 | 15 | export const baseOptions: CompilerOptions = { 16 | expectHTML: true, 17 | modules, 18 | directives, 19 | isPreTag, 20 | isUnaryTag, 21 | mustUseProp, 22 | canBeLeftOpenTag, 23 | isReservedTag, 24 | getTagNamespace, 25 | staticKeys: genStaticKeys(modules) 26 | } 27 | -------------------------------------------------------------------------------- /src/platforms/web/compiler/util.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { makeMap } from 'shared/util' 4 | 5 | // 自闭合标签 6 | export const isUnaryTag = makeMap( 7 | 'area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' + 8 | 'link,meta,param,source,track,wbr' 9 | ) 10 | 11 | // Elements that you can, intentionally, leave open 12 | // (and which close themselves) 13 | // 可以省略闭标签的 14 | export const canBeLeftOpenTag = makeMap( 15 | 'colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source' 16 | ) 17 | 18 | // HTML5 tags https://html.spec.whatwg.org/multipage/indices.html#elements-3 19 | // Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content 20 | // 非行内元素标签 21 | export const isNonPhrasingTag = makeMap( 22 | 'address,article,aside,base,blockquote,body,caption,col,colgroup,dd,' + 23 | 'details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,' + 24 | 'h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,' + 25 | 'optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,' + 26 | 'title,tr,track' 27 | ) 28 | -------------------------------------------------------------------------------- /src/platforms/web/entry-compiler.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export { parseComponent } from 'sfc/parser' 4 | export { compile, compileToFunctions } from './compiler/index' 5 | export { ssrCompile, ssrCompileToFunctions } from './server/compiler' 6 | export { generateCodeFrame } from 'compiler/codeframe' 7 | -------------------------------------------------------------------------------- /src/platforms/web/entry-runtime.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import Vue from './runtime/index' 4 | 5 | export default Vue 6 | -------------------------------------------------------------------------------- /src/platforms/web/entry-server-basic-renderer.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import modules from './server/modules/index' 4 | import directives from './server/directives/index' 5 | import { isUnaryTag, canBeLeftOpenTag } from './compiler/util' 6 | import { createBasicRenderer } from 'server/create-basic-renderer' 7 | 8 | export default createBasicRenderer({ 9 | modules, 10 | directives, 11 | isUnaryTag, 12 | canBeLeftOpenTag 13 | }) 14 | -------------------------------------------------------------------------------- /src/platforms/web/entry-server-renderer.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | process.env.VUE_ENV = 'server' 4 | 5 | import { extend } from 'shared/util' 6 | import modules from './server/modules/index' 7 | import baseDirectives from './server/directives/index' 8 | import { isUnaryTag, canBeLeftOpenTag } from './compiler/util' 9 | 10 | import { createRenderer as _createRenderer } from 'server/create-renderer' 11 | import { createBundleRendererCreator } from 'server/bundle-renderer/create-bundle-renderer' 12 | 13 | export function createRenderer (options?: Object = {}): { 14 | renderToString: Function, 15 | renderToStream: Function 16 | } { 17 | return _createRenderer(extend(extend({}, options), { 18 | isUnaryTag, 19 | canBeLeftOpenTag, 20 | modules, 21 | // user can provide server-side implementations for custom directives 22 | // when creating the renderer. 23 | directives: extend(baseDirectives, options.directives) 24 | })) 25 | } 26 | 27 | export const createBundleRenderer = createBundleRendererCreator(createRenderer) 28 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/components/index.js: -------------------------------------------------------------------------------- 1 | import Transition from './transition' 2 | import TransitionGroup from './transition-group' 3 | 4 | export default { 5 | Transition, 6 | TransitionGroup 7 | } 8 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/directives/index.js: -------------------------------------------------------------------------------- 1 | import model from './model' 2 | import show from './show' 3 | 4 | export default { 5 | model, 6 | show 7 | } 8 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/modules/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { 4 | isDef, 5 | isUndef 6 | } from 'shared/util' 7 | 8 | import { 9 | concat, 10 | stringifyClass, 11 | genClassForVnode 12 | } from 'web/util/index' 13 | 14 | // 更新class属性 15 | function updateClass (oldVnode: any, vnode: any) { 16 | const el = vnode.elm 17 | const data: VNodeData = vnode.data 18 | const oldData: VNodeData = oldVnode.data 19 | if ( 20 | isUndef(data.staticClass) && 21 | isUndef(data.class) && ( 22 | isUndef(oldData) || ( 23 | isUndef(oldData.staticClass) && 24 | isUndef(oldData.class) 25 | ) 26 | ) 27 | ) { 28 | // 1、新vnode的staticClass未定义(class属性) 29 | // 2、新vnode的class未定义(:class属性) 30 | // 3、旧vnode未定义 或 (旧vnode的staticClass未定义 和 旧vnode的class未定义) 31 | // 直接返回 32 | return 33 | } 34 | 35 | let cls = genClassForVnode(vnode) // 返回拼接后的class字符串 36 | 37 | // handle transition classes 38 | const transitionClass = el._transitionClasses 39 | if (isDef(transitionClass)) { // 拼接transitionClass 40 | cls = concat(cls, stringifyClass(transitionClass)) 41 | } 42 | 43 | // set the class 44 | if (cls !== el._prevClass) { // 新的class属性与上一次一致不做改变 45 | el.setAttribute('class', cls) // 更新class属性 46 | el._prevClass = cls // dom元素备份上一次的class字符串 47 | } 48 | } 49 | 50 | export default { 51 | create: updateClass, 52 | update: updateClass 53 | } 54 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/modules/index.js: -------------------------------------------------------------------------------- 1 | import attrs from './attrs' 2 | import klass from './class' 3 | import events from './events' 4 | import domProps from './dom-props' 5 | import style from './style' 6 | import transition from './transition' 7 | 8 | export default [ 9 | attrs, 10 | klass, 11 | events, 12 | domProps, 13 | style, 14 | transition 15 | ] 16 | -------------------------------------------------------------------------------- /src/platforms/web/runtime/patch.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import * as nodeOps from 'web/runtime/node-ops' 4 | import { createPatchFunction } from 'core/vdom/patch' 5 | import baseModules from 'core/vdom/modules/index' 6 | import platformModules from 'web/runtime/modules/index' 7 | 8 | // the directive module should be applied last, after all 9 | // built-in modules have been applied. 10 | const modules = platformModules.concat(baseModules) 11 | // nodeOps为dom操作函数集合 12 | // platformModules为attrs, klass, events, domProps, style, transition操作 13 | // baseModules为ref, directives操作 14 | export const patch: Function = createPatchFunction({ nodeOps, modules }) 15 | -------------------------------------------------------------------------------- /src/platforms/web/server/compiler.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { baseOptions } from '../compiler/options' 4 | import { createCompiler } from 'server/optimizing-compiler/index' 5 | 6 | const { compile, compileToFunctions } = createCompiler(baseOptions) 7 | 8 | export { 9 | compile as ssrCompile, 10 | compileToFunctions as ssrCompileToFunctions 11 | } 12 | -------------------------------------------------------------------------------- /src/platforms/web/server/directives/index.js: -------------------------------------------------------------------------------- 1 | import show from './show' 2 | import model from './model' 3 | 4 | export default { 5 | show, 6 | model 7 | } 8 | -------------------------------------------------------------------------------- /src/platforms/web/server/directives/model.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { looseEqual, looseIndexOf } from 'shared/util' 4 | 5 | // this is only applied for 14 |
15 | 16 | 25 | 26 | 27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 |
35 | countA: {{countA}} 36 |
37 |
38 | countB: {{countB}} 39 |
40 |
41 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /test/e2e/specs/async-edge-cases.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'async edge cases': function (browser) { 3 | browser 4 | .url('http://localhost:8080/test/e2e/specs/async-edge-cases.html') 5 | // #4510 6 | .assert.containsText('#case-1', '1') 7 | .assert.checked('#case-1 input', false) 8 | 9 | .click('#case-1 input') 10 | .assert.containsText('#case-1', '2') 11 | .assert.checked('#case-1 input', true) 12 | 13 | .click('#case-1 input') 14 | .assert.containsText('#case-1', '3') 15 | .assert.checked('#case-1 input', false) 16 | 17 | // // #6566 18 | .assert.containsText('#case-2 button', 'Expand is True') 19 | .assert.containsText('.count-a', 'countA: 0') 20 | .assert.containsText('.count-b', 'countB: 0') 21 | 22 | .click('#case-2 button') 23 | .assert.containsText('#case-2 button', 'Expand is False') 24 | .assert.containsText('.count-a', 'countA: 1') 25 | .assert.containsText('.count-b', 'countB: 0') 26 | 27 | .click('#case-2 button') 28 | .assert.containsText('#case-2 button', 'Expand is True') 29 | .assert.containsText('.count-a', 'countA: 1') 30 | .assert.containsText('.count-b', 'countB: 1') 31 | 32 | .end() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/e2e/specs/basic-ssr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
wtf
12 | 13 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/e2e/specs/basic-ssr.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'basic SSR': function (browser) { 3 | browser 4 | .url('http://localhost:8080/test/e2e/specs/basic-ssr.html') 5 | .assert.containsText('#result', '
foo
') 6 | .end() 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/e2e/specs/commits.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'commits': function (browser) { 3 | browser 4 | .url('http://localhost:8080/examples/commits/') 5 | .waitForElementVisible('li', 5000) 6 | .assert.count('input', 2) 7 | .assert.count('label', 2) 8 | .assert.containsText('label[for="master"]', 'master') 9 | .assert.containsText('label[for="dev"]', 'dev') 10 | .assert.checked('#master') 11 | .assert.checked('#dev', false) 12 | .assert.containsText('p', 'vuejs/vue@master') 13 | .assert.count('li', 3) 14 | .assert.count('li .commit', 3) 15 | .assert.count('li .message', 3) 16 | .click('#dev') 17 | .assert.containsText('p', 'vuejs/vue@dev') 18 | .assert.count('li', 3) 19 | .assert.count('li .commit', 3) 20 | .assert.count('li .message', 3) 21 | .end() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/e2e/specs/markdown.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'markdown': function (browser) { 3 | browser 4 | .url('http://localhost:8080/examples/markdown/') 5 | .waitForElementVisible('#editor', 1000) 6 | .assert.value('textarea', '# hello') 7 | .assert.hasHTML('#editor div', '

hello

') 8 | .setValue('textarea', '\n## foo\n\n- bar\n- baz') 9 | // assert the output is not updated yet because of debounce 10 | .assert.hasHTML('#editor div', '

hello

') 11 | .waitFor(500) 12 | .assert.hasHTML('#editor div', 13 | '

hello

\n' + 14 | '

foo

\n' + 15 | '
    \n
  • bar
  • \n
  • baz
  • \n
' 16 | ) 17 | .end() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/e2e/specs/modal.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'modal': function (browser) { 3 | browser 4 | .url('http://localhost:8080/examples/modal/') 5 | .waitForElementVisible('#app', 1000) 6 | .assert.elementNotPresent('.modal-mask') 7 | .click('#show-modal') 8 | .assert.elementPresent('.modal-mask') 9 | .assert.elementPresent('.modal-wrapper') 10 | .assert.elementPresent('.modal-container') 11 | .waitFor(50) 12 | .assert.cssClassPresent('.modal-mask', 'modal-enter-active') 13 | .waitFor(300) 14 | .assert.cssClassNotPresent('.modal-mask', 'modal-enter-active') 15 | .assert.containsText('.modal-header h3', 'custom header') 16 | .assert.containsText('.modal-body', 'default body') 17 | .assert.containsText('.modal-footer', 'default footer') 18 | .click('.modal-default-button') 19 | // should have transition 20 | .assert.elementPresent('.modal-mask') 21 | .waitFor(50) 22 | .assert.cssClassPresent('.modal-mask', 'modal-leave-active') 23 | .waitFor(300) 24 | .assert.elementNotPresent('.modal-mask') 25 | .end() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/helpers/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "globals": { 6 | "waitForUpdate": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/helpers/classlist.js: -------------------------------------------------------------------------------- 1 | beforeEach(() => { 2 | jasmine.addMatchers({ 3 | // since classList may not be supported in all browsers 4 | toHaveClass: () => { 5 | return { 6 | compare: (el, cls) => { 7 | const pass = el.classList 8 | ? el.classList.contains(cls) 9 | : el.getAttribute('class').split(/\s+/g).indexOf(cls) > -1 10 | return { 11 | pass, 12 | message: `Expected element${pass ? ' ' : ' not '}to have class ${cls}` 13 | } 14 | } 15 | } 16 | } 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /test/helpers/test-object-option.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export default function testObjectOption (name) { 4 | it(`Options ${name}: should warn non object value`, () => { 5 | const options = {} 6 | options[name] = () => {} 7 | new Vue(options) 8 | expect(`Invalid value for option "${name}"`).toHaveBeenWarned() 9 | }) 10 | 11 | it(`Options ${name}: should not warn valid object value`, () => { 12 | const options = {} 13 | options[name] = {} 14 | new Vue(options) 15 | expect(`Invalid value for option "${name}"`).not.toHaveBeenWarned() 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /test/helpers/to-equal.js: -------------------------------------------------------------------------------- 1 | import { isEqual } from 'lodash' 2 | 3 | beforeEach(() => { 4 | jasmine.addMatchers({ 5 | // override built-in toEqual because it behaves incorrectly 6 | // on Vue-observed arrays in Safari 7 | toEqual: () => { 8 | return { 9 | compare: (a, b) => { 10 | const pass = isEqual(a, b) 11 | return { 12 | pass, 13 | message: `Expected ${a} to equal ${b}` 14 | } 15 | } 16 | } 17 | } 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /test/helpers/trigger-event.js: -------------------------------------------------------------------------------- 1 | window.triggerEvent = function triggerEvent (target, event, process) { 2 | const e = document.createEvent('HTMLEvents') 3 | e.initEvent(event, true, true) 4 | if (event === 'click') { 5 | e.button = 0 6 | } 7 | if (process) process(e) 8 | target.dispatchEvent(e) 9 | } 10 | -------------------------------------------------------------------------------- /test/helpers/vdom.js: -------------------------------------------------------------------------------- 1 | import VNode from 'core/vdom/vnode' 2 | 3 | window.createTextVNode = function (text) { 4 | return new VNode(undefined, undefined, undefined, text) 5 | } 6 | -------------------------------------------------------------------------------- /test/ssr/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "plugins": ["jasmine"], 6 | "rules": { 7 | "jasmine/no-focused-tests": 2 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/ssr/async-loader.js: -------------------------------------------------------------------------------- 1 | const hash = require('hash-sum') 2 | 3 | module.exports = function (code) { 4 | const id = hash(this.request) // simulating vue-loader module id injection 5 | return code.replace('__MODULE_ID__', id) 6 | } 7 | -------------------------------------------------------------------------------- /test/ssr/compile-with-webpack.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import webpack from 'webpack' 3 | import MemoryFS from 'memory-fs' 4 | 5 | export function compileWithWebpack (file, extraConfig, cb) { 6 | const config = Object.assign({ 7 | mode: 'development', 8 | entry: path.resolve(__dirname, 'fixtures', file), 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.js$/, 13 | loader: 'babel-loader' 14 | }, 15 | { 16 | test: /async-.*\.js$/, 17 | loader: require.resolve('./async-loader') 18 | }, 19 | { 20 | test: /\.(png|woff2|css)$/, 21 | loader: 'file-loader', 22 | options: { 23 | name: '[name].[ext]' 24 | } 25 | } 26 | ] 27 | } 28 | }, extraConfig) 29 | 30 | const compiler = webpack(config) 31 | const fs = new MemoryFS() 32 | compiler.outputFileSystem = fs 33 | 34 | compiler.run((err, stats) => { 35 | expect(err).toBeFalsy() 36 | expect(stats.errors).toBeFalsy() 37 | cb(fs) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /test/ssr/fixtures/app.js: -------------------------------------------------------------------------------- 1 | import Vue from '../../../dist/vue.runtime.common.js' 2 | 3 | export default context => { 4 | return new Promise(resolve => { 5 | context.msg = 'hello' 6 | resolve(new Vue({ 7 | render (h) { 8 | return h('div', context.url) 9 | } 10 | })) 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /test/ssr/fixtures/async-bar.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | beforeCreate () { 3 | this.$vnode.ssrContext._registeredComponents.add('__MODULE_ID__') 4 | }, 5 | render (h) { 6 | return h('div', 'async bar') 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/ssr/fixtures/async-foo.js: -------------------------------------------------------------------------------- 1 | // import image and font 2 | import './test.css' 3 | import font from './test.woff2' 4 | import image from './test.png' 5 | 6 | export default { 7 | beforeCreate () { 8 | this.$vnode.ssrContext._registeredComponents.add('__MODULE_ID__') 9 | }, 10 | render (h) { 11 | return h('div', `async ${font} ${image}`) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/ssr/fixtures/cache-opt-out.js: -------------------------------------------------------------------------------- 1 | import Vue from '../../../dist/vue.runtime.common.js' 2 | 3 | const app = { 4 | name: 'app', 5 | props: ['id'], 6 | serverCacheKey: props => props.id === 1 ? false : props.id, 7 | render (h) { 8 | return h('div', '/test') 9 | } 10 | } 11 | 12 | export default () => { 13 | return Promise.resolve(new Vue({ 14 | render: h => h(app, { props: { id: 1 }}) 15 | })) 16 | } 17 | -------------------------------------------------------------------------------- /test/ssr/fixtures/cache.js: -------------------------------------------------------------------------------- 1 | import Vue from '../../../dist/vue.runtime.common.js' 2 | 3 | const app = { 4 | name: 'app', 5 | props: ['id'], 6 | serverCacheKey: props => props.id, 7 | render (h) { 8 | return h('div', '/test') 9 | } 10 | } 11 | 12 | export default () => { 13 | return Promise.resolve(new Vue({ 14 | render: h => h(app, { props: { id: 1 }}) 15 | })) 16 | } 17 | -------------------------------------------------------------------------------- /test/ssr/fixtures/error.js: -------------------------------------------------------------------------------- 1 | throw new Error('foo') 2 | -------------------------------------------------------------------------------- /test/ssr/fixtures/nested-cache.js: -------------------------------------------------------------------------------- 1 | import Vue from '../../../dist/vue.runtime.common.js' 2 | 3 | function createRegisterFn (id) { 4 | return function (context) { 5 | context = context || this.$vnode.ssrContext 6 | context.registered.push(id) 7 | } 8 | } 9 | 10 | function addHooks (comp) { 11 | const hook = createRegisterFn(comp.name) 12 | return Object.assign(comp, { 13 | _ssrRegister: hook, 14 | beforeCreate: hook 15 | }) 16 | } 17 | 18 | const grandchild = addHooks({ 19 | name: 'grandchild', 20 | props: ['id'], 21 | serverCacheKey: props => props.id, 22 | render (h) { 23 | return h('div', '/test') 24 | } 25 | }) 26 | 27 | const child = addHooks({ 28 | name: 'child', 29 | props: ['id'], 30 | serverCacheKey: props => props.id, 31 | render (h) { 32 | return h(grandchild, { props: { id: this.id }}) 33 | } 34 | }) 35 | 36 | const app = addHooks({ 37 | name: 'app', 38 | props: ['id'], 39 | serverCacheKey: props => props.id, 40 | render (h) { 41 | return h(child, { props: { id: this.id }}) 42 | } 43 | }) 44 | 45 | export default () => { 46 | return Promise.resolve(new Vue({ 47 | render: h => h(app, { props: { id: 1 }}) 48 | })) 49 | } 50 | -------------------------------------------------------------------------------- /test/ssr/fixtures/promise-rejection.js: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return Promise.reject(new Error('foo')) 3 | } 4 | -------------------------------------------------------------------------------- /test/ssr/fixtures/split.js: -------------------------------------------------------------------------------- 1 | import Vue from '../../../dist/vue.runtime.common.js' 2 | 3 | // async component! 4 | const Foo = () => import('./async-foo') 5 | const Bar = () => import('./async-bar') // eslint-disable-line 6 | 7 | export default context => { 8 | return new Promise(resolve => { 9 | context.msg = 'hello' 10 | const vm = new Vue({ 11 | render (h) { 12 | return h('div', [ 13 | context.url, 14 | h(Foo) 15 | ]) 16 | } 17 | }) 18 | 19 | // simulate router.onReady 20 | Foo().then(comp => { 21 | // resolve now to make the render sync 22 | Foo.resolved = Vue.extend(comp.default) 23 | resolve(vm) 24 | }) 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /test/ssr/fixtures/test.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supergaojian/vue/b6d212da7f74fd2fb1da998ed924c2ac3c7fee3a/test/ssr/fixtures/test.css -------------------------------------------------------------------------------- /test/ssr/fixtures/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supergaojian/vue/b6d212da7f74fd2fb1da998ed924c2ac3c7fee3a/test/ssr/fixtures/test.png -------------------------------------------------------------------------------- /test/ssr/fixtures/test.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supergaojian/vue/b6d212da7f74fd2fb1da998ed924c2ac3c7fee3a/test/ssr/fixtures/test.woff2 -------------------------------------------------------------------------------- /test/ssr/jasmine.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | spec_dir: 'test/ssr', 3 | spec_files: [ 4 | '*.spec.js' 5 | ], 6 | helpers: [ 7 | require.resolve('@babel/register'), 8 | '../helpers/to-have-been-warned.js' 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /test/unit/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "globals": { 6 | "waitForUpdate": true, 7 | "triggerEvent": true, 8 | "createTextVNode": true 9 | }, 10 | "plugins": ["jasmine"], 11 | "rules": { 12 | "jasmine/no-focused-tests": 2, 13 | "no-unused-vars": 0 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/unit/features/directives/cloak.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Directive v-cloak', () => { 4 | it('should be removed after compile', () => { 5 | const el = document.createElement('div') 6 | el.setAttribute('v-cloak', '') 7 | const vm = new Vue({ el }) 8 | expect(vm.$el.hasAttribute('v-cloak')).toBe(false) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /test/unit/features/directives/model-file.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Directive v-model file', () => { 4 | it('warn to use @change instead', () => { 5 | new Vue({ 6 | data: { 7 | file: '' 8 | }, 9 | template: '' 10 | }).$mount() 11 | expect('Use a v-on:change listener instead').toHaveBeenWarned() 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /test/unit/features/directives/model-parse.spec.js: -------------------------------------------------------------------------------- 1 | import { parseModel } from 'compiler/directives/model' 2 | 3 | describe('model expression parser', () => { 4 | it('parse single path', () => { 5 | const res = parseModel('foo') 6 | expect(res.exp).toBe('foo') 7 | expect(res.key).toBe(null) 8 | }) 9 | 10 | it('parse object dot notation', () => { 11 | const res = parseModel('a.b.c') 12 | expect(res.exp).toBe('a.b') 13 | expect(res.key).toBe('"c"') 14 | }) 15 | 16 | it('parse string in brackets', () => { 17 | const res = parseModel('a["b"][c]') 18 | expect(res.exp).toBe('a["b"]') 19 | expect(res.key).toBe('c') 20 | }) 21 | 22 | it('parse brackets with object dot notation', () => { 23 | const res = parseModel('a["b"][c].xxx') 24 | expect(res.exp).toBe('a["b"][c]') 25 | expect(res.key).toBe('"xxx"') 26 | }) 27 | 28 | it('parse nested brackets', () => { 29 | const res = parseModel('a[i[c]]') 30 | expect(res.exp).toBe('a') 31 | expect(res.key).toBe('i[c]') 32 | }) 33 | 34 | it('combined', () => { 35 | const res = parseModel('test.xxx.a["asa"][test1[key]]') 36 | expect(res.exp).toBe('test.xxx.a["asa"]') 37 | expect(res.key).toBe('test1[key]') 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /test/unit/features/directives/pre.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Directive v-pre', function () { 4 | it('should not compile inner content', function () { 5 | const vm = new Vue({ 6 | template: `
7 |
{{ a }}
8 |
{{ a }}
9 |
10 | 11 |
12 |
`, 13 | data: { 14 | a: 123 15 | } 16 | }) 17 | vm.$mount() 18 | expect(vm.$el.firstChild.textContent).toBe('{{ a }}') 19 | expect(vm.$el.children[1].textContent).toBe('123') 20 | expect(vm.$el.lastChild.innerHTML).toBe('') 21 | }) 22 | 23 | it('should not compile on root node', function () { 24 | const vm = new Vue({ 25 | template: '
{{ a }}
', 26 | replace: true, 27 | data: { 28 | a: 123 29 | } 30 | }) 31 | vm.$mount() 32 | expect(vm.$el.firstChild.textContent).toBe('{{ a }}') 33 | }) 34 | 35 | // #8286 36 | it('should not compile custom component tags', function () { 37 | Vue.component('vtest', { template: `
Hello World
` }) 38 | const vm = new Vue({ 39 | template: '
', 40 | replace: true 41 | }) 42 | vm.$mount() 43 | expect(vm.$el.firstChild.tagName).toBe('VTEST') 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /test/unit/features/global-api/compile.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Global API: compile', () => { 4 | it('should compile render functions', () => { 5 | const res = Vue.compile('
{{ msg }}
') 6 | const vm = new Vue({ 7 | data: { 8 | msg: 'hello' 9 | }, 10 | render: res.render, 11 | staticRenderFns: res.staticRenderFns 12 | }).$mount() 13 | expect(vm.$el.innerHTML).toContain('hello') 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /test/unit/features/global-api/observable.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Global API: observable', () => { 4 | it('should work', done => { 5 | const state = Vue.observable({ 6 | count: 0 7 | }) 8 | 9 | const app = new Vue({ 10 | render(h) { 11 | return h('div', [ 12 | h('span', state.count), 13 | h('button', { 14 | on: { 15 | click: () => { 16 | state.count++ 17 | } 18 | } 19 | }, '+') 20 | ]) 21 | } 22 | }).$mount() 23 | 24 | expect(app.$el.querySelector('span').textContent).toBe('0') 25 | app.$el.querySelector('button').click() 26 | waitForUpdate(() => { 27 | expect(app.$el.querySelector('span').textContent).toBe('1') 28 | }).then(done) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /test/unit/features/instance/init.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Initialization', () => { 4 | it('without new', () => { 5 | try { Vue() } catch (e) {} 6 | expect('Vue is a constructor and should be called with the `new` keyword').toHaveBeenWarned() 7 | }) 8 | 9 | it('with new', () => { 10 | expect(new Vue() instanceof Vue).toBe(true) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /test/unit/features/options/comments.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Comments', () => { 4 | it('comments should be kept', () => { 5 | const vm = new Vue({ 6 | comments: true, 7 | data () { 8 | return { 9 | foo: 1 10 | } 11 | }, 12 | template: '
node1{{foo}}
' 13 | }).$mount() 14 | expect(vm.$el.innerHTML).toEqual('node11') 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /test/unit/features/options/inheritAttrs.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options inheritAttrs', () => { 4 | it('should work', done => { 5 | const vm = new Vue({ 6 | template: ``, 7 | data: { foo: 'foo' }, 8 | components: { 9 | foo: { 10 | inheritAttrs: false, 11 | template: `
foo
` 12 | } 13 | } 14 | }).$mount() 15 | expect(vm.$el.id).toBe('') 16 | vm.foo = 'bar' 17 | waitForUpdate(() => { 18 | expect(vm.$el.id).toBe('') 19 | }).then(done) 20 | }) 21 | 22 | it('with inner v-bind', done => { 23 | const vm = new Vue({ 24 | template: ``, 25 | data: { foo: 'foo' }, 26 | components: { 27 | foo: { 28 | inheritAttrs: false, 29 | template: `
` 30 | } 31 | } 32 | }).$mount() 33 | expect(vm.$el.children[0].id).toBe('foo') 34 | vm.foo = 'bar' 35 | waitForUpdate(() => { 36 | expect(vm.$el.children[0].id).toBe('bar') 37 | }).then(done) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /test/unit/features/options/methods.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import testObjectOption from '../../../helpers/test-object-option' 3 | 4 | describe('Options methods', () => { 5 | testObjectOption('methods') 6 | 7 | it('should have correct context', () => { 8 | const vm = new Vue({ 9 | data: { 10 | a: 1 11 | }, 12 | methods: { 13 | plus () { 14 | this.a++ 15 | } 16 | } 17 | }) 18 | vm.plus() 19 | expect(vm.a).toBe(2) 20 | }) 21 | 22 | it('should warn methods of not function type', () => { 23 | new Vue({ 24 | methods: { 25 | hello: {} 26 | } 27 | }) 28 | expect('Method "hello" has type "object" in the component definition').toHaveBeenWarned() 29 | }) 30 | 31 | it('should warn methods conflicting with data', () => { 32 | new Vue({ 33 | data: { 34 | foo: 1 35 | }, 36 | methods: { 37 | foo () {} 38 | } 39 | }) 40 | expect(`Method "foo" has already been defined as a data property`).toHaveBeenWarned() 41 | }) 42 | 43 | it('should warn methods conflicting with internal methods', () => { 44 | new Vue({ 45 | methods: { 46 | _update () {} 47 | } 48 | }) 49 | expect(`Method "_update" conflicts with an existing Vue instance method`).toHaveBeenWarned() 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /test/unit/features/options/name.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options name', () => { 4 | it('should contain itself in self components', () => { 5 | const vm = Vue.extend({ 6 | name: 'SuperVue' 7 | }) 8 | 9 | expect(vm.options.components['SuperVue']).toEqual(vm) 10 | }) 11 | 12 | it('should warn when incorrect name given', () => { 13 | Vue.extend({ 14 | name: 'Hyper*Vue' 15 | }) 16 | 17 | /* eslint-disable */ 18 | expect(`Invalid component name: "Hyper*Vue".`) 19 | .toHaveBeenWarned() 20 | /* eslint-enable */ 21 | 22 | Vue.extend({ 23 | name: '2Cool2BValid' 24 | }) 25 | 26 | /* eslint-disable */ 27 | expect(`Invalid component name: "2Cool2BValid".`) 28 | .toHaveBeenWarned() 29 | /* eslint-enable */ 30 | }) 31 | 32 | it('id should not override given name when using Vue.component', () => { 33 | const SuperComponent = Vue.component('super-component', { 34 | name: 'SuperVue' 35 | }) 36 | 37 | expect(SuperComponent.options.components['SuperVue']).toEqual(SuperComponent) 38 | expect(SuperComponent.options.components['super-component']).toEqual(SuperComponent) 39 | }) 40 | 41 | it('should allow all potential custom element name for component name including non-alphanumeric characters', () => { 42 | Vue.extend({ 43 | name: 'my-컴포넌트' 44 | }) 45 | 46 | expect(`Invalid component name`).not.toHaveBeenWarned() 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /test/unit/features/options/parent.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options parent', () => { 4 | it('should work', () => { 5 | const parent = new Vue({ 6 | render () {} 7 | }).$mount() 8 | 9 | const child = new Vue({ 10 | parent: parent, 11 | render () {} 12 | }).$mount() 13 | 14 | // this option is straight-forward 15 | // it should register 'parent' as a $parent for 'child' 16 | // and push 'child' to $children array on 'parent' 17 | expect(child.$options.parent).toBeDefined() 18 | expect(child.$options.parent).toEqual(parent) 19 | expect(child.$parent).toBeDefined() 20 | expect(child.$parent).toEqual(parent) 21 | expect(parent.$children).toContain(child) 22 | 23 | // destroy 'child' and check if it was removed from 'parent' $children 24 | child.$destroy() 25 | expect(parent.$children.length).toEqual(0) 26 | parent.$destroy() 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /test/unit/features/options/propsData.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options propsData', () => { 4 | it('should work', done => { 5 | const A = Vue.extend({ 6 | props: ['a'], 7 | template: '
{{ a }}
' 8 | }) 9 | const vm = new A({ 10 | propsData: { 11 | a: 123 12 | } 13 | }).$mount() 14 | expect(vm.a).toBe(123) 15 | expect(vm.$el.textContent).toBe('123') 16 | vm.a = 234 17 | waitForUpdate(() => { 18 | expect(vm.$el.textContent).toBe('234') 19 | }).then(done) 20 | }) 21 | 22 | it('warn non instantiation usage', () => { 23 | Vue.extend({ 24 | propsData: { 25 | a: 123 26 | } 27 | }) 28 | expect('option "propsData" can only be used during instance creation').toHaveBeenWarned() 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /test/unit/features/options/render.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options render', () => { 4 | it('basic usage', () => { 5 | const vm = new Vue({ 6 | render (h) { 7 | const children = [] 8 | for (let i = 0; i < this.items.length; i++) { 9 | children.push(h('li', { staticClass: 'task' }, [this.items[i].name])) 10 | } 11 | return h('ul', { staticClass: 'tasks' }, children) 12 | }, 13 | data: { 14 | items: [{ id: 1, name: 'task1' }, { id: 2, name: 'task2' }] 15 | } 16 | }).$mount() 17 | expect(vm.$el.tagName).toBe('UL') 18 | for (let i = 0; i < vm.$el.children.length; i++) { 19 | const li = vm.$el.children[i] 20 | expect(li.tagName).toBe('LI') 21 | expect(li.textContent).toBe(vm.items[i].name) 22 | } 23 | }) 24 | 25 | it('allow null data', () => { 26 | const vm = new Vue({ 27 | render (h) { 28 | return h('div', null, 'hello' /* string as children*/) 29 | } 30 | }).$mount() 31 | expect(vm.$el.tagName).toBe('DIV') 32 | expect(vm.$el.textContent).toBe('hello') 33 | }) 34 | 35 | it('should warn non `render` option and non `template` option', () => { 36 | new Vue().$mount() 37 | expect('Failed to mount component: template or render function not defined.').toHaveBeenWarned() 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /test/unit/features/options/renderError.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Options renderError', () => { 4 | it('should be used on render errors', done => { 5 | Vue.config.errorHandler = () => {} 6 | const vm = new Vue({ 7 | data: { 8 | ok: true 9 | }, 10 | render (h) { 11 | if (this.ok) { 12 | return h('div', 'ok') 13 | } else { 14 | throw new Error('no') 15 | } 16 | }, 17 | renderError (h, err) { 18 | return h('div', err.toString()) 19 | } 20 | }).$mount() 21 | expect(vm.$el.textContent).toBe('ok') 22 | vm.ok = false 23 | waitForUpdate(() => { 24 | expect(vm.$el.textContent).toBe('Error: no') 25 | Vue.config.errorHandler = null 26 | }).then(done) 27 | }) 28 | 29 | it('should pass on errors in renderError to global handler', () => { 30 | const spy = Vue.config.errorHandler = jasmine.createSpy() 31 | const err = new Error('renderError') 32 | const vm = new Vue({ 33 | render () { 34 | throw new Error('render') 35 | }, 36 | renderError () { 37 | throw err 38 | } 39 | }).$mount() 40 | expect(spy).toHaveBeenCalledWith(err, vm, 'renderError') 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | require('es6-promise/auto') 2 | 3 | // import all helpers 4 | const helpersContext = require.context('../helpers', true) 5 | helpersContext.keys().forEach(helpersContext) 6 | 7 | // require all test files 8 | const testsContext = require.context('./', true, /\.spec$/) 9 | testsContext.keys().forEach(testsContext) 10 | -------------------------------------------------------------------------------- /test/unit/karma.base.config.js: -------------------------------------------------------------------------------- 1 | const alias = require('../../scripts/alias') 2 | const featureFlags = require('../../scripts/feature-flags') 3 | const webpack = require('webpack') 4 | 5 | const webpackConfig = { 6 | mode: 'development', 7 | resolve: { 8 | alias: alias 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.js$/, 14 | loader: 'babel-loader', 15 | exclude: /node_modules/ 16 | } 17 | ] 18 | }, 19 | plugins: [ 20 | new webpack.DefinePlugin({ 21 | __WEEX__: false, 22 | 'process.env': { 23 | TRANSITION_DURATION: process.env.CI ? 100 : 50, 24 | TRANSITION_BUFFER: 10, 25 | ...featureFlags 26 | } 27 | }) 28 | ], 29 | devtool: '#inline-source-map' 30 | } 31 | 32 | // shared config for all unit tests 33 | module.exports = { 34 | frameworks: ['jasmine'], 35 | files: [ 36 | './index.js' 37 | ], 38 | preprocessors: { 39 | './index.js': ['webpack', 'sourcemap'] 40 | }, 41 | webpack: webpackConfig, 42 | webpackMiddleware: { 43 | noInfo: true 44 | }, 45 | plugins: [ 46 | 'karma-jasmine', 47 | 'karma-mocha-reporter', 48 | 'karma-sourcemap-loader', 49 | 'karma-webpack' 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /test/unit/karma.cover.config.js: -------------------------------------------------------------------------------- 1 | const base = require('./karma.base.config.js') 2 | 3 | process.env.CHROME_BIN = require('puppeteer').executablePath() 4 | 5 | module.exports = function (config) { 6 | const options = Object.assign(base, { 7 | browsers: ['ChromeHeadlessCI'], 8 | customLaunchers: { 9 | 'ChromeHeadlessCI': { 10 | base: 'ChromeHeadless', 11 | flags: ['--no-sandbox'] 12 | } 13 | }, 14 | reporters: ['mocha', 'coverage'], 15 | coverageReporter: { 16 | reporters: [ 17 | { type: 'lcov', dir: '../../coverage', subdir: '.' }, 18 | { type: 'text-summary', dir: '../../coverage', subdir: '.' } 19 | ] 20 | }, 21 | singleRun: true, 22 | plugins: base.plugins.concat([ 23 | 'karma-coverage', 24 | 'karma-chrome-launcher' 25 | ]) 26 | }) 27 | 28 | // add babel-plugin-istanbul for code instrumentation 29 | options.webpack.module.rules[0].options = { 30 | plugins: [['istanbul', { 31 | exclude: [ 32 | 'test/', 33 | 'src/compiler/parser/html-parser.js', 34 | 'src/core/instance/proxy.js', 35 | 'src/sfc/deindent.js', 36 | 'src/platforms/weex/' 37 | ] 38 | }]] 39 | } 40 | 41 | config.set(options) 42 | } 43 | -------------------------------------------------------------------------------- /test/unit/karma.dev.config.js: -------------------------------------------------------------------------------- 1 | const base = require('./karma.base.config.js') 2 | 3 | process.env.CHROME_BIN = require('puppeteer').executablePath() 4 | 5 | module.exports = function (config) { 6 | config.set(Object.assign(base, { 7 | browsers: ['ChromeHeadless'], 8 | reporters: ['progress'], 9 | plugins: base.plugins.concat([ 10 | 'karma-chrome-launcher' 11 | ]) 12 | })) 13 | } 14 | -------------------------------------------------------------------------------- /test/unit/karma.unit.config.js: -------------------------------------------------------------------------------- 1 | const base = require('./karma.base.config.js') 2 | 3 | module.exports = function (config) { 4 | config.set(Object.assign(base, { 5 | browsers: ['Chrome', 'Firefox', 'Safari'], 6 | reporters: ['progress'], 7 | singleRun: true, 8 | plugins: base.plugins.concat([ 9 | 'karma-chrome-launcher', 10 | 'karma-firefox-launcher', 11 | 'karma-safari-launcher' 12 | ]) 13 | })) 14 | } 15 | -------------------------------------------------------------------------------- /test/unit/modules/observer/dep.spec.js: -------------------------------------------------------------------------------- 1 | import Dep from 'core/observer/dep' 2 | 3 | describe('Dep', () => { 4 | let dep 5 | 6 | beforeEach(() => { 7 | dep = new Dep() 8 | }) 9 | 10 | describe('instance', () => { 11 | it('should be created with correct properties', () => { 12 | expect(dep.subs.length).toBe(0) 13 | expect(new Dep().id).toBe(dep.id + 1) 14 | }) 15 | }) 16 | 17 | describe('addSub()', () => { 18 | it('should add sub', () => { 19 | dep.addSub(null) 20 | expect(dep.subs.length).toBe(1) 21 | expect(dep.subs[0]).toBe(null) 22 | }) 23 | }) 24 | 25 | describe('removeSub()', () => { 26 | it('should remove sub', () => { 27 | dep.subs.push(null) 28 | dep.removeSub(null) 29 | expect(dep.subs.length).toBe(0) 30 | }) 31 | }) 32 | 33 | describe('depend()', () => { 34 | let _target 35 | 36 | beforeAll(() => { 37 | _target = Dep.target 38 | }) 39 | 40 | afterAll(() => { 41 | Dep.target = _target 42 | }) 43 | 44 | it('should do nothing if no target', () => { 45 | Dep.target = null 46 | dep.depend() 47 | }) 48 | 49 | it('should add itself to target', () => { 50 | Dep.target = jasmine.createSpyObj('TARGET', ['addDep']) 51 | dep.depend() 52 | expect(Dep.target.addDep).toHaveBeenCalledWith(dep) 53 | }) 54 | }) 55 | 56 | describe('notify()', () => { 57 | it('should notify subs', () => { 58 | dep.subs.push(jasmine.createSpyObj('SUB', ['update'])) 59 | dep.notify() 60 | expect(dep.subs[0].update).toHaveBeenCalled() 61 | }) 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /test/unit/modules/server-compiler/compiler-options.spec.js: -------------------------------------------------------------------------------- 1 | import { ssrCompile } from 'web/server/compiler' 2 | 3 | describe('ssrCompile options', () => { 4 | it('comments', () => { 5 | const compiled = ssrCompile(` 6 |
7 | 8 |
9 | `, { comments: true }) 10 | 11 | expect(compiled.render).toContain('') 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /test/unit/modules/server-compiler/optimizer.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supergaojian/vue/b6d212da7f74fd2fb1da998ed924c2ac3c7fee3a/test/unit/modules/server-compiler/optimizer.spec.js -------------------------------------------------------------------------------- /test/unit/modules/util/error.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { invokeWithErrorHandling } from 'core/util/error' 3 | 4 | describe('invokeWithErrorHandling', () => { 5 | if (typeof Promise !== 'undefined') { 6 | it('should errorHandler call once when nested calls return rejected promise', done => { 7 | const originalHandler = Vue.config.errorHandler 8 | const handler = Vue.config.errorHandler = jasmine.createSpy() 9 | const userCatch = jasmine.createSpy() 10 | const err = new Error('fake error') 11 | 12 | invokeWithErrorHandling(() => { 13 | return invokeWithErrorHandling(() => { 14 | return Promise.reject(err) 15 | }) 16 | }).catch(userCatch).then(() => { 17 | Vue.config.errorHandler = originalHandler 18 | expect(handler.calls.count()).toBe(1) 19 | expect(userCatch).toHaveBeenCalledWith(err) 20 | done() 21 | }) 22 | }) 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /test/unit/modules/util/next-tick.spec.js: -------------------------------------------------------------------------------- 1 | import { nextTick } from 'core/util/next-tick' 2 | 3 | describe('nextTick', () => { 4 | it('accepts a callback', done => { 5 | nextTick(done) 6 | }) 7 | 8 | it('returns undefined when passed a callback', () => { 9 | expect(nextTick(() => {})).toBeUndefined() 10 | }) 11 | 12 | if (typeof Promise !== 'undefined') { 13 | it('returns a Promise when provided no callback', done => { 14 | nextTick().then(done) 15 | }) 16 | 17 | it('returns a Promise with a context argument when provided a falsy callback and an object', done => { 18 | const obj = {} 19 | nextTick(undefined, obj).then(ctx => { 20 | expect(ctx).toBe(obj) 21 | done() 22 | }) 23 | }) 24 | 25 | it('returned Promise should resolve correctly vs callback', done => { 26 | const spy = jasmine.createSpy() 27 | nextTick(spy) 28 | nextTick().then(() => { 29 | expect(spy).toHaveBeenCalled() 30 | done() 31 | }) 32 | }) 33 | } 34 | }) 35 | -------------------------------------------------------------------------------- /test/unit/modules/vdom/modules/directive.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { patch } from 'web/runtime/patch' 3 | import VNode from 'core/vdom/vnode' 4 | 5 | describe('vdom directive module', () => { 6 | it('should work', () => { 7 | const directive1 = { 8 | bind: jasmine.createSpy('bind'), 9 | update: jasmine.createSpy('update'), 10 | unbind: jasmine.createSpy('unbind') 11 | } 12 | const vm = new Vue({ directives: { directive1 }}) 13 | // create 14 | const vnode1 = new VNode('div', {}, [ 15 | new VNode('p', { 16 | directives: [{ 17 | name: 'directive1', value: 'hello', arg: 'arg1', modifiers: { modifier1: true } 18 | }] 19 | }, undefined, 'hello world', undefined, vm) 20 | ]) 21 | patch(null, vnode1) 22 | expect(directive1.bind).toHaveBeenCalled() 23 | // update 24 | const vnode2 = new VNode('div', {}, [ 25 | new VNode('p', { 26 | directives: [{ 27 | name: 'directive1', value: 'world', arg: 'arg1', modifiers: { modifier1: true } 28 | }] 29 | }, undefined, 'hello world', undefined, vm) 30 | ]) 31 | patch(vnode1, vnode2) 32 | expect(directive1.update).toHaveBeenCalled() 33 | // destroy 34 | const vnode3 = new VNode('div') 35 | patch(vnode2, vnode3) 36 | expect(directive1.unbind).toHaveBeenCalled() 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /test/unit/modules/vdom/modules/style.spec.js: -------------------------------------------------------------------------------- 1 | import { patch } from 'web/runtime/patch' 2 | import VNode from 'core/vdom/vnode' 3 | 4 | describe('vdom style module', () => { 5 | it('should create an element with style', () => { 6 | const vnode = new VNode('p', { style: { fontSize: '12px' }}) 7 | const elm = patch(null, vnode) 8 | expect(elm.style.fontSize).toBe('12px') 9 | }) 10 | 11 | it('should create an element with array style', () => { 12 | const vnode = new VNode('p', { style: [{ fontSize: '12px' }, { color: 'red' }] }) 13 | const elm = patch(null, vnode) 14 | expect(elm.style.fontSize).toBe('12px') 15 | expect(elm.style.color).toBe('red') 16 | }) 17 | 18 | it('should change elements style', () => { 19 | const vnode1 = new VNode('p', { style: { fontSize: '12px' }}) 20 | const vnode2 = new VNode('p', { style: { fontSize: '10px', display: 'block' }}) 21 | patch(null, vnode1) 22 | const elm = patch(vnode1, vnode2) 23 | expect(elm.style.fontSize).toBe('10px') 24 | expect(elm.style.display).toBe('block') 25 | }) 26 | 27 | it('should remove elements attrs', () => { 28 | const vnode1 = new VNode('p', { style: { fontSize: '12px' }}) 29 | const vnode2 = new VNode('p', { style: { display: 'block' }}) 30 | patch(null, vnode1) 31 | const elm = patch(vnode1, vnode2) 32 | expect(elm.style.fontSize).toBe('') 33 | expect(elm.style.display).toBe('block') 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /test/weex/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "plugins": ["jasmine"], 6 | "rules": { 7 | "jasmine/no-focused-tests": 2 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/weex/cases/event/click.after.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'div', 3 | event: ['click'], 4 | children: [{ 5 | type: 'text', 6 | attr: { 7 | value: '43' 8 | } 9 | }] 10 | }) 11 | -------------------------------------------------------------------------------- /test/weex/cases/event/click.before.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'div', 3 | event: ['click'], 4 | children: [{ 5 | type: 'text', 6 | attr: { 7 | value: '42' 8 | } 9 | }] 10 | }) 11 | -------------------------------------------------------------------------------- /test/weex/cases/event/click.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/attrs.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', count: 1, source: 'http://whatever.com/x.png' }, 7 | { type: 'A', count: 2, source: 'http://whatever.com/y.png' }, 8 | { type: 'A', count: 3, source: 'http://whatever.com/z.png' } 9 | ], 10 | switch: 'type', 11 | alias: 'item' 12 | }, 13 | children: [{ 14 | type: 'cell-slot', 15 | attr: { append: 'tree', case: 'A' }, 16 | children: [{ 17 | type: 'image', 18 | attr: { 19 | resize: 'cover', 20 | src: { 21 | '@binding': 'item.source' 22 | } 23 | } 24 | }, { 25 | type: 'text', 26 | attr: { 27 | lines: '3', 28 | count: { 29 | '@binding': 'item.count' 30 | } 31 | } 32 | }] 33 | }] 34 | }) 35 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/attrs.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | 24 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/classname.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', color: 'red' }, 7 | { type: 'A', color: 'blue' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | style: { 16 | backgroundColor: '#FF6600' 17 | }, 18 | children: [{ 19 | type: 'text', 20 | attr: { 21 | // not supported yet 22 | // classList: ['text', { '@binding': 'item.color' }], 23 | value: 'content' 24 | } 25 | }] 26 | }] 27 | }) 28 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/classname.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | 25 | 37 | 38 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/banner.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/counter.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 37 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/editor.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 32 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/footer.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/lifecycle.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 40 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/poster.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 22 | 23 | 34 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful-lifecycle.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'X' }, 7 | { type: 'X' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'X' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '@isComponentRoot': true, 19 | '@componentProps': {} 20 | }, 21 | children: [{ 22 | type: 'text', 23 | attr: { 24 | value: { '@binding': 'number' } 25 | } 26 | }] 27 | }] 28 | }] 29 | }) 30 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful-lifecycle.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful-v-model.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '@isComponentRoot': true, 19 | '@componentProps': { 20 | message: 'No binding' 21 | } 22 | }, 23 | children: [{ 24 | type: 'text', 25 | classList: ['output'], 26 | attr: { 27 | value: { '@binding': 'output' } 28 | } 29 | }, { 30 | type: 'input', 31 | event: ['input'], 32 | classList: ['input'], 33 | attr: { 34 | type: 'text', 35 | value: 'No binding' 36 | } 37 | }] 38 | }] 39 | }] 40 | }) 41 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful-v-model.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', number: 24 }, 7 | { type: 'A', number: 42 } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '@isComponentRoot': true, 19 | '@componentProps': { 20 | start: { '@binding': 'item.number' } 21 | } 22 | }, 23 | children: [{ 24 | type: 'text', 25 | classList: ['output'], 26 | attr: { 27 | value: { '@binding': 'count' } // need confirm 28 | } 29 | }, { 30 | type: 'text', 31 | event: ['click'], 32 | classList: ['button'], 33 | attr: { value: '+' } 34 | }] 35 | }, { 36 | type: 'text', 37 | attr: { value: 'other' } 38 | }] 39 | }] 40 | }) 41 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateful.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateless-multi-components.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 31 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateless-with-props.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', poster: 'xx', title: 'x' }, 7 | { type: 'A', poster: 'yy', title: 'y' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '@isComponentRoot': true, 19 | '@componentProps': { 20 | imageUrl: { '@binding': 'item.poster' }, 21 | title: { '@binding': 'item.title' } 22 | } 23 | }, 24 | children: [{ 25 | type: 'image', 26 | classList: ['image'], 27 | attr: { 28 | src: { '@binding': 'imageUrl' } 29 | } 30 | }, { 31 | type: 'text', 32 | classList: ['title'], 33 | attr: { 34 | value: { '@binding': 'title' } 35 | } 36 | }] 37 | }, { 38 | type: 'text', 39 | attr: { 40 | value: 'content' 41 | } 42 | }] 43 | }] 44 | }) 45 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateless-with-props.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateless.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '@isComponentRoot': true, 19 | '@componentProps': {} 20 | }, 21 | classList: ['banner'], 22 | children: [{ 23 | type: 'text', 24 | classList: ['title'], 25 | attr: { 26 | value: 'BANNER' 27 | } 28 | }] 29 | }, { 30 | type: 'text', 31 | attr: { 32 | value: 'content' 33 | } 34 | }] 35 | }] 36 | }) 37 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/components/stateless.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/inline-style.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', color: '#606060' }, 7 | { type: 'A', color: '#E5E5E5' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | style: { 16 | backgroundColor: '#FF6600' 17 | }, 18 | children: [{ 19 | type: 'text', 20 | style: { 21 | fontSize: '100px', 22 | color: { '@binding': 'item.color' } 23 | }, 24 | attr: { 25 | value: 'content' 26 | } 27 | }] 28 | }] 29 | }) 30 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/inline-style.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/text-node.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A', dynamic: 'decimal', two: '2', four: '4' }, 7 | { type: 'A', dynamic: 'binary', two: '10', four: '100' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'text', 17 | attr: { 18 | value: 'static' 19 | } 20 | }, { 21 | type: 'text', 22 | attr: { 23 | value: { '@binding': 'item.dynamic' } 24 | } 25 | }, { 26 | type: 'text', 27 | attr: { 28 | value: [ 29 | 'one ', 30 | { '@binding': 'item.two' }, 31 | ' three ', 32 | { '@binding': 'item.four' }, 33 | ' five' 34 | ] 35 | } 36 | }] 37 | }] 38 | }) 39 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/text-node.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-else-if.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'image', 17 | attr: { 18 | '[[match]]': 'item.sourceA', 19 | src: { '@binding': 'item.sourceA' } 20 | } 21 | }, { 22 | type: 'image', 23 | attr: { 24 | '[[match]]': '!(item.sourceA) && (item.sourceB)', 25 | src: { '@binding': 'item.sourceB' } 26 | } 27 | }, { 28 | type: 'image', 29 | attr: { 30 | '[[match]]': '!(item.sourceA || item.sourceB)', 31 | src: { '@binding': 'item.placeholder' } 32 | } 33 | }] 34 | }] 35 | }) 36 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-else-if.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-else.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'image', 17 | attr: { 18 | '[[match]]': 'item.source', 19 | src: { '@binding': 'item.source' } 20 | } 21 | }, { 22 | type: 'image', 23 | attr: { 24 | '[[match]]': '!(item.source)', 25 | src: { '@binding': 'item.placeholder' } 26 | } 27 | }] 28 | }] 29 | }) 30 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-else.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-for-iterator.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '[[repeat]]': { 19 | '@expression': 'item.list', 20 | '@index': 'index', 21 | '@alias': 'object' 22 | } 23 | }, 24 | children: [{ 25 | type: 'text', 26 | attr: { 27 | value: { 28 | '@binding': 'object.name' 29 | } 30 | } 31 | }, { 32 | type: 'text', 33 | attr: { 34 | '[[repeat]]': { 35 | '@expression': 'object', 36 | '@alias': 'v', 37 | '@key': 'k', 38 | '@index': 'i' 39 | }, 40 | value: { 41 | '@binding': 'v' 42 | } 43 | } 44 | }] 45 | }] 46 | }] 47 | }) 48 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-for-iterator.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 25 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-for.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'div', 17 | attr: { 18 | '[[repeat]]': { 19 | '@expression': 'item.list', 20 | '@alias': 'panel' 21 | } 22 | }, 23 | children: [{ 24 | type: 'text', 25 | attr: { 26 | value: { 27 | '@binding': 'panel.label' 28 | } 29 | } 30 | }] 31 | }] 32 | }] 33 | }) 34 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-for.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-if.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'image', 17 | attr: { 18 | '[[match]]': 'item.source', 19 | src: { '@binding': 'item.source' } 20 | } 21 | }, { 22 | type: 'text', 23 | attr: { 24 | '[[match]]': '!item.source', 25 | value: 'Title' 26 | } 27 | }] 28 | }] 29 | }) 30 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-if.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-on-inline.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'text', 17 | event: ['click', { 18 | type: 'longpress', 19 | params: [{ '@binding': 'item.key' }] 20 | }] 21 | }, { 22 | type: 'text', 23 | event: [{ 24 | type: 'appear', 25 | params: [ 26 | { '@binding': 'item.index' }, 27 | { '@binding': 'item.type' } 28 | ] 29 | }], 30 | attr: { value: 'Button' } 31 | }, { 32 | type: 'text', 33 | event: [{ type: 'disappear' }], 34 | attr: { value: 'Tips' } 35 | }] 36 | }] 37 | }) 38 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-on-inline.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 28 | 29 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-on.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | switch: 'type', 10 | alias: 'item' 11 | }, 12 | children: [{ 13 | type: 'cell-slot', 14 | attr: { append: 'tree', case: 'A' }, 15 | children: [{ 16 | type: 'text', 17 | event: ['click', 'longpress'], 18 | attr: { value: 'A' } 19 | }, { 20 | type: 'text', 21 | event: ['touchend'], 22 | attr: { value: 'B' } 23 | }] 24 | }] 25 | }) 26 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-on.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 26 | 27 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-once.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'recycle-list', 3 | attr: { 4 | append: 'tree', 5 | listData: [ 6 | { type: 'A' }, 7 | { type: 'A' } 8 | ], 9 | alias: 'item' 10 | }, 11 | children: [{ 12 | type: 'cell-slot', 13 | attr: { append: 'tree' }, 14 | children: [{ 15 | type: 'text', 16 | attr: { 17 | '[[once]]': true, 18 | value: { '@binding': 'item.type' } 19 | } 20 | }] 21 | }] 22 | }) 23 | -------------------------------------------------------------------------------- /test/weex/cases/recycle-list/v-once.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | -------------------------------------------------------------------------------- /test/weex/cases/render/class.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'div', 3 | children: [{ 4 | type: 'div', 5 | classList: ['box', 'box1'] 6 | }, { 7 | type: 'div', 8 | classList: ['box', 'box2'] 9 | }, { 10 | type: 'div', 11 | classList: ['box', 'box3'] 12 | }, { 13 | type: 'div', 14 | classList: ['box', 'box4'] 15 | }, { 16 | type: 'div', 17 | classList: ['box', 'box5'] 18 | }] 19 | }) 20 | -------------------------------------------------------------------------------- /test/weex/cases/render/class.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 37 | 38 | -------------------------------------------------------------------------------- /test/weex/cases/render/sample.vdom.js: -------------------------------------------------------------------------------- 1 | ({ 2 | type: 'div', 3 | style: { 4 | justifyContent: 'center' 5 | }, 6 | children: [{ 7 | type: 'text', 8 | attr: { 9 | value: 'Yo' 10 | }, 11 | classList: ['freestyle'] 12 | }] 13 | }) 14 | -------------------------------------------------------------------------------- /test/weex/cases/render/sample.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 24 | -------------------------------------------------------------------------------- /test/weex/compiler/props.spec.js: -------------------------------------------------------------------------------- 1 | import { compile } from '../../../packages/weex-template-compiler' 2 | import { strToRegExp } from '../helpers/index' 3 | 4 | describe('compile props', () => { 5 | it('custom props', () => { 6 | const { render, staticRenderFns, errors } = compile(`
`) 7 | expect(render).not.toBeUndefined() 8 | expect(staticRenderFns).not.toBeUndefined() 9 | expect(staticRenderFns.length).toEqual(0) 10 | expect(render).toMatch(strToRegExp(`attrs:{"custom":"whatever"}`)) 11 | expect(errors).toEqual([]) 12 | }) 13 | 14 | it('camelize props', () => { 15 | const { render, staticRenderFns, errors } = compile(`
`) 16 | expect(render).not.toBeUndefined() 17 | expect(staticRenderFns).not.toBeUndefined() 18 | expect(staticRenderFns.length).toEqual(0) 19 | expect(render).toMatch(strToRegExp(`attrs:{"kebabCase":"whatever"}`)) 20 | expect(errors).toEqual([]) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /test/weex/jasmine.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | spec_dir: 'test/weex', 3 | spec_files: [ 4 | '**/*[sS]pec.js' 5 | ], 6 | helpers: [ 7 | require.resolve('@babel/register') 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from "./vue"; 2 | 3 | export default Vue; 4 | 5 | export as namespace Vue; 6 | 7 | export { 8 | CreateElement, 9 | VueConstructor 10 | } from "./vue"; 11 | 12 | export { 13 | Component, 14 | AsyncComponent, 15 | ComponentOptions, 16 | FunctionalComponentOptions, 17 | RenderContext, 18 | PropType, 19 | PropOptions, 20 | ComputedOptions, 21 | WatchHandler, 22 | WatchOptions, 23 | WatchOptionsWithHandler, 24 | DirectiveFunction, 25 | DirectiveOptions 26 | } from "./options"; 27 | 28 | export { 29 | PluginFunction, 30 | PluginObject 31 | } from "./plugin"; 32 | 33 | export { 34 | VNodeChildren, 35 | VNodeChildrenArrayContents, 36 | VNode, 37 | VNodeComponentOptions, 38 | VNodeData, 39 | VNodeDirective 40 | } from "./vnode"; 41 | -------------------------------------------------------------------------------- /types/plugin.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue as _Vue } from "./vue"; 2 | 3 | export type PluginFunction = (Vue: typeof _Vue, options?: T) => void; 4 | 5 | export interface PluginObject { 6 | install: PluginFunction; 7 | [key: string]: any; 8 | } 9 | -------------------------------------------------------------------------------- /types/test/augmentation-test.ts: -------------------------------------------------------------------------------- 1 | import Vue from "../index"; 2 | 3 | declare module "../vue" { 4 | // add instance property and method 5 | interface Vue { 6 | $instanceProperty: string; 7 | $instanceMethod(): void; 8 | } 9 | 10 | // add static property and method 11 | interface VueConstructor { 12 | staticProperty: string; 13 | staticMethod(): void; 14 | } 15 | } 16 | 17 | // augment ComponentOptions 18 | declare module "../options" { 19 | interface ComponentOptions { 20 | foo?: string; 21 | } 22 | } 23 | 24 | const vm = new Vue({ 25 | props: ["bar"], 26 | data: { 27 | a: true 28 | }, 29 | foo: "foo", 30 | methods: { 31 | foo() { 32 | this.a = false; 33 | } 34 | }, 35 | computed: { 36 | BAR(): string { 37 | return this.bar.toUpperCase(); 38 | } 39 | } 40 | }); 41 | 42 | vm.$instanceProperty; 43 | vm.$instanceMethod(); 44 | 45 | Vue.staticProperty; 46 | Vue.staticMethod(); 47 | -------------------------------------------------------------------------------- /types/test/es-module.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /types/test/plugin-test.ts: -------------------------------------------------------------------------------- 1 | import Vue from "../index"; 2 | import { PluginFunction, PluginObject } from "../index"; 3 | 4 | class Option { 5 | prefix: string = ""; 6 | suffix: string = ""; 7 | } 8 | 9 | const plugin: PluginObject