├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .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 ├── examples ├── 1computed │ ├── computed.html │ ├── create-element.html │ └── watch.html ├── commits │ ├── app.js │ └── index.html ├── 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 ├── img ├── lifecycle.jpg ├── observer.jpg ├── vuex.jpg └── wx.jpg ├── package-lock.json ├── package.json ├── packages ├── vue-server-renderer │ ├── README.md │ ├── client-plugin.d.ts │ ├── index.js │ ├── package.json │ ├── server-plugin.d.ts │ └── types │ │ ├── index.d.ts │ │ ├── plugin.d.ts │ │ └── tsconfig.json ├── vue-template-compiler │ ├── README.md │ ├── browser.js │ ├── index.js │ └── package.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 ├── 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 │ ├── 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-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-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 │ │ ├── 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 │ ├── 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 │ ├── 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.js │ │ ├── error.js │ │ ├── nested-cache.js │ │ ├── promise-rejection.js │ │ ├── split.js │ │ ├── test.css │ │ ├── test.png │ │ └── test.woff2 │ ├── jasmine.json │ ├── ssr-basic-renderer.spec.js │ ├── ssr-bundle-render.spec.js │ ├── ssr-stream.spec.js │ ├── ssr-string.spec.js │ └── ssr-template.spec.js ├── unit │ ├── .eslintrc │ ├── 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 │ │ │ ├── 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 │ │ ├── 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 │ │ └── optimizer.spec.js │ │ ├── sfc │ │ └── sfc-parser.spec.js │ │ ├── util │ │ └── 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.json │ └── 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: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "flow-vue"], 3 | "plugins": ["transform-vue-jsx", "syntax-dynamic-import"], 4 | "ignore": [ 5 | "dist/*.js", 6 | "packages/**/*.js" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.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: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "plugins": [ 4 | "flowtype" 5 | ], 6 | "extends": [ 7 | "plugin:vue-libs/recommended", 8 | "plugin:flowtype/recommended" 9 | ], 10 | "globals": { 11 | "__WEEX__": true, 12 | "WXEnvironment": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # vue-source-analysis 3 | vue源码分析 4 | 5 | 最近在看vue源码,网上教程是很多,由于篇幅所限涉及细节太少。 6 | 7 | 这里是vue所有源码,每次提交针对一个问题去找实现代码,相对于网上大神的源码分析,我这里针对关键位置尽可能详细的注释,争取让小白也能看得懂。 8 | 9 | 欢迎加我QQ:631989611 一同探讨 10 | 这里前置知识: 11 | 12 | **Vue.js 的源码都在 src 目录下,其目录结构如下。** 13 | 14 | ```javascript 15 | src 16 | ├── compiler # 编译相关 17 | ├── core # 核心代码 18 | ├── platforms # 不同平台的支持 19 | ├── server # 服务端渲染 20 | ├── sfc # .vue 文件解析 21 | ├── shared # 共享代码 22 | 23 | ``` 24 | 25 | ``` 26 | **如果对你有帮助,欢迎打赏,感谢你对我的一点点肯定,将继续努力** 27 | ``` 28 | 29 | 30 | **Vue.js 生命周期** 31 | 32 | ![Vue.js 生命周期](https://github.com/dreamhuo/vue-source-analysis/blob/master/img/lifecycle.jpg) 33 | 34 | **watch实现** 35 | 36 | ![watch实现](https://github.com/dreamhuo/vue-source-analysis/blob/master/img/observer.jpg) 37 | 38 | **vuex实现** 39 | 40 | ![vuex实现](https://github.com/dreamhuo/vue-source-analysis/blob/master/img/vuex.jpg) 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /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 are 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 | -------------------------------------------------------------------------------- /examples/1computed/computed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | computed example 6 | 7 | 8 | 9 | 10 |
11 |
12 | {{name}} 13 |
14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/commits/app.js: -------------------------------------------------------------------------------- 1 | var apiURL = 'https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha=' 2 | 3 | /** 4 | * Actual demo 5 | */ 6 | 7 | var demo = new Vue({ 8 | 9 | el: '#demo', 10 | 11 | data: { 12 | branches: ['master', 'dev'], 13 | currentBranch: 'master', 14 | commits: null 15 | }, 16 | 17 | created: function () { 18 | this.fetchData() 19 | }, 20 | 21 | watch: { 22 | currentBranch: 'fetchData' 23 | }, 24 | 25 | filters: { 26 | truncate: function (v) { 27 | var newline = v.indexOf('\n') 28 | return newline > 0 ? v.slice(0, newline) : v 29 | }, 30 | formatDate: function (v) { 31 | return v.replace(/T|Z/g, ' ') 32 | } 33 | }, 34 | 35 | methods: { 36 | fetchData: function () { 37 | var xhr = new XMLHttpRequest() 38 | var self = this 39 | xhr.open('GET', apiURL + self.currentBranch) 40 | xhr.onload = function () { 41 | self.commits = JSON.parse(xhr.responseText) 42 | console.log(self.commits[0].html_url) 43 | } 44 | xhr.send() 45 | } 46 | } 47 | }) 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js tree view example 6 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 48 | 49 |

(You can double click on an item to turn it into a folder.)

50 | 51 | 52 |
    53 | 56 | 57 |
58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /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) => void; 12 | mixin: (mixin: Object) => void; 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 | // allow dynamic method registration 20 | [key: string]: any 21 | }; 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /img/lifecycle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamhuo/vue-source-analysis/c185318a02fb567ca0c8f278ca663069eb371415/img/lifecycle.jpg -------------------------------------------------------------------------------- /img/observer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamhuo/vue-source-analysis/c185318a02fb567ca0c8f278ca663069eb371415/img/observer.jpg -------------------------------------------------------------------------------- /img/vuex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamhuo/vue-source-analysis/c185318a02fb567ca0c8f278ca663069eb371415/img/vuex.jpg -------------------------------------------------------------------------------- /img/wx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamhuo/vue-source-analysis/c185318a02fb567ca0c8f278ca663069eb371415/img/wx.jpg -------------------------------------------------------------------------------- /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 | module.exports = require('./build') 17 | -------------------------------------------------------------------------------- /packages/vue-server-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-server-renderer", 3 | "version": "2.5.17-beta.0", 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 | 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 vue-loader@>=10.0, simply update vue-template-compiler.\n' + 14 | 'If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump ' + packageName + ' to the latest.\n' 15 | ) 16 | } 17 | 18 | module.exports = require('./build') 19 | -------------------------------------------------------------------------------- /packages/vue-template-compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-template-compiler", 3 | "version": "2.5.17-beta.0", 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 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/vuejs/vue.git" 12 | }, 13 | "keywords": [ 14 | "vue", 15 | "compiler" 16 | ], 17 | "author": "Evan You", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/vuejs/vue/issues" 21 | }, 22 | "homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-template-compiler#readme", 23 | "dependencies": { 24 | "he": "^1.1.0", 25 | "de-indent": "^1.0.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /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 | entries: resolve('src/entries'), 14 | sfc: resolve('src/sfc') 15 | } 16 | -------------------------------------------------------------------------------- /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 | var coreVersion = require('../package.json').version 2 | var weexVersion = require('../packages/weex-vue-framework/package.json').version 3 | var weexBaseVersion = weexVersion.match(/^[\d.]+/)[0] 4 | var 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/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 | // 解析模板字符串生成 AST 16 | const ast = parse(template.trim(), options) 17 | if (options.optimize !== false) { 18 | // 优化语法树 19 | optimize(ast, options) 20 | } 21 | // 生成代码 22 | const code = generate(ast, options) 23 | return { 24 | ast, 25 | render: code.render, 26 | staticRenderFns: code.staticRenderFns 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /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 | export function initAssetRegisters (Vue: GlobalAPI) { 7 | /** 8 | * Create asset registration methods. 9 | */ 10 | ASSET_TYPES.forEach(type => { 11 | Vue[type] = function ( 12 | id: string, 13 | definition: Function | Object 14 | ): Function | Object | void { 15 | if (!definition) { 16 | return this.options[type + 's'][id] 17 | } else { 18 | /* istanbul ignore if */ 19 | if (process.env.NODE_ENV !== 'production' && type === 'component') { 20 | validateComponentName(id) 21 | } 22 | if (type === 'component' && isPlainObject(definition)) { 23 | definition.name = definition.name || id 24 | definition = this.options._base.extend(definition) 25 | } 26 | if (type === 'directive' && typeof definition === 'function') { 27 | definition = { bind: definition, update: definition } 28 | } 29 | this.options[type + 's'][id] = definition 30 | return definition 31 | } 32 | } 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /src/core/global-api/mixin.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { mergeOptions } from '../util/index' 4 | 5 | export function initMixin (Vue: GlobalAPI) { 6 | Vue.mixin = function (mixin: Object) { 7 | this.options = mergeOptions(this.options, mixin) 8 | return this 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/core/global-api/use.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { toArray } from '../util/index' 4 | 5 | export function initUse (Vue: GlobalAPI) { 6 | Vue.use = function (plugin: Function | Object) { 7 | const installedPlugins = (this._installedPlugins || (this._installedPlugins = [])) 8 | if (installedPlugins.indexOf(plugin) > -1) { 9 | return this 10 | } 11 | 12 | // additional parameters 13 | const args = toArray(arguments, 1) 14 | args.unshift(this) 15 | if (typeof plugin.install === 'function') { 16 | plugin.install.apply(plugin, args) 17 | } else if (typeof plugin === 'function') { 18 | plugin.apply(null, args) 19 | } 20 | installedPlugins.push(plugin) 21 | return this 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /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 | Object.defineProperty(Vue.prototype, '$isServer', { 9 | get: isServerRendering 10 | }) 11 | 12 | Object.defineProperty(Vue.prototype, '$ssrContext', { 13 | get () { 14 | /* istanbul ignore next */ 15 | return this.$vnode && this.$vnode.ssrContext 16 | } 17 | }) 18 | 19 | // expose FunctionalRenderContext for ssr runtime helper installation 20 | Object.defineProperty(Vue, 'FunctionalRenderContext', { 21 | value: FunctionalRenderContext 22 | }) 23 | 24 | Vue.version = '__VERSION__' 25 | 26 | export default Vue 27 | -------------------------------------------------------------------------------- /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 | function Vue (options) { 9 | if (process.env.NODE_ENV !== 'production' && 10 | !(this instanceof Vue) 11 | ) { 12 | warn('Vue is a constructor and should be called with the `new` keyword') 13 | } 14 | this._init(options) 15 | } 16 | 17 | initMixin(Vue) 18 | stateMixin(Vue) 19 | eventsMixin(Vue) 20 | lifecycleMixin(Vue) 21 | renderMixin(Vue) 22 | 23 | export default Vue 24 | -------------------------------------------------------------------------------- /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/bind-object-props.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from 'core/config' 4 | 5 | import { 6 | warn, 7 | isObject, 8 | toObject, 9 | isReservedAttribute 10 | } from 'core/util/index' 11 | 12 | /** 13 | * Runtime helper for merging v-bind="object" into a VNode's data. 14 | */ 15 | export function bindObjectProps ( 16 | data: any, 17 | tag: string, 18 | value: any, 19 | asProp: boolean, 20 | isSync?: boolean 21 | ): VNodeData { 22 | if (value) { 23 | if (!isObject(value)) { 24 | process.env.NODE_ENV !== 'production' && warn( 25 | 'v-bind without argument expects an Object or Array value', 26 | this 27 | ) 28 | } else { 29 | if (Array.isArray(value)) { 30 | value = toObject(value) 31 | } 32 | let hash 33 | for (const key in value) { 34 | if ( 35 | key === 'class' || 36 | key === 'style' || 37 | isReservedAttribute(key) 38 | ) { 39 | hash = data 40 | } else { 41 | const type = data.attrs && data.attrs.type 42 | hash = asProp || config.mustUseProp(tag, type, key) 43 | ? data.domProps || (data.domProps = {}) 44 | : data.attrs || (data.attrs = {}) 45 | } 46 | if (!(key in hash)) { 47 | hash[key] = value[key] 48 | 49 | if (isSync) { 50 | const on = data.on || (data.on = {}) 51 | on[`update:${key}`] = function ($event) { 52 | value[key] = $event 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | return data 60 | } 61 | -------------------------------------------------------------------------------- /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-slots' 13 | 14 | export function installRenderHelpers (target: any) { 15 | target._o = markOnce 16 | target._n = toNumber // 数字 17 | target._s = toString // 字符串 18 | target._l = renderList // 列表 19 | target._t = renderSlot 20 | target._q = looseEqual 21 | target._i = looseIndexOf 22 | target._m = renderStatic 23 | target._f = resolveFilter 24 | target._k = checkKeyCodes 25 | target._b = bindObjectProps 26 | target._v = createTextVNode 27 | target._e = createEmptyVNode 28 | target._u = resolveScopedSlots 29 | target._g = bindObjectListeners 30 | } 31 | -------------------------------------------------------------------------------- /src/core/instance/render-helpers/render-list.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isObject, isDef } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for rendering v-for lists. 7 | */ 8 | export function renderList ( 9 | val: any, 10 | render: ( 11 | val: any, 12 | keyOrIndex: string | number, 13 | index?: number 14 | ) => VNode 15 | ): ?Array { 16 | let ret: ?Array, i, l, keys, key 17 | if (Array.isArray(val) || typeof val === 'string') { 18 | ret = new Array(val.length) 19 | for (i = 0, l = val.length; i < l; i++) { 20 | ret[i] = render(val[i], i) 21 | } 22 | } else if (typeof val === 'number') { 23 | ret = new Array(val) 24 | for (i = 0; i < val; i++) { 25 | ret[i] = render(i + 1, i) 26 | } 27 | } else if (isObject(val)) { 28 | keys = Object.keys(val) 29 | ret = new Array(keys.length) 30 | for (i = 0, l = keys.length; i < l; i++) { 31 | key = keys[i] 32 | ret[i] = render(val[key], key, i) 33 | } 34 | } 35 | if (isDef(ret)) { 36 | (ret: any)._isVList = true 37 | } 38 | return ret 39 | } 40 | -------------------------------------------------------------------------------- /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 | export function renderSlot ( 9 | name: string, 10 | fallback: ?Array, 11 | props: ?Object, 12 | bindObject: ?Object 13 | ): ?Array { 14 | const scopedSlotFn = this.$scopedSlots[name] 15 | let nodes 16 | if (scopedSlotFn) { // scoped slot 17 | props = props || {} 18 | if (bindObject) { 19 | if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) { 20 | warn( 21 | 'slot v-bind without argument expects an Object', 22 | this 23 | ) 24 | } 25 | props = extend(extend({}, bindObject), props) 26 | } 27 | nodes = scopedSlotFn(props) || fallback 28 | } else { 29 | const slotNodes = this.$slots[name] 30 | // warn duplicate slot usage 31 | if (slotNodes) { 32 | if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) { 33 | warn( 34 | `Duplicate presence of slot "${name}" found in the same render tree ` + 35 | `- this will likely cause render errors.`, 36 | this 37 | ) 38 | } 39 | slotNodes._rendered = true 40 | } 41 | nodes = slotNodes || fallback 42 | } 43 | 44 | const target = props && props.slot 45 | if (target) { 46 | return this.$createElement('template', { slot: target }, nodes) 47 | } else { 48 | return nodes 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /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/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 9 | export const arrayMethods = Object.create(arrayProto) 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 | methodsToPatch.forEach(function (method) { 25 | // cache original method 26 | const original = arrayProto[method] 27 | def(arrayMethods, method, function mutator (...args) { 28 | const result = original.apply(this, args) 29 | const ob = this.__ob__ 30 | let inserted 31 | switch (method) { 32 | case 'push': 33 | case 'unshift': 34 | inserted = args 35 | break 36 | case 'splice': 37 | inserted = args.slice(2) 38 | break 39 | } 40 | if (inserted) ob.observeArray(inserted) 41 | // notify change 42 | ob.dep.notify() 43 | return result 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /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 | // seenObjects 对象做为一个深层依赖收集的临时存储 8 | // 标识是否经过依赖收集 9 | const seenObjects = new Set() 10 | 11 | /** 12 | * Recursively traverse an object to evoke all converted 13 | * getters, so that every nested property inside the object 14 | * is collected as a "deep" dependency. 15 | */ 16 | export function traverse (val: any) { 17 | _traverse(val, seenObjects) 18 | seenObjects.clear() 19 | } 20 | 21 | function _traverse (val: any, seen: SimpleSet) { 22 | let i, keys 23 | const isA = Array.isArray(val) 24 | // 如果不是数组、不是对象、不可扩展、或是 vnode 直接 return 25 | // Object.isFrozen()方法判断一个对象是否被冻结。 26 | // 一个对象是冻结的是指它不可扩展,所有属性都是不可配置的,且所有数据属性(即没有getter或setter组件的访问器的属性)都是不可写的 27 | if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) { 28 | return 29 | } 30 | // 如果有 __ob__ 属性,则表示是一个响应式对象 31 | if (val.__ob__) { 32 | // 获取到 depId 判断 seenObjects 就否 has, 有则直接返回,没有则 add 进去 33 | const depId = val.__ob__.dep.id 34 | // 已经经过依赖收集就不再进行依赖收集 35 | if (seen.has(depId)) { 36 | return 37 | } 38 | // 没有经过依赖收集再进行依赖收集 39 | seen.add(depId) 40 | } 41 | // 如果是数组循环数组,若是对象遍历对象 42 | // 再调用 _traverse 获取到 depId add进 seenObjects 43 | if (isA) { 44 | i = val.length 45 | // 这里调用 val[i] 其实触发了对象的 get 方法 46 | while (i--) _traverse(val[i], seen) 47 | } else { 48 | keys = Object.keys(val) 49 | i = keys.length 50 | // 这里调用 val[i] 其实触发了对象的 get 方法 51 | while (i--) _traverse(val[keys[i]], seen) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/core/util/error.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import config from '../config' 4 | import { warn } from './debug' 5 | import { inBrowser, inWeex } from './env' 6 | 7 | export function handleError (err: Error, vm: any, info: string) { 8 | if (vm) { 9 | let cur = vm 10 | while ((cur = cur.$parent)) { 11 | const hooks = cur.$options.errorCaptured 12 | if (hooks) { 13 | for (let i = 0; i < hooks.length; i++) { 14 | try { 15 | const capture = hooks[i].call(cur, err, vm, info) === false 16 | if (capture) return 17 | } catch (e) { 18 | globalHandleError(e, cur, 'errorCaptured hook') 19 | } 20 | } 21 | } 22 | } 23 | } 24 | globalHandleError(err, vm, info) 25 | } 26 | 27 | function globalHandleError (err, vm, info) { 28 | if (config.errorHandler) { 29 | try { 30 | return config.errorHandler.call(null, err, vm, info) 31 | } catch (e) { 32 | logError(e, null, 'config.errorHandler') 33 | } 34 | } 35 | logError(err, vm, info) 36 | } 37 | 38 | function logError (err, vm, info) { 39 | if (process.env.NODE_ENV !== 'production') { 40 | warn(`Error in ${info}: "${err.toString()}"`, vm) 41 | } 42 | /* istanbul ignore else */ 43 | if ((inBrowser || inWeex) && typeof console !== 'undefined') { 44 | console.error(err) 45 | } else { 46 | throw err 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 | * Check if a string starts with $ or _ 5 | * 检测字符串是否以保留字符 $ or _ 开头 6 | */ 7 | export function isReserved (str: string): boolean { 8 | const c = (str + '').charCodeAt(0) 9 | return c === 0x24 || c === 0x5F 10 | } 11 | 12 | /** 13 | * Define a property. 14 | */ 15 | export function def (obj: Object, key: string, val: any, enumerable?: boolean) { 16 | Object.defineProperty(obj, key, { 17 | value: val, 18 | enumerable: !!enumerable, 19 | writable: true, 20 | configurable: true 21 | }) 22 | } 23 | 24 | /** 25 | * Parse simple path. 26 | */ 27 | // ^是正则表达式匹配字符串开始位置 28 | // $是正则表达式匹配字符串结束位置 29 | // \w 的释义都是指包含大小写字母数字和下划线 相当于([0-9a-zA-Z]) 30 | // [] 是定义匹配的字符范围 31 | // .匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。 32 | const bailRE = /[^\w.$]/ 33 | export function parsePath (path: string): any { 34 | if (bailRE.test(path)) { 35 | return 36 | } 37 | const segments = path.split('.') 38 | return function (obj) { 39 | for (let i = 0; i < segments.length; i++) { 40 | if (!obj) return 41 | obj = obj[segments[i]] 42 | } 43 | return obj 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/core/util/perf.js: -------------------------------------------------------------------------------- 1 | import { inBrowser } from './env' 2 | // inBrowser 通过判断是否存在 window 来判断是否在浏览器环境 3 | // export const inBrowser = typeof window !== 'undefined' 4 | 5 | export let mark 6 | export let measure 7 | 8 | // performance方法封装,mark创建性能标记点 9 | // measure计算两个性能标记点的耗时 10 | if (process.env.NODE_ENV !== 'production') { 11 | const perf = inBrowser && window.performance 12 | /* istanbul ignore if */ 13 | if ( 14 | perf && 15 | perf.mark && 16 | perf.measure && 17 | perf.clearMarks && 18 | perf.clearMeasures 19 | ) { 20 | mark = tag => perf.mark(tag) // 记录时刻间隔的毫秒数 21 | measure = (name, startTag, endTag) => { 22 | perf.measure(name, startTag, endTag) // 记录两个标记的时间间隔 23 | perf.clearMarks(startTag) // 清除开始标记和结束标记 24 | perf.clearMarks(endTag) 25 | perf.clearMeasures(name) // 清除指定记录间隔数据 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /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 | // vnode 里没有 ref 直接 return 什么也不做 22 | const key = vnode.data.ref 23 | if (!isDef(key)) return 24 | // vm 实例 25 | const vm = vnode.context 26 | const ref = vnode.componentInstance || vnode.elm // vnode.componentInstancef对应组件的真实 DOM 节点 ; vnode.elm 当前 vnode 节点对应的真实DOM节点 27 | // 获取当前 vm.$refs 28 | const refs = vm.$refs 29 | // isRemoval 为 true 即在 vm.$refs 上移除 30 | if (isRemoval) { 31 | if (Array.isArray(refs[key])) { 32 | remove(refs[key], ref) 33 | } else if (refs[key] === ref) { 34 | refs[key] = undefined 35 | } 36 | } else { 37 | // refInFor 是编译时加上的 38 | // 将 ref 加到 vm.$refs 上 39 | if (vnode.data.refInFor) { 40 | if (!Array.isArray(refs[key])) { 41 | refs[key] = [ref] 42 | } else if (refs[key].indexOf(ref) < 0) { 43 | // $flow-disable-line 44 | refs[key].push(ref) 45 | } 46 | } else { 47 | refs[key] = ref 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /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})`) 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})`) 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/class.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parseText } from 'compiler/parser/text-parser' 4 | import { 5 | getAndRemoveAttr, 6 | getBindingAttr, 7 | baseWarn 8 | } from 'compiler/helpers' 9 | 10 | function transformNode (el: ASTElement, options: CompilerOptions) { 11 | const warn = options.warn || baseWarn 12 | const staticClass = getAndRemoveAttr(el, 'class') 13 | if (process.env.NODE_ENV !== 'production' && staticClass) { 14 | const res = parseText(staticClass, options.delimiters) 15 | if (res) { 16 | warn( 17 | `class="${staticClass}": ` + 18 | 'Interpolation inside attributes has been removed. ' + 19 | 'Use v-bind or the colon shorthand instead. For example, ' + 20 | 'instead of
, use
.' 21 | ) 22 | } 23 | } 24 | if (staticClass) { 25 | el.staticClass = JSON.stringify(staticClass) 26 | } 27 | const classBinding = getBindingAttr(el, 'class', false /* getStatic */) 28 | if (classBinding) { 29 | el.classBinding = classBinding 30 | } 31 | } 32 | 33 | function genData (el: ASTElement): string { 34 | let data = '' 35 | if (el.staticClass) { 36 | data += `staticClass:${el.staticClass},` 37 | } 38 | if (el.classBinding) { 39 | data += `class:${el.classBinding},` 40 | } 41 | return data 42 | } 43 | 44 | export default { 45 | staticKeys: ['staticClass'], 46 | transformNode, 47 | genData 48 | } 49 | -------------------------------------------------------------------------------- /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/modules/style.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parseText } from 'compiler/parser/text-parser' 4 | import { parseStyleText } from 'web/util/style' 5 | import { 6 | getAndRemoveAttr, 7 | getBindingAttr, 8 | baseWarn 9 | } from 'compiler/helpers' 10 | 11 | function transformNode (el: ASTElement, options: CompilerOptions) { 12 | const warn = options.warn || baseWarn 13 | const staticStyle = getAndRemoveAttr(el, 'style') 14 | if (staticStyle) { 15 | /* istanbul ignore if */ 16 | if (process.env.NODE_ENV !== 'production') { 17 | const res = parseText(staticStyle, options.delimiters) 18 | if (res) { 19 | warn( 20 | `style="${staticStyle}": ` + 21 | 'Interpolation inside attributes has been removed. ' + 22 | 'Use v-bind or the colon shorthand instead. For example, ' + 23 | 'instead of
, use
.' 24 | ) 25 | } 26 | } 27 | el.staticStyle = JSON.stringify(parseStyleText(staticStyle)) 28 | } 29 | 30 | const styleBinding = getBindingAttr(el, 'style', false /* getStatic */) 31 | if (styleBinding) { 32 | el.styleBinding = styleBinding 33 | } 34 | } 35 | 36 | function genData (el: ASTElement): string { 37 | let data = '' 38 | if (el.staticStyle) { 39 | data += `staticStyle:${el.staticStyle},` 40 | } 41 | if (el.styleBinding) { 42 | data += `style:(${el.styleBinding}),` 43 | } 44 | return data 45 | } 46 | 47 | export default { 48 | staticKeys: ['staticStyle'], 49 | transformNode, 50 | genData 51 | } 52 | -------------------------------------------------------------------------------- /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 | export const isUnaryTag = makeMap( 6 | 'area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' + 7 | 'link,meta,param,source,track,wbr' 8 | ) 9 | 10 | // Elements that you can, intentionally, leave open 11 | // (and which close themselves) 12 | export const canBeLeftOpenTag = makeMap( 13 | 'colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source' 14 | ) 15 | 16 | // HTML5 tags https://html.spec.whatwg.org/multipage/indices.html#elements-3 17 | // Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content 18 | export const isNonPhrasingTag = makeMap( 19 | 'address,article,aside,base,blockquote,body,caption,col,colgroup,dd,' + 20 | 'details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,' + 21 | 'h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,' + 22 | 'optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,' + 23 | 'title,tr,track' 24 | ) 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | // create: updateClass 创建 class 3 | // update: updateClass 更新 class 4 | import { 5 | isDef, 6 | isUndef 7 | } from 'shared/util' 8 | 9 | import { 10 | concat, 11 | stringifyClass, 12 | genClassForVnode 13 | } from 'web/util/index' 14 | 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 | return 29 | } 30 | 31 | let cls = genClassForVnode(vnode) 32 | 33 | // handle transition classes 34 | const transitionClass = el._transitionClasses 35 | if (isDef(transitionClass)) { 36 | cls = concat(cls, stringifyClass(transitionClass)) 37 | } 38 | 39 | // set the class 40 | if (cls !== el._prevClass) { 41 | el.setAttribute('class', cls) 42 | el._prevClass = cls 43 | } 44 | } 45 | 46 | export default { 47 | create: updateClass, 48 | update: updateClass 49 | } 50 | -------------------------------------------------------------------------------- /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 | // node节点操作方法 4 | import * as nodeOps from 'web/runtime/node-ops' 5 | import { createPatchFunction } from 'core/vdom/patch' 6 | // baseModules返回一个数组 ref, directives更新操作工具方法 7 | import baseModules from 'core/vdom/modules/index' 8 | // platformModules返回一个数组, 定义了 attrs, klass, events, domProps, style, transition 的更新操作工具方法 9 | import platformModules from 'web/runtime/modules/index' 10 | 11 | // the directive module should be applied last, after all 12 | // built-in modules have been applied. 13 | // 合并所有DOM操作工具模块 14 | const modules = platformModules.concat(baseModules) 15 | 16 | // 函数createPatchFunction内会返回一个patch函数 17 | // patch函数接收4个参数 18 | // return function patch (oldVnode, vnode, hydrating, removeOnly) 19 | export const patch: Function = createPatchFunction({ nodeOps, modules }) 20 | -------------------------------------------------------------------------------- /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 15 |
16 |
17 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 |
35 |
36 | countA: {{countA}} 37 |
38 |
39 | countB: {{countB}} 40 |
41 |
42 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /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: -------------------------------------------------------------------------------- 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 | var e = document.createEvent('HTMLEvents') 3 | e.initEvent(event, true, true) 4 | if (process) process(e) 5 | target.dispatchEvent(e) 6 | } 7 | -------------------------------------------------------------------------------- /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 | entry: path.resolve(__dirname, 'fixtures', file), 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.js$/, 12 | loader: 'babel-loader' 13 | }, 14 | { 15 | test: /async-.*\.js$/, 16 | loader: require.resolve('./async-loader') 17 | }, 18 | { 19 | test: /\.(png|woff2|css)$/, 20 | loader: 'file-loader', 21 | options: { 22 | name: '[name].[ext]' 23 | } 24 | } 25 | ] 26 | } 27 | }, extraConfig) 28 | 29 | const compiler = webpack(config) 30 | const fs = new MemoryFS() 31 | compiler.outputFileSystem = fs 32 | 33 | compiler.run((err, stats) => { 34 | expect(err).toBeFalsy() 35 | expect(stats.errors).toBeFalsy() 36 | cb(fs) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /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 | module.exports = { 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.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) 23 | resolve(vm) 24 | }) 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /test/ssr/fixtures/test.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamhuo/vue-source-analysis/c185318a02fb567ca0c8f278ca663069eb371415/test/ssr/fixtures/test.css -------------------------------------------------------------------------------- /test/ssr/fixtures/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamhuo/vue-source-analysis/c185318a02fb567ca0c8f278ca663069eb371415/test/ssr/fixtures/test.png -------------------------------------------------------------------------------- /test/ssr/fixtures/test.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamhuo/vue-source-analysis/c185318a02fb567ca0c8f278ca663069eb371415/test/ssr/fixtures/test.woff2 -------------------------------------------------------------------------------- /test/ssr/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "test/ssr", 3 | "spec_files": [ 4 | "*.spec.js" 5 | ], 6 | "helpers": [ 7 | "../../node_modules/babel-register/lib/node.js" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 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 | } 14 | } 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/unit/features/directives/text.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | describe('Directive v-text', () => { 4 | it('should render text', () => { 5 | const vm = new Vue({ 6 | template: '
', 7 | data: { a: 'hello' } 8 | }).$mount() 9 | expect(vm.$el.innerHTML).toBe('hello') 10 | }) 11 | 12 | it('should encode html entities', () => { 13 | const vm = new Vue({ 14 | template: '
', 15 | data: { a: '' } 16 | }).$mount() 17 | expect(vm.$el.innerHTML).toBe('<foo>') 18 | }) 19 | 20 | it('should support all value types', done => { 21 | const vm = new Vue({ 22 | template: '
', 23 | data: { a: false } 24 | }).$mount() 25 | waitForUpdate(() => { 26 | expect(vm.$el.innerHTML).toBe('false') 27 | vm.a = [] 28 | }).then(() => { 29 | expect(vm.$el.innerHTML).toBe('[]') 30 | vm.a = {} 31 | }).then(() => { 32 | expect(vm.$el.innerHTML).toBe('{}') 33 | vm.a = 123 34 | }).then(() => { 35 | expect(vm.$el.innerHTML).toBe('123') 36 | vm.a = 0 37 | }).then(() => { 38 | expect(vm.$el.innerHTML).toBe('0') 39 | vm.a = ' ' 40 | }).then(() => { 41 | expect(vm.$el.innerHTML).toBe(' ') 42 | vm.a = ' ' 43 | }).then(() => { 44 | expect(vm.$el.innerHTML).toBe(' ') 45 | vm.a = null 46 | }).then(() => { 47 | expect(vm.$el.innerHTML).toBe('') 48 | vm.a = undefined 49 | }).then(() => { 50 | expect(vm.$el.innerHTML).toBe('') 51 | }).then(done) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /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/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/instance/render-proxy.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | if (typeof Proxy !== 'undefined') { 4 | describe('render proxy', () => { 5 | it('should warn missing property in render fns with `with`', () => { 6 | new Vue({ 7 | template: `
{{ a }}
` 8 | }).$mount() 9 | expect(`Property or method "a" is not defined`).toHaveBeenWarned() 10 | }) 11 | 12 | it('should warn missing property in render fns without `with`', () => { 13 | const render = function (h) { 14 | return h('div', [this.a]) 15 | } 16 | render._withStripped = true 17 | new Vue({ 18 | render 19 | }).$mount() 20 | expect(`Property or method "a" is not defined`).toHaveBeenWarned() 21 | }) 22 | 23 | it('should not warn for hand-written render functions', () => { 24 | new Vue({ 25 | render (h) { 26 | return h('div', [this.a]) 27 | } 28 | }).$mount() 29 | expect(`Property or method "a" is not defined`).not.toHaveBeenWarned() 30 | }) 31 | 32 | it('support symbols using the `in` operator in hand-written render functions', () => { 33 | const sym = Symbol() 34 | 35 | const vm = new Vue({ 36 | created () { 37 | this[sym] = 'foo' 38 | }, 39 | render (h) { 40 | if (sym in this) { 41 | return h('div', [this[sym]]) 42 | } 43 | } 44 | }).$mount() 45 | 46 | expect(vm.$el.textContent).toBe('foo') 47 | }) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /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 undefined methods', () => { 23 | new Vue({ 24 | methods: { 25 | hello: undefined 26 | } 27 | }) 28 | expect(`Method "hello" has an undefined value 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". Component names can only contain alphanumeric characters and the hyphen, and must start with a letter.`) 19 | .toHaveBeenWarned() 20 | /* eslint-enable */ 21 | 22 | Vue.extend({ 23 | name: '2Cool2BValid' 24 | }) 25 | 26 | /* eslint-disable */ 27 | expect(`Invalid component name: "2Cool2BValid". Component names can only contain alphanumeric characters and the hyphen, and must start with a letter.`) 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 | -------------------------------------------------------------------------------- /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 | var alias = require('../../scripts/alias') 2 | var webpack = require('webpack') 3 | 4 | var webpackConfig = { 5 | resolve: { 6 | alias: alias 7 | }, 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.js$/, 12 | loader: 'babel-loader', 13 | exclude: /node_modules/ 14 | } 15 | ] 16 | }, 17 | plugins: [ 18 | new webpack.DefinePlugin({ 19 | __WEEX__: false, 20 | 'process.env': { 21 | NODE_ENV: '"development"', 22 | TRANSITION_DURATION: process.env.CI ? 100 : 50, 23 | TRANSITION_BUFFER: 10 24 | } 25 | }) 26 | ], 27 | devtool: '#inline-source-map' 28 | } 29 | 30 | // shared config for all unit tests 31 | module.exports = { 32 | frameworks: ['jasmine'], 33 | files: [ 34 | './index.js' 35 | ], 36 | preprocessors: { 37 | './index.js': ['webpack', 'sourcemap'] 38 | }, 39 | webpack: webpackConfig, 40 | webpackMiddleware: { 41 | noInfo: true 42 | }, 43 | plugins: [ 44 | 'karma-jasmine', 45 | 'karma-mocha-reporter', 46 | 'karma-sourcemap-loader', 47 | 'karma-webpack' 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /test/unit/karma.cover.config.js: -------------------------------------------------------------------------------- 1 | var base = require('./karma.base.config.js') 2 | 3 | module.exports = function (config) { 4 | var options = Object.assign(base, { 5 | browsers: ['PhantomJS'], 6 | reporters: ['mocha', 'coverage'], 7 | coverageReporter: { 8 | reporters: [ 9 | { type: 'lcov', dir: '../../coverage', subdir: '.' }, 10 | { type: 'text-summary', dir: '../../coverage', subdir: '.' } 11 | ] 12 | }, 13 | singleRun: true, 14 | plugins: base.plugins.concat([ 15 | 'karma-coverage', 16 | 'karma-phantomjs-launcher' 17 | ]) 18 | }) 19 | 20 | // add babel-plugin-istanbul for code instrumentation 21 | options.webpack.module.rules[0].options = { 22 | plugins: [['istanbul', { 23 | exclude: [ 24 | 'test/', 25 | 'src/compiler/parser/html-parser.js', 26 | 'src/core/instance/proxy.js', 27 | 'src/sfc/deindent.js', 28 | 'src/platforms/weex/' 29 | ] 30 | }]] 31 | } 32 | 33 | config.set(options) 34 | } 35 | -------------------------------------------------------------------------------- /test/unit/karma.dev.config.js: -------------------------------------------------------------------------------- 1 | var base = require('./karma.base.config.js') 2 | 3 | module.exports = function (config) { 4 | config.set(Object.assign(base, { 5 | browsers: ['PhantomJS'], 6 | reporters: ['progress'], 7 | plugins: base.plugins.concat([ 8 | 'karma-phantomjs-launcher' 9 | ]) 10 | })) 11 | } 12 | -------------------------------------------------------------------------------- /test/unit/karma.unit.config.js: -------------------------------------------------------------------------------- 1 | var 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/optimizer.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamhuo/vue-source-analysis/c185318a02fb567ca0c8f278ca663069eb371415/test/unit/modules/server-compiler/optimizer.spec.js -------------------------------------------------------------------------------- /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.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "test/weex", 3 | "spec_files": [ 4 | "**/*[sS]pec.js" 5 | ], 6 | "helpers": [ 7 | "../../node_modules/babel-register/lib/node.js" 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 | PropOptions, 19 | ComputedOptions, 20 | WatchHandler, 21 | WatchOptions, 22 | WatchOptionsWithHandler, 23 | DirectiveFunction, 24 | DirectiveOptions 25 | } from "./options"; 26 | 27 | export { 28 | PluginFunction, 29 | PluginObject 30 | } from "./plugin"; 31 | 32 | export { 33 | VNodeChildren, 34 | VNodeChildrenArrayContents, 35 | VNode, 36 | VNodeComponentOptions, 37 | VNodeData, 38 | VNodeDirective 39 | } from "./vnode"; 40 | -------------------------------------------------------------------------------- /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